Skip to content

Commit db0f1aa

Browse files
authored
Merge branch 'main' into event-target-checked-does-not-exist-for-checkbox-inputs
2 parents cbb6d79 + f053551 commit db0f1aa

36 files changed

+581
-238
lines changed

.github/pull_request_template.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
## Issues
44

5-
<!-- Link the issues this change resolves, or describe them -->
5+
<!-- Describe or link the issues this change resolves -->
66

7-
## Summary
7+
## Solution
88

99
<!-- Describe how these changes resolve the aforementioned issues -->
1010

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
.jupyter
1212

1313
# --- Python ---
14+
.hatch
1415
.venv
1516
venv
1617
MANIFEST

.pre-commit-config.yaml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,32 @@ repos:
33
hooks:
44
- id: lint-py-fix
55
name: Fix Python Lint
6-
entry: hatch run lint-py --fix
7-
language: system
8-
pass_filenames: false
9-
- repo: local
10-
hooks:
11-
- id: lint-py-check
12-
name: Check Python Lint
136
entry: hatch run lint-py
147
language: system
8+
args: [--fix]
159
pass_filenames: false
10+
files: \.py$
1611
- repo: local
1712
hooks:
1813
- id: lint-js-fix
1914
name: Fix JS Lint
2015
entry: hatch run lint-js --fix
2116
language: system
2217
pass_filenames: false
18+
files: \.(js|jsx|ts|tsx)$
19+
- repo: local
20+
hooks:
21+
- id: lint-py-check
22+
name: Check Python Lint
23+
entry: hatch run lint-py
24+
language: system
25+
pass_filenames: false
26+
files: \.py$
2327
- repo: local
2428
hooks:
2529
- id: lint-js-check
2630
name: Check JS Lint
2731
entry: hatch run lint-py
2832
language: system
2933
pass_filenames: false
34+
files: \.(js|jsx|ts|tsx)$

docs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tool.poetry]
2-
name = "docs"
2+
name = "docs_app"
33
version = "0.0.0"
44
description = "docs"
55
authors = ["rmorshea <[email protected]>"]

docs/source/_exts/reactpy_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import re
44
from pathlib import Path
5-
from typing import Any
5+
from typing import Any, ClassVar
66

