Skip to content

Commit 3153e9b

Browse files
committed
trust empty requires dist with modern metadata
1 parent 53bf89a commit 3153e9b

File tree

6 files changed

+120
-9
lines changed

6 files changed

+120
-9
lines changed

src/poetry/inspection/info.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import pkginfo
1616

17+
from poetry.core.constraints.version import Version
1718
from poetry.core.factory import Factory
1819
from poetry.core.packages.dependency import Dependency
1920
from poetry.core.packages.package import Package
@@ -58,6 +59,8 @@
5859

5960
PEP517_META_BUILD_DEPS = ["build==1.0.3", "pyproject_hooks==1.0.0"]
6061

62+
DYNAMIC_METADATA_VERSION = Version.parse("2.2")
63+
6164

6265
class PackageInfoError(ValueError):
6366
def __init__(self, path: Path, *reasons: BaseException | str) -> None:
@@ -235,6 +238,43 @@ def to_package(
235238

236239
return package
237240

241+
@classmethod
242+
def _requirements_from_distribution(
243+
cls,
244+
dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel,
245+
) -> list[str] | None:
246+
"""
247+
Helper method to extract package requirements from a `pkginfo.Distribution`
248+
instance.
249+
250+
:param dist: The distribution instance to extract requirements from.
251+
"""
252+
# If the distribution lists requirements, we use those.
253+
#
254+
# If the distribution does not list requirements, but the metadata is new enough
255+
# to specify that this is because there definitely are none: then we return an
256+
# empty list.
257+
#
258+
# If there is a requires.txt, we use that.
259+
if dist.requires_dist:
260+
return list(dist.requires_dist)
261+
262+
if dist.metadata_version is not None:
263+
metadata_version = Version.parse(dist.metadata_version)
264+
if (
265+
metadata_version >= DYNAMIC_METADATA_VERSION
266+
and "Requires-Dist" not in dist.dynamic
267+
):
268+
return []
269+
270+
requires = Path(dist.filename) / "requires.txt"
271+
if requires.exists():
272+
text = requires.read_text(encoding="utf-8")
273+
requirements = parse_requires(text)
274+
return requirements
275+
276+
return None
277+
238278
@classmethod
239279
def _from_distribution(
240280
cls, dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel
@@ -245,15 +285,7 @@ def _from_distribution(
245285
246286
:param dist: The distribution instance to parse information from.
247287
"""
248-
requirements = None
249-
250-
if dist.requires_dist:
251-
requirements = list(dist.requires_dist)
252-
else:
253-
requires = Path(dist.filename) / "requires.txt"
254-
if requires.exists():
255-
text = requires.read_text(encoding="utf-8")
256-
requirements = parse_requires(text)
288+
requirements = cls._requirements_from_distribution(dist)
257289

258290
info = cls(
259291
name=dist.name,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Metadata-Version: 2.2
2+
Name: demo
3+
Version: 0.1.0
4+
Summary: Demo project.
5+
Home-page: https://github.com/demo/demo
6+
Author: Sébastien Eustace
7+
Author-email: [email protected]
8+
License: MIT
9+
Description: UNKNOWN
10+
Platform: UNKNOWN
11+
Dynamic: Requires-Dist
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# this was copied over and modified from orjson project's pyproject.toml
2+
# https://github.com/ijl/orjson/blob/master/pyproject.toml
3+
[project]
4+
name = "demo"
5+
repository = "https://github.com/demo/demo"
6+
7+
[build-system]
8+
build-backend = "maturin"
9+
requires = ["maturin>=0.8.1,<0.9"]
10+
11+
[tool.maturin]
12+
manylinux = "off"
13+
sdist-include = ["Cargo.lock", "json/**/*"]
14+
strip = "on"
15+
16+
[tool.black]
17+
line-length = 88
18+
target-version = ['py36', 'py37', 'py38']
19+
include = '\.pyi?$'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Metadata-Version: 2.2
2+
Name: demo
3+
Version: 0.1.0
4+
Summary: Demo project.
5+
Home-page: https://github.com/demo/demo
6+
Author: Sébastien Eustace
7+
Author-email: [email protected]
8+
License: MIT
9+
Description: UNKNOWN
10+
Platform: UNKNOWN
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# this was copied over and modified from orjson project's pyproject.toml
2+
# https://github.com/ijl/orjson/blob/master/pyproject.toml
3+
[project]
4+
name = "demo"
5+
repository = "https://github.com/demo/demo"
6+
7+
[build-system]
8+
build-backend = "maturin"
9+
requires = ["maturin>=0.8.1,<0.9"]
10+
11+
[tool.maturin]
12+
manylinux = "off"
13+
sdist-include = ["Cargo.lock", "json/**/*"]
14+
strip = "on"
15+
16+
[tool.black]
17+
line-length = 88
18+
target-version = ['py36', 'py37', 'py38']
19+
include = '\.pyi?$'

tests/inspection/test_info.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,26 @@ def test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None:
261261
assert info.requires_dist is None
262262

263263

264+
def test_info_no_setup_pkg_info_no_deps_for_sure(fixture_dir: FixtureDirGetter) -> None:
265+
info = PackageInfo.from_directory(
266+
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_for_sure",
267+
disable_build=True,
268+
)
269+
assert info.name == "demo"
270+
assert info.version == "0.1.0"
271+
assert info.requires_dist == []
272+
273+
274+
def test_info_no_setup_pkg_info_no_deps_dynamic(fixture_dir: FixtureDirGetter) -> None:
275+
info = PackageInfo.from_directory(
276+
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_dynamic",
277+
disable_build=True,
278+
)
279+
assert info.name == "demo"
280+
assert info.version == "0.1.0"
281+
assert info.requires_dist is None
282+
283+
264284
def test_info_setup_simple(mocker: MockerFixture, demo_setup: Path) -> None:
265285
spy = mocker.spy(VirtualEnv, "run")
266286
info = PackageInfo.from_directory(demo_setup)

0 commit comments

Comments
 (0)