Skip to content

Commit a5f8d9c

Browse files
committed
Allow reactpy install string override in pyscript
1 parent dcd5ff9 commit a5f8d9c

2 files changed

Lines changed: 45 additions & 10 deletions

File tree

src/reactpy/executors/pyscript/utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,15 @@ def extend_pyscript_config(
128128
extra_js: dict[str, str] | str,
129129
config: dict[str, Any] | str,
130130
modules: dict[str, str] | str | None = None,
131+
reactpy_pkg_string: str | None = None,
131132
) -> str:
132133
# Extends ReactPy's default PyScript config with user provided values.
133134
pyscript_config: dict[str, Any] = {
134-
"packages": [reactpy_version_string(), "jsonpointer==3.*", "ssl"],
135+
"packages": [
136+
reactpy_pkg_string or _reactpy_pkg_string(),
137+
"jsonpointer==3.*",
138+
"ssl",
139+
],
135140
"js_modules": {
136141
"main": modules
137142
or {
@@ -159,7 +164,7 @@ def extend_pyscript_config(
159164
return json.dumps(pyscript_config)
160165

161166

162-
def reactpy_version_string() -> str:
167+
def _reactpy_pkg_string() -> str:
163168
wheel_file = _ensure_local_reactpy_wheel()
164169
return (
165170
f"{REACTPY_PATH_PREFIX.current}static/{_PYSCRIPT_WHEELS_DIR}/{wheel_file.name}"

tests/test_pyscript/test_utils.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def test_extend_pyscript_config():
112112
config = {"packages_cache": "always"}
113113

114114
with mock.patch(
115-
"reactpy.executors.pyscript.utils.reactpy_version_string",
115+
"reactpy.executors.pyscript.utils._reactpy_pkg_string",
116116
return_value="/reactpy/static/wheels/reactpy-test.whl",
117117
):
118118
result = utils.extend_pyscript_config(extra_py, extra_js, config)
@@ -140,7 +140,7 @@ def test_extend_pyscript_config_string_values():
140140
extra_js_string = orjson.dumps(extra_js).decode()
141141
config_string = orjson.dumps(config).decode()
142142
with mock.patch(
143-
"reactpy.executors.pyscript.utils.reactpy_version_string",
143+
"reactpy.executors.pyscript.utils._reactpy_pkg_string",
144144
return_value="/reactpy/static/wheels/reactpy-test.whl",
145145
):
146146
result = utils.extend_pyscript_config(extra_py, extra_js_string, config_string)
@@ -157,7 +157,7 @@ def test_extend_pyscript_config_string_values():
157157
assert result["packages_cache"] == "always"
158158

159159

160-
def test_reactpy_version_string_prefers_packaged_wheel():
160+
def test_reactpy_pkg_string_prefers_packaged_wheel():
161161
wheel_file = _current_wheel_path()
162162

163163
with (
@@ -173,13 +173,13 @@ def test_reactpy_version_string_prefers_packaged_wheel():
173173
"reactpy.executors.pyscript.utils._rebuild_installed_reactpy_wheel"
174174
) as rebuild_wheel,
175175
):
176-
assert utils.reactpy_version_string() == (
176+
assert utils._reactpy_pkg_string() == (
177177
f"{REACTPY_PATH_PREFIX.current}static/wheels/{wheel_file.name}"
178178
)
179179
rebuild_wheel.assert_not_called()
180180

181181

182-
def test_reactpy_version_string_rebuilds_source_checkout_when_wheel_is_stale():
182+
def test_reactpy_pkg_string_rebuilds_source_checkout_when_wheel_is_stale():
183183
built_wheel = _current_wheel_path("dist")
184184
copied_wheel = _current_wheel_path("static", "wheels")
185185

@@ -205,14 +205,14 @@ def test_reactpy_version_string_rebuilds_source_checkout_when_wheel_is_stale():
205205
return_value=copied_wheel,
206206
) as copy_wheel,
207207
):
208-
assert utils.reactpy_version_string() == (
208+
assert utils._reactpy_pkg_string() == (
209209
f"{REACTPY_PATH_PREFIX.current}static/wheels/{copied_wheel.name}"
210210
)
211211
build_wheel.assert_called_once_with()
212212
copy_wheel.assert_called_once_with(built_wheel)
213213

214214

215-
def test_reactpy_version_string_reconstructs_installed_wheel_when_needed():
215+
def test_reactpy_pkg_string_reconstructs_installed_wheel_when_needed():
216216
wheel_file = _current_wheel_path()
217217

218218
with (
@@ -229,7 +229,7 @@ def test_reactpy_version_string_reconstructs_installed_wheel_when_needed():
229229
return_value=wheel_file,
230230
) as rebuild_wheel,
231231
):
232-
assert utils.reactpy_version_string() == (
232+
assert utils._reactpy_pkg_string() == (
233233
f"{REACTPY_PATH_PREFIX.current}static/wheels/{wheel_file.name}"
234234
)
235235
rebuild_wheel.assert_called_once_with()
@@ -669,6 +669,36 @@ def test_copy_reactpy_wheel_to_static_dir_replaces_same_name_wheel(tmp_path):
669669
assert static_wheel.read_text(encoding="utf-8") == "new wheel"
670670

671671

672+
def test_copy_reactpy_wheel_to_static_dir_returns_existing_static_wheel(tmp_path):
673+
static_wheels_dir = tmp_path / "static" / "wheels"
674+
source_wheel = _write_file(
675+
static_wheels_dir / _current_wheel_name(),
676+
"existing wheel",
677+
)
678+
679+
with (
680+
mock.patch(
681+
"reactpy.executors.pyscript.utils._packaged_reactpy_wheels_dir",
682+
return_value=static_wheels_dir,
683+
),
684+
mock.patch("reactpy.executors.pyscript.utils.shutil.copy2") as copy_wheel,
685+
):
686+
static_wheel = utils._copy_reactpy_wheel_to_static_dir(source_wheel)
687+
688+
assert static_wheel == source_wheel
689+
assert static_wheel.read_text(encoding="utf-8") == "existing wheel"
690+
copy_wheel.assert_not_called()
691+
assert not static_wheel.with_suffix(f"{static_wheel.suffix}.tmp").exists()
692+
693+
694+
def test_wheel_archive_name_rejects_unsafe_paths_and_normalizes_relative_paths():
695+
assert utils._wheel_archive_name(Path("reactpy") / "module.py") == (
696+
"reactpy/module.py"
697+
)
698+
assert utils._wheel_archive_name(Path("..") / "bin" / "reactpy") is None
699+
assert utils._wheel_archive_name(Path.cwd() / "reactpy" / "module.py") is None
700+
701+
672702
def test_rebuild_installed_reactpy_wheel_returns_none_when_distribution_missing():
673703
with assert_reactpy_did_log(
674704
match_message="Could not inspect the installed ReactPy distribution.",

0 commit comments

Comments
 (0)