Skip to content

Commit dcd5ff9

Browse files
committed
Improve pyscript wheel generation
1 parent 02a2ab7 commit dcd5ff9

8 files changed

Lines changed: 1180 additions & 115 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
src/reactpy/static/*.js*
33
src/reactpy/static/morphdom/
44
src/reactpy/static/pyscript/
5+
src/reactpy/static/wheels/
56
src/js/**/*.tgz
67
src/js/**/LICENSE
78

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ installer = "uv"
7171
reactpy = "reactpy._console.cli:entry_point"
7272

7373
[[tool.hatch.build.hooks.build-scripts.scripts]]
74-
commands = []
75-
artifacts = []
74+
commands = ['python "src/build_scripts/build_local_wheel.py"']
75+
artifacts = ["src/reactpy/static/wheels/*.whl"]
7676

7777
#############################
7878
# >>> Hatch Test Runner <<< #
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# /// script
2+
# requires-python = ">=3.11"
3+
# dependencies = []
4+
# ///
5+
6+
from __future__ import annotations
7+
8+
import logging
9+
import os
10+
import re
11+
import shutil
12+
import subprocess
13+
from pathlib import Path
14+
15+
_logger = logging.getLogger(__name__)
16+
_SKIP_ENV_VAR = "REACTPY_SKIP_LOCAL_WHEEL_BUILD"
17+
18+
19+
def _reactpy_version(root_dir: Path) -> str:
20+
init_file = root_dir / "src" / "reactpy" / "__init__.py"
21+
if match := re.search(
22+
r'^__version__ = "([^"]+)"$',
23+
init_file.read_text(encoding="utf-8"),
24+
re.MULTILINE,
25+
):
26+
return match.group(1)
27+
raise RuntimeError("Could not determine the current ReactPy version.")
28+
29+
30+
def _matching_reactpy_wheel(dist_dir: Path, version: str) -> Path | None:
31+
matching_wheels = sorted(
32+
dist_dir.glob(f"reactpy-{version}-*.whl"),
33+
key=lambda path: path.stat().st_mtime,
34+
reverse=True,
35+
)
36+
return matching_wheels[0] if matching_wheels else None
37+
38+
39+
def _hatch_build_command(root_dir: Path) -> list[str] | None:
40+
for candidate in (
41+
root_dir / ".venv" / "Scripts" / "hatch.exe",
42+
root_dir / ".venv" / "bin" / "hatch",
43+
):
44+
if candidate.exists():
45+
return [str(candidate), "build", "-t", "wheel"]
46+
47+
if hatch_command := shutil.which("hatch"):
48+
return [hatch_command, "build", "-t", "wheel"]
49+
50+
return None
51+
52+
53+
def main() -> int:
54+
if os.environ.get(_SKIP_ENV_VAR):
55+
print("Skipping local ReactPy wheel build.") # noqa: T201
56+
return 0
57+
58+
root_dir = Path(__file__).parent.parent.parent
59+
version = _reactpy_version(root_dir)
60+
static_wheels_dir = root_dir / "src" / "reactpy" / "static" / "wheels"
61+
dist_dir = root_dir / "dist"
62+
hatch_build_command = _hatch_build_command(root_dir)
63+
64+
if not hatch_build_command:
65+
_logger.error("Could not locate Hatch while building the embedded wheel.")
66+
return 1
67+
68+
static_wheels_dir.mkdir(parents=True, exist_ok=True)
69+
for wheel_file in static_wheels_dir.glob("reactpy-*.whl"):
70+
wheel_file.unlink()
71+
72+
env = os.environ.copy()
73+
env[_SKIP_ENV_VAR] = "1"
74+
for key in tuple(env):
75+
if key.startswith("HATCH_ENV_"):
76+
env.pop(key)
77+
78+
result = subprocess.run( # noqa: S603
79+
hatch_build_command,
80+
capture_output=True,
81+
text=True,
82+
check=False,
83+
cwd=root_dir,
84+
env=env,
85+
)
86+
87+
if result.returncode != 0:
88+
_logger.error(
89+
"Failed to build the embedded ReactPy wheel.\nstdout:\n%s\nstderr:\n%s",
90+
result.stdout,
91+
result.stderr,
92+
)
93+
return result.returncode
94+
95+
built_wheel = _matching_reactpy_wheel(dist_dir, version)
96+
if not built_wheel:
97+
_logger.error("Failed to locate the newly built ReactPy wheel in %s", dist_dir)
98+
return 1
99+
100+
shutil.copy2(built_wheel, static_wheels_dir / built_wheel.name)
101+
print(f"Embedded local ReactPy wheel at '{static_wheels_dir / built_wheel.name}'") # noqa: T201
102+
return 0
103+
104+
105+
if __name__ == "__main__":
106+
raise SystemExit(main())

src/reactpy/executors/asgi/middleware.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ async def __call__(
268268
Error404App(),
269269
root=self.parent.static_dir,
270270
prefix=self.parent.static_path,
271+
autorefresh=True,
271272
)
272273

273274
await self._static_file_server(

0 commit comments

Comments
 (0)