Skip to content

Commit 6ad4667

Browse files
authored
feat: port pytest_test macro (#401)
Comes from caseyduquettesc/rules_python_pytest#13 It's much easier to properly gazelle-generate a BUILD file in this shape rather than the awkward way you're forced to hold our py_pytest_main.
1 parent 9e99103 commit 6ad4667

File tree

6 files changed

+42
-71
lines changed

6 files changed

+42
-71
lines changed

docs/py_test.md

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

e2e/use_release/src/BUILD.bazel

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_pytest_main", "py_test")
1+
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_test")
22

33
py_binary(
44
name = "main",
@@ -9,25 +9,14 @@ py_binary(
99
main = "__main__.py",
1010
)
1111

12-
# TODO(alex): land https://github.com/aspect-build/rules_py/pull/401 and shorten this
13-
py_pytest_main(
14-
name = "__test__",
15-
data = ["//:.coveragerc"],
16-
deps = [
17-
"@pip//coverage",
18-
"@pip//pytest",
19-
],
20-
)
21-
2212
py_test(
2313
name = "test",
24-
srcs = [
25-
"my_test.py",
26-
":__test__",
27-
],
28-
main = ":__test__.py",
14+
srcs = ["my_test.py"],
15+
data = ["//:.coveragerc"],
16+
pytest_main = True,
2917
deps = [
30-
":__test__",
3118
":main",
19+
"@pip//coverage", # keep
20+
"@pip//pytest", # keep
3221
],
3322
)

examples/pytest/BUILD.bazel

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,37 @@
1-
load("@aspect_rules_py//py:defs.bzl", "py_library", "py_pytest_main", "py_test")
1+
load("@aspect_rules_py//py:defs.bzl", "py_library", "py_test")
22

33
py_library(
44
name = "lib",
55
srcs = ["foo.py"],
66
imports = ["../.."],
77
)
88

9-
py_pytest_main(
10-
name = "__test__",
11-
chdir = package_name(), # So that test fixtures are available at the correct path
12-
deps = [
13-
"@pypi_coverage//:pkg",
14-
"@pypi_pytest//:pkg",
15-
],
16-
)
17-
189
py_test(
1910
name = "pytest_test",
20-
srcs = [
21-
"foo_test.py",
22-
":__test__",
23-
],
11+
srcs = ["foo_test.py"],
2412
data = glob([
2513
"fixtures/*.json",
2614
]),
2715
env_inherit = ["FOO"],
2816
imports = ["../.."],
29-
main = ":__test__.py",
3017
package_collisions = "warning",
18+
pytest_main = True,
3119
deps = [
32-
":__test__",
33-
":lib",
34-
"@pypi_ftfy//:pkg",
35-
"@pypi_neptune//:pkg",
36-
"@pypi_pytest//:pkg",
37-
],
38-
)
39-
40-
py_test(
41-
name = "nested/pytest",
42-
srcs = [
43-
"foo_test.py",
44-
":__test__",
45-
],
46-
data = glob([
47-
"fixtures/*.json",
48-
]),
49-
env_inherit = ["FOO"],
50-
imports = ["../.."],
51-
main = ":__test__.py",
52-
package_collisions = "warning",
53-
deps = [
54-
":__test__",
5520
":lib",
21+
"@pypi_coverage//:pkg",
5622
"@pypi_ftfy//:pkg",
5723
"@pypi_neptune//:pkg",
5824
"@pypi_pytest//:pkg",
5925
],
6026
)
6127

6228
py_test(
63-
name = "sharding_test",
64-
srcs = [
65-
"__test__.py",
66-
"sharding_test.py",
67-
],
29+
# NB: name contains a slash as regression test for #483
30+
name = "sharded/test",
31+
srcs = ["sharding_test.py"],
6832
imports = ["../.."],
69-
main = "__test__.py",
7033
package_collisions = "warning",
34+
pytest_main = True,
7135
shard_count = 2,
72-
deps = [
73-
"__test__",
74-
],
36+
deps = ["@pypi_pytest//:pkg"],
7537
)

examples/pytest/foo_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import pytest
21
import json
32

3+
import os
4+
45
from examples.pytest.foo import add
56

67
def test_add():
78
assert add(1, 1) == 2, "Expected 1 + 1 to equal 2"
89

910
def test_hello_json():
10-
content = open('fixtures/hello.json', 'r').read()
11+
# NB: we don't use the chdir attribute so the test working directory is the repository root
12+
content = open(os.path.join(os.getenv('TEST_TARGET').lstrip('/').split(':')[0], 'fixtures/hello.json'), 'r').read()
1113
data = json.loads(content)
1214
assert data["message"] == "Hello, world.", "Message is as expected"

py/defs.bzl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ 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, srcs = [], main = None, **kwargs):
120+
def py_test(name, srcs = [], main = None, pytest_main = False, **kwargs):
121121
"""Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`.
122122
123123
Args:
@@ -127,6 +127,8 @@ def py_test(name, srcs = [], main = None, **kwargs):
127127
Like rules_python, this is treated as a suffix of a file that should appear among the srcs.
128128
If absent, then `[name].py` is tried. As a final fallback, if the srcs has a single file,
129129
that is used as the main.
130+
pytest_main: If set, generate a [py_pytest_main](#py_pytest_main) script and use it as the main.
131+
The deps should include the pytest package (as well as the coverage package if desired).
130132
**kwargs: additional named parameters to `py_binary_rule`.
131133
"""
132134

@@ -139,4 +141,14 @@ def py_test(name, srcs = [], main = None, **kwargs):
139141
if resolutions:
140142
resolutions = resolutions.to_label_keyed_dict()
141143

142-
_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, resolutions = resolutions, **kwargs)
144+
deps = kwargs.pop("deps", [])
145+
if pytest_main:
146+
if main:
147+
fail("When pytest_main is set, the main attribute should not be set.")
148+
pytest_main_target = name + ".pytest_main"
149+
main = pytest_main_target + ".py"
150+
py_pytest_main(name = pytest_main_target)
151+
srcs.append(main)
152+
deps.append(pytest_main_target)
153+
154+
_py_binary_or_test(name = name, rule = _py_test, srcs = srcs, deps = deps, main = main, resolutions = resolutions, **kwargs)

py/private/pytest.py.tmpl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ import os
1717
from pathlib import Path
1818
from typing import List
1919

20-
import pytest
20+
try:
21+
import pytest
22+
except ModuleNotFoundError as e:
23+
print("ERROR: pytest must be included in the deps of the py_pytest_main or py_test target")
24+
raise e
25+
2126
# None means coverage wasn't enabled
2227
cov = None
2328
# For workaround of https://github.com/nedbat/coveragepy/issues/963
@@ -37,7 +42,7 @@ if "COVERAGE_MANIFEST" in os.environ:
3742
coveragepy_absfile_mapping = {coverage.files.abs_file(mfe): mfe for mfe in manifest_entries}
3843
cov.start()
3944
except ModuleNotFoundError as e:
40-
print("WARNING: python coverage setup failed. Do you need to include the 'coverage' library as a dependency of py_pytest_main?", e)
45+
print("WARNING: python coverage setup failed. Do you need to include the 'coverage' package as a dependency of py_pytest_main?", e)
4146
pass
4247

4348
from pytest_shard import ShardPlugin

0 commit comments

Comments
 (0)