88from datetime import datetime
99from 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
1212from .specifiers import SpecifierSet
1313from .tags import sys_tags
1414from .utils import NormalizedName , is_normalized_name , parse_wheel_filename
1717if TYPE_CHECKING : # pragma: no cover
1818 from collections .abc import Collection , Iterator
1919 from pathlib import Path
20+ from typing import AbstractSet
2021
2122 from typing_extensions import Self
2223
23- from .markers import Environment
2424 from .tags import Tag
2525
2626_logger = logging .getLogger (__name__ )
@@ -47,6 +47,11 @@ def __dir__() -> list[str]:
4747_T2 = TypeVar ("_T2" )
4848
4949
50+ class _PylockEnvironment (Environment ):
51+ extras : AbstractSet [str ]
52+ dependency_groups : AbstractSet [str ]
53+
54+
5055class _FromMappingProtocol (Protocol ): # pragma: no cover
5156 @classmethod
5257 def _from_dict (cls , d : Mapping [str , Any ]) -> Self : ...
@@ -649,7 +654,7 @@ def select(
649654 tags : Sequence [Tag ] | None = None ,
650655 extras : Collection [str ] | None = None ,
651656 dependency_groups : Collection [str ] | None = None ,
652- ) -> Iterator [ # XXX or Iterable?
657+ ) -> Iterator [
653658 tuple [
654659 Package ,
655660 PackageVcs
@@ -675,8 +680,6 @@ def select(
675680 valid Pylock instances (i.e. one obtained from :meth:`Pylock.from_dict`
676681 or if constructed manually, after calling :meth:`Pylock.validate`).
677682 """
678- if environment is None :
679- environment = default_environment ()
680683 if tags is None :
681684 tags = list (sys_tags ())
682685
@@ -686,14 +689,23 @@ def select(
686689 # #. ``extras`` SHOULD be set to the empty set by default.
687690 # #. ``dependency_groups`` SHOULD be the set created from
688691 # :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 []
692+ env = cast (
693+ "dict[str, str | frozenset[str]]" ,
694+ dict (
695+ environment or {}, # Marker.evaluate will fill-up
696+ extras = frozenset (extras or []),
697+ dependency_groups = frozenset (
698+ (self .default_groups or [])
699+ if dependency_groups is None # to allow selecting no groups
700+ else dependency_groups
701+ ),
694702 ),
695- }
696- env_python_version = environment .get ("python_version" )
703+ )
704+ env_python_version = (
705+ environment ["python_version" ]
706+ if environment
707+ else default_environment ()["python_version" ]
708+ )
697709
698710 # #. Check if the metadata version specified by :ref:`pylock-lock-version` is
699711 # supported; an error or warning MUST be raised as appropriate.
@@ -702,25 +714,21 @@ def select(
702714 # #. If :ref:`pylock-requires-python` is specified, check that the environment
703715 # being installed for meets the requirement; an error MUST be raised if it is
704716 # 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- )
717+ if self .requires_python and not self .requires_python .contains (
718+ env_python_version ,
719+ prereleases = True , # XXX confirm prereleases=True
720+ ):
721+ raise PylockSelectError (
722+ f"Provided environment does not satisfy the Python version "
723+ f"requirement { self .requires_python !r} "
724+ )
717725
718726 # #. If :ref:`pylock-environments` is specified, check that at least one of the
719727 # environment marker expressions is satisfied; an error MUST be raised if no
720728 # expression is satisfied.
721729 if self .environments :
722730 for env_marker in self .environments :
723- if env_marker .evaluate (env , context = "lock_file" ): # XXX check context
731+ if env_marker .evaluate (env , context = "lock_file" ):
724732 break
725733 else :
726734 raise PylockSelectError (
@@ -733,29 +741,20 @@ def select(
733741 for package_index , package in enumerate (self .packages ):
734742 # #. If :ref:`pylock-packages-marker` is specified, check if it is
735743 # 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
744+ if package .marker and not package .marker .evaluate (env , context = "lock_file" ):
739745 continue
740746
741747 # #. If :ref:`pylock-packages-requires-python` is specified, check if it is
742748 # 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- )
749+ if package .requires_python and not package .requires_python .contains (
750+ env_python_version ,
751+ prereleases = True , # XXX confirm prereleases=True
752+ ):
753+ raise PylockSelectError (
754+ f"Provided environment does not satisfy the Python version "
755+ f"requirement { package .requires_python !r} for package "
756+ f"{ package .name !r} at packages[{ package_index } ]"
757+ )
759758
760759 # #. Check that no other conflicting instance of the package has been slated
761760 # to be installed; an error about the ambiguity MUST be raised otherwise.
@@ -769,7 +768,7 @@ def select(
769768 # #. Check that the source of the package is specified appropriately (i.e.
770769 # there are no conflicting sources in the package entry);
771770 # an error MUST be raised if any issues are found.
772- # Covered by lock.validate() above .
771+ # Covered by lock.validate() which is a precondition for this method .
773772
774773 # #. Add the package to the set of packages to install.
775774 selected_packages_by_name [package .name ] = (package_index , package )
@@ -817,5 +816,5 @@ def select(
817816 yield package , package .sdist
818817
819818 else :
820- # Covered by lock.validate() above .
821- raise NotImplementedError
819+ # Covered by lock.validate() which is a precondition for this method .
820+ raise NotImplementedError # pragma: no cover
0 commit comments