Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4dc81d0

Browse files
authored
Python 3.12 compatibility (#236)
Add compatibility for Python 3.12 and some misc code cleanup
1 parentc80f94d commit4dc81d0

20 files changed

+73
-60
lines changed

‎.github/workflows/test-src.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on:ubuntu-latest
1616
strategy:
1717
matrix:
18-
python-version:["3.9", "3.10", "3.11"]
18+
python-version:["3.9", "3.10", "3.11", "3.12"]
1919
steps:
2020
-uses:actions/checkout@v4
2121
-name:Use Python ${{ matrix.python-version }}

‎CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ Using the following categories, list your changes in this order:
3434

3535
##[Unreleased]
3636

37-
- Nothing (yet)!
37+
###Added
38+
39+
- Python 3.12 compatibility
3840

3941
##[3.8.0] - 2024-02-20
4042

@@ -50,7 +52,7 @@ Using the following categories, list your changes in this order:
5052

5153
###Changed
5254

53-
- Simplified code for cascading deletion ofUserData.
55+
- Simplified code for cascading deletion ofuser data.
5456

5557
##[3.7.0] - 2024-01-30
5658

‎docs/examples/python/configure-asgi-middleware.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111
application=ProtocolTypeRouter(
1212
{
1313
"http":django_asgi_app,
14-
"websocket":AuthMiddlewareStack(
15-
URLRouter(
16-
[REACTPY_WEBSOCKET_ROUTE],
17-
)
18-
),
14+
"websocket":AuthMiddlewareStack(URLRouter([REACTPY_WEBSOCKET_ROUTE])),
1915
}
2016
)

‎docs/examples/python/use-channel-layer.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,20 @@
33

44

55
@component
6-
defmy_sender_component():
7-
sender=use_channel_layer("my-channel-name")
6+
defmy_component():
7+
asyncdefreceive_message(message):
8+
set_message(message["text"])
89

9-
asyncdefsubmit_event(event):
10+
asyncdefsend_message(event):
1011
ifevent["key"]=="Enter":
1112
awaitsender({"text":event["target"]["value"]})
1213

13-
returnhtml.div(
14-
"Message Sender: ",
15-
html.input({"type":"text","onKeyDown":submit_event}),
16-
)
17-
18-
19-
@component
20-
defmy_receiver_component():
2114
message,set_message=hooks.use_state("")
15+
sender=use_channel_layer("my-channel-name",receiver=receive_message)
2216

23-
asyncdefreceive_event(message):
24-
set_message(message["text"])
25-
26-
use_channel_layer("my-channel-name",receiver=receive_event)
27-
28-
returnhtml.div(f"Message Receiver:{message}")
17+
returnhtml.div(
18+
f"Received:{message}",
19+
html.br(),
20+
"Send: ",
21+
html.input({"type":"text","onKeyDown":send_message}),
22+
)

‎docs/examples/python/user-passes-test-component-fallback.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ def my_component_fallback():
77
returnhtml.div("I am NOT logged in!")
88

99

10-
defauth_check(user):
10+
defis_authenticated(user):
1111
returnuser.is_authenticated
1212

1313

14-
@user_passes_test(auth_check,fallback=my_component_fallback)
14+
@user_passes_test(is_authenticated,fallback=my_component_fallback)
1515
@component
1616
defmy_component():
1717
returnhtml.div("I am logged in!")

‎docs/examples/python/user-passes-test-vdom-fallback.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
fromreactpy_django.decoratorsimportuser_passes_test
33

44

5-
defauth_check(user):
5+
defis_authenticated(user):
66
returnuser.is_authenticated
77

88

9-
@user_passes_test(auth_check,fallback=html.div("I am NOT logged in!"))
9+
@user_passes_test(is_authenticated,fallback=html.div("I am NOT logged in!"))
1010
@component
1111
defmy_component():
1212
returnhtml.div("I am logged in!")

‎docs/examples/python/user-passes-test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
fromreactpy_django.decoratorsimportuser_passes_test
33

44

5-
defauth_check(user):
5+
defis_authenticated(user):
66
returnuser.is_authenticated
77

88

9-
@user_passes_test(auth_check)
9+
@user_passes_test(is_authenticated)
1010
@component
1111
defmy_component():
1212
returnhtml.div("I am logged in!")

‎docs/src/reference/hooks.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,15 +536,15 @@ This is useful when used in combination with [`#!python use_channel_layer`](#use
536536

537537
??? example "See Interface"
538538

539-
<font size="4">**Parameters**</font>
539+
<font size="4">**Parameters**</font>
540540

541-
`#!python None`
541+
`#!python None`
542542

543-
<font size="4">**Returns**</font>
543+
<font size="4">**Returns**</font>
544544

545-
| Type | Description |
546-
| --- | --- |
547-
| `#!python str` | A string containing the root component's `#!python id`. |
545+
| Type | Description |
546+
| --- | --- |
547+
| `#!python str` | A string containing the root component's `#!python id`. |
548548

549549
---
550550

‎docs/src/reference/management-commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ReactPy exposes Django management commands that can be used to perform various R
1212

1313
Command used to manually clean ReactPy data.
1414

15-
When using this command without arguments, it will perform all cleaning operations. You canspecify only performing specific cleaning operations through arguments such as`--sessions`.
15+
When using this command without arguments, it will perform all cleaning operations. You canlimit cleaning to specific operations through arguments such as`--sessions`.
1616

1717
!!! example "Terminal"
1818

‎docs/src/reference/settings.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ This setting is incompatible with [`daphne`](https://github.com/django/daphne).
131131

132132
The default host(s) that can render your ReactPy components.
133133

134-
ReactPy will use these hosts in a round-robin fashion, allowing for easy distributed computing.
134+
ReactPy will use these hosts in a round-robin fashion, allowing for easy distributed computing. This is typically useful for self-hosted applications.
135135

136136
You can use the`#!python host` argument in your[template tag](../reference/template-tag.md#component) to manually override this default.
137137

@@ -147,9 +147,10 @@ Configures whether to pre-render your components via HTTP, which enables SEO com
147147

148148
During pre-rendering, there are some key differences in behavior:
149149

150-
1. Only the component's firstrender is pre-rendered.
150+
1. Only the component's firstpaint is pre-rendered.
151151
2. All[`connection` hooks](https://reactive-python.github.io/reactpy-django/latest/reference/hooks/#connection-hooks) will provide HTTP variants.
152152
3. The component will be non-interactive until a WebSocket connection is formed.
153+
4. The component is re-rendered once a WebSocket connection is formed.
153154

154155
<!-- TODO: The comment below will become true when ReactPy no longer strips scripts from the DOM-->
155156
<!-- 4. `#!python html.script` elements are executed twice (pre-render and post-render).-->

‎pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ warn_redundant_casts = true
1010
warn_unused_ignores =true
1111
check_untyped_defs =true
1212

13-
[tool.ruff.isort]
13+
[tool.ruff.lint.isort]
1414
known-first-party = ["src","tests"]
1515

16-
[tool.ruff]
16+
[tool.ruff.lint]
1717
ignore = ["E501"]
18+
19+
[tool.ruff]
1820
extend-exclude = ["*/migrations/*",".venv/*",".eggs/*",".nox/*","build/*"]
1921
line-length =120

‎setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"Programming Language :: Python :: 3.9",
5151
"Programming Language :: Python :: 3.10",
5252
"Programming Language :: Python :: 3.11",
53+
"Programming Language :: Python :: 3.12",
5354
"Operating System :: OS Independent",
5455
"Intended Audience :: Developers",
5556
"Intended Audience :: Science/Research",

‎src/reactpy_django/hooks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
)
1616
fromuuidimportuuid4
1717

18-
importorjsonaspickle
18+
importorjson
1919
fromchannelsimportDEFAULT_CHANNEL_LAYER
2020
fromchannels.dbimportdatabase_sync_to_async
2121
fromchannels.layersimportInMemoryChannelLayer,get_channel_layer
@@ -351,7 +351,7 @@ async def _set_user_data(data: dict):
351351

352352
pk=get_pk(user)
353353
model,_=awaitUserDataModel.objects.aget_or_create(user_pk=pk)
354-
model.data=pickle.dumps(data)
354+
model.data=orjson.dumps(data)
355355
awaitmodel.asave()
356356

357357
query:Query[dict|None]=use_query(
@@ -471,7 +471,7 @@ async def _get_user_data(
471471

472472
pk=get_pk(user)
473473
model,_=awaitUserDataModel.objects.aget_or_create(user_pk=pk)
474-
data=pickle.loads(model.data)ifmodel.dataelse {}
474+
data=orjson.loads(model.data)ifmodel.dataelse {}
475475

476476
ifnotisinstance(data,dict):
477477
raiseTypeError(f"Expected dict while loading user data, got{type(data)}")
@@ -489,7 +489,7 @@ async def _get_user_data(
489489
data[key]=new_value
490490
changed=True
491491
ifchanged:
492-
model.data=pickle.dumps(data)
492+
model.data=orjson.dumps(data)
493493
ifsave_default_data:
494494
awaitmodel.asave()
495495

‎src/reactpy_django/http/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
urlpatterns= [
88
path(
99
"web_module/<path:file>",
10-
views.web_modules_file,# type: ignore[arg-type]
10+
views.web_modules_file,
1111
name="web_modules",
1212
),
1313
path(
1414
"iframe/<str:dotted_path>",
15-
views.view_to_iframe,# type: ignore[arg-type]
15+
views.view_to_iframe,
1616
name="view_to_iframe",
1717
),
1818
]

‎src/reactpy_django/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
fromdjango.db.models.signalsimportpre_delete
44
fromdjango.dispatchimportreceiver
55

6+
fromreactpy_django.utilsimportget_pk
7+
68

79
classComponentSession(models.Model):
810
"""A model for storing component sessions."""
@@ -41,6 +43,6 @@ class UserDataModel(models.Model):
4143
@receiver(pre_delete,sender=get_user_model(),dispatch_uid="reactpy_delete_user_data")
4244
defdelete_user_data(sender,instance,**kwargs):
4345
"""Delete ReactPy's `UserDataModel` when a Django `User` is deleted."""
44-
pk=getattr(instance,instance._meta.pk.name)
46+
pk=get_pk(instance)
4547

4648
UserDataModel.objects.filter(user_pk=pk).delete()

‎src/reactpy_django/router/resolvers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]:
5252
pattern+=f"{re.escape(path[last_match_end:])}$"
5353

5454
# Replace literal `*` with "match anything" regex pattern, if it's at the end of the path
55-
ifpattern.endswith("\*$"):
55+
ifpattern.endswith(r"\*$"):
5656
pattern=f"{pattern[:-3]}.*$"
5757

5858
returnre.compile(pattern),converters

‎src/reactpy_django/templatetags/reactpy.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__importannotations
22

3-
fromdistutils.utilimportstrtobool
43
fromloggingimportgetLogger
54
fromuuidimportuuid4
65

@@ -22,7 +21,7 @@
2221
OfflineComponentMissing,
2322
)
2423
fromreactpy_django.typesimportComponentParams
25-
fromreactpy_django.utilsimportSyncLayout,validate_component_args
24+
fromreactpy_django.utilsimportSyncLayout,strtobool,validate_component_args
2625

2726
try:
2827
RESOLVED_WEB_MODULES_PATH=reverse("reactpy:web_modules",args=["/"]).strip("/")

‎src/reactpy_django/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,19 @@ def render(self):
366366
defget_pk(model):
367367
"""Returns the value of the primary key for a Django model."""
368368
returngetattr(model,model._meta.pk.name)
369+
370+
371+
defstrtobool(val):
372+
"""Convert a string representation of truth to true (1) or false (0).
373+
374+
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
375+
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
376+
'val' is anything else.
377+
"""
378+
val=val.lower()
379+
ifvalin ("y","yes","t","true","on","1"):
380+
return1
381+
elifvalin ("n","no","f","false","off","0"):
382+
return0
383+
else:
384+
raiseValueError("invalid truth value %r"% (val,))

‎tests/test_app/tests/test_components.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
importos
33
importsocket
44
importsys
5-
fromdistutils.utilimportstrtobool
65
fromfunctoolsimportpartial
76
fromtimeimportsleep
87

@@ -14,6 +13,7 @@
1413
fromdjango.test.utilsimportmodify_settings
1514
fromplaywright.sync_apiimportTimeoutError,sync_playwright
1615
fromreactpy_django.modelsimportComponentSession
16+
fromreactpy_django.utilsimportstrtobool
1717

1818
GITHUB_ACTIONS=os.getenv("GITHUB_ACTIONS","False")
1919
CLICK_DELAY=250ifstrtobool(GITHUB_ACTIONS)else25# Delay in miliseconds.
@@ -628,7 +628,7 @@ def test_url_router(self):
628628
path.get_attribute("data-path"),
629629
)
630630
string=new_page.query_selector("#router-string")
631-
self.assertEquals("Path 12",string.text_content())
631+
self.assertEqual("Path 12",string.text_content())
632632

633633
finally:
634634
new_page.close()

‎tests/test_app/tests/test_regex.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,25 +99,25 @@ def test_comment_regex(self):
9999
self.assertNotRegex(r'{% component "my.component" %}',COMMENT_REGEX)
100100

101101
# Components surrounded by comments
102-
self.assertEquals(
102+
self.assertEqual(
103103
COMMENT_REGEX.sub(
104104
"",r'{% component "my.component" %} <!-- comment -->'
105105
).strip(),
106106
'{% component "my.component" %}',
107107
)
108-
self.assertEquals(
108+
self.assertEqual(
109109
COMMENT_REGEX.sub(
110110
"",r'<!-- comment --> {% component "my.component" %}'
111111
).strip(),
112112
'{% component "my.component" %}',
113113
)
114-
self.assertEquals(
114+
self.assertEqual(
115115
COMMENT_REGEX.sub(
116116
"",r'<!-- comment --> {% component "my.component" %} <!-- comment -->'
117117
).strip(),
118118
'{% component "my.component" %}',
119119
)
120-
self.assertEquals(
120+
self.assertEqual(
121121
COMMENT_REGEX.sub(
122122
"",
123123
r"""<!-- comment
@@ -130,11 +130,11 @@ def test_comment_regex(self):
130130
)
131131

132132
# Components surrounded by comments
133-
self.assertEquals(
133+
self.assertEqual(
134134
COMMENT_REGEX.sub("",r'<!-- {% component "my.component" %} -->'),
135135
"",
136136
)
137-
self.assertEquals(
137+
self.assertEqual(
138138
COMMENT_REGEX.sub(
139139
"",
140140
r"""<!--

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp