Skip to content

fix: make py_binary#resolutions and py_test#resolutions identical. #487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/py_binary.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions docs/py_test.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions examples/virtual_deps/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library", "py_pytest_main", "py_test", "resolutions")
load("@pypi//:requirements.bzl", "requirement")

py_library(
name = "greet",
imports = ["."],
srcs = ["greet.py"],
virtual_deps = ["cowsay"],
)

# A library that *looks* like cowsay... but isn't!
py_library(
name = "cowsnake",
imports = ["cowsnake"],
srcs = ["cowsnake/cowsay.py"],
virtual_deps = ["snakesay"],
)

py_binary(
name = "app",
srcs = ["main.py"],
resolutions = resolutions.from_requirements([
"cowsay",
], requirement),
deps = [
":greet",
],
python_version = "3.8.12",
)

# Here we swap out the cowsay module for our own implementation
py_binary(
name = "app_snake",
srcs = ["main.py"],
deps = [
":greet",
],
resolutions = resolutions.from_requirements([
"snakesay",
], requirement).override({
"cowsay": ":cowsnake",
}),
python_version = "3.8.12",
)

py_pytest_main(
name = "__test__",
)

py_test(
name = "pytest_test",
srcs = [
"greet_test.py",
":__test__",
],
main = ":__test__.py",
package_collisions = "error",
resolutions = resolutions.from_requirements([
"cowsay",
], requirement),
deps = [
requirement("pytest"),
":greet",
],
)
9 changes: 9 additions & 0 deletions examples/virtual_deps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# virtual_deps

The example shows how to use `virtual_deps` feature.

- `greet` is a library that has a virtual dependency on `cowsay`
- `cowsnake` is a library that implements some of the `cowsay` API
- `app` is a binary that uses `greet` and resolves the `cowsay` virtual dependency
- `app_snake` is like `app`, but swaps out `cowsay` for `cowsnake`!
- `pytest_test` tests `greet` using a resolved `cowsay`
4 changes: 4 additions & 0 deletions examples/virtual_deps/cowsnake/cowsay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import snakesay

def get_output_string(_, x):
return snakesay.snakesay(x)
4 changes: 4 additions & 0 deletions examples/virtual_deps/greet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import cowsay

def greet(x):
return cowsay.get_output_string("cow", x)
6 changes: 6 additions & 0 deletions examples/virtual_deps/greet_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pytest
from greet import greet

def test_greet_contains_input():
input = "Hello Alice!"
assert input in greet(input), ""
3 changes: 3 additions & 0 deletions examples/virtual_deps/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from examples.virtual_deps.greet import greet

print(greet("Hello virtual_deps!"))
4 changes: 3 additions & 1 deletion gazelle_python.yaml
Original file line number Diff line number Diff line change
@@ -3860,6 +3860,8 @@ manifest:
smmap.test.test_tutorial: smmap
smmap.test.test_util: smmap
smmap.util: smmap
snakesay: snakesay
snakesay.snakesay: snakesay
sqlparse: sqlparse
sqlparse.cli: sqlparse
sqlparse.engine: sqlparse
@@ -3988,4 +3990,4 @@ manifest:
yaml.tokens: PyYAML
pip_repository:
name: pypi
integrity: 2e0cbc780621ce75951013e6968f66d1aadd8d1ecb8b7c470883c998b169e733
integrity: de76d829a37e0a5afc12859854a0f8fa096534eb4c32e6a346b6378b55634f44
27 changes: 22 additions & 5 deletions py/defs.bzl
Original file line number Diff line number Diff line change
@@ -14,12 +14,12 @@ load("@rules_python//python:repositories.bzl", "py_repositories", "python_regist
python_register_toolchains(
name = "python_toolchain_3_8",
python_version = "3.8.12",
# setting set_python_version_constraint makes it so that only matches py_* rule
# setting set_python_version_constraint makes it so that only matches py_* rule
# which has this exact version set in the `python_version` attribute.
set_python_version_constraint = True,
)

# It's important to register the default toolchain last it will match any py_* target.
# It's important to register the default toolchain last it will match any py_* target.
python_register_toolchains(
name = "python_toolchain",
python_version = "3.9",
@@ -116,9 +116,26 @@ def py_binary(name, srcs = [], main = None, **kwargs):

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

def py_test(name, main = None, srcs = [], **kwargs):
"Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`."
def py_test(name, srcs = [], main = None, **kwargs):
"""Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`.

Args:
name: Name of the rule.
srcs: Python source files.
main: Entry point.
Like rules_python, this is treated as a suffix of a file that should appear among the srcs.
If absent, then `[name].py` is tried. As a final fallback, if the srcs has a single file,
that is used as the main.
**kwargs: additional named parameters to `py_binary_rule`.
"""

# Ensure that any other targets we write will be testonly like the py_test target
kwargs["testonly"] = True
_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, **kwargs)

# For a clearer DX when updating resolutions, the resolutions dict is "string" -> "label",
# where the rule attribute is a label-keyed-dict, so reverse them here.
resolutions = kwargs.pop("resolutions", None)
if resolutions:
resolutions = resolutions.to_label_keyed_dict()

_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, resolutions = resolutions, **kwargs)
7 changes: 6 additions & 1 deletion py/private/virtual.bzl
Original file line number Diff line number Diff line change
@@ -60,7 +60,12 @@ def _make_resolution(name, requirement):
requirement = requirement,
)

def _from_requirements(base, requirement_fn = lambda r: r):
if type(base) == "list":
base = { k: None for k in base }
return _make_resolutions(base, requirement_fn)

resolutions = struct(
from_requirements = _make_resolutions,
from_requirements = _from_requirements,
empty = lambda: _make_resolutions({}),
)
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ colorama~=0.4.0
click
pytest
cowsay
snakesay
ftfy==6.2.0
neptune==1.10.2
six
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -780,6 +780,10 @@ smmap==5.0.1 \
--hash=sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62 \
--hash=sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da
# via gitdb
snakesay==0.10.3 \
--hash=sha256:0a601a0c408deba05a20b11ba2f0db336b1915274601053ef8de3a6b354c60fc \
--hash=sha256:6346aa7231b1970efc6fa8b3ea78bd015b3d5a7e33ba709c17e00bcc3328f93f
# via -r requirements.in
sqlparse==0.5.1 \
--hash=sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4 \
--hash=sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e