Skip to content

Commit b5d058d

Browse files
Merge pull request #3 from dillon-giacoppo/DEPLOY-445-improvements
2 parents 2a64fd6 + 2531e67 commit b5d058d

File tree

514 files changed

+173
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

514 files changed

+173
-279
lines changed

.gitattributes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
third_party/** linguist-generated=true
1+
third_party/python/** linguist-generated=true

README.md

Lines changed: 5 additions & 4 deletions

WORKSPACE

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1 @@
1-
workspace(name = "rules_pip")
2-
3-
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
4-
5-
http_archive(
6-
name = "rules_python",
7-
url = "https://github.com/bazelbuild/rules_python/releases/download/0.0.1/rules_python-0.0.1.tar.gz",
8-
sha256 = "aa96a691d3a8177f3215b14b0edc9641787abaaa30363a080165d06ab65e1161",
9-
)
10-
load("@rules_python//python:repositories.bzl", "py_repositories")
11-
py_repositories()
12-
13-
load("//:defs.bzl", "pip_repository")
14-
15-
pip_repository(
16-
name = "py_deps",
17-
requirements = "//:requirements.txt",
18-
)
1+
workspace(name = "rules_python_external")

defs.bzl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
def _pip_repository_impl(rctx):
2-
if not rctx.which("python3"):
3-
fail("python not found")
2+
if not rctx.which(rctx.attr.python_interpreter):
3+
fail("python interpreter not found")
44

55
rctx.file("BUILD", "")
66

77
result = rctx.execute(
88
[
9-
rctx.which("python3"),
9+
rctx.which(rctx.attr.python_interpreter),
1010
rctx.path(rctx.attr._script),
1111
"--requirements",
1212
rctx.path(rctx.attr.requirements),
@@ -26,12 +26,12 @@ pip_repository = repository_rule(
2626
attrs={
2727
"requirements": attr.label(allow_single_file=True, mandatory=True,),
2828
"wheel_env": attr.string_dict(),
29+
"python_interpreter": attr.string(default="python3"),
2930
"_script": attr.label(
3031
executable=True,
3132
default=Label("//tools:wheel_wrapper.py"),
3233
cfg="host",
3334
),
3435
},
35-
local=False,
3636
implementation=_pip_repository_impl,
3737
)

requirements.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

third_party/pip/_internal/distributions/source/__init__.py renamed to src/__init__.py

File renamed without changes.

src/__init__.pyc

199 Bytes
Binary file not shown.

src/extract_wheels.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import argparse
2+
import glob
3+
import os
4+
import subprocess
5+
import sys
6+
7+
from .wheel import Wheel
8+
9+
BUILD_TEMPLATE = """\
10+
package(default_visibility = ["//visibility:public"])
11+
12+
load("@rules_python//python:defs.bzl", "py_library")
13+
14+
py_library(
15+
name = "{name}",
16+
srcs = glob(["**/*.py"]),
17+
data = glob(["**/*"], exclude=["**/*.py", "**/* *", "BUILD", "WORKSPACE"]),
18+
# This makes this directory a top-level in the python import
19+
# search path for anything that depends on this.
20+
imports = ["."],
21+
deps = [{dependencies}],
22+
)
23+
"""
24+
25+
26+
def sanitise_name(name):
27+
return name.replace("-", "_").replace(".", "_").lower()
28+
29+
30+
def extract_wheel(whl, directory, extras):
31+
"""
32+
Unzips a wheel into the Bazel repository and creates the BUILD file
33+
34+
:param whl: the Wheel object we are unpacking
35+
:param directory: the subdirectory of the external repo to unzip to
36+
:param extras: list of extras that we want to create targets for
37+
"""
38+
39+
whl.unzip(directory)
40+
41+
with open(os.path.join(directory, "BUILD"), "w") as f:
42+
f.write(
43+
BUILD_TEMPLATE.format(
44+
name=sanitise_name(whl.name()),
45+
dependencies=",".join(
46+
# Python libraries cannot have hyphen https://github.com/bazelbuild/bazel/issues/9171
47+
[
48+
'"//%s"' % sanitise_name(d)
49+
for d in whl.dependencies(extras_requested=extras)
50+
]
51+
),
52+
)
53+
)
54+
55+
56+
def main():
57+
parser = argparse.ArgumentParser(
58+
description="Resolve and fetch artifacts transitively from PyPI"
59+
)
60+
parser.add_argument(
61+
"--requirements",
62+
action="store",
63+
help="Path to requirements.txt from where to install dependencies",
64+
)
65+
parser.add_argument(
66+
"--repo",
67+
action="store",
68+
help="The external repo name to install dependencies.",
69+
)
70+
args = parser.parse_args()
71+
72+
# Assumes any errors are logged by pip so do nothing. This command will fail if pip fails
73+
subprocess.check_output(
74+
[sys.executable, "-m", "pip", "wheel", "-r", args.requirements]
75+
)
76+
77+
targets = set()
78+
79+
for wheel in [Wheel(whl) for whl in glob.glob("*.whl")]:
80+
whl_label = sanitise_name(wheel.name())
81+
os.mkdir(whl_label)
82+
extract_wheel(wheel, whl_label, [])
83+
targets.add('"{repo}//{name}"'.format(repo=args.repo, name=whl_label))
84+
os.remove(wheel.path())
85+
86+
with open("requirements.bzl", "w") as f:
87+
f.write(
88+
"""\
89+
all_requirements = [{requirement_labels}]
90+
91+
def requirement(name):
92+
name_key = name.replace("-", "_").replace(".", "_").lower()
93+
return "{repo}//" + name_key
94+
""".format(
95+
requirement_labels=",".join(targets), repo=args.repo
96+
)
97+
)
98+
99+
100+
if __name__ == "__main__":
101+
main()

src/extract_wheels.pyc

2.95 KB
Binary file not shown.

src/wheel.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pkginfo
2+
import zipfile
3+
import pkg_resources
4+
5+
6+
class Wheel(object):
7+
def __init__(self, path):
8+
self._path = path
9+
10+
def path(self):
11+
return self._path
12+
13+
def name(self):
14+
return self.metadata().name
15+
16+
def metadata(self):
17+
return pkginfo.get_metadata(self.path())
18+
19+
def dependencies(self, extras_requested=None):
20+
if not extras_requested:
21+
# Provide an extra to safely evaluate the markers
22+
# without matching any extra
23+
extras_requested = [""]
24+
25+
dependency_set = set()
26+
27+
for req in self.metadata().requires_dist:
28+
r = pkg_resources.Requirement(req)
29+
30+
if r.marker is None or any(
31+
r.marker.evaluate({"extra": extra}) for extra in extras_requested
32+
):
33+
dependency_set.add(r.name)
34+
35+
return dependency_set
36+
37+
def unzip(self, directory):
38+
with zipfile.ZipFile(self.path(), "r") as whl:
39+
whl.extractall(directory)

0 commit comments

Comments
 (0)