77
from docs_app.examples import (
88
SOURCE_DIR,
@@ -21,7 +21,7 @@ class WidgetExample(SphinxDirective):
2121
required_arguments = 1
2222
_next_id = 0
2323

24-
option_spec = {
24+
option_spec: ClassVar[dict[str, Any]] = {
2525
"result-is-default-tab": directives.flag,
2626
"activate-button": directives.flag,
2727
}

docs/source/_exts/reactpy_view.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import os
2-
import sys
3-
4-
print(sys.path)
2+
from typing import Any, ClassVar
53

64
from docs_app.examples import get_normalized_example_name
75
from docutils.nodes import raw
@@ -20,7 +18,7 @@ class IteractiveWidget(SphinxDirective):
2018
required_arguments = 1
2119
_next_id = 0
2220

23-
option_spec = {
21+
option_spec: ClassVar[dict[str, Any]] = {
2422
"activate-button": directives.flag,
2523
"margin": float,
2624
}

docs/source/about/changelog.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,34 @@ more info, see the :ref:`Contributor Guide <Creating a Changelog Entry>`.
2323
Unreleased
2424
----------
2525

26+
**Fixed**
27+
28+
- :pull:`1118` - `module_from_template` is broken with a recent release of `requests`
29+
30+
31+
v1.0.2
32+
------
33+
34+
**Fixed**
35+
36+
- :issue:`1086` - fix rendering bug when children change positions (via :pull:`1085`)
37+
38+
39+
v1.0.1
40+
------
41+
2642
**Changed**
2743

2844
- :pull:`1050` - Warn and attempt to fix missing mime types, which can result in ``reactpy.run`` not working as expected.
45+
- :pull:`1051` - Rename ``reactpy.backend.BackendImplementation`` to ``reactpy.backend.BackendType``
46+
- :pull:`1051` - Allow ``reactpy.run`` to fail in more predictable ways
2947

3048
**Fixed**
3149

3250
- :issue:`930` - better traceback for JSON serialization errors (via :pull:`1008`)
3351
- :issue:`437` - explain that JS component attributes must be JSON (via :pull:`1008`)
52+
- :pull:`1051` - Fix ``reactpy.run`` port assignment sometimes attaching to in-use ports on Windows
53+
- :pull:`1051` - Fix ``reactpy.run`` not recognizing ``fastapi``
3454

3555

3656
v1.0.0

docs/source/about/contributor-guide.rst

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ Then, you should be able to activate your development environment with:
118118
119119
hatch shell
120120
121+
From within the shell, to install the projects in this repository, you should then run:
122+
123+
.. code-block:: bash
124+
125+
invoke env
126+
127+
Project Structure
128+
-----------------
129+
130+
This repository is set up to be able to manage many applications and libraries written
131+
in a variety of languages. All projects can be found under the ``src`` directory:
132+
133+
- ``src/py/{project}`` - Python packages
134+
- ``src/js/app`` - ReactPy's built-in JS client
135+
- ``src/js/packages/{project}`` - JS packages
136+
137+
At the root of the repository is a ``pyproject.toml`` file that contains scripts and
138+
their respective dependencies for managing all other projects. Most of these global
139+
scripts can be run via ``hatch run ...`` however, for more complex scripting tasks, we
140+
rely on Invoke_. Scripts implements with Invoke can be found in ``tasks.py``.
121141

122142
Running The Tests
123143
-----------------
@@ -302,12 +322,14 @@ you should refer to their respective documentation in the links below:
302322
Jupyter
303323
- `reactpy-dash <https://github.com/reactive-python/reactpy-dash>`__ - ReactPy integration for Plotly
304324
Dash
305-
- `django-reactpy <https://github.com/reactive-python/django-reactpy>`__ - ReactPy integration for
325+
- `reactpy-django <https://github.com/reactive-python/reactpy-django>`__ - ReactPy integration for
306326
Django
307327

308328
.. Links
309329
.. =====
310330
331+
.. _Hatch: https://hatch.pypa.io/
332+
.. _Invoke: https://www.pyinvoke.org/
311333
.. _Google Chrome: https://www.google.com/chrome/
312334
.. _Docker: https://docs.docker.com/get-docker/
313335
.. _Git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

pyproject.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ dependencies = [
1313
"invoke",
1414
# lint
1515
"black",
16-
"ruff",
16+
"ruff==0.0.278", # Ruff is moving really fast, so pinning for now.
1717
"toml",
1818
"flake8",
1919
"flake8-pyproject",
2020
"reactpy-flake8 >=0.7",
21+
# types
22+
"mypy",
23+
"types-toml",
2124
# publish
2225
"semver >=2, <3",
2326
"twine",
@@ -42,7 +45,15 @@ test-docs = "invoke test-docs"
4245
target-version = ["py39"]
4346
line-length = 88
4447

45-
# --- Flake8 ----------------------------------------------------------------------------
48+
# --- MyPy -----------------------------------------------------------------------------
49+
50+
[tool.mypy]
51+
ignore_missing_imports = true
52+
warn_unused_configs = true
53+
warn_redundant_casts = true
54+
warn_unused_ignores = true
55+
56+
# --- Flake8 ---------------------------------------------------------------------------
4657

4758
[tool.flake8]
4859
select = ["RPY"] # only need to check with reactpy-flake8

requirements.txt

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/py/reactpy/.temp.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from reactpy import component, html, run, use_state
2+
from reactpy.core.types import State
3+
4+
5+
@component
6+
def Item(item: str, all_items: State[list[str]]):
7+
color = use_state(None)
8+
9+
def deleteme(event):
10+
all_items.set_value([i for i in all_items.value if (i != item)])
11+
12+
def colorize(event):
13+
color.set_value("blue" if not color.value else None)
14+
15+
return html.div(
16+
{"id": item, "style": {"background_color": color.value}},
17+
html.button({"on_click": colorize}, f"Color {item}"),
18+
html.button({"on_click": deleteme}, f"Delete {item}"),
19+
)
20+
21+
22+
@component
23+
def App():
24+
items = use_state(["A", "B", "C"])
25+
return html._([Item(item, items, key=item) for item in items.value])
26+
27+
28+
run(App)

src/py/reactpy/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ testpaths = "tests"
139139
xfail_strict = true
140140
python_files = "*asserts.py test_*.py"
141141
asyncio_mode = "auto"
142+
log_cli_level = "INFO"
142143

143144
# --- MyPy -----------------------------------------------------------------------------
144145

src/py/reactpy/reactpy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from reactpy.utils import Ref, html_to_vdom, vdom_to_html
2222

2323
__author__ = "The Reactive Python Team"
24-
__version__ = "1.0.1" # DO NOT MODIFY
24+
__version__ = "1.0.2" # DO NOT MODIFY
2525

2626
__all__ = [
2727
"backend",

src/py/reactpy/reactpy/backend/_common.py

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,49 @@
1414
from reactpy.utils import vdom_to_html
1515

1616
if TYPE_CHECKING:
17+
import uvicorn
1718
from asgiref.typing import ASGIApplication
1819

1920
PATH_PREFIX = PurePosixPath("/_reactpy")
2021
MODULES_PATH = PATH_PREFIX / "modules"
2122
ASSETS_PATH = PATH_PREFIX / "assets"
2223
STREAM_PATH = PATH_PREFIX / "stream"
23-
2424
CLIENT_BUILD_DIR = Path(_reactpy_file_path).parent / "_static" / "app" / "dist"
2525

26-
try:
26+
27+
async def serve_with_uvicorn(
28+
app: ASGIApplication | Any,
29+
host: str,
30+
port: int,
31+
started: asyncio.Event | None,
32+
) -> None:
33+
"""Run a development server for an ASGI application"""
2734
import uvicorn
28-
except ImportError: # nocov
29-
pass
30-
else:
31-
32-
async def serve_development_asgi(
33-
app: ASGIApplication | Any,
34-
host: str,
35-
port: int,
36-
started: asyncio.Event | None,
37-
) -> None:
38-
"""Run a development server for an ASGI application"""
39-
server = uvicorn.Server(
40-
uvicorn.Config(
41-
app,
42-
host=host,
43-
port=port,
44-
loop="asyncio",
45-
reload=True,
46-
)
35+
36+
server = uvicorn.Server(
37+
uvicorn.Config(
38+
app,
39+
host=host,
40+
port=port,
41+
loop="asyncio",
4742
)
48-
server.config.setup_event_loop()
49-
coros: list[Awaitable[Any]] = [server.serve()]
43+
)
44+
server.config.setup_event_loop()
45+
coros: list[Awaitable[Any]] = [server.serve()]
5046

51-
# If a started event is provided, then use it signal based on `server.started`
52-
if started:
53-
coros.append(_check_if_started(server, started))
47+
# If a started event is provided, then use it signal based on `server.started`
48+
if started:
49+
coros.append(_check_if_started(server, started))
5450

55-
try:
56-
await asyncio.gather(*coros)
57-
finally:
58-
# Since we aren't using the uvicorn's `run()` API, we can't guarantee uvicorn's
59-
# order of operations. So we need to make sure `shutdown()` always has an initialized
60-
# list of `self.servers` to use.
61-
if not hasattr(server, "servers"): # nocov
62-
server.servers = []
63-
await asyncio.wait_for(server.shutdown(), timeout=3)
51+
try:
52+
await asyncio.gather(*coros)
53+
finally:
54+
# Since we aren't using the uvicorn's `run()` API, we can't guarantee uvicorn's
55+
# order of operations. So we need to make sure `shutdown()` always has an initialized
56+
# list of `self.servers` to use.
57+
if not hasattr(server, "servers"): # nocov
58+
server.servers = []
59+
await asyncio.wait_for(server.shutdown(), timeout=3)
6460

6561

6662
async def _check_if_started(server: uvicorn.Server, started: asyncio.Event) -> None:
@@ -72,8 +68,7 @@ async def _check_if_started(server: uvicorn.Server, started: asyncio.Event) -> N
7268
def safe_client_build_dir_path(path: str) -> Path:
7369
"""Prevent path traversal out of :data:`CLIENT_BUILD_DIR`"""
7470
return traversal_safe_path(
75-
CLIENT_BUILD_DIR,
76-
*("index.html" if path in ("", "/") else path).split("/"),
71+
CLIENT_BUILD_DIR, *("index.html" if path in {"", "/"} else path).split("/")
7772
)
7873

7974

@@ -140,6 +135,9 @@ class CommonOptions:
140135
url_prefix: str = ""
141136
"""The URL prefix where ReactPy resources will be served from"""
142137

138+
serve_index_route: bool = True
139+
"""Automatically generate and serve the index route (``/``)"""
140+
143141
def __post_init__(self) -> None:
144142
if self.url_prefix and not self.url_prefix.startswith("/"):
145143
msg = "Expected 'url_prefix' to start with '/'"

0 commit comments

Comments
 (0)