Skip to content

v3.3.2 #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ Using the following categories, list your changes in this order:

- Nothing (yet)!

## [3.3.2] - 2023-08-13

### Added

- ReactPy Websocket will now decode messages via `orjson` resulting in an ~6% overall performance improvement.
- Built-in `asyncio` event loops are now patched via `nest_asyncio`, resulting in an ~10% overall performance improvement. This has no performance impact if you are running your webserver with `uvloop`.

### Fixed

- Fix bug where `REACTPY_WEBSOCKET_URL` always generates a warning if unset.
- Fixed bug on Windows where `assert f is self._write_fut` would be raised by `uvicorn` when `REACTPY_BACKHAUL_THREAD = True`.
- Fixed bug on Windows where rendering behavior would be jittery with `daphne` when `REACTPY_BACKHAUL_THREAD = True`.

## [3.3.1] - 2023-08-08

### Added
Expand Down
4 changes: 2 additions & 2 deletions docs/python/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
# 3. Your Django user model does not define a `backend` attribute
REACTPY_AUTH_BACKEND = "django.contrib.auth.backends.ModelBackend"

# Whether to enable rendering ReactPy via a dedicated backhaul thread
# This allows the webserver to process traffic while during ReactPy rendering
# Whether to enable rendering ReactPy via a dedicated backhaul thread.
# This allows the webserver to process traffic while during ReactPy rendering.
REACTPY_BACKHAUL_THREAD = False
2 changes: 2 additions & 0 deletions requirements/pkg-deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ django >=4.1.0
reactpy >=1.0.0, <1.1.0
aiofile >=3.0
dill >=0.3.5
orjson >=3.6.0
nest_asyncio >=1.5.0
typing_extensions
10 changes: 10 additions & 0 deletions src/reactpy_django/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import contextlib

import nest_asyncio

from reactpy_django import checks, components, decorators, hooks, types, utils
from reactpy_django.websocket.paths import REACTPY_WEBSOCKET_PATH

Expand All @@ -11,3 +15,9 @@
"utils",
"checks",
]
# Built-in asyncio event loops can create `assert f is self._write_fut` exceptions
# while we are using our backhaul thread with Uvicorn, so we use this patch to fix this.
# This also resolves jittery rendering behaviors within Daphne. Can be demonstrated
# using our "Renders Per Second" test page.
with contextlib.suppress(ValueError):
nest_asyncio.apply()
2 changes: 1 addition & 1 deletion src/reactpy_django/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def reactpy_warnings(app_configs, **kwargs):
)

# Check if REACTPY_WEBSOCKET_URL doesn't end with a slash
REACTPY_WEBSOCKET_URL = getattr(settings, "REACTPY_WEBSOCKET_URL", "")
REACTPY_WEBSOCKET_URL = getattr(settings, "REACTPY_WEBSOCKET_URL", "reactpy/")
if isinstance(REACTPY_WEBSOCKET_URL, str):
if not REACTPY_WEBSOCKET_URL or not REACTPY_WEBSOCKET_URL.endswith("/"):
warnings.append(
Expand Down
11 changes: 10 additions & 1 deletion src/reactpy_django/websocket/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Any, MutableMapping, Sequence

import dill as pickle
import orjson
from channels.auth import login
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
Expand Down Expand Up @@ -46,7 +47,7 @@ async def connect(self) -> None:
await super().connect()

# Authenticate the user, if possible
user: Any = self.scope.get("user")
user = self.scope.get("user")
if user and user.is_authenticated:
try:
await login(self.scope, user, backend=REACTPY_AUTH_BACKEND)
Expand Down Expand Up @@ -105,6 +106,14 @@ async def receive_json(self, content: Any, **_) -> None:
else:
await self.recv_queue.put(content)

@classmethod
async def decode_json(cls, text_data):
return orjson.loads(text_data)

@classmethod
async def encode_json(cls, content):
return orjson.dumps(content).decode()

async def run_dispatcher(self):
"""Runs the main loop that performs component rendering tasks."""
from reactpy_django import models
Expand Down
11 changes: 11 additions & 0 deletions tests/test_app/templates/events_renders_per_second.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ <h1>ReactPy Event Driven Renders Per Second Test Page</h1>
<p>Total Active Components: <b id="total-active"></b></p>
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
<p>Event Renders Per Second: <b id="total-erps"></b></p>
<p>Event Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
<p>Average Round-Trip Time: <b id="avg-event-rt"></b></p>
</b>

Expand Down Expand Up @@ -50,6 +51,16 @@ <h1>ReactPy Event Driven Renders Per Second Test Page</h1>
}
document.getElementById("total-erps").textContent = totalEPS;

// Calculate Min RPS
let minRPS = 0;
for (let i = 0; i < elements.length; i++) {
let rpsValue = parseFloat(elements[i].getAttribute("data-erps"));
if (rpsValue < minRPS || minRPS == 0) {
minRPS = rpsValue;
}
}
document.getElementById("min-rps").textContent = minRPS * elements.length;

// Calculate Average Event Round-Trip Time
document.getElementById("avg-event-rt").textContent = ((1000 / totalEPS) * elements.length).toFixed(4) + " ms";

Expand Down
11 changes: 11 additions & 0 deletions tests/test_app/templates/mixed_time_to_load.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ <h1>ReactPy IO/CPU Mixed Renders Per Second Test Page</h1>
<p>Total Active Components: <b id="total-active"></b></p>
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
<p>Total Renders Per Second: <b id="total-rps"></b></p>
<p>Total Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
</b>

<script>
Expand All @@ -43,6 +44,16 @@ <h1>ReactPy IO/CPU Mixed Renders Per Second Test Page</h1>
}
document.getElementById("total-rps").textContent = totalRPS;

// Calculate Min RPS
let minRPS = 0;
for (let i = 0; i < elements.length; i++) {
let rpsValue = parseFloat(elements[i].getAttribute("data-rps"));
if (rpsValue < minRPS || minRPS == 0) {
minRPS = rpsValue;
}
}
document.getElementById("min-rps").textContent = minRPS * elements.length;

await new Promise(resolve => setTimeout(resolve, 50));
}
}
Expand Down
11 changes: 11 additions & 0 deletions tests/test_app/templates/renders_per_second.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ <h1>ReactPy Renders Per Second Test Page</h1>
<p>Total Active Components: <b id="total-active"></b></p>
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
<p>Total Renders Per Second: <b id="total-rps"></b></p>
<p>Total Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
</b>

<script>
Expand All @@ -43,6 +44,16 @@ <h1>ReactPy Renders Per Second Test Page</h1>
}
document.getElementById("total-rps").textContent = totalRPS;

// Calculate Min RPS
let minRPS = 0;
for (let i = 0; i < elements.length; i++) {
let rpsValue = parseFloat(elements[i].getAttribute("data-rps"));
if (rpsValue < minRPS || minRPS == 0) {
minRPS = rpsValue;
}
}
document.getElementById("min-rps").textContent = minRPS * elements.length;

await new Promise(resolve => setTimeout(resolve, 50));
}
}
Expand Down