Skip to content

Commit 66bc9be

Browse files
authored
fix: make py_binary#resolutions and py_test#resolutions identical. (#487)
Closes #466 - Make resolutions consistent between `py_binary` and `py_test` - Allow a list to be passed to `from_requirements` - Add an example of `virtual_deps` feature --- ### Changes are visible to end-users: yes - Searched for relevant documentation and updated as needed: yes - Breaking change (forces users to change their own code or config): no - Suggested release notes appear below: > - Fix: Make resolutions consistent between `py_binary` and `py_test` > - Feature: Allow a list to be passed to `from_requirements` > - Docs: Add an example of `virtual_deps` feature ### Test plan - Manual testing; please provide instructions so we can reproduce: Added an example
1 parent d49da0f commit 66bc9be

File tree

13 files changed

+136
-16
lines changed

13 files changed

+136
-16
lines changed

docs/py_binary.md

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/py_test.md

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/virtual_deps/BUILD.bazel

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library", "py_pytest_main", "py_test", "resolutions")
2+
load("@pypi//:requirements.bzl", "requirement")
3+
4+
py_library(
5+
name = "greet",
6+
imports = ["."],
7+
srcs = ["greet.py"],
8+
virtual_deps = ["cowsay"],
9+
)
10+
11+
# A library that *looks* like cowsay... but isn't!
12+
py_library(
13+
name = "cowsnake",
14+
imports = ["cowsnake"],
15+
srcs = ["cowsnake/cowsay.py"],
16+
virtual_deps = ["snakesay"],
17+
)
18+
19+
py_binary(
20+
name = "app",
21+
srcs = ["main.py"],
22+
resolutions = resolutions.from_requirements([
23+
"cowsay",
24+
], requirement),
25+
deps = [
26+
":greet",
27+
],
28+
python_version = "3.8.12",
29+
)
30+
31+
# Here we swap out the cowsay module for our own implementation
32+
py_binary(
33+
name = "app_snake",
34+
srcs = ["main.py"],
35+
deps = [
36+
":greet",
37+
],
38+
resolutions = resolutions.from_requirements([
39+
"snakesay",
40+
], requirement).override({
41+
"cowsay": ":cowsnake",
42+
}),
43+
python_version = "3.8.12",
44+
)
45+
46+
py_pytest_main(
47+
name = "__test__",
48+
)
49+
50+
py_test(
51+
name = "pytest_test",
52+
srcs = [
53+
"greet_test.py",
54+
":__test__",
55+
],
56+
main = ":__test__.py",
57+
package_collisions = "error",
58+
resolutions = resolutions.from_requirements([
59+
"cowsay",
60+
], requirement),
61+
deps = [
62+
requirement("pytest"),
63+
":greet",
64+
],
65+
)

examples/virtual_deps/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# virtual_deps
2+
3+
The example shows how to use `virtual_deps` feature.
4+
5+
- `greet` is a library that has a virtual dependency on `cowsay`
6+
- `cowsnake` is a library that implements some of the `cowsay` API
7+
- `app` is a binary that uses `greet` and resolves the `cowsay` virtual dependency
8+
- `app_snake` is like `app`, but swaps out `cowsay` for `cowsnake`!
9+
- `pytest_test` tests `greet` using a resolved `cowsay`
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import snakesay
2+
3+
def get_output_string(_, x):
4+
return snakesay.snakesay(x)

examples/virtual_deps/greet.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import cowsay
2+
3+
def greet(x):
4+
return cowsay.get_output_string("cow", x)

examples/virtual_deps/greet_test.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import pytest
2+
from greet import greet
3+
4+
def test_greet_contains_input():
5+
input = "Hello Alice!"
6+
assert input in greet(input), ""

examples/virtual_deps/main.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from examples.virtual_deps.greet import greet
2+
3+
print(greet("Hello virtual_deps!"))

