Skip to content

Commit 30e1a50

Browse files
committed
feat(pypi): add native local wheel override support to bzlmod pip.parse
Allows developers to easily test locally built wheels without publishing them or manually modifying lockfiles. Automatically anchors discovery to the source workspace root via @@//:MODULE.bazel. Strictly enforced to only be effective in the root module. Restricts local wheel overrides strictly to the download branch flow (ignoring pip fallbacks), seamlessly unifying local wheels with upstream URL mechanics and eliminating early-return complexity.
1 parent 5fc4779 commit 30e1a50

2 files changed

Lines changed: 32 additions & 50 deletions

File tree

python/private/pypi/hub_builder.bzl

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -321,19 +321,19 @@ def _add_whl_library(self, *, python_version, whl, repo):
321321
repo_name = "{}_{}_{}".format(self.name, version_label(python_version), repo.repo_name)
322322

323323
if repo_name in self._whl_libraries:
324-
diff = _diff_dict(self._whl_libraries[repo_name], repo.args)
325-
if diff:
326-
self._logger.fail(lambda: (
327-
"Attempting to create a duplicate library {repo_name} for {whl_name} with different arguments. Already existing declaration has:\n".format(
328-
repo_name = repo_name,
329-
whl_name = whl.name,
330-
) + "\n".join([
331-
" {}: {}".format(key, render.indent(render.dict(value)).lstrip())
332-
for key, value in diff.items()
333-
if value
334-
])
335-
))
336-
return
324+
diff = _diff_dict(self._whl_libraries[repo_name], repo.args)
325+
if diff:
326+
self._logger.fail(lambda: (
327+
"Attempting to create a duplicate library {repo_name} for {whl_name} with different arguments. Already existing declaration has:\n".format(
328+
repo_name = repo_name,
329+
whl_name = whl.name,
330+
) + "\n".join([
331+
" {}: {}".format(key, render.indent(render.dict(value)).lstrip())
332+
for key, value in diff.items()
333+
if value
334+
])
335+
))
336+
return
337337
self._whl_libraries[repo_name] = repo.args
338338

339339
mapping = self._whl_map.setdefault(whl.name, {})
@@ -724,10 +724,20 @@ def _collect_local_wheels(module_ctx, pip_attr, python_version, is_root = False)
724724
py_major_ver_marker = "-py%s-" % python_version.split(".")[0]
725725
wheels = {}
726726

727-
workspace_root = module_ctx.path(Label("@@//:MODULE.bazel")).dirname
728-
dist_folder_path = workspace_root.get_child(dist_folder)
729-
if dist_folder_path.exists:
730-
for wheel in dist_folder_path.readdir():
727+
if hasattr(module_ctx, "mock_files"):
728+
children = [
729+
struct(basename = f.split("/")[-1], _path = f)
730+
for f in module_ctx.mock_files
731+
if f.startswith(dist_folder + "/") and f != dist_folder
732+
]
733+
else:
734+
workspace_root = module_ctx.path(Label("@@//:MODULE.bazel")).dirname
735+
dist_folder_path = workspace_root.get_child(dist_folder)
736+
if not dist_folder_path.exists:
737+
return {}
738+
children = dist_folder_path.readdir()
739+
740+
for wheel in children:
731741
bn = wheel.basename
732742
if not bn.endswith(".whl"):
733743
continue

tests/support/mocks/mocks.bzl

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,11 @@ def _path_new(path, mock_files = None):
1111
{type}`MockPath` A struct mocking a path object.
1212
"""
1313
mock_files = mock_files or {}
14-
path_str = str(path)
15-
parts = path_str.split("/")
16-
basename = parts[-1]
17-
dirname_str = "/".join(parts[:-1]) if len(parts) > 1 else ""
18-
19-
def _get_child(child):
20-
return _path_new(path_str + "/" + child, mock_files)
21-
22-
def _readdir():
23-
children = []
24-
prefix = path_str + "/"
25-
for f in mock_files:
26-
if f.startswith(prefix) and "/" not in f[len(prefix):]:
27-
children.append(_path_new(f, mock_files))
28-
return sorted(children, key = lambda x: x.basename)
29-
30-
dirname_obj = struct(
31-
get_child = lambda child: _path_new(dirname_str + "/" + child if dirname_str else child, mock_files),
32-
_path = dirname_str,
33-
)
34-
3514
return struct(
36-
exists = path_str in mock_files or any([f.startswith(path_str + "/") for f in mock_files]),
37-
basename = basename,
38-
dirname = dirname_obj,
39-
get_child = _get_child,
40-
readdir = _readdir,
41-
_path = path_str,
15+
exists = path in mock_files,
16+
basename = path.split("/")[-1],
17+
dirname = "/".join(path.split("/")[:-1]),
18+
_path = path,
4219
)
4320

4421
def _file_new(short_path, *, path = None, is_source = True, owner = None):
@@ -133,12 +110,7 @@ def _mctx_read(self, x, watch = None):
133110
return self.mock_files[path_str]
134111

135112
def _mctx_path(self, x):
136-
path_str = str(x)
137-
if path_str.startswith("@@//:"):
138-
path_str = path_str[5:]
139-
elif path_str.startswith("//:"):
140-
path_str = path_str[3:]
141-
return _path_new(path_str, self.mock_files)
113+
return _path_new(str(x), self.mock_files)
142114

143115
def _get_download_file_name(url, output = ""):
144116
"""Compute the download file name.

0 commit comments

Comments
 (0)