diff --git a/src/poetry/inspection/info.py b/src/poetry/inspection/info.py index 8ddd0455e5d..a088fd9c0db 100644 --- a/src/poetry/inspection/info.py +++ b/src/poetry/inspection/info.py @@ -15,6 +15,8 @@ import pkginfo from build import BuildBackendException +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 from poetry.core.pyproject.toml import PyProjectTOML @@ -23,7 +25,6 @@ from poetry.core.version.markers import InvalidMarker from poetry.core.version.requirements import InvalidRequirement -from poetry.factory import Factory from poetry.utils.helpers import extractall from poetry.utils.isolated_build import isolated_builder from poetry.utils.setup_reader import SetupReader @@ -39,6 +40,8 @@ logger = logging.getLogger(__name__) +DYNAMIC_METADATA_VERSION = Version.parse("2.2") + class PackageInfoError(ValueError): def __init__(self, path: Path, *reasons: BaseException | str) -> None: @@ -216,6 +219,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 @@ -226,15 +266,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..824b60bcd55 --- /dev/null +++ b/tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 2.3 +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 32962ff7e52..66bb1f825fb 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -272,10 +272,30 @@ def test_info_from_setup_cfg(demo_setup_cfg: Path) -> None: def test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None: - info = PackageInfo.from_directory( - fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps", - disable_build=True, + info = PackageInfo.from_metadata_directory( + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps" + ) + assert info is not None + assert info.name == "demo" + assert info.version == "0.1.0" + assert info.requires_dist is None + + +def test_info_no_setup_pkg_info_no_deps_for_sure(fixture_dir: FixtureDirGetter) -> None: + info = PackageInfo.from_metadata_directory( + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_for_sure", ) + assert info is not None + 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_metadata_directory( + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_dynamic", + ) + assert info is not None assert info.name == "demo" assert info.version == "0.1.0" assert info.requires_dist is None