def test_make_setup(): poetry = Factory().create_poetry(project("complete")) builder = SdistBuilder(poetry) setup = builder.build_setup() setup_ast = ast.parse(setup) setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)] ns = {} exec(compile(setup_ast, filename="setup.py", mode="exec"), ns) assert ns["packages"] == [ "my_package", "my_package.sub_pkg1", "my_package.sub_pkg2", "my_package.sub_pkg3", ] assert ns["install_requires"] == [ "cachy[msgpack]>=0.2.0,<0.3.0", "cleo>=0.6,<0.7" ] assert ns["entry_points"] == { "console_scripts": [ "extra-script = my_package.extra:main[time]", "my-2nd-script = my_package:main2", "my-script = my_package:main", ] } assert ns["extras_require"] == { 'time:python_version ~= "2.7" and sys_platform == "win32" or python_version in "3.4 3.5"': ["pendulum>=1.4,<2.0"] }
def test_src_excluded_nested_data(): module_path = fixtures_dir / "exclude_nested_data_toml" poetry = Factory().create_poetry(module_path) builder = SdistBuilder(poetry) builder.build() sdist = module_path / "dist" / "my-package-1.2.3.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: names = tar.getnames() assert len(names) == len(set(names)) assert "my-package-1.2.3/LICENSE" in names assert "my-package-1.2.3/README.rst" in names assert "my-package-1.2.3/pyproject.toml" in names assert "my-package-1.2.3/setup.py" in names assert "my-package-1.2.3/PKG-INFO" in names assert "my-package-1.2.3/my_package/__init__.py" in names assert "my-package-1.2.3/my_package/data/sub_data/data2.txt" not in names assert "my-package-1.2.3/my_package/data/sub_data/data3.txt" not in names assert "my-package-1.2.3/my_package/data/data1.txt" not in names assert "my-package-1.2.3/my_package/data/data2.txt" in names assert "my-package-1.2.3/my_package/puplic/publicdata.txt" in names assert "my-package-1.2.3/my_package/public/item1/itemdata1.txt" not in names assert ( "my-package-1.2.3/my_package/public/item1/subitem/subitemdata.txt" not in names) assert "my-package-1.2.3/my_package/public/item2/itemdata2.txt" not in names
def _setup_build(self) -> None: builder = SdistBuilder(self._poetry) setup = self._path / "setup.py" has_setup = setup.exists() if has_setup: self._io.write_error_line( "<warning>A setup.py file already exists. Using it.</warning>") else: with setup.open("w", encoding="utf-8") as f: f.write(decode(builder.build_setup())) try: if self._env.pip_version < Version.from_parts(19, 0): pip_install(self._path, self._env, upgrade=True, editable=True) else: # Temporarily rename pyproject.toml shutil.move(str(self._poetry.file), str(self._poetry.file.with_suffix(".tmp"))) try: pip_install(self._path, self._env, upgrade=True, editable=True) finally: shutil.move( str(self._poetry.file.with_suffix(".tmp")), str(self._poetry.file), ) finally: if not has_setup: os.remove(str(setup))
def _setup_build(self): builder = SdistBuilder(self._poetry) setup = self._path / "setup.py" has_setup = setup.exists() if has_setup: self._io.write_line( "<warning>A setup.py file already exists. Using it.</warning>") else: with setup.open("w", encoding="utf-8") as f: f.write(decode(builder.build_setup())) try: if self._env.pip_version < Version(19, 0): self._env.run_pip("install", "-e", str(self._path), "--no-deps") else: # Temporarily rename pyproject.toml shutil.move(str(self._poetry.file), str(self._poetry.file.with_suffix(".tmp"))) try: self._env.run_pip("install", "-e", str(self._path), "--no-deps") finally: shutil.move( str(self._poetry.file.with_suffix(".tmp")), str(self._poetry.file), ) finally: if not has_setup: os.remove(str(setup))
def test_proper_python_requires_if_two_digits_precision_version_specified(): poetry = Factory().create_poetry(project("simple_version")) builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() parsed = p.parsestr(pkg_info.decode()) assert parsed["Requires-Python"] == ">=3.6,<3.7"
def test_default_with_excluded_data(mocker): # Patch git module to return specific excluded files p = mocker.patch("poetry.core.vcs.git.Git.get_ignored_files") p.return_value = [ ( ( Path(__file__).parent / "fixtures" / "default_with_excluded_data" / "my_package" / "data" / "sub_data" / "data2.txt" ) .relative_to(project("default_with_excluded_data")) .as_posix() ) ] poetry = Factory().create_poetry(project("default_with_excluded_data")) builder = SdistBuilder(poetry) # Check setup.py setup = builder.build_setup() setup_ast = ast.parse(setup) setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)] ns = {} exec(compile(setup_ast, filename="setup.py", mode="exec"), ns) assert "package_dir" not in ns assert ns["packages"] == ["my_package"] assert ns["package_data"] == { "": ["*"], "my_package": ["data/*", "data/sub_data/data3.txt"], } builder.build() sdist = ( fixtures_dir / "default_with_excluded_data" / "dist" / "my-package-1.2.3.tar.gz" ) assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: names = tar.getnames() assert len(names) == len(set(names)) assert "my-package-1.2.3/LICENSE" in names assert "my-package-1.2.3/README.rst" in names assert "my-package-1.2.3/my_package/__init__.py" in names assert "my-package-1.2.3/my_package/data/data1.txt" in names assert "my-package-1.2.3/pyproject.toml" in names assert "my-package-1.2.3/setup.py" in names assert "my-package-1.2.3/PKG-INFO" in names # all last modified times should be set to a valid timestamp for tarinfo in tar.getmembers(): assert 0 < tarinfo.mtime
def test_make_pkg_info(mocker): get_metadata_content = mocker.patch( "poetry.core.masonry.builders.builder.Builder.get_metadata_content") poetry = Factory().create_poetry(project("complete")) builder = SdistBuilder(poetry) builder.build_pkg_info() assert get_metadata_content.called
def test_make_pkg_info_any_python(): poetry = Factory().create_poetry(project("module1")) builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() parsed = p.parsestr(to_str(pkg_info)) assert "Requires-Python" not in parsed
def test_proper_python_requires_if_three_digits_precision_version_specified(): poetry = Factory().create_poetry(project("single_python")) builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() parsed = p.parsestr(to_str(pkg_info)) assert parsed["Requires-Python"] == "==2.7.15"
def test_prelease(): poetry = Factory().create_poetry(project("prerelease")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "prerelease" / "dist" / "prerelease-0.1b1.tar.gz" assert sdist.exists()
def test_module(): poetry = Factory().create_poetry(project("module1")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "module1" / "dist" / "module1-0.1.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "module1-0.1/module1.py" in tar.getnames()
def test_package(): poetry = Factory().create_poetry(project("complete")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "complete" / "dist" / "my-package-1.2.3.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "my-package-1.2.3/LICENSE" in tar.getnames()
def test_excluded_subpackage(): poetry = Factory().create_poetry(project("excluded_subpackage")) builder = SdistBuilder(poetry) setup = builder.build_setup() setup_ast = ast.parse(setup) setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)] ns = {} exec(compile(setup_ast, filename="setup.py", mode="exec"), ns) assert ns["packages"] == ["example"]
def test_with_c_extensions_src_layout(): poetry = Factory().create_poetry(project("src_extended")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "extended-0.1/build.py" in tar.getnames() assert "extended-0.1/src/extended/extended.c" in tar.getnames()
def test_sdist_mtime_zero(): poetry = Factory().create_poetry(project("module1")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "module1" / "dist" / "module1-0.1.tar.gz" assert sdist.exists() with gzip.open(str(sdist), "rb") as gz: gz.read(100) assert gz.mtime == 0
def test_split_source(): root = fixtures_dir / "split_source" poetry = Factory().create_poetry(root) builder = SdistBuilder(poetry) # Check setup.py setup = builder.build_setup() setup_ast = ast.parse(setup) setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)] ns = {} exec(compile(setup_ast, filename="setup.py", mode="exec"), ns) assert "" in ns["package_dir"] and "module_b" in ns["package_dir"]
def test_includes(): poetry = Factory().create_poetry(project("with-include")) builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "with-include" / "dist" / "with-include-1.2.3.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "with-include-1.2.3/extra_dir/vcs_excluded.txt" in tar.getnames( ) assert "with-include-1.2.3/notes.txt" in tar.getnames()
def test_make_pkg_info_multi_constraints_dependency(): poetry = Factory().create_poetry( Path(__file__).parent.parent.parent / "fixtures" / "project_with_multi_constraints_dependency") builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() parsed = p.parsestr(to_str(pkg_info)) requires = parsed.get_all("Requires-Dist") assert requires == [ 'pendulum (>=1.5,<2.0); python_version < "3.4"', 'pendulum (>=2.0,<3.0); python_version >= "3.4" and python_version < "4.0"', ]
def test_sdist_package_pep_561_stub_only(): root = fixtures_dir / "pep_561_stub_only" poetry = Factory().create_poetry(root) builder = SdistBuilder(poetry) builder.build() sdist = root / "dist" / "pep-561-stubs-0.1.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: names = tar.getnames() assert "pep-561-stubs-0.1/pkg-stubs/__init__.pyi" in names assert "pep-561-stubs-0.1/pkg-stubs/module.pyi" in names assert "pep-561-stubs-0.1/pkg-stubs/subpkg/__init__.pyi" in names
def test_includes_with_inline_table(): poetry = Factory().create_poetry(project("with_include_inline_table")) builder = SdistBuilder(poetry) builder.build() sdist = (fixtures_dir / "with_include_inline_table" / "dist" / "with-include-1.2.3.tar.gz") assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "with-include-1.2.3/wheel_only.txt" not in tar.getnames() assert "with-include-1.2.3/tests/__init__.py" in tar.getnames() assert "with-include-1.2.3/tests/test_foo/test.py" in tar.getnames()
def test_sdist_reproducibility(): poetry = Factory().create_poetry(project("complete")) hashes = set() for _ in range(2): builder = SdistBuilder(poetry) builder.build() sdist = fixtures_dir / "complete" / "dist" / "my-package-1.2.3.tar.gz" assert sdist.exists() hashes.add(hashlib.sha256(sdist.read_bytes()).hexdigest()) assert len(hashes) == 1
def _install_directory(self, operation: Install | Update) -> int: from poetry.factory import Factory package = operation.package operation_message = self.get_operation_message(operation) message = ( f" <fg=blue;options=bold>•</> {operation_message}:" " <info>Building...</info>" ) self._write(operation, message) assert package.source_url is not None if package.root_dir: req = package.root_dir / package.source_url else: req = Path(package.source_url).resolve(strict=False) pyproject = PyProjectTOML(os.path.join(req, "pyproject.toml")) if pyproject.is_poetry_project(): # Even if there is a build system specified # some versions of pip (< 19.0.0) don't understand it # so we need to check the version of pip to know # if we can rely on the build system legacy_pip = ( self._env.pip_version < self._env.pip_version.__class__.from_parts(19, 0, 0) ) package_poetry = Factory().create_poetry(pyproject.file.path.parent) builder: Builder if package.develop and not package_poetry.package.build_script: from poetry.masonry.builders.editable import EditableBuilder # This is a Poetry package in editable mode # we can use the EditableBuilder without going through pip # to install it, unless it has a build script. builder = EditableBuilder(package_poetry, self._env, NullIO()) builder.build() return 0 elif legacy_pip or package_poetry.package.build_script: from poetry.core.masonry.builders.sdist import SdistBuilder # We need to rely on creating a temporary setup.py # file since the version of pip does not support # build-systems # We also need it for non-PEP-517 packages builder = SdistBuilder(package_poetry) with builder.setup_py(): if package.develop: return self.pip_install(req, upgrade=True, editable=True) return self.pip_install(req, upgrade=True) if package.develop: return self.pip_install(req, upgrade=True, editable=True) return self.pip_install(req, upgrade=True)
def test_find_files_to_add(): poetry = Factory().create_poetry(project("complete")) builder = SdistBuilder(poetry) result = [f.relative_to_source_root() for f in builder.find_files_to_add()] assert sorted(result) == sorted([ Path("LICENSE"), Path("README.rst"), Path("my_package/__init__.py"), Path("my_package/data1/test.json"), Path("my_package/sub_pkg1/__init__.py"), Path("my_package/sub_pkg2/__init__.py"), Path("my_package/sub_pkg2/data2/data.json"), Path("my_package/sub_pkg3/foo.py"), Path("pyproject.toml"), ])
def install_directory(self, package: "Package") -> Union[str, int]: from cleo.io.null_io import NullIO from poetry.factory import Factory req: Path if package.root_dir: req = (package.root_dir / package.source_url).as_posix() else: req = Path(package.source_url).resolve(strict=False) pyproject = PyProjectTOML(os.path.join(req, "pyproject.toml")) if pyproject.is_poetry_project(): # Even if there is a build system specified # some versions of pip (< 19.0.0) don't understand it # so we need to check the version of pip to know # if we can rely on the build system legacy_pip = self._env.pip_version < self._env.pip_version.__class__( 19, 0, 0) package_poetry = Factory().create_poetry( pyproject.file.path.parent) if package.develop and not package_poetry.package.build_script: from poetry.masonry.builders.editable import EditableBuilder # This is a Poetry package in editable mode # we can use the EditableBuilder without going through pip # to install it, unless it has a build script. builder = EditableBuilder(package_poetry, self._env, NullIO()) builder.build() return 0 elif legacy_pip or package_poetry.package.build_script: from poetry.core.masonry.builders.sdist import SdistBuilder # We need to rely on creating a temporary setup.py # file since the version of pip does not support # build-systems # We also need it for non-PEP-517 packages builder = SdistBuilder(package_poetry) with builder.setup_py(): if package.develop: return pip_editable_install(directory=req, environment=self._env) return pip_install(path=req, environment=self._env, deps=False, upgrade=True) if package.develop: return pip_editable_install(directory=req, environment=self._env) return pip_install(path=req, environment=self._env, deps=False, upgrade=True)
def test_sdist_disable_setup_py(): module_path = fixtures_dir / "disable_setup_py" poetry = Factory().create_poetry(module_path) builder = SdistBuilder(poetry) builder.build() sdist = module_path / "dist" / "my-package-1.2.3.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert set(tar.getnames()) == { "my-package-1.2.3/README.rst", "my-package-1.2.3/pyproject.toml", "my-package-1.2.3/PKG-INFO", "my-package-1.2.3/my_package/__init__.py", }
def build_sdist( sdist_directory: str, config_settings: dict[str, Any] | None = None ) -> str: """Builds an sdist, places it in sdist_directory""" poetry = Factory().create_poetry(Path(".").resolve(), with_groups=False) path = SdistBuilder(poetry).build(Path(sdist_directory)) return path.name
def install_directory(self, package): from poetry.factory import Factory from poetry.io.null_io import NullIO if package.root_dir: req = (package.root_dir / package.source_url).as_posix() else: req = os.path.realpath(package.source_url) args = ["install", "--no-deps", "-U"] pyproject = PyProjectTOML(os.path.join(req, "pyproject.toml")) if pyproject.is_poetry_project(): # Even if there is a build system specified # some versions of pip (< 19.0.0) don't understand it # so we need to check the version of pip to know # if we can rely on the build system legacy_pip = self._env.pip_version < self._env.pip_version.__class__( 19, 0, 0) package_poetry = Factory().create_poetry( pyproject.file.path.parent) if package.develop and not package_poetry.package.build_script: from poetry.masonry.builders.editable import EditableBuilder # This is a Poetry package in editable mode # we can use the EditableBuilder without going through pip # to install it, unless it has a build script. builder = EditableBuilder(package_poetry, self._env, NullIO()) builder.build() return 0 elif legacy_pip or package_poetry.package.build_script: from poetry.core.masonry.builders.sdist import SdistBuilder # We need to rely on creating a temporary setup.py # file since the version of pip does not support # build-systems # We also need it for non-PEP-517 packages builder = SdistBuilder(package_poetry) with builder.setup_py(): if package.develop: args.append("-e") args.append(req) return self.run_pip(*args) if package.develop: args.append("-e") args.append(req) return self.run(*args)
def main(args: argparse.Namespace) -> None: input_dir = args.input_directory # create poetry things poetry = Factory().create_poetry(input_dir) sdist_builder = SdistBuilder(poetry) # generate setup.py code code = sdist_builder.build_setup().decode("UTF-8") # pull out black config config = tomli.loads(input_dir.joinpath("pyproject.toml").read_text()) black_config = config["tool"]["black"] black_config["string_normalization"] = black_config.pop( "skip_string_normalization", False ) black_config.pop("exclude", None) out = black.format_file_contents( code, fast=False, mode=black.Mode(**black_config) ) print(DOUBLE_PIPE_REGEX.sub("|", out), file=args.output_file, end="")
def test_find_packages(): poetry = Factory().create_poetry(project("complete")) builder = SdistBuilder(poetry) base = project("complete") include = PackageInclude(base, "my_package") pkg_dir, packages, pkg_data = builder.find_packages(include) assert pkg_dir is None assert packages == [ "my_package", "my_package.sub_pkg1", "my_package.sub_pkg2", "my_package.sub_pkg3", ] assert pkg_data == { "": ["*"], "my_package": ["data1/*"], "my_package.sub_pkg2": ["data2/*"], } poetry = Factory().create_poetry(project("source_package")) builder = SdistBuilder(poetry) base = project("source_package") include = PackageInclude(base, "package_src", source="src") pkg_dir, packages, pkg_data = builder.find_packages(include) assert pkg_dir == str(base / "src") assert packages == ["package_src"] assert pkg_data == {"": ["*"]}
def test_with_src_module_file(): poetry = Factory().create_poetry(project("source_file")) builder = SdistBuilder(poetry) # Check setup.py setup = builder.build_setup() setup_ast = ast.parse(setup) setup_ast.body = [n for n in setup_ast.body if isinstance(n, ast.Assign)] ns = {} exec(compile(setup_ast, filename="setup.py", mode="exec"), ns) assert ns["package_dir"] == {"": "src"} assert ns["modules"] == ["module_src"] builder.build() sdist = fixtures_dir / "source_file" / "dist" / "module-src-0.1.tar.gz" assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: assert "module-src-0.1/src/module_src.py" in tar.getnames()