Skip to content

Commit 9b3893a

Browse files
authored
trust empty requires dist with modern metadata (#9078)
1 parent a562bd1 commit 9b3893a

File tree

6 files changed

+124
-13
lines changed

6 files changed

+124
-13
lines changed

src/poetry/inspection/info.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import pkginfo
1616

1717
from build import BuildBackendException
18+
from poetry.core.constraints.version import Version
19+
from poetry.core.factory import Factory
1820
from poetry.core.packages.dependency import Dependency
1921
from poetry.core.packages.package import Package
2022
from poetry.core.pyproject.toml import PyProjectTOML
@@ -23,7 +25,6 @@
2325
from poetry.core.version.markers import InvalidMarker
2426
from poetry.core.version.requirements import InvalidRequirement
2527

26-
from poetry.factory import Factory
2728
from poetry.utils.helpers import extractall
2829
from poetry.utils.isolated_build import isolated_builder
2930
from poetry.utils.setup_reader import SetupReader
@@ -39,6 +40,8 @@
3940

4041
logger = logging.getLogger(__name__)
4142

43+
DYNAMIC_METADATA_VERSION = Version.parse("2.2")
44+
4245

4346
class PackageInfoError(ValueError):
4447
def __init__(self, path: Path, *reasons: BaseException | str) -> None:
@@ -216,6 +219,43 @@ def to_package(
216219

217220
return package
218221

222+
@classmethod
223+
def _requirements_from_distribution(
224+
cls,
225+
dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel,
226+
) -> list[str] | None:
227+
"""
228+
Helper method to extract package requirements from a `pkginfo.Distribution`
229+
instance.
230+
231+
:param dist: The distribution instance to extract requirements from.
232+
"""
233+
# If the distribution lists requirements, we use those.
234+
#
235+
# If the distribution does not list requirements, but the metadata is new enough
236+
# to specify that this is because there definitely are none: then we return an
237+
# empty list.
238+
#
239+
# If there is a requires.txt, we use that.
240+
if dist.requires_dist:
241+
return list(dist.requires_dist)
242+
243+
if dist.metadata_version is not None:
244+
metadata_version = Version.parse(dist.metadata_version)
245+
if (
246+
metadata_version >= DYNAMIC_METADATA_VERSION
247+
and "Requires-Dist" not in dist.dynamic
248+
):
249+
return []
250+
251+
requires = Path(dist.filename) / "requires.txt"
252+
if requires.exists():
253+
text = requires.read_text(encoding="utf-8")
254+
requirements = parse_requires(text)
255+
return requirements
256+
257+
return None
258+
219259
@classmethod
220260
def _from_distribution(
221261
cls, dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel
@@ -226,15 +266,7 @@ def _from_distribution(
226266
227267
:param dist: The distribution instance to parse information from.
228268
"""
229-
requirements = None
230-
231-
if dist.requires_dist:
232-
requirements = list(dist.requires_dist)
233-
else:
234-
requires = Path(dist.filename) / "requires.txt"
235-
if requires.exists():
236-
text = requires.read_text(encoding="utf-8")
237-
requirements = parse_requires(text)
269+
requirements = cls._requirements_from_distribution(dist)
238270

239271
info = cls(
240272
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.3
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: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,30 @@ def test_info_from_setup_cfg(demo_setup_cfg: Path) -> None:
272272

273273

274274
def test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None:
275-
info = PackageInfo.from_directory(
276-
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps",
277-
disable_build=True,
275+
info = PackageInfo.from_metadata_directory(
276+
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps"
277+
)
278+
assert info is not None
279+
assert info.name == "demo"
280+
assert info.version == "0.1.0"
281+
assert info.requires_dist is None
282+
283+
284+
def test_info_no_setup_pkg_info_no_deps_for_sure(fixture_dir: FixtureDirGetter) -> None:
285+
info = PackageInfo.from_metadata_directory(
286+
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_for_sure",
278287
)
288+
assert info is not None
289+
assert info.name == "demo"
290+
assert info.version == "0.1.0"
291+
assert info.requires_dist == []
292+
293+
294+
def test_info_no_setup_pkg_info_no_deps_dynamic(fixture_dir: FixtureDirGetter) -> None:
295+
info = PackageInfo.from_metadata_directory(
296+
fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps_dynamic",
297+
)
298+
assert info is not None
279299
assert info.name == "demo"
280300
assert info.version == "0.1.0"
281301
assert info.requires_dist is None

0 commit comments

Comments
 (0)