Skip to content

Commit 4984423

Browse files
authored
Flatten python packages (#743)
1 parent 35391d9 commit 4984423

30 files changed

+334
-411
lines changed

python/pip_install/BUILD

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ filegroup(
66
"BUILD",
77
"pip_compile.py",
88
"//python/pip_install/extract_wheels:distribution",
9-
"//python/pip_install/parse_requirements_to_bzl:distribution",
109
"//python/pip_install/private:distribution",
1110
],
1211
visibility = ["//:__pkg__"],
@@ -24,7 +23,6 @@ filegroup(
2423
name = "py_srcs",
2524
srcs = [
2625
"//python/pip_install/extract_wheels:py_srcs",
27-
"//python/pip_install/parse_requirements_to_bzl:py_srcs",
2826
],
2927
visibility = ["//python/pip_install/private:__pkg__"],
3028
)
Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,179 @@
1-
load("@rules_python//python:defs.bzl", "py_binary")
1+
load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
2+
load("//python/pip_install:repositories.bzl", "requirement")
3+
load(":annotations_test_helpers.bzl", "package_annotation", "package_annotations_file")
4+
5+
py_library(
6+
name = "lib",
7+
srcs = [
8+
"annotation.py",
9+
"arguments.py",
10+
"bazel.py",
11+
"extract_single_wheel.py",
12+
"extract_wheels.py",
13+
"namespace_pkgs.py",
14+
"parse_requirements_to_bzl.py",
15+
"requirements.py",
16+
"wheel.py",
17+
],
18+
deps = [
19+
requirement("installer"),
20+
requirement("setuptools"),
21+
],
22+
)
223

324
py_binary(
425
name = "extract_wheels",
526
srcs = [
6-
"__init__.py",
7-
"__main__.py",
27+
"extract_wheels.py",
28+
],
29+
deps = [":lib"],
30+
)
31+
32+
py_binary(
33+
name = "extract_single_wheel",
34+
srcs = [
35+
"extract_single_wheel.py",
36+
],
37+
deps = [":lib"],
38+
)
39+
40+
py_binary(
41+
name = "parse_requirements_to_bzl",
42+
srcs = [
43+
"parse_requirements_to_bzl.py",
44+
],
45+
deps = [":lib"],
46+
)
47+
48+
package_annotations_file(
49+
name = "mock_annotations",
50+
annotations = {
51+
"pkg_a": package_annotation(),
52+
"pkg_b": package_annotation(
53+
data_exclude_glob = [
54+
"*.foo",
55+
"*.bar",
56+
],
57+
),
58+
"pkg_c": package_annotation(
59+
# The `join` and `strip` here accounts for potential differences
60+
# in new lines between unix and windows hosts.
61+
additive_build_content = "\n".join([line.strip() for line in """\
62+
cc_library(
63+
name = "my_target",
64+
hdrs = glob(["**/*.h"]),
65+
srcs = glob(["**/*.cc"]),
66+
)
67+
""".splitlines()]),
68+
data = [":my_target"],
69+
),
70+
"pkg_d": package_annotation(
71+
srcs_exclude_glob = ["pkg_d/tests/**"],
72+
),
73+
},
74+
tags = ["manual"],
75+
)
76+
77+
py_test(
78+
name = "annotations_test",
79+
size = "small",
80+
srcs = ["annotations_test.py"],
81+
data = [":mock_annotations"],
82+
env = {"MOCK_ANNOTATIONS": "$(rootpath :mock_annotations)"},
83+
tags = ["unit"],
84+
deps = [
85+
":lib",
86+
"//python/runfiles",
87+
],
88+
)
89+
90+
py_test(
91+
name = "bazel_test",
92+
size = "small",
93+
srcs = [
94+
"bazel_test.py",
95+
],
96+
tags = ["unit"],
97+
deps = [
98+
":lib",
99+
],
100+
)
101+
102+
py_test(
103+
name = "namespace_pkgs_test",
104+
size = "small",
105+
srcs = [
106+
"namespace_pkgs_test.py",
107+
],
108+
tags = ["unit"],
109+
deps = [
110+
":lib",
111+
],
112+
)
113+
114+
py_test(
115+
name = "requirements_test",
116+
size = "small",
117+
srcs = [
118+
"requirements_test.py",
119+
],
120+
tags = ["unit"],
121+
deps = [
122+
":lib",
123+
],
124+
)
125+
126+
py_test(
127+
name = "arguments_test",
128+
size = "small",
129+
srcs = [
130+
"arguments_test.py",
131+
],
132+
tags = ["unit"],
133+
deps = [
134+
":lib",
135+
],
136+
)
137+
138+
py_test(
139+
name = "whl_filegroup_test",
140+
size = "small",
141+
srcs = ["whl_filegroup_test.py"],
142+
data = ["//examples/wheel:minimal_with_py_package"],
143+
main = "whl_filegroup_test.py",
144+
tags = ["unit"],
145+
deps = [":lib"],
146+
)
147+
148+
py_test(
149+
name = "parse_requirements_to_bzl_test",
150+
size = "small",
151+
srcs = [
152+
"parse_requirements_to_bzl_test.py",
153+
],
154+
tags = ["unit"],
155+
deps = [
156+
":lib",
157+
],
158+
)
159+
160+
py_test(
161+
name = "requirements_bzl_test",
162+
size = "small",
163+
srcs = [
164+
"requirements_bzl_test.py",
165+
],
166+
deps = [
167+
":lib",
8168
],
9-
main = "__main__.py",
10-
deps = ["//python/pip_install/extract_wheels/lib"],
11169
)
12170

13171
filegroup(
14172
name = "distribution",
15-
srcs = glob(["*"]) + [
16-
"//python/pip_install/extract_wheels/lib:distribution",
17-
],
173+
srcs = glob(
174+
["*"],
175+
exclude = ["*_test.py"],
176+
),
18177
visibility = ["//python/pip_install:__subpackages__"],
19178
)
20179

@@ -23,8 +182,6 @@ filegroup(
23182
srcs = glob(
24183
include = ["**/*.py"],
25184
exclude = ["**/*_test.py"],
26-
) + [
27-
"//python/pip_install/extract_wheels/lib:py_srcs",
28-
],
185+
),
29186
visibility = ["//python/pip_install:__subpackages__"],
30187
)
Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +0,0 @@
1-
"""extract_wheels
2-
3-
extract_wheels resolves and fetches artifacts transitively from the Python Package Index (PyPI) based on a
4-
requirements.txt. It generates the required BUILD files to consume these packages as Python libraries.
5-
6-
Under the hood, it depends on the `pip wheel` command to do resolution, download, and compilation into wheels.
7-
"""
8-
import argparse
9-
import glob
10-
import os
11-
import pathlib
12-
import subprocess
13-
import sys
14-
15-
from python.pip_install.extract_wheels.lib import (
16-
annotation,
17-
arguments,
18-
bazel,
19-
requirements,
20-
wheel,
21-
)
22-
23-
24-
def configure_reproducible_wheels() -> None:
25-
"""Modifies the environment to make wheel building reproducible.
26-
27-
Wheels created from sdists are not reproducible by default. We can however workaround this by
28-
patching in some configuration with environment variables.
29-
"""
30-
31-
# wheel, by default, enables debug symbols in GCC. This incidentally captures the build path in the .so file
32-
# We can override this behavior by disabling debug symbols entirely.
33-
# https://github.com/pypa/pip/issues/6505
34-
if "CFLAGS" in os.environ:
35-
os.environ["CFLAGS"] += " -g0"
36-
else:
37-
os.environ["CFLAGS"] = "-g0"
38-
39-
# set SOURCE_DATE_EPOCH to 1980 so that we can use python wheels
40-
# https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/python.section.md#python-setuppy-bdist_wheel-cannot-create-whl
41-
if "SOURCE_DATE_EPOCH" not in os.environ:
42-
os.environ["SOURCE_DATE_EPOCH"] = "315532800"
43-
44-
# Python wheel metadata files can be unstable.
45-
# See https://bitbucket.org/pypa/wheel/pull-requests/74/make-the-output-of-metadata-files/diff
46-
if "PYTHONHASHSEED" not in os.environ:
47-
os.environ["PYTHONHASHSEED"] = "0"
48-
49-
50-
def main() -> None:
51-
"""Main program.
52-
53-
Exits zero on successful program termination, non-zero otherwise.
54-
"""
55-
56-
configure_reproducible_wheels()
57-
58-
parser = argparse.ArgumentParser(
59-
description="Resolve and fetch artifacts transitively from PyPI"
60-
)
61-
parser.add_argument(
62-
"--requirements",
63-
action="store",
64-
required=True,
65-
help="Path to requirements.txt from where to install dependencies",
66-
)
67-
parser.add_argument(
68-
"--annotations",
69-
type=annotation.annotations_map_from_str_path,
70-
help="A json encoded file containing annotations for rendered packages.",
71-
)
72-
arguments.parse_common_args(parser)
73-
args = parser.parse_args()
74-
deserialized_args = dict(vars(args))
75-
arguments.deserialize_structured_args(deserialized_args)
76-
77-
# Pip is run with the working directory changed to the folder containing the requirements.txt file, to allow for
78-
# relative requirements to be correctly resolved. The --wheel-dir is therefore required to be repointed back to the
79-
# current calling working directory (the repo root in .../external/name), where the wheel files should be written to
80-
pip_args = (
81-
[sys.executable, "-m", "pip"]
82-
+ (["--isolated"] if args.isolated else [])
83-
+ ["wheel", "-r", args.requirements]
84-
+ ["--wheel-dir", os.getcwd()]
85-
+ deserialized_args["extra_pip_args"]
86-
)
87-
88-
env = os.environ.copy()
89-
env.update(deserialized_args["environment"])
90-
91-
# Assumes any errors are logged by pip so do nothing. This command will fail if pip fails
92-
subprocess.run(
93-
pip_args,
94-
check=True,
95-
env=env,
96-
cwd=str(pathlib.Path(args.requirements).parent.resolve()),
97-
)
98-
99-
extras = requirements.parse_extras(args.requirements)
100-
101-
repo_label = "@%s" % args.repo
102-
103-
# Locate all wheels
104-
wheels = [whl for whl in glob.glob("*.whl")]
105-
106-
# Collect all annotations
107-
reqs = {whl: wheel.Wheel(whl).name for whl in wheels}
108-
annotations = args.annotations.collect(reqs.values())
109-
110-
targets = [
111-
'"{}{}"'.format(
112-
repo_label,
113-
bazel.extract_wheel(
114-
wheel_file=whl,
115-
extras=extras,
116-
pip_data_exclude=deserialized_args["pip_data_exclude"],
117-
enable_implicit_namespace_pkgs=args.enable_implicit_namespace_pkgs,
118-
repo_prefix=args.repo_prefix,
119-
annotation=annotations.get(name),
120-
),
121-
)
122-
for whl, name in reqs.items()
123-
]
124-
125-
with open("requirements.bzl", "w") as requirement_file:
126-
requirement_file.write(
127-
bazel.generate_requirements_file_contents(repo_label, targets)
128-
)

