Skip to content

Commit f61e4cd

Browse files
committed
Fix VCS fallout of pypa/pip#13602
1 parent bf266a8 commit f61e4cd

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

pex/hashing.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,16 @@ def zip_hash(
290290
dirs.add(dirname)
291291
dirname = os.path.dirname(dirname)
292292

293-
accept_dirs = frozenset(d for d in dirs if dir_filter(d))
293+
accept_dirs = frozenset(
294+
d for d in dirs if dir_filter(os.path.relpath(d, relpath) if relpath else d)
295+
)
294296
reject_dirs = tuple("{dir}/".format(dir=path) for path in (dirs - accept_dirs))
295297
accept_files = sorted(
296298
name
297299
for name in namelist
298-
if not name.endswith("/") and not name.startswith(reject_dirs) and file_filter(name)
300+
if not name.endswith("/")
301+
and not name.startswith(reject_dirs)
302+
and file_filter(os.path.relpath(name, relpath) if relpath else name)
299303
)
300304

301305
hashed_names = (

pex/pip/vcs.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@
2424
from pex.hashing import HintedDigest
2525

2626

27+
def _project_name_re(project_name):
28+
# type: (ProjectName) -> str
29+
return project_name.normalized.replace("-", "[-_.]+")
30+
31+
2732
def _built_source_dist_pattern(project_name):
2833
# type: (ProjectName) -> Pattern
2934
return re.compile(
30-
r"(?P<project_name>{project_name})-(?P<version>.+)\.zip".format(
31-
project_name=project_name.normalized.replace("-", "[-_.]+")
35+
r"(?P<project_name>{project_name_re})-(?P<version>.+)\.zip".format(
36+
project_name_re=_project_name_re(project_name)
3237
),
3338
re.IGNORECASE,
3439
)
@@ -78,8 +83,11 @@ def fingerprint_downloaded_vcs_archive(
7883
return Fingerprint.from_digest(digest), archive_path
7984

8085

81-
def _vcs_dir_filter(vcs):
82-
# type: (VCS.Value) -> Callable[[Text], bool]
86+
def _vcs_dir_filter(
87+
vcs, # type: VCS.Value
88+
project_name, # type: ProjectName
89+
):
90+
# type: (...) -> Callable[[Text], bool]
8391

8492
# Ignore VCS control directories for the purposes of fingerprinting the version controlled
8593
# source tree. VCS control directories can contain non-reproducible content (Git at least
@@ -92,10 +100,27 @@ def _vcs_dir_filter(vcs):
92100
# hashes the same.
93101
vcs_control_dir = ".{vcs}".format(vcs=vcs)
94102

95-
return lambda dir_path: (
96-
not is_pyc_dir(dir_path) and os.path.basename(dir_path) != vcs_control_dir
103+
# N.B.: If the VCS project uses setuptools as its build backend, depending on the version of
104+
# Pip used, the VCS checkout can have a `<project name>.egg-info/` directory littering its root
105+
# left over from Pip generating project metadata to determine version and dependencies. No other
106+
# well known build-backend has this problem at this time (checked hatchling, poetry-core,
107+
# pdm-backend and uv_build).
108+
# C.F.: https://github.com/pypa/pip/pull/13602
109+
egg_info_dir_re = re.compile(
110+
r"^{project_name_re}\.egg-info$".format(project_name_re=_project_name_re(project_name)),
111+
re.IGNORECASE,
97112
)
98113

114+
def vcs_dir_filter(dir_path):
115+
# type: (Text) -> bool
116+
if is_pyc_dir(dir_path):
117+
return False
118+
119+
base_dir_name = dir_path.split(os.sep)[0]
120+
return base_dir_name != vcs_control_dir and not egg_info_dir_re.match(base_dir_name)
121+
122+
return vcs_dir_filter
123+
99124

100125
def _vcs_file_filter(vcs):
101126
# type: (VCS.Value) -> Callable[[Text], bool]
@@ -132,12 +157,13 @@ def digest_vcs_archive(
132157
zip_path=archive_path,
133158
digest=digest,
134159
relpath=top_dir,
135-
dir_filter=_vcs_dir_filter(vcs),
160+
dir_filter=_vcs_dir_filter(vcs, project_name),
136161
file_filter=_vcs_file_filter(vcs),
137162
)
138163

139164

140165
def digest_vcs_repo(
166+
project_name, # type: ProjectName
141167
repo_path, # type: str
142168
vcs, # type: VCS.Value
143169
digest, # type: HintedDigest
@@ -148,6 +174,6 @@ def digest_vcs_repo(
148174
hashing.dir_hash(
149175
directory=os.path.join(repo_path, subdirectory) if subdirectory else repo_path,
150176
digest=digest,
151-
dir_filter=_vcs_dir_filter(vcs),
177+
dir_filter=_vcs_dir_filter(vcs, project_name),
152178
file_filter=_vcs_file_filter(vcs),
153179
)

pex/resolve/locker.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ def analyze(self, line):
455455
if isinstance(artifact_url.scheme, VCSScheme):
456456
digest = Sha256()
457457
digest_vcs_repo(
458+
project_name=build_result.pin.project_name,
458459
repo_path=build_result.path,
459460
vcs=artifact_url.scheme.vcs,
460461
digest=digest,

0 commit comments

Comments
 (0)