From 3988765aa3b02d182124755922ab86dbefd91bdb Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 08:15:12 +0530 Subject: [PATCH 01/16] Don't raise `ValueError` in `RecipeBuilder` instantiation --- pyodide_build/recipe/builder.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 8b6885ad..29e7cf64 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -136,11 +136,9 @@ def __init__( self.build_dir = ( Path(build_dir).resolve() if build_dir else self.pkg_root / "build" ) - if len(str(self.build_dir).split(maxsplit=1)) > 1: - raise ValueError( - "PIP_CONSTRAINT contains spaces so pip will misinterpret it. Make sure the path to the package build directory has no spaces.\n" - "See https://github.com/pypa/pip/issues/13283" - ) + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283 + # We work around this by converting the path to a URI when needed. self.library_install_prefix = self.build_dir.parent.parent / ".libs" self.src_extract_dir = ( self.build_dir / self.fullname From 02ed53541ec6f45288c1182e4680cf6898d3a2bb Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 08:19:38 +0530 Subject: [PATCH 02/16] Add filename arg to `_create_constraints_file` --- pyodide_build/recipe/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 29e7cf64..dc7c31ed 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -365,7 +365,7 @@ def _download_and_extract(self) -> None: shutil.move(self.build_dir / extract_dir_name, self.src_extract_dir) self.src_dist_dir.mkdir(parents=True, exist_ok=True) - def _create_constraints_file(self) -> str: + def _create_constraints_file(self, filename: str = "constraints.txt") -> str: """ Creates a pip constraints file by concatenating global constraints (PIP_CONSTRAINT) with constraints specific to this package. @@ -379,7 +379,7 @@ def _create_constraints_file(self) -> str: # nothing to override return host_constraints - new_constraints_file = self.build_dir / "constraints.txt" + new_constraints_file = self.build_dir / filename with new_constraints_file.open("w") as f: for constraint in constraints: f.write(constraint + "\n") From 427df2a4ff9699c0465b1763583edf400cb27d6b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 08:20:12 +0530 Subject: [PATCH 03/16] Handle case where `host_constraints` can be empty --- pyodide_build/recipe/builder.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index dc7c31ed..61d60926 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -384,7 +384,19 @@ def _create_constraints_file(self, filename: str = "constraints.txt") -> str: for constraint in constraints: f.write(constraint + "\n") - return host_constraints + " " + str(new_constraints_file) + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283 + # We work around this by converting the path to a URI. + new_constraints_str = ( + new_constraints_file.as_uri() + if " " in str(new_constraints_file) + else str(new_constraints_file) + ) + + if host_constraints: + return host_constraints + " " + new_constraints_str + else: + return new_constraints_str def _compile( self, From 20f449649e3b0b6747b819a41707f076119b5eda Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 08:21:28 +0530 Subject: [PATCH 04/16] Drop another `ValueError` --- pyodide_build/build_env.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pyodide_build/build_env.py b/pyodide_build/build_env.py index 4a13080b..4f54abfc 100644 --- a/pyodide_build/build_env.py +++ b/pyodide_build/build_env.py @@ -334,13 +334,16 @@ def _create_constraints_file() -> str: if not constraints: return "" - if len(constraints.split(maxsplit=1)) > 1: - raise ValueError( - "PIP_CONSTRAINT contains spaces so pip will misinterpret it. Make sure the path to pyodide has no spaces.\n" - "See https://github.com/pypa/pip/issues/13283" - ) - + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283 + # We work around this by converting the path to a URI. constraints_file = Path(constraints) + constraints = ( + constraints_file.as_uri() + if " " in str(constraints_file) + else str(constraints_file) + ) + if not constraints_file.is_file(): constraints_file.parent.mkdir(parents=True, exist_ok=True) constraints_file.write_text("") From 80b6791eac7f2f1a6aebd7e2e82ca34a0e5778a3 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 08:23:44 +0530 Subject: [PATCH 05/16] Add tests --- pyodide_build/tests/recipe/test_builder.py | 23 ++++++++++++++++++++++ pyodide_build/tests/test_build_env.py | 20 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/pyodide_build/tests/recipe/test_builder.py b/pyodide_build/tests/recipe/test_builder.py index ba80a027..d28ca48d 100644 --- a/pyodide_build/tests/recipe/test_builder.py +++ b/pyodide_build/tests/recipe/test_builder.py @@ -203,6 +203,29 @@ def test_create_constraints_file_override(tmp_path, dummy_xbuildenv): assert data[-3:] == ["numpy < 2.0", "pytest == 7.0", "setuptools < 75"], data +def test_create_constraints_file_space_in_path_uri_conversion( + tmp_path, dummy_xbuildenv +): + build_dir_with_spaces = tmp_path / "build dir with spaces" + build_dir_with_spaces.mkdir() + + builder = RecipeBuilder.get_builder( + recipe=RECIPE_DIR / "pkg_test_constraint", + build_args=BuildArgs(), + build_dir=build_dir_with_spaces, + ) + + paths = builder._create_constraints_file(filename="constraints with space.txt") + + parts = paths.split() + if len(parts) > 1: + last_part = parts[-1] + if "with%20space" in last_part or last_part.startswith("file://"): + assert True + else: + assert "constraints with space.txt" in last_part + + class MockSourceSpec(_SourceSpec): @pydantic.model_validator(mode="after") def _check_patches_extra(self) -> Self: diff --git a/pyodide_build/tests/test_build_env.py b/pyodide_build/tests/test_build_env.py index d3476c63..631cfcbb 100644 --- a/pyodide_build/tests/test_build_env.py +++ b/pyodide_build/tests/test_build_env.py @@ -237,3 +237,23 @@ def test_wheel_paths(dummy_xbuildenv): f"{current_version}-none-any", ] ) + + +def test_create_constraints_file_with_spaces(tmp_path, monkeypatch, reset_cache): + from pyodide_build.build_env import _create_constraints_file + + constraints_dir = tmp_path / "path with spaces" + constraints_dir.mkdir() + constraints_file = constraints_dir / "constraints.txt" + constraints_file.write_text("numpy==1.0\n") + + def mock_get_build_flag(name): + if name == "PIP_CONSTRAINT": + return str(constraints_file) + + monkeypatch.setattr("pyodide_build.build_env.get_build_flag", mock_get_build_flag) + + result = _create_constraints_file() + + assert result.startswith("file://") + assert "path%20with%20spaces" in result or "path with spaces" in result From 356b3fa8ce97f602c4c9c785d77bc1eb3cbca173 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 12:54:20 +0530 Subject: [PATCH 06/16] Extract URI conversion to a separate function Suggested-by: Gyeongjae Choi --- pyodide_build/build_env.py | 16 +++++++--------- pyodide_build/common.py | 21 +++++++++++++++++++++ pyodide_build/recipe/builder.py | 10 ++-------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/pyodide_build/build_env.py b/pyodide_build/build_env.py index 4f54abfc..d5e25f8a 100644 --- a/pyodide_build/build_env.py +++ b/pyodide_build/build_env.py @@ -14,7 +14,12 @@ from packaging.tags import Tag, compatible_tags, cpython_tags from pyodide_build import __version__ -from pyodide_build.common import default_xbuildenv_path, search_pyproject_toml, to_bool +from pyodide_build.common import ( + default_xbuildenv_path, + path_to_uri_if_spaces, + search_pyproject_toml, + to_bool, +) RUST_BUILD_PRELUDE = """ rustup default ${RUST_TOOLCHAIN} @@ -334,15 +339,8 @@ def _create_constraints_file() -> str: if not constraints: return "" - # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret - # it as multiple files; see https://github.com/pypa/pip/issues/13283 - # We work around this by converting the path to a URI. constraints_file = Path(constraints) - constraints = ( - constraints_file.as_uri() - if " " in str(constraints_file) - else str(constraints_file) - ) + constraints = path_to_uri_if_spaces(constraints_file) if not constraints_file.is_file(): constraints_file.parent.mkdir(parents=True, exist_ok=True) diff --git a/pyodide_build/common.py b/pyodide_build/common.py index 1b159079..775f6996 100644 --- a/pyodide_build/common.py +++ b/pyodide_build/common.py @@ -646,3 +646,24 @@ def retrying_rmtree(d): else: raise raise RuntimeError(f"shutil.rmtree('{d}') failed with ENOTEMPTY three times") + + +def path_to_uri_if_spaces(path: str | Path) -> str: + """ + Convert a file path to a URI if it contains spaces, otherwise return as string. + + This works around a pip bug where paths with spaces in PIP_CONSTRAINT are + misinterpreted as multiple files; see https://github.com/pypa/pip/issues/13283 + + Parameters + ---------- + path + The file path to potentially convert + + Returns + ------- + str + The path as a URI if it contains spaces, or otherwise as a string. + """ + path_obj = Path(path) + return path_obj.as_uri() if " " in str(path_obj) else str(path_obj) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 61d60926..41c088ba 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -40,6 +40,7 @@ find_matching_wheel, make_zip_archive, modify_wheel, + path_to_uri_if_spaces, retag_wheel, retrying_rmtree, ) @@ -384,14 +385,7 @@ def _create_constraints_file(self, filename: str = "constraints.txt") -> str: for constraint in constraints: f.write(constraint + "\n") - # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret - # it as multiple files; see https://github.com/pypa/pip/issues/13283 - # We work around this by converting the path to a URI. - new_constraints_str = ( - new_constraints_file.as_uri() - if " " in str(new_constraints_file) - else str(new_constraints_file) - ) + new_constraints_str = path_to_uri_if_spaces(new_constraints_file) if host_constraints: return host_constraints + " " + new_constraints_str From a151d11078d50a209f3ada2cd6e027bd2909bfbd Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 23 May 2025 12:57:15 +0530 Subject: [PATCH 07/16] Add CHANGELOG entry for #210 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a9b7442..09bf3486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Fixed + - Fixed a regression in quoting of pip arguments introduced in [#185](https://github.com/pyodide/pyodide-build/pull/185). [#209](https://github.com/pyodide/pyodide-build/pull/209) +- `pyodide-build` now correctly works with file paths with spaces passed to the `PIP_CONSTRAINT` environment variable. + [#210](https://github.com/pyodide/pyodide-build/pull/210) + ## [0.30.4] - 2025/05/20 - Fixed compatibility with `virtualenv` 20.31 and later. The Pyodide virtual environment via `pyodide venv` no longer seeds From 75534779b3b336d3873a44513c73879049fa9014 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:18:39 +0530 Subject: [PATCH 08/16] Revert "Extract URI conversion to a separate function" This reverts commit 356b3fa8ce97f602c4c9c785d77bc1eb3cbca173. --- pyodide_build/build_env.py | 16 +++++++++------- pyodide_build/common.py | 21 --------------------- pyodide_build/recipe/builder.py | 10 ++++++++-- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/pyodide_build/build_env.py b/pyodide_build/build_env.py index d5e25f8a..4f54abfc 100644 --- a/pyodide_build/build_env.py +++ b/pyodide_build/build_env.py @@ -14,12 +14,7 @@ from packaging.tags import Tag, compatible_tags, cpython_tags from pyodide_build import __version__ -from pyodide_build.common import ( - default_xbuildenv_path, - path_to_uri_if_spaces, - search_pyproject_toml, - to_bool, -) +from pyodide_build.common import default_xbuildenv_path, search_pyproject_toml, to_bool RUST_BUILD_PRELUDE = """ rustup default ${RUST_TOOLCHAIN} @@ -339,8 +334,15 @@ def _create_constraints_file() -> str: if not constraints: return "" + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283 + # We work around this by converting the path to a URI. constraints_file = Path(constraints) - constraints = path_to_uri_if_spaces(constraints_file) + constraints = ( + constraints_file.as_uri() + if " " in str(constraints_file) + else str(constraints_file) + ) if not constraints_file.is_file(): constraints_file.parent.mkdir(parents=True, exist_ok=True) diff --git a/pyodide_build/common.py b/pyodide_build/common.py index 775f6996..1b159079 100644 --- a/pyodide_build/common.py +++ b/pyodide_build/common.py @@ -646,24 +646,3 @@ def retrying_rmtree(d): else: raise raise RuntimeError(f"shutil.rmtree('{d}') failed with ENOTEMPTY three times") - - -def path_to_uri_if_spaces(path: str | Path) -> str: - """ - Convert a file path to a URI if it contains spaces, otherwise return as string. - - This works around a pip bug where paths with spaces in PIP_CONSTRAINT are - misinterpreted as multiple files; see https://github.com/pypa/pip/issues/13283 - - Parameters - ---------- - path - The file path to potentially convert - - Returns - ------- - str - The path as a URI if it contains spaces, or otherwise as a string. - """ - path_obj = Path(path) - return path_obj.as_uri() if " " in str(path_obj) else str(path_obj) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 41c088ba..61d60926 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -40,7 +40,6 @@ find_matching_wheel, make_zip_archive, modify_wheel, - path_to_uri_if_spaces, retag_wheel, retrying_rmtree, ) @@ -385,7 +384,14 @@ def _create_constraints_file(self, filename: str = "constraints.txt") -> str: for constraint in constraints: f.write(constraint + "\n") - new_constraints_str = path_to_uri_if_spaces(new_constraints_file) + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283 + # We work around this by converting the path to a URI. + new_constraints_str = ( + new_constraints_file.as_uri() + if " " in str(new_constraints_file) + else str(new_constraints_file) + ) if host_constraints: return host_constraints + " " + new_constraints_str From 5a0597f7e3250521eaebf3d46415f561b999ef04 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:18:46 +0530 Subject: [PATCH 09/16] Revert "Add tests" This reverts commit 80b6791eac7f2f1a6aebd7e2e82ca34a0e5778a3. --- pyodide_build/tests/recipe/test_builder.py | 23 ---------------------- pyodide_build/tests/test_build_env.py | 20 ------------------- 2 files changed, 43 deletions(-) diff --git a/pyodide_build/tests/recipe/test_builder.py b/pyodide_build/tests/recipe/test_builder.py index d28ca48d..ba80a027 100644 --- a/pyodide_build/tests/recipe/test_builder.py +++ b/pyodide_build/tests/recipe/test_builder.py @@ -203,29 +203,6 @@ def test_create_constraints_file_override(tmp_path, dummy_xbuildenv): assert data[-3:] == ["numpy < 2.0", "pytest == 7.0", "setuptools < 75"], data -def test_create_constraints_file_space_in_path_uri_conversion( - tmp_path, dummy_xbuildenv -): - build_dir_with_spaces = tmp_path / "build dir with spaces" - build_dir_with_spaces.mkdir() - - builder = RecipeBuilder.get_builder( - recipe=RECIPE_DIR / "pkg_test_constraint", - build_args=BuildArgs(), - build_dir=build_dir_with_spaces, - ) - - paths = builder._create_constraints_file(filename="constraints with space.txt") - - parts = paths.split() - if len(parts) > 1: - last_part = parts[-1] - if "with%20space" in last_part or last_part.startswith("file://"): - assert True - else: - assert "constraints with space.txt" in last_part - - class MockSourceSpec(_SourceSpec): @pydantic.model_validator(mode="after") def _check_patches_extra(self) -> Self: diff --git a/pyodide_build/tests/test_build_env.py b/pyodide_build/tests/test_build_env.py index 631cfcbb..d3476c63 100644 --- a/pyodide_build/tests/test_build_env.py +++ b/pyodide_build/tests/test_build_env.py @@ -237,23 +237,3 @@ def test_wheel_paths(dummy_xbuildenv): f"{current_version}-none-any", ] ) - - -def test_create_constraints_file_with_spaces(tmp_path, monkeypatch, reset_cache): - from pyodide_build.build_env import _create_constraints_file - - constraints_dir = tmp_path / "path with spaces" - constraints_dir.mkdir() - constraints_file = constraints_dir / "constraints.txt" - constraints_file.write_text("numpy==1.0\n") - - def mock_get_build_flag(name): - if name == "PIP_CONSTRAINT": - return str(constraints_file) - - monkeypatch.setattr("pyodide_build.build_env.get_build_flag", mock_get_build_flag) - - result = _create_constraints_file() - - assert result.startswith("file://") - assert "path%20with%20spaces" in result or "path with spaces" in result From d2395a68aa48307a6a8b8cdcee41f5213db39943 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:29:37 +0530 Subject: [PATCH 10/16] Revert "Drop another `ValueError`" This reverts commit 20f449649e3b0b6747b819a41707f076119b5eda. --- pyodide_build/build_env.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pyodide_build/build_env.py b/pyodide_build/build_env.py index 4f54abfc..4a13080b 100644 --- a/pyodide_build/build_env.py +++ b/pyodide_build/build_env.py @@ -334,16 +334,13 @@ def _create_constraints_file() -> str: if not constraints: return "" - # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret - # it as multiple files; see https://github.com/pypa/pip/issues/13283 - # We work around this by converting the path to a URI. - constraints_file = Path(constraints) - constraints = ( - constraints_file.as_uri() - if " " in str(constraints_file) - else str(constraints_file) - ) + if len(constraints.split(maxsplit=1)) > 1: + raise ValueError( + "PIP_CONSTRAINT contains spaces so pip will misinterpret it. Make sure the path to pyodide has no spaces.\n" + "See https://github.com/pypa/pip/issues/13283" + ) + constraints_file = Path(constraints) if not constraints_file.is_file(): constraints_file.parent.mkdir(parents=True, exist_ok=True) constraints_file.write_text("") From 90860d00510924fce46b4d23d0351d36e30b162b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:29:50 +0530 Subject: [PATCH 11/16] Revert "Handle case where `host_constraints` can be empty" This reverts commit 427df2a4ff9699c0465b1763583edf400cb27d6b. --- pyodide_build/recipe/builder.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 61d60926..dc7c31ed 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -384,19 +384,7 @@ def _create_constraints_file(self, filename: str = "constraints.txt") -> str: for constraint in constraints: f.write(constraint + "\n") - # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret - # it as multiple files; see https://github.com/pypa/pip/issues/13283 - # We work around this by converting the path to a URI. - new_constraints_str = ( - new_constraints_file.as_uri() - if " " in str(new_constraints_file) - else str(new_constraints_file) - ) - - if host_constraints: - return host_constraints + " " + new_constraints_str - else: - return new_constraints_str + return host_constraints + " " + str(new_constraints_file) def _compile( self, From be360b324f591aab65572795d18a5c135380979b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:29:54 +0530 Subject: [PATCH 12/16] Revert "Add filename arg to `_create_constraints_file`" This reverts commit 02ed53541ec6f45288c1182e4680cf6898d3a2bb. --- pyodide_build/recipe/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index dc7c31ed..29e7cf64 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -365,7 +365,7 @@ def _download_and_extract(self) -> None: shutil.move(self.build_dir / extract_dir_name, self.src_extract_dir) self.src_dist_dir.mkdir(parents=True, exist_ok=True) - def _create_constraints_file(self, filename: str = "constraints.txt") -> str: + def _create_constraints_file(self) -> str: """ Creates a pip constraints file by concatenating global constraints (PIP_CONSTRAINT) with constraints specific to this package. @@ -379,7 +379,7 @@ def _create_constraints_file(self, filename: str = "constraints.txt") -> str: # nothing to override return host_constraints - new_constraints_file = self.build_dir / filename + new_constraints_file = self.build_dir / "constraints.txt" with new_constraints_file.open("w") as f: for constraint in constraints: f.write(constraint + "\n") From 21addb13dc67e0ae0fa22d5746dd284dc564b32b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:40:24 +0530 Subject: [PATCH 13/16] Raise message if constraint file contains spaces --- pyodide_build/build_env.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pyodide_build/build_env.py b/pyodide_build/build_env.py index 4a13080b..508d273c 100644 --- a/pyodide_build/build_env.py +++ b/pyodide_build/build_env.py @@ -15,6 +15,7 @@ from pyodide_build import __version__ from pyodide_build.common import default_xbuildenv_path, search_pyproject_toml, to_bool +from pyodide_build.logger import logger RUST_BUILD_PRELUDE = """ rustup default ${RUST_TOOLCHAIN} @@ -334,10 +335,16 @@ def _create_constraints_file() -> str: if not constraints: return "" - if len(constraints.split(maxsplit=1)) > 1: - raise ValueError( - "PIP_CONSTRAINT contains spaces so pip will misinterpret it. Make sure the path to pyodide has no spaces.\n" - "See https://github.com/pypa/pip/issues/13283" + # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret + # it as multiple files; see https://github.com/pypa/pip/issues/13283. This is fine if + # the user wants to use multiple constraints files, but if they use a single file, we + # warn them about it and ask to convert the path to a URI or to remove spaces in it. + if " " in constraints: + logger.info( + "The value of PIP_CONSTRAINT contains spaces, and pip will interpret it as " + "multiple files. If you are using a single constraints file and it has spaces " + "in its file path, please convert it to a URI instead. Please ignore this " + "message if you are using multiple constraints files." ) constraints_file = Path(constraints) From 91e15c09b52f69af16057b6c1121f0141043c94e Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:41:03 +0530 Subject: [PATCH 14/16] =?UTF-8?q?`=5Fcreate=5Fconstraints=5Ffile`=20?= =?UTF-8?q?=E2=9E=A1=EF=B8=8F=20`=5Fcreate=5Fcombined=5Fconstraints=5Ffile?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyodide_build/recipe/builder.py | 4 ++-- pyodide_build/tests/recipe/test_builder.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 29e7cf64..4d99a87b 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -365,7 +365,7 @@ def _download_and_extract(self) -> None: shutil.move(self.build_dir / extract_dir_name, self.src_extract_dir) self.src_dist_dir.mkdir(parents=True, exist_ok=True) - def _create_constraints_file(self) -> str: + def _create_combined_constraints_file(self) -> str: """ Creates a pip constraints file by concatenating global constraints (PIP_CONSTRAINT) with constraints specific to this package. @@ -432,7 +432,7 @@ def _compile( ) build_env = runner.env - build_env["PIP_CONSTRAINT"] = str(self._create_constraints_file()) + build_env["PIP_CONSTRAINT"] = str(self._create_combined_constraints_file()) wheel_path = pypabuild.build( self.src_extract_dir, self.src_dist_dir, build_env, config_settings diff --git a/pyodide_build/tests/recipe/test_builder.py b/pyodide_build/tests/recipe/test_builder.py index ba80a027..aa649802 100644 --- a/pyodide_build/tests/recipe/test_builder.py +++ b/pyodide_build/tests/recipe/test_builder.py @@ -183,7 +183,7 @@ def test_create_constraints_file_no_override(tmp_path, dummy_xbuildenv): build_dir=tmp_path, ) - path = builder._create_constraints_file() + path = builder._create_combined_constraints_file() assert path == get_build_flag("PIP_CONSTRAINT") @@ -194,7 +194,7 @@ def test_create_constraints_file_override(tmp_path, dummy_xbuildenv): build_dir=tmp_path, ) - paths = builder._create_constraints_file() + paths = builder._create_combined_constraints_file() assert paths == get_build_flag("PIP_CONSTRAINT") + " " + str( tmp_path / "constraints.txt" ) From 77197d4687b79bce5831b1e4aa81489160854512 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Tue, 27 May 2025 23:49:43 +0530 Subject: [PATCH 15/16] Fix bad revert --- pyodide_build/recipe/builder.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyodide_build/recipe/builder.py b/pyodide_build/recipe/builder.py index 4d99a87b..e2f15fbc 100755 --- a/pyodide_build/recipe/builder.py +++ b/pyodide_build/recipe/builder.py @@ -139,6 +139,14 @@ def __init__( # If a path to a file specified PIP_CONSTRAINT contains spaces, pip will misinterpret # it as multiple files; see https://github.com/pypa/pip/issues/13283 # We work around this by converting the path to a URI when needed. + if " " in str(self.build_dir): + logger.info( + "The value of PIP_CONSTRAINT contains spaces, and pip will interpret it as " + "multiple files. If you are using a single constraints file and it has spaces " + "in its file path, please convert it to a URI instead. Please ignore this " + "message if you are using multiple constraints files." + ) + self.library_install_prefix = self.build_dir.parent.parent / ".libs" self.src_extract_dir = ( self.build_dir / self.fullname From 785477150e635bab9a488049e45c890c5ea15f06 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 28 May 2025 00:17:41 +0530 Subject: [PATCH 16/16] Fix new failing test --- pyodide_build/tests/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyodide_build/tests/test_cli.py b/pyodide_build/tests/test_cli.py index 743a26f4..14e328fb 100644 --- a/pyodide_build/tests/test_cli.py +++ b/pyodide_build/tests/test_cli.py @@ -72,7 +72,7 @@ def test_skeleton_pypi(tmp_path): skeleton.app, ["pypi", test_pkg, "--recipe-dir", str(tmp_path)] ) assert result.exit_code != 0 - assert "already exists" in str(result.stdout) + assert "already exists" in str(result.stderr) assert isinstance(result.exception, SystemExit)