Skip to content

Commit 6ba12ce

Browse files
aignasrickeylev
andauthored
refactor: split code into multiple files (#4044)
A followup to #4043 to split code into multiple files to make the interaction and reuse more intentional and explicit. Work towards #2948. --------- Co-authored-by: Richard Levasseur <richardlev@gmail.com>
1 parent 83df7ac commit 6ba12ce

11 files changed

Lines changed: 1030 additions & 950 deletions

File tree

docs/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ sphinx_stardocs(
146146
"//python/private/api:py_common_api",
147147
"//python/private/pypi:config_settings",
148148
"//python/private/pypi:env_marker_info",
149+
"//python/private/pypi:pip_archive",
149150
"//python/private/pypi:pkg_aliases",
151+
"//python/private/pypi:whl_archive",
150152
"//python/private/pypi:whl_config_setting",
151153
"//python/private/pypi:whl_library",
152154
"//python/private/zipapp:py_zipapp_rule",

python/private/pypi/BUILD.bazel

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -473,20 +473,8 @@ bzl_library(
473473
name = "whl_library",
474474
srcs = ["whl_library.bzl"],
475475
deps = [
476-
":attrs",
477-
":deps",
478-
":generate_whl_library_build_bazel",
479-
":patch_whl",
480-
":pep508_requirement",
481-
":pypi_repo_utils",
482-
":urllib",
483-
":whl_extract",
484-
":whl_metadata",
485-
"//python/private:auth",
486-
"//python/private:envsubst",
487-
"//python/private:is_standalone_interpreter",
488-
"//python/private:normalize_name",
489-
"//python/private:repo_utils",
476+
":pip_archive",
477+
":whl_archive",
490478
],
491479
)
492480

@@ -547,6 +535,61 @@ bzl_library(
547535
deps = [":hash"],
548536
)
549537

538+
bzl_library(
539+
name = "pip_archive",
540+
srcs = ["pip_archive.bzl"],
541+
deps = [
542+
":attrs",
543+
":deps",
544+
":patch_and_extract_whl",
545+
":pypi_repo_utils",
546+
":urllib",
547+
":whl_archive",
548+
"//python/private:auth",
549+
"//python/private:envsubst",
550+
"//python/private:is_standalone_interpreter",
551+
"//python/private:repo_utils",
552+
],
553+
)
554+
555+
bzl_library(
556+
name = "whl_archive",
557+
srcs = ["whl_archive.bzl"],
558+
deps = [
559+
":attrs",
560+
":patch_and_extract_whl",
561+
":urllib",
562+
":whl_deps_repo",
563+
"//python/private:auth",
564+
"//python/private:repo_utils",
565+
],
566+
)
567+
568+
bzl_library(
569+
name = "whl_deps_repo",
570+
srcs = ["whl_deps_repo.bzl"],
571+
deps = [
572+
":generate_whl_library_build_bazel",
573+
":pep508_requirement",
574+
"//python/private:repo_utils",
575+
],
576+
)
577+
578+
bzl_library(
579+
name = "patch_and_extract_whl",
580+
srcs = ["patch_and_extract_whl.bzl"],
581+
deps = [
582+
":generate_whl_library_build_bazel",
583+
":patch_whl",
584+
":pep508_requirement",
585+
":pypi_repo_utils",
586+
":whl_extract",
587+
":whl_metadata",
588+
"//python/private:normalize_name",
589+
"//python/private:repo_utils",
590+
],
591+
)
592+
550593
bzl_library(
551594
name = "argparse",
552595
srcs = ["argparse.bzl"],
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
""
2+
3+
load("//python/private:normalize_name.bzl", "normalize_name")
4+
load("//python/private:repo_utils.bzl", "repo_utils")
5+
load(":generate_whl_library_build_bazel.bzl", "generate_whl_library_build_bazel")
6+
load(":patch_whl.bzl", "patch_whl")
7+
load(":pep508_requirement.bzl", "requirement")
8+
load(":pypi_repo_utils.bzl", "pypi_repo_utils")
9+
load(":whl_extract.bzl", "whl_extract")
10+
load(":whl_metadata.bzl", "parse_entry_points", "whl_metadata")
11+
12+
def _get_entry_points(rctx, install_dir_path, metadata):
13+
dist_info_dir = "{}-{}.dist-info".format(
14+
metadata.name.replace("-", "_"),
15+
metadata.version.replace("-", "_"),
16+
)
17+
entry_points_txt = install_dir_path.get_child(dist_info_dir).get_child("entry_points.txt")
18+
if entry_points_txt.exists:
19+
return parse_entry_points(rctx.read(entry_points_txt))
20+
return {}
21+
22+
def _move_scripts_needing_shebang_rewrite(rctx, entry_points):
23+
bin_dir = rctx.path("bin")
24+
if not bin_dir.exists:
25+
return
26+
27+
ep_names = {name.lower(): True for name in entry_points}
28+
for script in bin_dir.readdir():
29+
if script.is_dir:
30+
continue
31+
if script.basename.lower() in ep_names:
32+
rctx.delete(script)
33+
continue
34+
if script.basename.endswith(".exe") or script.basename.endswith(".dll"):
35+
continue
36+
content = rctx.read(script)
37+
if content.startswith("#!python"):
38+
rewrite_bin_dir = rctx.path("rewrite-bin")
39+
repo_utils.mkdir(rctx, rewrite_bin_dir)
40+
repo_utils.rename(rctx, script, rctx.path("rewrite-bin/" + script.basename))
41+
42+
def _to_purl(*, index, metadata, filename):
43+
"""
44+
Produce a PyPI PURL from the metadata.
45+
46+
https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md
47+
"""
48+
49+
# https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md#name-definition
50+
name = normalize_name(metadata.name).replace("_", "-")
51+
52+
qualifiers = {}
53+
if index:
54+
qualifiers["repository_url"] = index
55+
if filename:
56+
qualifiers["file_name"] = filename
57+
58+
return "pkg:pypi/{}@{}?{}".format(name, metadata.version, "&".join(["{}={}".format(key, val) for key, val in qualifiers.items()]))
59+
60+
def _remove_files(rctx, *basenames):
61+
paths = list(rctx.path(".").readdir())
62+
for _ in range(10000000):
63+
if not paths:
64+
break
65+
path = paths.pop()
66+
67+
if path.basename in basenames:
68+
rctx.delete(path)
69+
elif path.is_dir:
70+
paths.extend(path.readdir())
71+
72+
def patch_and_extract_whl(rctx, *, whl_path, logger, sdist_filename = None):
73+
"""Extract the wheel, apply patches and generate BUILD.bazel files.
74+
75+
Reused in pip and http wheel download code.
76+
77+
Args:
78+
rctx: the repository ctx.
79+
whl_path: the whl path to extract.
80+
logger: The logger to use
81+
sdist_filename: The filename to ignore in the BUILD.bazel files as sources.
82+
83+
Returns:
84+
The repository metadata if the extraction is reproducible
85+
"""
86+
if rctx.attr.whl_patches:
87+
patches = {}
88+
for patch_file, json_args in rctx.attr.whl_patches.items():
89+
patch_dst = struct(**json.decode(json_args))
90+
if whl_path.basename in patch_dst.whls:
91+
patches[patch_file] = patch_dst.patch_strip
92+
93+
if patches:
94+
whl_path = patch_whl(
95+
rctx,
96+
whl_path = whl_path,
97+
patches = patches,
98+
)
99+
100+
whl_extract(rctx, whl_path = whl_path, logger = logger)
101+
102+
install_dir_path = whl_path.dirname.get_child("site-packages")
103+
metadata = whl_metadata(
104+
install_dir = install_dir_path,
105+
read_fn = rctx.read,
106+
logger = logger,
107+
)
108+
rctx.file("metadata.json", json.encode_indent({
109+
"name": metadata.name,
110+
"provides_extra": metadata.provides_extra,
111+
"requires_dist": metadata.requires_dist,
112+
"version": metadata.version,
113+
}))
114+
namespace_package_files = pypi_repo_utils.find_namespace_package_files(rctx, install_dir_path)
115+
116+
entry_points = _get_entry_points(rctx, install_dir_path, metadata)
117+
_move_scripts_needing_shebang_rewrite(rctx, entry_points)
118+
119+
build_file_contents = generate_whl_library_build_bazel(
120+
name = whl_path.basename,
121+
dep_template = rctx.attr.dep_template,
122+
sdist_filename = sdist_filename,
123+
config_load = rctx.attr.config_load,
124+
metadata_name = metadata.name,
125+
metadata_version = metadata.version,
126+
requires_dist = metadata.requires_dist,
127+
# TODO @aignas 2025-05-17: maybe have a build flag for this instead
128+
enable_implicit_namespace_pkgs = rctx.attr.enable_implicit_namespace_pkgs,
129+
# TODO @aignas 2025-04-14: load through the hub:
130+
annotation = None if not rctx.attr.annotation else struct(**json.decode(rctx.read(rctx.attr.annotation))),
131+
data_exclude = rctx.attr.pip_data_exclude,
132+
group_deps = rctx.attr.group_deps,
133+
group_name = rctx.attr.group_name,
134+
namespace_package_files = namespace_package_files,
135+
extras = requirement(rctx.attr.requirement).extras,
136+
entry_points = entry_points,
137+
purl = _to_purl(
138+
index = rctx.attr.index_url,
139+
metadata = metadata,
140+
filename = sdist_filename or whl_path.basename,
141+
),
142+
)
143+
144+
# Delete these in case the wheel had them. They generally don't cause
145+
# a problem, but let's avoid the chance of that happening.
146+
rctx.file("WORKSPACE")
147+
rctx.file("WORKSPACE.bazel")
148+
rctx.file("MODULE.bazel")
149+
rctx.file("REPO.bazel", """\
150+
repo(
151+
default_package_metadata = [
152+
"//:package_metadata",
153+
],
154+
)
155+
""")
156+
157+
# BUILD files interfere with globbing and Bazel package boundaries.
158+
_remove_files(rctx, "BUILD", "BUILD.bazel")
159+
rctx.file("BUILD.bazel", build_file_contents)
160+
161+
if hasattr(rctx, "repo_metadata"):
162+
return rctx.repo_metadata(reproducible = True)
163+
164+
return None

0 commit comments

Comments
 (0)