Skip to content

Commit bbe906b

Browse files
committed
Handle most of the ruff errors in src/
1 parent c5b5c81 commit bbe906b

27 files changed

+327
-443
lines changed

.github/workflows/test-python.yml

+15
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,18 @@ jobs:
3131
run: hatch test --python ${{ matrix.python-version }} --ds=test_app.settings_single_db -v
3232
- name: Run Multi-DB Tests
3333
run: hatch test --python ${{ matrix.python-version }} --ds=test_app.settings_multi_db -v
34+
35+
python-formatting:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- uses: actions/checkout@v4
39+
- uses: oven-sh/setup-bun@v2
40+
with:
41+
bun-version: latest
42+
- uses: actions/setup-python@v5
43+
with:
44+
python-version: 3.x
45+
- name: Install Python Dependencies
46+
run: pip install --upgrade pip hatch uv
47+
- name: Check Python formatting
48+
run: hatch fmt src tests --check

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Don't forget to remove deprecated code on each major release!
2626
### Changed
2727

2828
- Set upper limit on ReactPy version to `<2.0.0`.
29+
- ReactPy web modules are now streamed in chunks.
30+
- ReactPy web modules are now streamed using asychronous file reading to improve performance.
31+
- Performed refactoring to utilize `ruff` as this repository's linter.
2932

3033
## [5.1.0] - 2024-11-24
3134

pyproject.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ lint.extend-ignore = [
210210
"SLF001", # Private member accessed
211211
"E501", # Line too long
212212
"PLC0415", # `import` should be at the top-level of a file
213+
"BLE001", # Do not catch blind exception: `Exception`
214+
"PLW0603", # Using global statement is discouraged
215+
"PLR6301", # Method could be a function, class method, or static method
216+
"S403", # `dill` module is possibly insecure
217+
"S301", # `dill` deserialization is possibly insecure unless using trusted data
213218
]
214219
lint.preview = true
215-
lint.isort.known-first-party = ["src", "tests"]
220+
lint.isort.known-first-party = ["reactpy_django", "test_app"]
221+
lint.isort.known-third-party = ["js"]

src/build_scripts/copy_dir.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa: INP001
12
import shutil
23
import sys
34
from pathlib import Path
@@ -17,15 +18,13 @@ def copy_files(source: Path, destination: Path) -> None:
1718

1819
if __name__ == "__main__":
1920
if len(sys.argv) != 3:
20-
print("Usage: python copy_dir.py <source_dir> <destination>")
2121
sys.exit(1)
2222

2323
root_dir = Path(__file__).parent.parent.parent
2424
src = Path(root_dir / sys.argv[1])
2525
dest = Path(root_dir / sys.argv[2])
2626

2727
if not src.exists():
28-
print(f"Source directory {src} does not exist")
2928
sys.exit(1)
3029

3130
copy_files(src, dest)

src/reactpy_django/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
__version__ = "5.1.0"
1717
__all__ = [
1818
"REACTPY_WEBSOCKET_ROUTE",
19-
"html",
20-
"hooks",
2119
"components",
2220
"decorators",
21+
"hooks",
22+
"html",
23+
"router",
2324
"types",
2425
"utils",
25-
"router",
2626
]
2727

2828
# Fixes bugs with REACTPY_BACKHAUL_THREAD + built-in asyncio event loops.

src/reactpy_django/checks.py

+27-71
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,16 @@ def reactpy_warnings(app_configs, **kwargs):
1717
from reactpy_django.config import REACTPY_FAILED_COMPONENTS
1818

1919
warnings = []
20-
INSTALLED_APPS: list[str] = getattr(settings, "INSTALLED_APPS", [])
20+
installed_apps: list[str] = getattr(settings, "INSTALLED_APPS", [])
2121

