diff --git a/src/poetry/inspection/info.py b/src/poetry/inspection/info.py index 32575c4bd99..53d867e9684 100644 --- a/src/poetry/inspection/info.py +++ b/src/poetry/inspection/info.py @@ -13,6 +13,7 @@ import pkginfo +from poetry.core.constraints.version import Version from poetry.core.factory import Factory from poetry.core.packages.dependency import Dependency from poetry.core.packages.package import Package @@ -57,6 +58,8 @@ PEP517_META_BUILD_DEPS = ["build==1.1.1", "pyproject_hooks==1.0.0"] +DYNAMIC_METADATA_VERSION = Version.parse("2.2") + class PackageInfoError(ValueError): def __init__(self, path: Path, *reasons: BaseException | str) -> None: @@ -234,6 +237,43 @@ def to_package( return package + @classmethod + def _requirements_from_distribution( + cls, + dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel, + ) -> list[str] | None: + """ + Helper method to extract package requirements from a `pkginfo.Distribution` + instance. + + :param dist: The distribution instance to extract requirements from. + """ + # If the distribution lists requirements, we use those. + # + # If the distribution does not list requirements, but the metadata is new enough + # to specify that this is because there definitely are none: then we return an + # empty list. + # + # If there is a requires.txt, we use that. + if dist.requires_dist: + return list(dist.requires_dist) + + if dist.metadata_version is not None: + metadata_version = Version.parse(dist.metadata_version) + if ( + metadata_version >= DYNAMIC_METADATA_VERSION + and "Requires-Dist" not in dist.dynamic + ): + return [] + + requires = Path(dist.filename) / "requires.txt" + if requires.exists(): + text = requires.read_text(encoding="utf-8") + requirements = parse_requires(text) + return requirements + + return None + @classmethod def _from_distribution( cls, dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel @@ -244,15 +284,7 @@ def _from_distribution( :param dist: The distribution instance to parse information from. """ - requirements = None - - if dist.requires_dist: - requirements = list(dist.requires_dist) - else: - requires = Path(dist.filename) / "requires.txt" - if requires.exists(): - text = requires.read_text(encoding="utf-8") - requirements = parse_requires(text) + requirements = cls._requirements_from_distribution(dist) info = cls( name=dist.name, diff --git a/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/PKG-INFO b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/PKG-INFO new file mode 100644 index 00000000000..325d14b3bf9 --- /dev/null +++ b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 2.2 +Name: demo +Version: 0.1.0 +Summary: Demo project. +Home-page: https://github.com/demo/demo +Author: Sébastien Eustace +Author-email: sebastien@eustace.io +License: MIT +Description: UNKNOWN +Platform: UNKNOWN +Dynamic: Requires-Dist diff --git a/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/pyproject.toml b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/pyproject.toml new file mode 100644 index 00000000000..0f09fb96fc4 --- /dev/null +++ b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/pyproject.toml @@ -0,0 +1,19 @@ +# this was copied over and modified from orjson project's pyproject.toml +# https://github.com/ijl/orjson/blob/master/pyproject.toml +[project] +name = "demo" +repository = "https://github.com/demo/demo" + +[build-system] +build-backend = "maturin" +requires = ["maturin>=0.8.1,<0.9"] + +[tool.maturin] +manylinux = "off" +sdist-include = ["Cargo.lock", "json/**/*"] +strip = "on" + +[tool.black] +line-length = 88 +target-version = ['py36', 'py37', 'py38'] +include = '\.pyi?$' diff --git a/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/PKG-INFO b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/PKG-INFO new file mode 100644 index 00000000000..276e07cea6c --- /dev/null +++ b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 2.2 +Name: demo +Version: 0.1.0 +Summary: Demo project. +Home-page: https://github.com/demo/demo +Author: Sébastien Eustace +Author-email: sebastien@eustace.io +License: MIT +Description: UNKNOWN +Platform: UNKNOWN diff --git a/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/pyproject.toml b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/pyproject.toml new file mode 100644 index 00000000000..0f09fb96fc4 --- /dev/null +++ b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/pyproject.toml @@ -0,0 +1,19 @@ +# this was copied over and modified from orjson project's pyproject.toml +# https://github.com/ijl/orjson/blob/master/pyproject.toml +[project] +name = "demo" +repository = "https://github.com/demo/demo" + +[build-system] +build-backend = "maturin" +requires = ["maturin>=0.8.1,<0.9"] + +[tool.maturin] +manylinux = "off" +sdist-include = ["Cargo.lock", "json/**/*"] +strip = "on" + +[tool.black] +line-length = 88 +target-version = ['py36', 'py37', 'py38'] +include = '\.pyi?$' diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index ef94aee43db..5d065f0b650 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -261,6 +261,26 @@ def test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None: assert info.requires_dist is None +def test_info_no_setup_pkg_info_no_deps_for_sure(fixture_dir: FixtureDirGetter) -> None: + info = PackageInfo.from_directory( + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_for_sure", + disable_build=True, + ) + assert info.name == "demo" + assert info.version == "0.1.0" + assert info.requires_dist == [] + + +def test_info_no_setup_pkg_info_no_deps_dynamic(fixture_dir: FixtureDirGetter) -> None: + info = PackageInfo.from_directory( + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_dynamic", + disable_build=True, + ) + assert info.name == "demo" + assert info.version == "0.1.0" + assert info.requires_dist is None + + def test_info_setup_simple(mocker: MockerFixture, demo_setup: Path) -> None: spy = mocker.spy(VirtualEnv, "run") info = PackageInfo.from_directory(demo_setup)