Skip to content

Commit 54fb80b

Browse files
authored
Fix Poetry req synthesis for URLs with markers. (pantsbuild#18535)
Previously the PEP-508 direct reference URL requirement synthesis ran afoul of creating ambiguous requirement strings as detailed here: pypa/packaging#456 (comment) Fixes pantsbuild#18530
1 parent 1477734 commit 54fb80b

File tree

2 files changed

+62
-49
lines changed

2 files changed

+62
-49
lines changed

src/python/pants/backend/python/macros/poetry_requirements.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def add_markers(base: str, attributes: PyprojectAttr, fp) -> str:
254254
if not markers_lookup and not python_lookup:
255255
return base
256256

257-
result = f"{base};("
257+
result = f"{base} ;("
258258

259259
if markers_lookup:
260260
result += f"{markers_lookup})"

src/python/pants/backend/python/macros/poetry_requirements_test.py

+61-48
Original file line numberDiff line numberDiff line change
@@ -107,35 +107,43 @@ def test_handle_str(test, exp) -> None:
107107
assert parse_str_version(test, proj_name="foo", file_path="", extras_str="") == f"foo {exp}"
108108

109109

110+
def assert_equal_requirements(actual: str | None, expected: str) -> None:
111+
assert actual is not None
112+
assert PipRequirement.parse(expected) == PipRequirement.parse(actual)
113+
114+
110115
def test_add_markers() -> None:
111116
attr_mark = PyprojectAttr({"markers": "platform_python_implementation == 'CPython'"})
112-
assert (
113-
add_markers("foo==1.0.0", attr_mark, "somepath")
114-
== "foo==1.0.0;(platform_python_implementation == 'CPython')"
117+
assert_equal_requirements(
118+
add_markers("foo==1.0.0", attr_mark, "somepath"),
119+
"foo==1.0.0;(platform_python_implementation == 'CPython')",
115120
)
116121

117122
attr_mark_adv = PyprojectAttr(
118123
{"markers": "platform_python_implementation == 'CPython' or sys_platform == 'win32'"}
119124
)
120-
assert (
121-
add_markers("foo==1.0.0", attr_mark_adv, "somepath")
122-
== "foo==1.0.0;(platform_python_implementation == 'CPython' or sys_platform == 'win32')"
125+
assert_equal_requirements(
126+
add_markers("foo==1.0.0", attr_mark_adv, "somepath"),
127+
"foo==1.0.0;(platform_python_implementation == 'CPython' or sys_platform == 'win32')",
123128
)
124129
attr_basic_both = PyprojectAttr({"python": "3.6"})
125130
attr_basic_both.update(attr_mark)
126131

127-
assert (
128-
add_markers("foo==1.0.0", attr_basic_both, "somepath")
129-
== "foo==1.0.0;(platform_python_implementation == 'CPython') and (python_version == '3.6')"
132+
assert_equal_requirements(
133+
add_markers("foo==1.0.0", attr_basic_both, "somepath"),
134+
"foo==1.0.0;(platform_python_implementation == 'CPython') and (python_version == '3.6')",
130135
)
131136
attr_adv_py_both = PyprojectAttr(
132137
{"python": "^3.6", "markers": "platform_python_implementation == 'CPython'"}
133138
)
134-
assert add_markers("foo==1.0.0", attr_adv_py_both, "somepath") == softwrap(
135-
"""
136-
foo==1.0.0;(platform_python_implementation == 'CPython') and
137-
(python_version >= '3.6' and python_version< '4.0')
138-
"""
139+
assert_equal_requirements(
140+
add_markers("foo==1.0.0", attr_adv_py_both, "somepath"),
141+
softwrap(
142+
"""
143+
foo==1.0.0;(platform_python_implementation == 'CPython') and
144+
(python_version >= '3.6' and python_version< '4.0')
145+
"""
146+
),
139147
)
140148

141149
attr_adv_both = PyprojectAttr(
@@ -144,11 +152,14 @@ def test_add_markers() -> None:
144152
"markers": "platform_python_implementation == 'CPython' or sys_platform == 'win32'",
145153
}
146154
)
147-
assert add_markers("foo==1.0.0", attr_adv_both, "somepath") == softwrap(
148-
"""
149-
foo==1.0.0;(platform_python_implementation == 'CPython' or
150-
sys_platform == 'win32') and (python_version >= '3.6' and python_version< '4.0')
151-
"""
155+
assert_equal_requirements(
156+
add_markers("foo==1.0.0", attr_adv_both, "somepath"),
157+
softwrap(
158+
"""
159+
foo==1.0.0;(platform_python_implementation == 'CPython' or
160+
sys_platform == 'win32') and (python_version >= '3.6' and python_version< '4.0')
161+
"""
162+
),
152163
)
153164

154165

@@ -195,9 +206,9 @@ def test_handle_git(empty_pyproject_toml: PyProjectToml) -> None:
195206
def assert_git(extra_opts: PyprojectAttr, suffix: str) -> None:
196207
attr = PyprojectAttr({"git": "https://github.com/requests/requests.git"})
197208
attr.update(extra_opts)
198-
assert (
199-
handle_dict_attr("requests", attr, empty_pyproject_toml)
200-
== f"requests @ git+https://github.com/requests/requests.git{suffix}"
209+
assert_equal_requirements(
210+
handle_dict_attr("requests", attr, empty_pyproject_toml),
211+
f"requests @ git+https://github.com/requests/requests.git{suffix}",
201212
)
202213

203214
assert_git({}, "")
@@ -212,7 +223,7 @@ def assert_git(extra_opts: PyprojectAttr, suffix: str) -> None:
212223
"python": "3.6",
213224
}
214225
),
215-
"@main;(platform_python_implementation == 'CPython') and (python_version == '3.6')",
226+
"@main ;(platform_python_implementation == 'CPython') and (python_version == '3.6')",
216227
)
217228

218229

@@ -256,53 +267,53 @@ def test_handle_path_arg(tmp_path: Path) -> None:
256267
file_attr_extras = PyprojectAttr({"path": "../../my_py_proj.whl", "extras": ["extra1"]})
257268
dir_attr = PyprojectAttr({"path": "../../my_py_proj"})
258269

259-
assert (
260-
handle_dict_attr("my_py_proj", file_attr, one_pyproject_toml)
261-
== f"my_py_proj @ file://{external_file}"
270+
assert_equal_requirements(
271+
handle_dict_attr("my_py_proj", file_attr, one_pyproject_toml),
272+
f"my_py_proj @ file://{external_file}",
262273
)
263274

264-
assert (
265-
handle_dict_attr("my_py_proj", file_attr_extras, one_pyproject_toml)
266-
== f"my_py_proj[extra1] @ file://{external_file}"
275+
assert_equal_requirements(
276+
handle_dict_attr("my_py_proj", file_attr_extras, one_pyproject_toml),
277+
f"my_py_proj[extra1] @ file://{external_file}",
267278
)
268279

269-
assert (
270-
handle_dict_attr("my_py_proj", file_attr_mark, one_pyproject_toml)
271-
== f"my_py_proj @ file://{external_file};(os_name=='darwin')"
280+
assert_equal_requirements(
281+
handle_dict_attr("my_py_proj", file_attr_mark, one_pyproject_toml),
282+
f"my_py_proj @ file://{external_file} ;(os_name=='darwin')",
272283
)
273284

274-
assert (
275-
handle_dict_attr("my_py_proj", file_attr, two_pyproject_toml)
276-
== f"my_py_proj @ file://{internal_file}"
285+
assert_equal_requirements(
286+
handle_dict_attr("my_py_proj", file_attr, two_pyproject_toml),
287+
f"my_py_proj @ file://{internal_file}",
277288
)
278289

279-
assert (
280-
handle_dict_attr("my_py_proj", dir_attr, one_pyproject_toml)
281-
== f"my_py_proj @ file://{external_project}"
290+
assert_equal_requirements(
291+
handle_dict_attr("my_py_proj", dir_attr, one_pyproject_toml),
292+
f"my_py_proj @ file://{external_project}",
282293
)
283294

284295
assert handle_dict_attr("my_py_proj", dir_attr, two_pyproject_toml) is None
285296

286297

287298
def test_handle_url_arg(empty_pyproject_toml: PyProjectToml) -> None:
288299
attr = PyprojectAttr({"url": "https://my-site.com/mydep.whl"})
289-
assert (
290-
handle_dict_attr("my_py_proj", attr, empty_pyproject_toml)
291-
== "my_py_proj @ https://my-site.com/mydep.whl"
300+
assert_equal_requirements(
301+
handle_dict_attr("my_py_proj", attr, empty_pyproject_toml),
302+
"my_py_proj @ https://my-site.com/mydep.whl",
292303
)
293304

294305
attr_with_extra = PyprojectAttr({"extras": ["extra1"]})
295306
attr_with_extra.update(attr)
296-
assert (
297-
handle_dict_attr("my_py_proj", attr_with_extra, empty_pyproject_toml)
298-
== "my_py_proj[extra1] @ https://my-site.com/mydep.whl"
307+
assert_equal_requirements(
308+
handle_dict_attr("my_py_proj", attr_with_extra, empty_pyproject_toml),
309+
"my_py_proj[extra1] @ https://my-site.com/mydep.whl",
299310
)
300311

301312
attr_with_mark = PyprojectAttr({"markers": "os_name=='darwin'"})
302313
attr_with_mark.update(attr)
303-
assert (
304-
handle_dict_attr("my_py_proj", attr_with_mark, empty_pyproject_toml)
305-
== "my_py_proj @ https://my-site.com/mydep.whl;(os_name=='darwin')"
314+
assert_equal_requirements(
315+
handle_dict_attr("my_py_proj", attr_with_mark, empty_pyproject_toml),
316+
"my_py_proj @ https://my-site.com/mydep.whl ;(os_name=='darwin')",
306317
)
307318

308319

@@ -314,7 +325,9 @@ def test_version_only(empty_pyproject_toml: PyProjectToml) -> None:
314325
def test_py_constraints(empty_pyproject_toml: PyProjectToml) -> None:
315326
def assert_py_constraints(py_req: str, suffix: str) -> None:
316327
attr = PyprojectAttr({"version": "1.2.3", "python": py_req})
317-
assert handle_dict_attr("foo", attr, empty_pyproject_toml) == f"foo ==1.2.3;{suffix}"
328+
assert_equal_requirements(
329+
handle_dict_attr("foo", attr, empty_pyproject_toml), f"foo ==1.2.3;{suffix}"
330+
)
318331

319332
assert_py_constraints("3.6", "(python_version == '3.6')")
320333
assert_py_constraints("3.6 || 3.7", "((python_version == '3.6') or (python_version == '3.7'))")

0 commit comments

Comments
 (0)