python/pip_install/extract_wheels/__main__.py

Lines changed: 0 additions & 5 deletions
This file was deleted.
File renamed without changes.

python/pip_install/extract_wheels/lib/annotations_test.py renamed to python/pip_install/extract_wheels/annotations_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import unittest
66
from pathlib import Path
77

8-
from python.pip_install.extract_wheels.lib.annotation import Annotation, AnnotationsMap
8+
from python.pip_install.extract_wheels.annotation import Annotation, AnnotationsMap
99
from python.runfiles import runfiles
1010

1111

File renamed without changes.
File renamed without changes.

python/pip_install/extract_wheels/lib/arguments_test.py renamed to python/pip_install/extract_wheels/arguments_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import unittest
44

5-
from python.pip_install.extract_wheels.lib import arguments
5+
from python.pip_install.extract_wheels import arguments
66

77

88
class ArgumentsTestCase(unittest.TestCase):

python/pip_install/extract_wheels/lib/bazel.py renamed to python/pip_install/extract_wheels/bazel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77
from typing import Dict, Iterable, List, Optional, Set
88

9-
from python.pip_install.extract_wheels.lib import (
9+
from python.pip_install.extract_wheels import (
1010
annotation,
1111
namespace_pkgs,
1212
wheel,

0 commit comments

Comments
 (0)