diff --git a/docs/docs/plugin.md b/docs/docs/plugin.md index a98d47dc63634ef9bb34ec400ae9e6d45234bbe2..926737d33580411f1634d7c0e6875811ae34b4e1 100644 --- a/docs/docs/plugin.md +++ b/docs/docs/plugin.md @@ -125,7 +125,7 @@ which are not covered in the above example: - `core.repository_class`: change the class of repository object, which controls how to look for candidates and metadata of a package - `core.resolver_class`: change the resolver class, to control the resolution process - `core.synchronizer_class`: change the synchronizer_class, to control the installation process -- `core.parser = None`: add arguments to the **root** argument parser +- `core.parser`: add arguments to the **root** argument parser ## Publish your plugin diff --git a/news/81.bugfix b/news/81.bugfix new file mode 100644 index 0000000000000000000000000000000000000000..f994c546f26091fa4143a44d1c3b117f88120f7d --- /dev/null +++ b/news/81.bugfix @@ -0,0 +1 @@ +Fix wheel builds when `package_dir` is mapped. diff --git a/news/81.feature b/news/81.feature new file mode 100644 index 0000000000000000000000000000000000000000..9d62f484d8824a39ab7f22ce98465a090ebd2eab --- /dev/null +++ b/news/81.feature @@ -0,0 +1 @@ +Add test cases for `pdm build`. diff --git a/pdm/builders/base.py b/pdm/builders/base.py index f3d73648e0f44494a6358d1fa67c95b9e3a5b01a..ef3208309f883444b83728701fc0fc675a02ce91 100644 --- a/pdm/builders/base.py +++ b/pdm/builders/base.py @@ -72,9 +72,9 @@ def _merge_globs(include_globs, excludes_globs): return includes, excludes -def _find_top_packages() -> List[str]: +def _find_top_packages(root) -> List[str]: result = [] - for path in os.listdir("."): + for path in os.listdir(root): if ( os.path.isdir(path) and os.path.exists(os.path.join(path, "__init__.py")) @@ -111,6 +111,7 @@ class Builder: self.ireq = ireq self.project = Project(ireq.unpacked_source_directory) self._old_cwd = None + self.package_dir = None def __enter__(self) -> "Builder": self._old_cwd = os.getcwd() @@ -134,11 +135,17 @@ class Builder: dont_find_froms = [] if not self.meta.includes: - find_froms = ["src"] if os.path.isdir("src") else _find_top_packages() + if os.path.isdir("src"): + find_froms = ["src"] + self.package_dir = "src" + else: + find_froms = _find_top_packages(".") if not find_froms: includes = ["*.py"] else: for pat in self.meta.includes: + if os.path.basename(pat) == "*": + pat = pat[:-2] if "*" in pat or os.path.isfile(pat): includes.append(pat) else: @@ -157,6 +164,12 @@ class Builder: includes, excludes = _merge_globs(include_globs, excludes_globs) for path in find_froms: + if ( + not os.path.isfile(os.path.join(path, "__init__.py")) + and _find_top_packages(path) + and not self.package_dir + ): + self.package_dir = path for root, dirs, filenames in os.walk(path): if root == "__pycache__" or any( @@ -189,7 +202,7 @@ class Builder: if self.project.pyproject_file.exists(): yield "pyproject.toml" - def find_files_to_add(self, include_build: bool = False) -> List[str]: + def find_files_to_add(self, include_build: bool = False) -> List[Path]: """Traverse the project path and return a list of file names that should be included in a sdist distribution. If include_build is True, will include files like LICENSE, README and pyproject diff --git a/pdm/builders/wheel.py b/pdm/builders/wheel.py index abdde2ed3e5d7957586e59901d2e2df7f38fa37f..7deb73b393f882f0ce32353dab7b8ffccbc221f9 100644 --- a/pdm/builders/wheel.py +++ b/pdm/builders/wheel.py @@ -182,7 +182,13 @@ class WheelBuilder(Builder): def _copy_module(self, wheel): for path in self.find_files_to_add(): - self._add_file(wheel, str(path)) + rel_path = None + if self.package_dir: + try: + rel_path = path.relative_to(self.package_dir).as_posix() + except ValueError: + pass + self._add_file(wheel, str(path), rel_path) def _add_file(self, wheel, full_path, rel_path=None): if not rel_path: diff --git a/pdm/cli/actions.py b/pdm/cli/actions.py index 0095529dd6ae69898bfd00c19e39200a950d7a7d..4fe1e4e80aaeb6552eae74371854e1de49c86573 100644 --- a/pdm/cli/actions.py +++ b/pdm/cli/actions.py @@ -378,7 +378,7 @@ def do_build( stream.echo("All artifacts are disabled, nothing to do.", err=True) return ireq = project.make_self_candidate(False).ireq - ireq.source_dir = "." + ireq.source_dir = project.root.as_posix() if clean: shutil.rmtree(dest, ignore_errors=True) if sdist: diff --git a/pdm/project/meta.py b/pdm/project/meta.py index e3586b4c7ade26994baa1ae171e36c7a6629a855..ac1e9936c4a3958b3d2f53fd421ba2bf90b96027 100644 --- a/pdm/project/meta.py +++ b/pdm/project/meta.py @@ -48,7 +48,7 @@ class PackageMeta: if isinstance(value, str): return value version_source = value.get("from") - with open(version_source, encoding="utf-8") as fp: + with self.project.root.joinpath(version_source).open(encoding="utf-8") as fp: version = re.findall( r"^__version__\s*=\s*[\"'](.+?)[\"']\s*$", fp.read(), re.M )[0] diff --git a/tests/cli/test_build.py b/tests/cli/test_build.py new file mode 100644 index 0000000000000000000000000000000000000000..0845271e968468da5d52cdad662e26302dd31bb2 --- /dev/null +++ b/tests/cli/test_build.py @@ -0,0 +1,115 @@ +import tarfile +import zipfile + +from distlib.wheel import Wheel + +from pdm.cli import actions + + +def get_tarball_names(path): + with tarfile.open(path, "r:gz") as tar: + return tar.getnames() + + +def get_wheel_names(path): + with zipfile.ZipFile(path) as zf: + return zf.namelist() + + +def test_build_single_module(fixture_project): + project = fixture_project("demo-module") + assert project.meta.version == "0.1.0" + + actions.do_build(project) + tar_names = get_tarball_names(project.root / "dist/demo-module-0.1.0.tar.gz") + for name in [ + "foo_module.py", + "bar_module.py", + "LICENSE", + "pyproject.toml", + "PKG-INFO", + ]: + assert f"demo-module-0.1.0/{name}" in tar_names + + zip_names = get_wheel_names( + project.root / "dist/demo_module-0.1.0-py3-none-any.whl" + ) + for name in ["foo_module.py", "bar_module.py"]: + assert name in zip_names + + for name in ("pyproject.toml", "LICENSE"): + assert name not in zip_names + + assert Wheel( + (project.root / "dist/demo_module-0.1.0-py3-none-any.whl").as_posix() + ).metadata + + +def test_build_single_module_with_readme(fixture_project): + project = fixture_project("demo-module") + project.tool_settings["readme"] = "README.md" + project.write_pyproject() + actions.do_build(project) + assert "demo-module-0.1.0/README.md" in get_tarball_names( + project.root / "dist/demo-module-0.1.0.tar.gz" + ) + + +def test_build_package(fixture_project): + project = fixture_project("demo-package") + actions.do_build(project) + + tar_names = get_tarball_names(project.root / "dist/demo-package-0.1.0.tar.gz") + assert "demo-package-0.1.0/my_package/__init__.py" in tar_names + assert "demo-package-0.1.0/my_package/data.json" in tar_names + assert "demo-package-0.1.0/single_module.py" not in tar_names + assert "demo-package-0.1.0/data_out.json" not in tar_names + + zip_names = get_wheel_names( + project.root / "dist/demo_package-0.1.0-py3-none-any.whl" + ) + assert "my_package/__init__.py" in zip_names + assert "my_package/data.json" in zip_names + assert "single_module.py" not in zip_names + assert "data_out.json" not in zip_names + + +def test_build_src_package(fixture_project): + project = fixture_project("demo-src-package") + actions.do_build(project) + + tar_names = get_tarball_names(project.root / "dist/demo-package-0.1.0.tar.gz") + assert "demo-package-0.1.0/src/my_package/__init__.py" in tar_names + assert "demo-package-0.1.0/src/my_package/data.json" in tar_names + + zip_names = get_wheel_names( + project.root / "dist/demo_package-0.1.0-py3-none-any.whl" + ) + assert "my_package/__init__.py" in zip_names + assert "my_package/data.json" in zip_names + + +def test_build_package_include(fixture_project): + project = fixture_project("demo-package") + project.tool_settings["includes"] = [ + "my_package/", + "single_module.py", + "data_out.json", + ] + project.tool_settings["excludes"] = ["my_package/*.json"] + project.write_pyproject() + actions.do_build(project) + + tar_names = get_tarball_names(project.root / "dist/demo-package-0.1.0.tar.gz") + assert "demo-package-0.1.0/my_package/__init__.py" in tar_names + assert "demo-package-0.1.0/my_package/data.json" not in tar_names + assert "demo-package-0.1.0/single_module.py" in tar_names + assert "demo-package-0.1.0/data_out.json" in tar_names + + zip_names = get_wheel_names( + project.root / "dist/demo_package-0.1.0-py3-none-any.whl" + ) + assert "my_package/__init__.py" in zip_names + assert "my_package/data.json" not in zip_names + assert "single_module.py" in zip_names + assert "data_out.json" in zip_names diff --git a/tests/conftest.py b/tests/conftest.py index c4cbca192b57d64165da9c7412e3f39bf553059a..ea3078a245c5373cbcee81827d156f60c70a90d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,7 @@ import json import os import shutil import sys +from distutils.dir_util import copy_tree from io import BytesIO from pathlib import Path from typing import Callable, Iterable, List, Optional, Tuple @@ -259,6 +260,18 @@ def project(project_no_init): return project_no_init +@pytest.fixture() +def fixture_project(project_no_init): + """Initailize a project from a fixture project""" + + def func(project_name): + source = FIXTURES / "projects" / project_name + copy_tree(source.as_posix(), project_no_init.root.as_posix()) + return project_no_init + + return func + + @pytest.fixture() def repository(project, mocker): rv = TestRepository([], project.environment) diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/fixtures/projects/__init__.py b/tests/fixtures/projects/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/fixtures/projects/demo-module/LICENSE b/tests/fixtures/projects/demo-module/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d1e1072ee5e1d109c15b6fd18756aedc2a401840 --- /dev/null +++ b/tests/fixtures/projects/demo-module/LICENSE @@ -0,0 +1 @@ +MIT License diff --git a/tests/fixtures/projects/demo-module/README.md b/tests/fixtures/projects/demo-module/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b71cf80043e998e80c7d08b9e2abbf866db1ec74 --- /dev/null +++ b/tests/fixtures/projects/demo-module/README.md @@ -0,0 +1 @@ +# This is a demo module diff --git a/tests/fixtures/projects/demo-module/bar_module.py b/tests/fixtures/projects/demo-module/bar_module.py new file mode 100644 index 0000000000000000000000000000000000000000..c095cd59a2ee8a61e2d5603f8486b4e1928b78b0 --- /dev/null +++ b/tests/fixtures/projects/demo-module/bar_module.py @@ -0,0 +1 @@ +bar = "Hello" diff --git a/tests/fixtures/projects/demo-module/foo_module.py b/tests/fixtures/projects/demo-module/foo_module.py new file mode 100644 index 0000000000000000000000000000000000000000..1c7642a50c8188cf6ec01ec398ba5a281c4beba5 --- /dev/null +++ b/tests/fixtures/projects/demo-module/foo_module.py @@ -0,0 +1,2 @@ +__version__ = "0.1.0" +foo = "hello" diff --git a/tests/fixtures/projects/demo-module/pyproject.toml b/tests/fixtures/projects/demo-module/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..c2c26e4be77be711aaddb9c5ef67914133987a10 --- /dev/null +++ b/tests/fixtures/projects/demo-module/pyproject.toml @@ -0,0 +1,17 @@ +[build-system] +build-backend = "pdm.builders.api" +requires = ["pdm"] + +[tool] +[tool.pdm] +author = "frostming " +description = "" +homepage = "" +license = "MIT" +name = "demo-module" +python_requires = ">=3.5" +version = { from = "foo_module.py" } + +[tool.pdm.dependencies] + +[tool.pdm.dev-dependencies] diff --git a/tests/fixtures/projects/demo-package/LICENSE b/tests/fixtures/projects/demo-package/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d1e1072ee5e1d109c15b6fd18756aedc2a401840 --- /dev/null +++ b/tests/fixtures/projects/demo-package/LICENSE @@ -0,0 +1 @@ +MIT License diff --git a/tests/fixtures/projects/demo-package/README.md b/tests/fixtures/projects/demo-package/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b71cf80043e998e80c7d08b9e2abbf866db1ec74 --- /dev/null +++ b/tests/fixtures/projects/demo-package/README.md @@ -0,0 +1 @@ +# This is a demo module diff --git a/tests/fixtures/projects/demo-package/data_out.json b/tests/fixtures/projects/demo-package/data_out.json new file mode 100644 index 0000000000000000000000000000000000000000..7c4c2f5ce2c6bab4bf622b4489306de7100142d9 --- /dev/null +++ b/tests/fixtures/projects/demo-package/data_out.json @@ -0,0 +1 @@ +{"name": "foo"} diff --git a/tests/fixtures/projects/demo-package/my_package/__init__.py b/tests/fixtures/projects/demo-package/my_package/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3dc1f76bc69e3f559bee6253b24fc93acee9e1f9 --- /dev/null +++ b/tests/fixtures/projects/demo-package/my_package/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/tests/fixtures/projects/demo-package/my_package/data.json b/tests/fixtures/projects/demo-package/my_package/data.json new file mode 100644 index 0000000000000000000000000000000000000000..68372a39f38fbc281e4e5fea28de51ba4556904b --- /dev/null +++ b/tests/fixtures/projects/demo-package/my_package/data.json @@ -0,0 +1 @@ +{"name": "demo-module"} diff --git a/tests/fixtures/projects/demo-package/pyproject.toml b/tests/fixtures/projects/demo-package/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..2afb228757df16ba3c2eb5c1448aa471e529ea86 --- /dev/null +++ b/tests/fixtures/projects/demo-package/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +build-backend = "pdm.builders.api" +requires = ["pdm"] + +[tool] +[tool.pdm] +author = "frostming " +description = "" +homepage = "" +license = "MIT" +name = "demo-package" +python_requires = ">=3.5" +version = { from = "my_package/__init__.py" } +readme = "README.md" + +[tool.pdm.dependencies] + +[tool.pdm.dev-dependencies] diff --git a/tests/fixtures/projects/demo-package/single_module.py b/tests/fixtures/projects/demo-package/single_module.py new file mode 100644 index 0000000000000000000000000000000000000000..8cde7829c178ede96040e03f17c416d15bdacd01 --- /dev/null +++ b/tests/fixtures/projects/demo-package/single_module.py @@ -0,0 +1 @@ +print("hello world") diff --git a/tests/fixtures/projects/demo-src-package/LICENSE b/tests/fixtures/projects/demo-src-package/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d1e1072ee5e1d109c15b6fd18756aedc2a401840 --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/LICENSE @@ -0,0 +1 @@ +MIT License diff --git a/tests/fixtures/projects/demo-src-package/README.md b/tests/fixtures/projects/demo-src-package/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b71cf80043e998e80c7d08b9e2abbf866db1ec74 --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/README.md @@ -0,0 +1 @@ +# This is a demo module diff --git a/tests/fixtures/projects/demo-src-package/__init__.py b/tests/fixtures/projects/demo-src-package/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/fixtures/projects/demo-src-package/data_out.json b/tests/fixtures/projects/demo-src-package/data_out.json new file mode 100644 index 0000000000000000000000000000000000000000..7c4c2f5ce2c6bab4bf622b4489306de7100142d9 --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/data_out.json @@ -0,0 +1 @@ +{"name": "foo"} diff --git a/tests/fixtures/projects/demo-src-package/pyproject.toml b/tests/fixtures/projects/demo-src-package/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..02343c38cd218bd4a43a54983f32840dac7f346b --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/pyproject.toml @@ -0,0 +1,17 @@ +[build-system] +build-backend = "pdm.builders.api" +requires = ["pdm"] + +[tool] +[tool.pdm] +author = "frostming " +description = "" +homepage = "" +license = "MIT" +name = "demo-package" +python_requires = ">=3.5" +version = { from = "src/my_package/__init__.py" } + +[tool.pdm.dependencies] + +[tool.pdm.dev-dependencies] diff --git a/tests/fixtures/projects/demo-src-package/single_module.py b/tests/fixtures/projects/demo-src-package/single_module.py new file mode 100644 index 0000000000000000000000000000000000000000..8cde7829c178ede96040e03f17c416d15bdacd01 --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/single_module.py @@ -0,0 +1 @@ +print("hello world") diff --git a/tests/fixtures/projects/demo-src-package/src/__init__.py b/tests/fixtures/projects/demo-src-package/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/fixtures/projects/demo-src-package/src/my_package/__init__.py b/tests/fixtures/projects/demo-src-package/src/my_package/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3dc1f76bc69e3f559bee6253b24fc93acee9e1f9 --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/src/my_package/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/tests/fixtures/projects/demo-src-package/src/my_package/data.json b/tests/fixtures/projects/demo-src-package/src/my_package/data.json new file mode 100644 index 0000000000000000000000000000000000000000..68372a39f38fbc281e4e5fea28de51ba4556904b --- /dev/null +++ b/tests/fixtures/projects/demo-src-package/src/my_package/data.json @@ -0,0 +1 @@ +{"name": "demo-module"} diff --git a/tests/fixtures/projects/test-plugin-pdm/setup.py b/tests/fixtures/projects/test-plugin-pdm/setup.py deleted file mode 100644 index 6228f4e99d909cacbd0d1488fabd938596ede2ca..0000000000000000000000000000000000000000 --- a/tests/fixtures/projects/test-plugin-pdm/setup.py +++ /dev/null @@ -1,9 +0,0 @@ -from setuptools import setup - - -setup( - name="test-plugin", - version="0.0.1", - py_modules=["hello"], - entry_points={"pdm.plugin": ["hello = hello:main"]}, -)