diff --git a/news/3011.feature.md b/news/3011.feature.md deleted file mode 100644 index fe237a3ca6..0000000000 --- a/news/3011.feature.md +++ /dev/null @@ -1 +0,0 @@ -Add `__init__.py` to the `pdm` package, making it a normal package. diff --git a/news/3022.bugfix.md b/news/3022.bugfix.md new file mode 100644 index 0000000000..485b75216a --- /dev/null +++ b/news/3022.bugfix.md @@ -0,0 +1 @@ +Fix a mistake in build env setup that will cause the `PATH` env var length to grow. diff --git a/pyproject.toml b/pyproject.toml index 77a20a90b2..acff9c3863 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ pdm = "pdm.core:main" [tool.pdm.version] source = "scm" -write_to = "pdm/VERSION" +write_to = "pdm/models/VERSION" [tool.pdm.build] excludes = ["./**/.git"] diff --git a/src/pdm/__init__.py b/src/pdm/__init__.py deleted file mode 100644 index 3186b73248..0000000000 --- a/src/pdm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""This __init__.py file is empty and does not expose any members at the package level.""" diff --git a/src/pdm/__version__.py b/src/pdm/__version__.py index 278be57499..34405f3b1a 100644 --- a/src/pdm/__version__.py +++ b/src/pdm/__version__.py @@ -5,7 +5,7 @@ def read_version() -> str: try: return importlib_metadata.version(__package__ or "pdm") except importlib_metadata.PackageNotFoundError: - return resources_read_text("pdm", "VERSION").strip() + return resources_read_text("pdm.models", "VERSION").strip() __version__ = read_version() diff --git a/src/pdm/builders/base.py b/src/pdm/builders/base.py index e93c6c5d51..408c253d5e 100644 --- a/src/pdm/builders/base.py +++ b/src/pdm/builders/base.py @@ -137,7 +137,7 @@ def __init__(self, executable: str, shared: str, overlay: str) -> None: for path in (overlay, shared): paths = get_sys_config_paths(executable, vars={"base": path, "platbase": path}, kind="prefix") self.bin_dirs.append(paths["scripts"]) - self.lib_dirs.extend([paths["platlib"], paths["purelib"]]) + self.lib_dirs.extend({paths["platlib"], paths["purelib"]}) self.site_dir = os.path.join(overlay, "site") if os.path.isdir(self.site_dir): # Clear existing site dir as .pyc may be cached. @@ -181,7 +181,7 @@ class EnvBuilder: if TYPE_CHECKING: _hook: BuildBackendHookCaller _requires: list[str] - _prefix: _Prefix + _prefix: _Prefix | None def get_shared_env(self, key: int) -> str: if key in self._shared_envs: @@ -234,21 +234,24 @@ def init_build_system(self, build_system: dict[str, Any]) -> None: python_executable=self.executable, ) self._requires = build_system["requires"] - self._prefix = _Prefix( - self.executable, - # Build backends with the same requires list share the cached base env. - shared=self.get_shared_env(hash(frozenset(self._requires))), - # Overlay envs are unique for each source to be built. - overlay=self.get_overlay_env(os.path.normcase(self.src_dir).rstrip("\\/")), + self._prefix = ( + _Prefix( + self.executable, + # Build backends with the same requires list share the cached base env. + shared=self.get_shared_env(hash(frozenset(self._requires))), + # Overlay envs are unique for each source to be built. + overlay=self.get_overlay_env(os.path.normcase(self.src_dir).rstrip("\\/")), + ) + if self.isolated + else None ) @property def _env_vars(self) -> dict[str, str]: - paths = self._prefix.bin_dirs - if "PATH" in os.environ: - paths.append(os.getenv("PATH", "")) env: dict[str, str] = {} if self.isolated: + assert self._prefix is not None + paths = self._prefix.bin_dirs[:] env.update( { "PYTHONPATH": self._prefix.site_dir, @@ -257,14 +260,15 @@ def _env_vars(self) -> dict[str, str]: ) else: env_paths = self._env.get_paths() - project_libs = env_paths["purelib"] - pythonpath = [*self._prefix.lib_dirs, project_libs] + pythonpath = list({env_paths["purelib"], env_paths["platlib"]}) if "PYTHONPATH" in os.environ: pythonpath.append(os.getenv("PYTHONPATH", "")) env.update( PYTHONPATH=os.pathsep.join(pythonpath), ) - paths.append(env_paths["scripts"]) + paths = [env_paths["scripts"]] + if "PATH" in os.environ: + paths.append(os.getenv("PATH", "")) env["PATH"] = os.pathsep.join(paths) return env @@ -279,8 +283,12 @@ def subprocess_runner( def check_requirements(self, reqs: Iterable[str]) -> Iterable[Requirement]: missing = set() conflicting = set() - project_lib = self._env.get_paths()["purelib"] - libs = self._prefix.lib_dirs + ([project_lib] if not self.isolated else []) + env_paths = self._env.get_paths() + libs = ( + list({env_paths["purelib"], env_paths["platlib"]}) + if not self.isolated + else cast(_Prefix, self._prefix).lib_dirs + ) if reqs: ws = WorkingSet(libs) for req in reqs: @@ -308,6 +316,7 @@ def install(self, requirements: Iterable[str], shared: bool = False) -> None: missing = list(self.check_requirements(requirements)) if not missing: return + assert self._prefix is not None path = self._prefix.shared if shared else self._prefix.overlay env = PythonEnvironment(self._env.project, python=str(self._env.interpreter.path), prefix=path) install_requirements(missing, env) diff --git a/src/pdm/builders/sdist.py b/src/pdm/builders/sdist.py index 0728171227..bfffcb9580 100644 --- a/src/pdm/builders/sdist.py +++ b/src/pdm/builders/sdist.py @@ -3,6 +3,7 @@ import os from pdm.builders.base import EnvBuilder, wrap_error +from pdm.termui import logger class SdistBuilder(EnvBuilder): @@ -14,5 +15,8 @@ def build(self, out_dir: str, metadata_directory: str | None = None) -> str: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_sdist(self.config_settings) self.install(requires) + lib = self._env.get_paths()["purelib"] + logger.info("Libs(%s): %s", lib, os.listdir(lib)) + logger.info("PYTHONPATH: %s", self._env_vars["PYTHONPATH"]) filename = self._hook.build_sdist(out_dir, self.config_settings) return os.path.join(out_dir, filename) diff --git a/tests/cli/test_build.py b/tests/cli/test_build.py index 6e49d25d25..cef49baa93 100644 --- a/tests/cli/test_build.py +++ b/tests/cli/test_build.py @@ -159,17 +159,12 @@ def test_cli_build_with_config_settings(fixture_project, pdm): @pytest.mark.usefixtures("local_finder") -@pytest.mark.parametrize("isolated", (True, False)) -def test_build_with_no_isolation(fixture_project, pdm, isolated): - project = fixture_project("demo-failure") - project.pyproject.set_data({"project": {"name": "demo", "version": "0.1.0"}}) - project.pyproject.write() - pdm(["add", "first"], obj=project) - args = ["build"] - if not isolated: - args.append("--no-isolation") - result = pdm(args, obj=project) - assert result.exit_code == int(isolated) +def test_build_with_no_isolation(pdm, project): + result = pdm(["build", "--no-isolation"], obj=project) + assert result.exit_code == 1 + pdm(["add", "pdm-backend", "--no-self"], obj=project, strict=True) + result = pdm(["build", "--no-isolation"], obj=project) + assert result.exit_code == 0 def test_build_ignoring_pip_environment(fixture_project, monkeypatch):