2222
# Check if REACTPY_DATABASE is not an in-memory database.
2323
if (
24-
getattr(settings, "DATABASES", {})
25-
.get(getattr(settings, "REACTPY_DATABASE", "default"), {})
26-
.get("NAME", None)
24+
getattr(settings, "DATABASES", {}).get(getattr(settings, "REACTPY_DATABASE", "default"), {}).get("NAME", None)
2725
== ":memory:"
2826
):
2927
warnings.append(
3028
Warning(
31-
"Using ReactPy with an in-memory database can cause unexpected "
32-
"behaviors.",
29+
"Using ReactPy with an in-memory database can cause unexpected behaviors.",
3330
hint="Configure settings.py:DATABASES[REACTPY_DATABASE], to use a "
3431
"multiprocessing and thread safe database.",
3532
id="reactpy_django.W001",
@@ -52,14 +49,12 @@ def reactpy_warnings(app_configs, **kwargs):
5249
)
5350

5451
# Warn if REACTPY_BACKHAUL_THREAD is set to True with Daphne
55-
if (
56-
sys.argv[0].endswith("daphne")
57-
or ("runserver" in sys.argv and "daphne" in INSTALLED_APPS)
58-
) and getattr(settings, "REACTPY_BACKHAUL_THREAD", False):
52+
if (sys.argv[0].endswith("daphne") or ("runserver" in sys.argv and "daphne" in installed_apps)) and getattr(
53+
settings, "REACTPY_BACKHAUL_THREAD", False
54+
):
5955
warnings.append(
6056
Warning(
61-
"Unstable configuration detected. REACTPY_BACKHAUL_THREAD is enabled "
62-
"and you running with Daphne.",
57+
"Unstable configuration detected. REACTPY_BACKHAUL_THREAD is enabled and you running with Daphne.",
6358
hint="Set settings.py:REACTPY_BACKHAUL_THREAD to False or use a different web server.",
6459
id="reactpy_django.W003",
6560
)
@@ -79,10 +74,8 @@ def reactpy_warnings(app_configs, **kwargs):
7974
if REACTPY_FAILED_COMPONENTS:
8075
warnings.append(
8176
Warning(
82-
"ReactPy failed to register the following components:\n\t+ "
83-
+ "\n\t+ ".join(REACTPY_FAILED_COMPONENTS),
84-
hint="Check if these paths are valid, or if an exception is being "
85-
"raised during import.",
77+
"ReactPy failed to register the following components:\n\t+ " + "\n\t+ ".join(REACTPY_FAILED_COMPONENTS),
78+
hint="Check if these paths are valid, or if an exception is being raised during import.",
8679
id="reactpy_django.W005",
8780
)
8881
)
@@ -106,10 +99,8 @@ def reactpy_warnings(app_configs, **kwargs):
10699

107100
# Check if REACTPY_URL_PREFIX is being used properly in our HTTP URLs
108101
with contextlib.suppress(NoReverseMatch):
109-
full_path = reverse("reactpy:web_modules", kwargs={"file": "example"}).strip(
110-
"/"
111-
)
112-
reactpy_http_prefix = f'{full_path[: full_path.find("web_module/")].strip("/")}'
102+
full_path = reverse("reactpy:web_modules", kwargs={"file": "example"}).strip("/")
103+
reactpy_http_prefix = f"{full_path[: full_path.find('web_module/')].strip('/')}"
113104
if reactpy_http_prefix != config.REACTPY_URL_PREFIX:
114105
warnings.append(
115106
Warning(
@@ -138,9 +129,7 @@ def reactpy_warnings(app_configs, **kwargs):
138129
)
139130

140131
# Check if `daphne` is not in installed apps when using `runserver`
141-
if "runserver" in sys.argv and "daphne" not in getattr(
142-
settings, "INSTALLED_APPS", []
143-
):
132+
if "runserver" in sys.argv and "daphne" not in getattr(settings, "INSTALLED_APPS", []):
144133
warnings.append(
145134
Warning(
146135
"You have not configured the `runserver` command to use ASGI. "
@@ -153,10 +142,7 @@ def reactpy_warnings(app_configs, **kwargs):
153142
# DELETED W013: Check if deprecated value REACTPY_RECONNECT_MAX exists
154143

155144
# Check if REACTPY_RECONNECT_INTERVAL is set to a large value
156-
if (
157-
isinstance(config.REACTPY_RECONNECT_INTERVAL, int)
158-
and config.REACTPY_RECONNECT_INTERVAL > 30000
159-
):
145+
if isinstance(config.REACTPY_RECONNECT_INTERVAL, int) and config.REACTPY_RECONNECT_INTERVAL > 30000:
160146
warnings.append(
161147
Warning(
162148
"REACTPY_RECONNECT_INTERVAL is set to >30 seconds. Are you sure this is intentional? "
@@ -167,10 +153,7 @@ def reactpy_warnings(app_configs, **kwargs):
167153
)
168154

169155
# Check if REACTPY_RECONNECT_MAX_RETRIES is set to a large value
170-
if (
171-
isinstance(config.REACTPY_RECONNECT_MAX_RETRIES, int)
172-
and config.REACTPY_RECONNECT_MAX_RETRIES > 5000
173-
):
156+
if isinstance(config.REACTPY_RECONNECT_MAX_RETRIES, int) and config.REACTPY_RECONNECT_MAX_RETRIES > 5000:
174157
warnings.append(
175158
Warning(
176159
"REACTPY_RECONNECT_MAX_RETRIES is set to a very large value "
@@ -204,18 +187,12 @@ def reactpy_warnings(app_configs, **kwargs):
204187
and config.REACTPY_RECONNECT_MAX_INTERVAL > 0
205188
and config.REACTPY_RECONNECT_MAX_RETRIES > 0
206189
and config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER > 1
207-
and (
208-
config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER
209-
** config.REACTPY_RECONNECT_MAX_RETRIES
210-
)
190+
and (config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER**config.REACTPY_RECONNECT_MAX_RETRIES)
211191
* config.REACTPY_RECONNECT_INTERVAL
212192
< config.REACTPY_RECONNECT_MAX_INTERVAL
213193
):
214194
max_value = math.floor(
215-
(
216-
config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER
217-
** config.REACTPY_RECONNECT_MAX_RETRIES
218-
)
195+
(config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER**config.REACTPY_RECONNECT_MAX_RETRIES)
219196
* config.REACTPY_RECONNECT_INTERVAL
220197
)
221198
warnings.append(
@@ -229,13 +206,10 @@ def reactpy_warnings(app_configs, **kwargs):
229206

230207
# Check if 'reactpy_django' is in the correct position in INSTALLED_APPS
231208
position_to_beat = 0
232-
for app in INSTALLED_APPS:
209+
for app in installed_apps:
233210
if app.startswith("django.contrib."):
234-
position_to_beat = INSTALLED_APPS.index(app)
235-
if (
236-
"reactpy_django" in INSTALLED_APPS
237-
and INSTALLED_APPS.index("reactpy_django") < position_to_beat
238-
):
211+
position_to_beat = installed_apps.index(app)
212+
if "reactpy_django" in installed_apps and installed_apps.index("reactpy_django") < position_to_beat:
239213
warnings.append(
240214
Warning(
241215
"The position of 'reactpy_django' in INSTALLED_APPS is suspicious.",
@@ -276,17 +250,13 @@ def reactpy_errors(app_configs, **kwargs):
276250
)
277251

278252
# DATABASE_ROUTERS is properly configured when REACTPY_DATABASE is defined
279-
if getattr(
280-
settings, "REACTPY_DATABASE", None
281-
) and "reactpy_django.database.Router" not in getattr(
253+
if getattr(settings, "REACTPY_DATABASE", None) and "reactpy_django.database.Router" not in getattr(
282254
settings, "DATABASE_ROUTERS", []
283255
):
284256
errors.append(
285257
Error(
286-
"ReactPy database has been changed but the database router is "
287-
"not configured.",
288-
hint="Set settings.py:DATABASE_ROUTERS to "
289-
"['reactpy_django.database.Router', ...]",
258+
"ReactPy database has been changed but the database router is not configured.",
259+
hint="Set settings.py:DATABASE_ROUTERS to ['reactpy_django.database.Router', ...]",
290260
id="reactpy_django.E002",
291261
)
292262
)
@@ -336,9 +306,7 @@ def reactpy_errors(app_configs, **kwargs):
336306
)
337307

338308
# Check if REACTPY_DEFAULT_QUERY_POSTPROCESSOR is a valid data type
339-
if not isinstance(
340-
getattr(settings, "REACTPY_DEFAULT_QUERY_POSTPROCESSOR", ""), (str, type(None))
341-
):
309+
if not isinstance(getattr(settings, "REACTPY_DEFAULT_QUERY_POSTPROCESSOR", ""), (str, type(None))):
342310
errors.append(
343311
Error(
344312
"Invalid type for REACTPY_DEFAULT_QUERY_POSTPROCESSOR.",
@@ -397,10 +365,7 @@ def reactpy_errors(app_configs, **kwargs):
397365
)
398366

399367
# Check if REACTPY_RECONNECT_INTERVAL is a positive integer
400-
if (
401-
isinstance(config.REACTPY_RECONNECT_INTERVAL, int)
402-
and config.REACTPY_RECONNECT_INTERVAL < 0
403-
):
368+
if isinstance(config.REACTPY_RECONNECT_INTERVAL, int) and config.REACTPY_RECONNECT_INTERVAL < 0:
404369
errors.append(
405370
Error(
406371
"Invalid value for REACTPY_RECONNECT_INTERVAL.",
@@ -420,10 +385,7 @@ def reactpy_errors(app_configs, **kwargs):
420385
)
421386

422387
# Check if REACTPY_RECONNECT_MAX_INTERVAL is a positive integer
423-
if (
424-
isinstance(config.REACTPY_RECONNECT_MAX_INTERVAL, int)
425-
and config.REACTPY_RECONNECT_MAX_INTERVAL < 0
426-
):
388+
if isinstance(config.REACTPY_RECONNECT_MAX_INTERVAL, int) and config.REACTPY_RECONNECT_MAX_INTERVAL < 0:
427389
errors.append(
428390
Error(
429391
"Invalid value for REACTPY_RECONNECT_MAX_INTERVAL.",
@@ -457,10 +419,7 @@ def reactpy_errors(app_configs, **kwargs):
457419
)
458420

459421
# Check if REACTPY_RECONNECT_MAX_RETRIES is a positive integer
460-
if (
461-
isinstance(config.REACTPY_RECONNECT_MAX_RETRIES, int)
462-
and config.REACTPY_RECONNECT_MAX_RETRIES < 0
463-
):
422+
if isinstance(config.REACTPY_RECONNECT_MAX_RETRIES, int) and config.REACTPY_RECONNECT_MAX_RETRIES < 0:
464423
errors.append(
465424
Error(
466425
"Invalid value for REACTPY_RECONNECT_MAX_RETRIES.",
@@ -523,10 +482,7 @@ def reactpy_errors(app_configs, **kwargs):
523482
)
524483

525484
# Check if REACTPY_CLEAN_INTERVAL is a positive integer
526-
if (
527-
isinstance(config.REACTPY_CLEAN_INTERVAL, int)
528-
and config.REACTPY_CLEAN_INTERVAL < 0
529-
):
485+
if isinstance(config.REACTPY_CLEAN_INTERVAL, int) and config.REACTPY_CLEAN_INTERVAL < 0:
530486
errors.append(
531487
Error(
532488
"Invalid value for REACTPY_CLEAN_INTERVAL.",

src/reactpy_django/clean.py

+9-17
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
if TYPE_CHECKING:
1313
from reactpy_django.models import Config
1414

15-
CLEAN_NEEDED_BY: datetime = datetime(
16-
year=1, month=1, day=1, tzinfo=timezone.now().tzinfo
17-
)
15+
CLEAN_NEEDED_BY: datetime = datetime(year=1, month=1, day=1, tzinfo=timezone.now().tzinfo)
1816

1917

2018
def clean(
@@ -36,8 +34,8 @@ def clean(
3634
user_data = REACTPY_CLEAN_USER_DATA
3735

3836
if args:
39-
sessions = any(value in args for value in {"sessions", "all"})
40-
user_data = any(value in args for value in {"user_data", "all"})
37+
sessions = any(value in args for value in ("sessions", "all"))
38+
user_data = any(value in args for value in ("user_data", "all"))
4139

4240
if sessions:
4341
clean_sessions(verbosity)
@@ -54,16 +52,14 @@ def clean_sessions(verbosity: int = 1):
5452
from reactpy_django.models import ComponentSession
5553

5654
if verbosity >= 2:
57-
print("Cleaning ReactPy component sessions...")
55+
_logger.info("Cleaning ReactPy component sessions...")
5856

5957
start_time = timezone.now()
6058
expiration_date = timezone.now() - timedelta(seconds=REACTPY_SESSION_MAX_AGE)
61-
session_objects = ComponentSession.objects.filter(
62-
last_accessed__lte=expiration_date
63-
)
59+
session_objects = ComponentSession.objects.filter(last_accessed__lte=expiration_date)
6460

6561
if verbosity >= 2:
66-
print(f"Deleting {session_objects.count()} expired component sessions...")
62+
_logger.info("Deleting %d expired component sessions...", session_objects.count())
6763

6864
session_objects.delete()
6965

@@ -83,7 +79,7 @@ def clean_user_data(verbosity: int = 1):
8379
from reactpy_django.models import UserDataModel
8480

8581
if verbosity >= 2:
86-
print("Cleaning ReactPy user data...")
82+
_logger.info("Cleaning ReactPy user data...")
8783

8884
start_time = timezone.now()
8985
user_model = get_user_model()
@@ -97,9 +93,7 @@ def clean_user_data(verbosity: int = 1):
9793
user_data_objects = UserDataModel.objects.exclude(user_pk__in=all_user_pks)
9894

9995
if verbosity >= 2:
100-
print(
101-
f"Deleting {user_data_objects.count()} user data objects not associated with an existing user..."
102-
)
96+
_logger.info("Deleting %d user data objects not associated with an existing user...", user_data_objects.count())
10397

10498
user_data_objects.delete()
10599

@@ -129,9 +123,7 @@ def inspect_clean_duration(start_time: datetime, task_name: str, verbosity: int)
129123
clean_duration = timezone.now() - start_time
130124

131125
if verbosity >= 3:
132-
print(
133-
f"Cleaned ReactPy {task_name} in {clean_duration.total_seconds()} seconds."
134-
)
126+
_logger.info("Cleaned ReactPy %s in %s seconds.", task_name, clean_duration.total_seconds())
135127

136128
if clean_duration.total_seconds() > 1:
137129
_logger.warning(

0 commit comments

Comments
 (0)