Skip to content

Commit b7232ec

Browse files
committed
pylock select: full coverage and fixes
1 parent 0256dca commit b7232ec

File tree

3 files changed

+410
-108
lines changed

3 files changed

+410
-108
lines changed

src/packaging/pylock.py

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from datetime import datetime
99
from typing import TYPE_CHECKING, Any, Callable, Protocol, TypeVar, cast
1010

11-
from .markers import Marker, default_environment
11+
from .markers import Environment, Marker, default_environment
1212
from .specifiers import SpecifierSet
1313
from .tags import sys_tags
1414
from .utils import NormalizedName, is_normalized_name, parse_wheel_filename
@@ -20,7 +20,6 @@
2020

2121
from typing_extensions import Self
2222

23-
from .markers import Environment
2423
from .tags import Tag
2524

2625
_logger = logging.getLogger(__name__)
@@ -649,7 +648,7 @@ def select(
649648
tags: Sequence[Tag] | None = None,
650649
extras: Collection[str] | None = None,
651650
dependency_groups: Collection[str] | None = None,
652-
) -> Iterator[ # XXX or Iterable?
651+
) -> Iterator[
653652
tuple[
654653
Package,
655654
PackageVcs
@@ -675,8 +674,6 @@ def select(
675674
valid Pylock instances (i.e. one obtained from :meth:`Pylock.from_dict`
676675
or if constructed manually, after calling :meth:`Pylock.validate`).
677676
"""
678-
if environment is None:
679-
environment = default_environment()
680677
if tags is None:
681678
tags = list(sys_tags())
682679

@@ -686,14 +683,23 @@ def select(
686683
# #. ``extras`` SHOULD be set to the empty set by default.
687684
# #. ``dependency_groups`` SHOULD be the set created from
688685
# :ref:`pylock-default-groups` by default.
689-
env: dict[str, str | frozenset[str]] = {
690-
**cast("dict[str, str]", environment),
691-
"extras": frozenset(extras or []),
692-
"dependency_groups": frozenset(
693-
dependency_groups or self.default_groups or []
686+
env = cast(
687+
"dict[str, str | frozenset[str]]",
688+
dict(
689+
environment or {}, # Marker.evaluate will fill-up
690+
extras=frozenset(extras or []),
691+
dependency_groups=frozenset(
692+
(self.default_groups or [])
693+
if dependency_groups is None # to allow selecting no groups
694+
else dependency_groups
695+
),
694696
),
695-
}
696-
env_python_version = environment.get("python_version")
697+
)
698+
env_python_version = (
699+
environment["python_version"]
700+
if environment
701+
else default_environment()["python_version"]
702+
)
697703

698704
# #. Check if the metadata version specified by :ref:`pylock-lock-version` is
699705
# supported; an error or warning MUST be raised as appropriate.
@@ -702,25 +708,21 @@ def select(
702708
# #. If :ref:`pylock-requires-python` is specified, check that the environment
703709
# being installed for meets the requirement; an error MUST be raised if it is
704710
# not met.
705-
if self.requires_python is not None:
706-
if not env_python_version:
707-
raise PylockSelectError(
708-
f"Provided environment does not specify a Python version, "
709-
f"but the lock file requires Python {self.requires_python!r}"
710-
)
711-
if not self.requires_python.contains(env_python_version, prereleases=True):
712-
# XXX confirm prereleases=True
713-
raise PylockSelectError(
714-
f"Provided environment does not satisfy the Python version "
715-
f"requirement {self.requires_python!r}"
716-
)
711+
if self.requires_python and not self.requires_python.contains(
712+
env_python_version,
713+
prereleases=True, # XXX confirm prereleases=True
714+
):
715+
raise PylockSelectError(
716+
f"Provided environment does not satisfy the Python version "
717+
f"requirement {self.requires_python!r}"
718+
)
717719

718720
# #. If :ref:`pylock-environments` is specified, check that at least one of the
719721
# environment marker expressions is satisfied; an error MUST be raised if no
720722
# expression is satisfied.
721723
if self.environments:
722724
for env_marker in self.environments:
723-
if env_marker.evaluate(env, context="lock_file"): # XXX check context
725+
if env_marker.evaluate(env, context="lock_file"):
724726
break
725727
else:
726728
raise PylockSelectError(
@@ -733,29 +735,20 @@ def select(
733735
for package_index, package in enumerate(self.packages):
734736
# #. If :ref:`pylock-packages-marker` is specified, check if it is
735737
# satisfied;if it isn't, skip to the next package.
736-
if package.marker and not package.marker.evaluate(
737-
env, context="requirement"
738-
): # XXX check context
738+
if package.marker and not package.marker.evaluate(env, context="lock_file"):
739739
continue
740740

741741
# #. If :ref:`pylock-packages-requires-python` is specified, check if it is
742742
# satisfied; an error MUST be raised if it isn't.
743-
if package.requires_python:
744-
if not env_python_version:
745-
raise PylockSelectError(
746-
f"Provided environment does not specify a Python version, "
747-
f"but package {package.name!r} at packages[{package_index}] "
748-
f"requires Python {package.requires_python!r}"
749-
)
750-
if not package.requires_python.contains(
751-
env_python_version, prereleases=True
752-
):
753-
# XXX confirm prereleases=True
754-
raise PylockSelectError(
755-
f"Provided environment does not satisfy the Python version "
756-
f"requirement {package.requires_python!r} for package "
757-
f"{package.name!r} at packages[{package_index}]"
758-
)
743+
if package.requires_python and not package.requires_python.contains(
744+
env_python_version,
745+
prereleases=True, # XXX confirm prereleases=True
746+
):
747+
raise PylockSelectError(
748+
f"Provided environment does not satisfy the Python version "
749+
f"requirement {package.requires_python!r} for package "
750+
f"{package.name!r} at packages[{package_index}]"
751+
)
759752

760753
# #. Check that no other conflicting instance of the package has been slated
761754
# to be installed; an error about the ambiguity MUST be raised otherwise.
@@ -769,7 +762,7 @@ def select(
769762
# #. Check that the source of the package is specified appropriately (i.e.
770763
# there are no conflicting sources in the package entry);
771764
# an error MUST be raised if any issues are found.
772-
# Covered by lock.validate() above.
765+
# Covered by lock.validate() which is a precondition for this method.
773766

774767
# #. Add the package to the set of packages to install.
775768
selected_packages_by_name[package.name] = (package_index, package)
@@ -817,5 +810,5 @@ def select(
817810
yield package, package.sdist
818811

819812
else:
820-
# Covered by lock.validate() above.
821-
raise NotImplementedError
813+
# Covered by lock.validate() which is a precondition for this method.
814+
raise NotImplementedError # pragma: no cover

tests/test_pylock.py

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,24 @@
22

33
import datetime
44
import sys
5-
from dataclasses import dataclass
65
from pathlib import Path
76
from typing import Any
87

98
import pytest
109
import tomli_w
1110

12-
from packaging.markers import Environment, Marker
11+
from packaging.markers import Marker
1312
from packaging.pylock import (
1413
Package,
1514
PackageDirectory,
1615
PackageVcs,
1716
PackageWheel,
1817
Pylock,
19-
PylockSelectError,
2018
PylockUnsupportedVersionError,
2119
PylockValidationError,
2220
is_valid_pylock_path,
2321
)
2422
from packaging.specifiers import SpecifierSet
25-
from packaging.tags import Tag
2623
from packaging.utils import NormalizedName
2724
from packaging.version import Version
2825

@@ -581,60 +578,3 @@ def test_validate_attestation_identity_invalid_kind() -> None:
581578
"Unexpected type int (expected str) "
582579
"in 'packages[0].attestation-identities[0].kind'"
583580
)
584-
585-
586-
@dataclass
587-
class Platform:
588-
tags: list[Tag]
589-
environment: Environment
590-
591-
592-
_py312_linux = Platform(
593-
tags=[
594-
Tag("cp312", "cp312", "manylinux_2_17_x86_64"),
595-
Tag("py3", "none", "any"),
596-
],
597-
environment={
598-
"implementation_name": "cpython",
599-
"implementation_version": "3.12.12",
600-
"os_name": "posix",
601-
"platform_machine": "x86_64",
602-
"platform_release": "6.8.0-100-generic",
603-
"platform_system": "Linux",
604-
"platform_version": "#100-Ubuntu SMP PREEMPT_DYNAMIC",
605-
"python_full_version": "3.12.12",
606-
"platform_python_implementation": "CPython",
607-
"python_version": "3.12",
608-
"sys_platform": "linux",
609-
},
610-
)
611-
612-
613-
def test_select_smoke_test() -> None:
614-
pylock_path = Path(__file__).parent / "pylock" / "pylock.spec-example.toml"
615-
lock = Pylock.from_dict(tomllib.loads(pylock_path.read_text()))
616-
for package, dist in lock.select(
617-
tags=_py312_linux.tags,
618-
environment=_py312_linux.environment,
619-
):
620-
assert isinstance(package, Package)
621-
assert isinstance(dist, PackageWheel)
622-
623-
624-
def test_select_require_python_mismatch() -> None:
625-
pylock = Pylock(
626-
lock_version=Version("1.0"),
627-
created_by="some_tool",
628-
requires_python=SpecifierSet("==3.14.*"),
629-
packages=[],
630-
)
631-
with pytest.raises(
632-
PylockSelectError,
633-
match="Provided environment does not satisfy the Python version requirement",
634-
):
635-
list(
636-
pylock.select(
637-
tags=_py312_linux.tags,
638-
environment=_py312_linux.environment,
639-
)
640-
)

0 commit comments

Comments
 (0)