diff --git a/docs/py_test.md b/docs/py_test.md index b2e15da6..ea1294df 100644 --- a/docs/py_test.md +++ b/docs/py_test.md @@ -95,7 +95,7 @@ py_pytest_main wraps the template rendering target and the final py_library. ## py_test
-py_test(name, srcs, main, kwargs) +py_test(name, srcs, main, pytest_main, kwargs)Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`. @@ -108,6 +108,7 @@ Identical to [py_binary](./py_binary.md), but produces a target that can be used | name | Name of the rule. | none | | 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. | None |
+| pytest_main | If set, generate a [py_pytest_main](#py_pytest_main) script and use it as the main. The deps should include the pytest package (as well as the coverage package if desired). | False |
| kwargs | additional named parameters to py_binary_rule. | none |
diff --git a/e2e/use_release/src/BUILD.bazel b/e2e/use_release/src/BUILD.bazel
index 900377ba..a50b68ea 100644
--- a/e2e/use_release/src/BUILD.bazel
+++ b/e2e/use_release/src/BUILD.bazel
@@ -1,4 +1,4 @@
-load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_pytest_main", "py_test")
+load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_test")
py_binary(
name = "main",
@@ -9,25 +9,14 @@ py_binary(
main = "__main__.py",
)
-# TODO(alex): land https://github.com/aspect-build/rules_py/pull/401 and shorten this
-py_pytest_main(
- name = "__test__",
- data = ["//:.coveragerc"],
- deps = [
- "@pip//coverage",
- "@pip//pytest",
- ],
-)
-
py_test(
name = "test",
- srcs = [
- "my_test.py",
- ":__test__",
- ],
- main = ":__test__.py",
+ srcs = ["my_test.py"],
+ data = ["//:.coveragerc"],
+ pytest_main = True,
deps = [
- ":__test__",
":main",
+ "@pip//coverage", # keep
+ "@pip//pytest", # keep
],
)
diff --git a/examples/pytest/BUILD.bazel b/examples/pytest/BUILD.bazel
index d423cacd..6e93e5f4 100644
--- a/examples/pytest/BUILD.bazel
+++ b/examples/pytest/BUILD.bazel
@@ -1,4 +1,4 @@
-load("@aspect_rules_py//py:defs.bzl", "py_library", "py_pytest_main", "py_test")
+load("@aspect_rules_py//py:defs.bzl", "py_library", "py_test")
py_library(
name = "lib",
@@ -6,53 +6,19 @@ py_library(
imports = ["../.."],
)
-py_pytest_main(
- name = "__test__",
- chdir = package_name(), # So that test fixtures are available at the correct path
- deps = [
- "@pypi_coverage//:pkg",
- "@pypi_pytest//:pkg",
- ],
-)
-
py_test(
name = "pytest_test",
- srcs = [
- "foo_test.py",
- ":__test__",
- ],
+ srcs = ["foo_test.py"],
data = glob([
"fixtures/*.json",
]),
env_inherit = ["FOO"],
imports = ["../.."],
- main = ":__test__.py",
package_collisions = "warning",
+ pytest_main = True,
deps = [
- ":__test__",
- ":lib",
- "@pypi_ftfy//:pkg",
- "@pypi_neptune//:pkg",
- "@pypi_pytest//:pkg",
- ],
-)
-
-py_test(
- name = "nested/pytest",
- srcs = [
- "foo_test.py",
- ":__test__",
- ],
- data = glob([
- "fixtures/*.json",
- ]),
- env_inherit = ["FOO"],
- imports = ["../.."],
- main = ":__test__.py",
- package_collisions = "warning",
- deps = [
- ":__test__",
":lib",
+ "@pypi_coverage//:pkg",
"@pypi_ftfy//:pkg",
"@pypi_neptune//:pkg",
"@pypi_pytest//:pkg",
@@ -60,16 +26,12 @@ py_test(
)
py_test(
- name = "sharding_test",
- srcs = [
- "__test__.py",
- "sharding_test.py",
- ],
+ # NB: name contains a slash as regression test for #483
+ name = "sharded/test",
+ srcs = ["sharding_test.py"],
imports = ["../.."],
- main = "__test__.py",
package_collisions = "warning",
+ pytest_main = True,
shard_count = 2,
- deps = [
- "__test__",
- ],
+ deps = ["@pypi_pytest//:pkg"],
)
diff --git a/examples/pytest/foo_test.py b/examples/pytest/foo_test.py
index 26366250..b8696c9b 100644
--- a/examples/pytest/foo_test.py
+++ b/examples/pytest/foo_test.py
@@ -1,12 +1,14 @@
-import pytest
import json
+import os
+
from examples.pytest.foo import add
def test_add():
assert add(1, 1) == 2, "Expected 1 + 1 to equal 2"
def test_hello_json():
- content = open('fixtures/hello.json', 'r').read()
+ # NB: we don't use the chdir attribute so the test working directory is the repository root
+ content = open(os.path.join(os.getenv('TEST_TARGET').lstrip('/').split(':')[0], 'fixtures/hello.json'), 'r').read()
data = json.loads(content)
assert data["message"] == "Hello, world.", "Message is as expected"
diff --git a/py/defs.bzl b/py/defs.bzl
index dc0e1772..5316355f 100644
--- a/py/defs.bzl
+++ b/py/defs.bzl
@@ -117,7 +117,7 @@ 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, srcs = [], main = None, **kwargs):
+def py_test(name, srcs = [], main = None, pytest_main = False, **kwargs):
"""Identical to [py_binary](./py_binary.md), but produces a target that can be used with `bazel test`.
Args:
@@ -127,6 +127,8 @@ def py_test(name, srcs = [], main = None, **kwargs):
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.
+ pytest_main: If set, generate a [py_pytest_main](#py_pytest_main) script and use it as the main.
+ The deps should include the pytest package (as well as the coverage package if desired).
**kwargs: additional named parameters to `py_binary_rule`.
"""
@@ -139,4 +141,14 @@ def py_test(name, srcs = [], main = None, **kwargs):
if resolutions:
resolutions = resolutions.to_label_keyed_dict()
- _py_binary_or_test(name = name, rule = _py_test, srcs = srcs, main = main, resolutions = resolutions, **kwargs)
+ deps = kwargs.pop("deps", [])
+ if pytest_main:
+ if main:
+ fail("When pytest_main is set, the main attribute should not be set.")
+ pytest_main_target = name + ".pytest_main"
+ main = pytest_main_target + ".py"
+ py_pytest_main(name = pytest_main_target)
+ srcs.append(main)
+ deps.append(pytest_main_target)
+
+ _py_binary_or_test(name = name, rule = _py_test, srcs = srcs, deps = deps, main = main, resolutions = resolutions, **kwargs)
diff --git a/py/private/pytest.py.tmpl b/py/private/pytest.py.tmpl
index b890b9a0..aaf5282f 100644
--- a/py/private/pytest.py.tmpl
+++ b/py/private/pytest.py.tmpl
@@ -17,7 +17,12 @@ import os
from pathlib import Path
from typing import List
-import pytest
+try:
+ import pytest
+except ModuleNotFoundError as e:
+ print("ERROR: pytest must be included in the deps of the py_pytest_main or py_test target")
+ raise e
+
# None means coverage wasn't enabled
cov = None
# For workaround of https://github.com/nedbat/coveragepy/issues/963
@@ -37,7 +42,7 @@ if "COVERAGE_MANIFEST" in os.environ:
coveragepy_absfile_mapping = {coverage.files.abs_file(mfe): mfe for mfe in manifest_entries}
cov.start()
except ModuleNotFoundError as e:
- print("WARNING: python coverage setup failed. Do you need to include the 'coverage' library as a dependency of py_pytest_main?", e)
+ print("WARNING: python coverage setup failed. Do you need to include the 'coverage' package as a dependency of py_pytest_main?", e)
pass
from pytest_shard import ShardPlugin