8000 Expose resolved .whl files in filegroup target of pypi__ packages. (#… · AutomatedTester/rules_python@69f55c1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 69f55c1

Browse files
authored
Expose resolved .whl files in filegroup target of pypi__ packages. (bazel-contrib#364)
1 parent 5c948dc commit 69f55c1

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

python/pip_install/README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,27 @@ pip_install(
6464
#### Example `BUILD` file.
6565

6666
```python
67-
load("@py_deps//:requirements.bzl", "requirement")
67+
load("@py_deps//:requirements.bzl", "requirement", "whl_requirement")
6868

6969
py_binary(
7070
name = "main",
7171
srcs = ["main.py"],
7272
deps = [
7373
requirement("boto3"),
74-
],
74+
]
75+
)
76+
77+
# If you need to depend on the wheel dists themselves, for instance to pass them
78+
# to some other packaging tool, you can get a handle to them with the whl_requirement macro.
79+
filegroup(
80+
name = "whl_files",
81+
data = [
82+
whl_requirement("boto3"),
83+
]
7584
)
7685
```
7786

78-
Note that above you do not need to add transitively required packages to `deps = [ ... ]`
87+
Note that above you do not need to add transitively required packages to `deps = [ ... ]` or `data = [ ... ]`
7988

8089
#### Setup `requirements.txt`
8190

python/pip_install/extract_wheels/lib/BUILD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ py_test(
4141
],
4242
)
4343

44+
py_test(
45+
name = "whl_filegroup_test",
46+
size = "small",
47+
srcs = [
48+
"whl_filegroup_test.py",
49+
],
50+
tags = ["unit"],
51+
deps = [
52+
":lib",
53+
],
54+
data = ["//experimental/examples/wheel:minimal_with_py_package"]
55+
)
56+
4457
filegroup(
4558
name = "distribution",
4659
srcs = glob(

python/pip_install/extract_wheels/lib/bazel.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@
33
import textwrap
44
import json
55
from typing import Iterable, List, Dict, Set
6+
import shutil
67

78
from python.pip_install.extract_wheels.lib import namespace_pkgs, wheel, purelib
89

910

11+
WHEEL_FILE_LABEL = "whl"
12+
1013
def generate_build_file_contents(
11-
name: str, dependencies: List[str], pip_data_exclude: List[str]
14+
name: str, dependencies: List[str], whl_file_deps: List[str], pip_data_exclude: List[str],
1215
) -> str:
1316
"""Generate a BUILD file for an unzipped Wheel
1417
1518
Args:
1619
name: the target name of the py_library
1720
dependencies: a list of Bazel labels pointing to dependencies of the library
21+
whl_file_deps: a list of Bazel labels pointing to wheel file dependencies of this wheel.
1822
1923
Returns:
2024
A complete BUILD file as a string
@@ -23,14 +27,20 @@ def generate_build_file_contents(
2327
there may be no Python sources whatsoever (e.g. packages written in Cython: like `pymssql`).
2428
"""
2529

26-
data_exclude = ["**/*.py", "**/* *", "BUILD", "WORKSPACE"] + pip_data_exclude
30+
data_exclude = ["*.whl", "**/*.py", "**/* *", "BUILD", "WORKSPACE"] + pip_data_exclude
2731

2832
return textwrap.dedent(
2933
"""\
3034
package(default_visibility = ["//visibility:public"])
3135
3236
load("@rules_python//python:defs.bzl", "py_library")
3337
38+
filegroup(
39+
name="{whl_file_label}",
40+
srcs=glob(["*.whl"]),
41+
data=[{whl_file_deps}]
42+
)
43+
3444
py_library(
3545
name = "{name}",
3646
srcs = glob(["**/*.py"], allow_empty = True),
@@ -44,6 +54,8 @@ def generate_build_file_contents(
4454
name=name,
4555
dependencies=",".join(dependencies),
4656
data_exclude=json.dumps(data_exclude),
57+
whl_file_label=WHEEL_FILE_LABEL,
58+
whl_file_deps=",".join(whl_file_deps),
4759
)
4860
)
4961

@@ -70,6 +82,9 @@ def generate_requirements_file_contents(repo_name: str, targets: Iterable[str])
7082
def requirement(name):
7183
name_key = name.replace("-", "_").replace(".", "_").lower()
7284
return "{repo}//pypi__" + name_key
85+
86+
def whl_requirement(name):
87+
return requirement(name) + ":whl"
7388
""".format(
7489
repo=repo_name, requirement_labels=",".join(sorted(targets))
7590
)
@@ -125,7 +140,7 @@ def extract_wheel(
125140
pip_data_exclude: List[str],
126141
enable_implicit_namespace_pkgs: bool,
127142
) -> str:
128-
"""Extracts wheel into given directory and creates a py_library target.
143+
"""Extracts wheel into given directory and creates py_library and filegroup targets.
129144
130145
Args:
131146
wheel_file: the filepath of the .whl
@@ -141,6 +156,8 @@ def extract_wheel(
141156
directory = sanitise_name(whl.name)
142157

143158
os.mkdir(directory)
159+
# copy the original wheel
160+
shutil.copy(whl.path, directory)
144161
whl.unzip(directory)
145162

146163
# Note: Order of operations matters here
@@ -150,14 +167,18 @@ def extract_wheel(
150167
setup_namespace_pkg_compatibility(directory)
151168

152169
extras_requested = extras[whl.name] if whl.name in extras else set()
170+
whl_deps = sorted(whl.dependencies(extras_requested))
153171

154172
sanitised_dependencies = [
155-
'"//%s"' % sanitise_name(d) for d in sorted(whl.dependencies(extras_requested))
173+
'"//%s"' % sanitise_name(d) for d in whl_deps
174+
]
175+
sanitised_wheel_file_dependencies = [
176+
'"//%s:%s"' % (sanitise_name(d), WHEEL_FILE_LABEL) for d in whl_deps
156177
]
157178

158179
with open(os.path.join(directory, "BUILD"), "w") as build_file:
159180
contents = generate_build_file_contents(
160-
sanitise_name(whl.name), sanitised_dependencies, pip_data_exclude,
181+
sanitise_name(whl.name), sanitised_dependencies, sanitised_wheel_file_dependencies, pip_data_exclude
161182
)
162183
build_file.write(contents)
163184

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
import unittest
3+
4+
from python.pip_install.extract_wheels.lib import bazel
5+
6+
7+
class TestExtractWheel(unittest.TestCase):
8+
def test_generated_build_file_has_filegroup_target(self) -> None:
9+
wheel_name = "example_minimal_package-0.0.1-py3-none-any.whl"
10+
wheel_dir = "experimental/examples/wheel/"
11+
wheel_path = wheel_dir + wheel_name
12+
generated_bazel_dir = bazel.extract_wheel(
13+
wheel_path,
14+
extras={},
15+
pip_data_exclude=[],
16+
enable_implicit_namespace_pkgs=False,
17+
)[2:] # Take off the leading // from the returned label.
18+
# Assert that the raw wheel ends up in the package.
19+
self.assertIn(wheel_name, os.listdir(generated_bazel_dir))
20+
with open("{}/BUILD".format(generated_bazel_dir)) as build_file:
21+
build_file_content = build_file.read()
22+
self.assertIn('filegroup', build_file_content)
23+
24+
25+
if __name__ == "__main__":
26+
unittest.main()

0 commit comments

Comments
 (0)
0