Skip to content

Commit 6d5409b

Browse files
committed
fix to python constraints ignored when markers present #4965
1 parent f6022ea commit 6d5409b

2 files changed

Lines changed: 186 additions & 0 deletions

File tree

src/poetry/factory.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from pathlib import Path
22
from typing import TYPE_CHECKING
3+
from typing import Any
34
from typing import Dict
45
from typing import List
56
from typing import Optional
7+
from typing import Union
8+
from warnings import warn
69

710
from cleo.io.null_io import NullIO
811
from poetry.core.factory import Factory as BaseFactory
@@ -22,6 +25,13 @@
2225

2326
from poetry.repositories.legacy_repository import LegacyRepository
2427

28+
from poetry.core.packages.types import DependencyTypes
29+
30+
import logging
31+
32+
33+
logger = logging.getLogger(__name__)
34+
2535

2636
class Factory(BaseFactory):
2737
"""
@@ -221,3 +231,154 @@ def create_pyproject_from_package(cls, package: ProjectPackage, path: Path) -> N
221231
path.joinpath("pyproject.toml").write_text(
222232
pyproject.as_string(), encoding="utf-8"
223233
)
234+
235+
@classmethod
236+
def create_dependency(
237+
cls,
238+
name: str,
239+
constraint: Union[str, Dict[str, Any]],
240+
groups: Optional[List[str]] = None,
241+
root_dir: Optional[Path] = None,
242+
) -> "DependencyTypes":
243+
# NOTE: moved and adapted from poetry-core, to fix the issue with ignored python versions
244+
from poetry.core.packages.constraints import (
245+
parse_constraint as parse_generic_constraint,
246+
)
247+
from poetry.core.packages.dependency import Dependency
248+
from poetry.core.packages.directory_dependency import DirectoryDependency
249+
from poetry.core.packages.file_dependency import FileDependency
250+
from poetry.core.packages.url_dependency import URLDependency
251+
from poetry.core.packages.utils.utils import create_nested_marker
252+
from poetry.core.packages.vcs_dependency import VCSDependency
253+
from poetry.core.version.markers import AnyMarker
254+
from poetry.core.version.markers import parse_marker
255+
256+
if groups is None:
257+
groups = ["default"]
258+
259+
if constraint is None:
260+
constraint = "*"
261+
262+
if isinstance(constraint, dict):
263+
optional = constraint.get("optional", False)
264+
python_versions = constraint.get("python")
265+
platform = constraint.get("platform")
266+
markers = constraint.get("markers")
267+
if "allows-prereleases" in constraint:
268+
message = (
269+
f'The "{name}" dependency specifies '
270+
'the "allows-prereleases" property, which is deprecated. '
271+
'Use "allow-prereleases" instead.'
272+
)
273+
warn(message, DeprecationWarning)
274+
logger.warning(message)
275+
276+
allows_prereleases = constraint.get(
277+
"allow-prereleases", constraint.get("allows-prereleases", False)
278+
)
279+
280+
if "git" in constraint:
281+
# VCS dependency
282+
dependency = VCSDependency(
283+
name,
284+
"git",
285+
constraint["git"],
286+
branch=constraint.get("branch", None),
287+
tag=constraint.get("tag", None),
288+
rev=constraint.get("rev", None),
289+
groups=groups,
290+
optional=optional,
291+
develop=constraint.get("develop", False),
292+
extras=constraint.get("extras", []),
293+
)
294+
elif "file" in constraint:
295+
file_path = Path(constraint["file"])
296+
297+
dependency = FileDependency(
298+
name,
299+
file_path,
300+
groups=groups,
301+
base=root_dir,
302+
extras=constraint.get("extras", []),
303+
)
304+
elif "path" in constraint:
305+
path = Path(constraint["path"])
306+
307+
if root_dir:
308+
is_file = root_dir.joinpath(path).is_file()
309+
else:
310+
is_file = path.is_file()
311+
312+
if is_file:
313+
dependency = FileDependency(
314+
name,
315+
path,
316+
groups=groups,
317+
optional=optional,
318+
base=root_dir,
319+
extras=constraint.get("extras", []),
320+
)
321+
else:
322+
dependency = DirectoryDependency(
323+
name,
324+
path,
325+
groups=groups,
326+
optional=optional,
327+
base=root_dir,
328+
develop=constraint.get("develop", False),
329+
extras=constraint.get("extras", []),
330+
)
331+
elif "url" in constraint:
332+
dependency = URLDependency(
333+
name,
334+
constraint["url"],
335+
groups=groups,
336+
optional=optional,
337+
extras=constraint.get("extras", []),
338+
)
339+
else:
340+
version = constraint["version"]
341+
342+
dependency = Dependency(
343+
name,
344+
version,
345+
optional=optional,
346+
groups=groups,
347+
allows_prereleases=allows_prereleases,
348+
extras=constraint.get("extras", []),
349+
)
350+
351+
if not markers:
352+
marker = AnyMarker()
353+
if python_versions:
354+
dependency.python_versions = python_versions
355+
marker = marker.intersect(
356+
parse_marker(
357+
create_nested_marker(
358+
"python_version", dependency.python_constraint
359+
)
360+
)
361+
)
362+
363+
if platform:
364+
marker = marker.intersect(
365+
parse_marker(
366+
create_nested_marker(
367+
"sys_platform", parse_generic_constraint(platform)
368+
)
369+
)
370+
)
371+
else:
372+
marker = parse_marker(markers)
373+
# patch to not ignore python versions with markers
374+
if python_versions:
375+
dependency.python_versions = python_versions
376+
377+
if not marker.is_any():
378+
dependency.marker = marker
379+
380+
dependency.source_name = constraint.get("source")
381+
else:
382+
dependency = Dependency(name, constraint, groups=groups)
383+
384+
return dependency

tests/puzzle/test_solver.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,3 +3076,28 @@ def test_solver_should_not_raise_errors_for_irrelevant_transitive_python_constra
30763076
{"job": "install", "package": virtualenv},
30773077
],
30783078
)
3079+
3080+
3081+
def test_collect_python_constraint_when_markers_appear(
3082+
solver: Solver, repo: Repository, package: ProjectPackage
3083+
):
3084+
solver.provider.set_package_python_versions("3.8")
3085+
3086+
package_a = get_package("a", "2.0")
3087+
package_b = get_package("a", "1.0")
3088+
repo.add_package(package_a)
3089+
repo.add_package(package_b)
3090+
package.add_dependency(
3091+
Factory.create_dependency(
3092+
"a", {"version": "2.0", "python": "3.9", "markers": "sys_platform=='linux'"}
3093+
)
3094+
)
3095+
package.add_dependency(
3096+
Factory.create_dependency("a", {"version": "1.0", "python": "3.8"})
3097+
)
3098+
3099+
# expect: ignore a, install b
3100+
transaction = solver.solve()
3101+
packages = [p for (p, _) in transaction._result_packages]
3102+
expected_packages = [package_b]
3103+
assert packages == expected_packages

0 commit comments

Comments
 (0)