Skip to content

Commit c096436

Browse files
committed
Support extras and python on poetry deps
1 parent 271a5a6 commit c096436

File tree

2 files changed

+65
-21
lines changed

2 files changed

+65
-21
lines changed

metadata_please/source_checkout.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
Notably, does not read setup.py or attempt to emulate anything that can't be read staticly.
1212
"""
13-
13+
import re
1414
from pathlib import Path
1515

1616
try:
@@ -24,16 +24,29 @@
2424

2525
from .types import BasicMetadata
2626

27+
OPERATOR_RE = re.compile(r"([<>=~]+)(\d.*)")
28+
29+
30+
def combine_markers(*markers: str) -> str:
31+
filtered_markers = [m for m in markers if m and m.strip()]
32+
if len(filtered_markers) == 0:
33+
return ""
34+
elif len(filtered_markers) == 1:
35+
return filtered_markers[0]
36+
else:
37+
return " and ".join(f"({m})" for m in filtered_markers)
38+
2739

28-
def merge_markers(extra_name: str, value: str) -> str:
40+
def merge_extra_marker(extra_name: str, value: str) -> str:
2941
"""Simulates what a dist-info requirement string would look like if also restricted to an extra."""
3042
if ";" not in value:
3143
return f'{value} ; extra == "{extra_name}"'
3244
else:
3345
a, _, b = value.partition(";")
3446
a = a.strip()
3547
b = b.strip()
36-
return f'{a} ; ({b}) and extra == "{extra_name}"'
48+
c = f'extra == "{extra_name}"'
49+
return f"{a} ; {combine_markers(b, c)}"
3750

3851

3952
def from_source_checkout(path: Path) -> bytes:
@@ -61,7 +74,7 @@ def from_pep621_checkout(path: Path) -> bytes:
6174
extra_name = canonicalize_name(k)
6275
buf.append(f"Provides-Extra: {extra_name}\n")
6376
for i in v:
64-
buf.append("Requires-Dist: " + merge_markers(extra_name, i) + "\n")
77+
buf.append("Requires-Dist: " + merge_extra_marker(extra_name, i) + "\n")
6578

6679
return "".join(buf).encode("utf-8")
6780

@@ -127,10 +140,21 @@ def from_poetry_checkout(path: Path) -> bytes:
127140
extras = "[%s]" % (",".join(v["extras"]))
128141
else:
129142
extras = ""
143+
markers = v.get("markers", "")
144+
python = v.get("python", "")
145+
if python:
146+
m = OPERATOR_RE.fullmatch(python)
147+
assert m is not None
148+
# TODO do ^/~ work on python version?
149+
python = f"python_version {m.group(1)} '{m.group(2)}'"
150+
markers = combine_markers(markers, python)
151+
if markers:
152+
markers = " ; " + markers
130153
optional = v.get("optional", False)
131154
else:
132155
version = v
133156
extras = ""
157+
markers = ""
134158
optional = False
135159

136160
if not version:
@@ -150,17 +174,18 @@ def from_poetry_checkout(path: Path) -> bytes:
150174
version = "==" + version
151175

152176
if optional:
153-
saved_extra_constraints[k] = f"{extras}{version}"
177+
saved_extra_constraints[k] = (f"{extras}{version}", markers)
154178
else:
155-
buf.append(f"Requires-Dist: {k}{extras}{version}\n")
179+
buf.append(f"Requires-Dist: {k}{extras}{version}{markers}\n")
156180

157181
for k, v in doc.get("tool", {}).get("poetry", {}).get("extras", {}).items():
158182
k = canonicalize_name(k)
159183
buf.append(f"Provides-Extra: {k}\n")
160184
for vi in v:
161185
vi = canonicalize_name(vi)
186+
constraints, markers = saved_extra_constraints[vi]
162187
buf.append(
163-
f"Requires-Dist: {vi}{merge_markers(k, saved_extra_constraints[vi])}"
188+
f"Requires-Dist: {vi}{constraints}{merge_extra_marker(k, markers)}"
164189
)
165190

166191
return "".join(buf).encode("utf-8")
@@ -195,7 +220,9 @@ def from_setup_cfg_checkout(path: Path) -> bytes:
195220
for i in v.splitlines():
196221
i = i.strip()
197222
if i:
198-
buf.append("Requires-Dist: " + merge_markers(extra_name, i) + "\n")
223+
buf.append(
224+
"Requires-Dist: " + merge_extra_marker(extra_name, i) + "\n"
225+
)
199226

200227
return "".join(buf).encode("utf-8")
201228

metadata_please/tests/source_checkout.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,25 @@ def test_pep621_extras(self) -> None:
2121
Path(d, "pyproject.toml").write_text(
2222
"""\
2323
[project]
24-
dependencies = ["x"]
24+
dependencies = ["x", "y ; platform_system != 'Windows'"]
2525
2626
[project.optional-dependencies]
2727
dev = ["Foo <= 2"]
28+
marker = ["Bar ; python_version < '3'", "Baz <= 2; python_version < '3'"]
2829
"""
2930
)
31+
rv = basic_metadata_from_source_checkout(Path(d))
3032
self.assertEqual(
31-
BasicMetadata(["x", 'Foo <= 2 ; extra == "dev"'], frozenset(["dev"])),
32-
basic_metadata_from_source_checkout(Path(d)),
33+
[
34+
"x",
35+
"y ; platform_system != 'Windows'",
36+
'Foo <= 2 ; extra == "dev"',
37+
"Bar ; (python_version < '3') and (extra == \"marker\")",
38+
"Baz <= 2 ; (python_version < '3') and (extra == \"marker\")",
39+
],
40+
rv.reqs,
3341
)
42+
self.assertEqual(frozenset({"dev", "marker"}), rv.provides_extra)
3443

3544
def test_poetry_full(self) -> None:
3645
with tempfile.TemporaryDirectory() as d:
@@ -45,6 +54,8 @@ def test_poetry_full(self) -> None:
4554
c = "~1.2.3"
4655
c2 = "~1.2"
4756
c3 = "~1"
57+
d = {version="2", python="<3.11"}
58+
e = {version="2", markers="sys_platform == 'darwin'"}
4859
skipped = {git = "..."}
4960
complex = {extras=["bar", "baz"], version="2"}
5061
opt = { version = "^2.9", optional = true}
@@ -64,6 +75,8 @@ def test_poetry_full(self) -> None:
6475
"c>=1.2.3,<1.3",
6576
"c2>=1.2,<1.3",
6677
"c3>=1,<2",
78+
"d==2 ; python_version < '3.11'",
79+
"e==2 ; sys_platform == 'darwin'",
6780
"complex[bar,baz]==2",
6881
'opt>=2.9,<3 ; extra == "foo"',
6982
],
@@ -98,17 +111,21 @@ def test_setuptools_extras(self) -> None:
98111
# comment after
99112
marker =
100113
Bar ; python_version < "3"
114+
Baz <= 2; python_version < "3"
101115
"""
102116
)
117+
rv = basic_metadata_from_source_checkout(Path(d))
103118
self.assertEqual(
104-
BasicMetadata(
105-
[
106-
"x",
107-
"y",
108-
'Foo <= 2 ; extra == "dev"',
109-
'Bar ; (python_version < "3") and extra == "marker"',
110-
],
111-
frozenset(["dev", "marker"]),
112-
),
113-
basic_metadata_from_source_checkout(Path(d)),
119+
[
120+
"x",
121+
"y",
122+
'Foo <= 2 ; extra == "dev"',
123+
'Bar ; (python_version < "3") and (extra == "marker")',
124+
'Baz <= 2 ; (python_version < "3") and (extra == "marker")',
125+
],
126+
rv.reqs,
127+
)
128+
self.assertEqual(
129+
frozenset({"dev", "marker"}),
130+
rv.provides_extra,
114131
)

0 commit comments

Comments
 (0)