gazelle_python.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -3860,6 +3860,8 @@ manifest:
38603860
smmap.test.test_tutorial: smmap
38613861
smmap.test.test_util: smmap
38623862
smmap.util: smmap
3863+
snakesay: snakesay
3864+
snakesay.snakesay: snakesay
38633865
sqlparse: sqlparse
38643866
sqlparse.cli: sqlparse
38653867
sqlparse.engine: sqlparse
@@ -3988,4 +3990,4 @@ manifest:
39883990
yaml.tokens: PyYAML
39893991
pip_repository:
39903992
name: pypi
3991-
integrity: 2e0cbc780621ce75951013e6968f66d1aadd8d1ecb8b7c470883c998b169e733
3993+
integrity: de76d829a37e0a5afc12859854a0f8fa096534eb4c32e6a346b6378b55634f44

py/defs.bzl

+22-5
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ load("@rules_python//python:repositories.bzl", "py_repositories", "python_regist
1414
python_register_toolchains(
1515
name = "python_toolchain_3_8",
1616
python_version = "3.8.12",
17-
# setting set_python_version_constraint makes it so that only matches py_* rule
17+
# setting set_python_version_constraint makes it so that only matches py_* rule
1818
# which has this exact version set in the `python_version` attribute.
1919
set_python_version_constraint = True,
2020
)
2121
22-
# It's important to register the default toolchain last it will match any py_* target.
22+
# It's important to register the default toolchain last it will match any py_* target.
2323
python_register_toolchains(
2424
name = "python_toolchain",
2525
python_version = "3.9",
@@ -117,9 +117,26 @@ def py_binary(name, srcs = [], main = None, **kwargs):
117117

118118
_py_binary_or_test(name = name, rule = _py_binary, srcs = srcs, main = main, resolutions = resolutions, **kwargs)
119119

120-
def py_test(name, main = None, srcs = [], **kwargs):
121-
"Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`."
120+
def py_test(name, srcs = [], main = None, **kwargs):
121+
"""Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`.
122+
123+
Args:
124+
name: Name of the rule.
125+
srcs: Python source files.
126+
main: Entry point.
127+
Like rules_python, this is treated as a suffix of a file that should appear among the srcs.
128+
If absent, then `[name].py` is tried. As a final fallback, if the srcs has a single file,
129+
that is used as the main.
130+
**kwargs: additional named parameters to `py_binary_rule`.
131+
"""
122132

123133
# Ensure that any other targets we write will be testonly like the py_test target
124134
kwargs["testonly"] = True
125-
_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, **kwargs)
135+
136+
# For a clearer DX when updating resolutions, the resolutions dict is "string" -> "label",
137+
# where the rule attribute is a label-keyed-dict, so reverse them here.
138+
resolutions = kwargs.pop("resolutions", None)
139+
if resolutions:
140+
resolutions = resolutions.to_label_keyed_dict()
141+
142+
_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, resolutions = resolutions, **kwargs)

py/private/virtual.bzl

+6-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ def _make_resolution(name, requirement):
6060
requirement = requirement,
6161
)
6262

63+
def _from_requirements(base, requirement_fn = lambda r: r):
64+
if type(base) == "list":
65+
base = { k: None for k in base }
66+
return _make_resolutions(base, requirement_fn)
67+
6368
resolutions = struct(
64-
from_requirements = _make_resolutions,
69+
from_requirements = _from_requirements,
6570
empty = lambda: _make_resolutions({}),
6671
)

requirements.in

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ colorama~=0.4.0
33
click
44
pytest
55
cowsay
6+
snakesay
67
ftfy==6.2.0
78
neptune==1.10.2
89
six

requirements.txt

+4
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,10 @@ smmap==5.0.1 \
780780
--hash=sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62 \
781781
--hash=sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da
782782
# via gitdb
783+
snakesay==0.10.3 \
784+
--hash=sha256:0a601a0c408deba05a20b11ba2f0db336b1915274601053ef8de3a6b354c60fc \
785+
--hash=sha256:6346aa7231b1970efc6fa8b3ea78bd015b3d5a7e33ba709c17e00bcc3328f93f
786+
# via -r requirements.in
783787
sqlparse==0.5.1 \
784788
--hash=sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4 \
785789
--hash=sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e

0 commit comments

Comments
 (0)