4040from pex .orderedset import OrderedSet
4141from pex .os import safe_execv
4242from pex .pip .version import PipVersionValue
43- from pex .requirements import LocalProjectRequirement , ParseError , parse_requirement_string
43+ from pex .requirements import (
44+ LocalProjectRequirement ,
45+ ParseError ,
46+ as_parsed_requirement ,
47+ parse_requirement_string ,
48+ )
4449from pex .resolve import lock_resolver , requirement_options , resolver_options , target_options
4550from pex .resolve .configured_resolver import ConfiguredResolver
4651from pex .resolve .locked_resolve import FileArtifact , UnFingerprintedLocalProjectArtifact
5459from pex .targets import LocalInterpreter , Targets
5560from pex .tracer import TRACER
5661from pex .typing import TYPE_CHECKING
62+ from pex .util import CacheHelper
5763from pex .variables import ENV , venv_dir
5864from pex .venv import installer
5965from pex .venv .installer import Provenance
@@ -115,7 +121,7 @@ class RunSpec(object):
115121 entry_point = attr .ib () # type: str
116122 is_script = attr .ib () # type: bool
117123 pip_configuration = attr .ib () # type: PipConfiguration
118- run_requirement = attr .ib (default = None ) # type: Optional[Requirement ]
124+ run_requirement = attr .ib (default = None ) # type: Optional[ParsedRequirement ]
119125 all_requirements = attr .ib (default = RequirementConfiguration ()) # type: RequirementConfiguration
120126 args = attr .ib (default = ()) # type: Tuple[str, ...]
121127 locks = attr .ib (default = ()) # type: Tuple[str, ...]
@@ -263,8 +269,8 @@ def resolve_run_spec(
263269 ):
264270 # type: (...) -> RunSpec
265271
266- requirement = None # type: Optional[Requirement ]
267- extra_requirements = OrderedSet () # type: OrderedSet[Requirement ]
272+ requirement = None # type: Optional[ParsedRequirement ]
273+ extra_requirements = OrderedSet () # type: OrderedSet[ParsedRequirement ]
268274 local_projects = OrderedSet () # type: OrderedSet[LocalProjectRequirement]
269275
270276 entry_point = None # type: Optional[str]
@@ -289,7 +295,7 @@ def resolve_run_spec(
289295 if isinstance (self .requirement , LocalProjectRequirement ):
290296 local_projects .add (self .requirement )
291297 elif self .requirement :
292- requirement = self .requirement . requirement
298+ requirement = self .requirement
293299
294300 local_project_to_dist = dict (
295301 _create_dists (
@@ -301,8 +307,13 @@ def resolve_run_spec(
301307 )
302308 for local_project , dist in local_project_to_dist .items ():
303309 project_name = DistMetadata .load (dist ).project_name
304- local_project_req = Requirement .local (
305- project_name = project_name , path = dist , editable = local_project .editable
310+ local_project_req = as_parsed_requirement (
311+ Requirement .local (
312+ project_name = project_name ,
313+ path = dist ,
314+ editable = local_project .editable ,
315+ fragment_parameters = [("sha256" , CacheHelper .hash (dist , hasher = hashlib .sha256 ))],
316+ )
306317 )
307318 if self .entry_point == local_project :
308319 entry_point = project_name .raw
@@ -326,10 +337,10 @@ def resolve_run_spec(
326337 locks .append ("pylock.{name}.toml" .format (name = name ))
327338 locks .append ("pylock.toml" )
328339
329- requirements = OrderedSet () # type: OrderedSet[str ]
340+ requirements = OrderedSet () # type: OrderedSet[ParsedRequirement ]
330341 if requirement :
331- requirements .add (str ( requirement ) )
332- requirements .update (map ( str , extra_requirements ) )
342+ requirements .add (requirement )
343+ requirements .update (extra_requirements )
333344 if self .extra_requirements .requirements :
334345 requirements .update (self .extra_requirements .requirements )
335346
@@ -424,7 +435,7 @@ def resolve(
424435 refresh = False , # type: bool
425436 locked_choice = LockedChoice .AUTO , # type: LockedChoice.Value
426437 ):
427- # type: (...) -> Union[Tuple[str, Iterable[Requirement ], LocalInterpreter], Error]
438+ # type: (...) -> Union[Tuple[str, Iterable[ParsedRequirement ], LocalInterpreter], Error]
428439
429440 script = try_ (
430441 self ._resolve_script (pip_configuration , refresh = refresh , locked_choice = locked_choice )
@@ -438,14 +449,11 @@ def resolve(
438449 except Unsatisfiable as e :
439450 return Error (str (e ))
440451
441- requirements = OrderedSet () # type: OrderedSet[Requirement ]
452+ requirements = OrderedSet () # type: OrderedSet[ParsedRequirement ]
442453 if script_metadata_application .target_does_not_apply (target ):
443454 target = try_ (_resolve_local_interpreter (target_configuration , script ))
444455 if script_metadata_application .requirement_configuration .requirements :
445- requirements .update (
446- Requirement .parse (req )
447- for req in script_metadata_application .requirement_configuration .requirements
448- )
456+ requirements .update (script_metadata_application .requirement_configuration .requirements )
449457
450458 return script , requirements , target
451459
@@ -626,7 +634,7 @@ def _resolve_pylock(self, run_spec):
626634
627635 downloaded = pip_resolver .download (
628636 targets = targets ,
629- requirements = [str ( requirement ) ],
637+ requirements = [requirement ],
630638 constraint_files = run_spec .all_requirements .constraint_files ,
631639 allow_prereleases = pip_configuration .allow_prereleases ,
632640 transitive = False ,
@@ -658,7 +666,11 @@ def _resolve_pylock(self, run_spec):
658666 index = - 1 ,
659667 project_name = dist_metadata .project_name ,
660668 artifact = FileArtifact (
661- url = ArtifactURL .parse ("file://{path}" .format (path = distribution .path )),
669+ url = ArtifactURL .parse (
670+ "file://{path}#sha256={hash}" .format (
671+ path = distribution .path , hash = distribution .fingerprint
672+ )
673+ ),
662674 verified = True ,
663675 fingerprint = Fingerprint (algorithm = "sha256" , hash = distribution .fingerprint ),
664676 filename = os .path .basename (distribution .path ),
0 commit comments