Skip to content

feat: implement pytest_test rule#3931

Open
rickeylev wants to merge 13 commits into
bazel-contrib:mainfrom
rickeylev:implement-pytest-main-gen
Open

feat: implement pytest_test rule#3931
rickeylev wants to merge 13 commits into
bazel-contrib:mainfrom
rickeylev:implement-pytest-main-gen

Conversation

@rickeylev

Copy link
Copy Markdown
Collaborator

This implements a pytest_test rule based upon the pytest_bazel library.

The core implementation of this is fairly simple: a file is generated that calls
pytest.main() with the srcs to test. This generated file is the file that is
run by py_test.

DO NOT MERGE: Needs discussion. This was quickly thrown together. Haven't put any thought into how to handle conf.py or other pytest flags/settings. Works in the simple example, but haven't vetted it much.

rickeylev and others added 12 commits July 13, 2026 19:59
Introduce pytest_test rule to run tests using pytest. It generates a bootstrap script that invokes pytest_bazel wrapper.

- Create python/private/pytest_test package with template and implementation.
- Re-export pytest_test macro in python/pytest_test.bzl.
- Update features.bzl.
- Add bzl_library build tests and a tool to update them automatically.
- Register update tool as pre-commit hook.
…ild test

- Update default visibility in python/uv/private/BUILD.bazel to //:__subpackages__.
- Remove redundant explicit visibility from targets in python/uv/private/BUILD.bazel.
- Regenerate tests/bzl_build_tests/BUILD.bazel to include these targets (total 203 targets).
Undo creation of bzl_library build tests package and the automatic update tool. Revert visibility changes that were needed for it. This reverts commit 66632db and parts of 12a555e.
Remove local src/pytest_bazel implementation and use the pytest-bazel package from PyPI instead.

- Add pytest-bazel to docs/pyproject.toml.
- Update docs/requirements.txt and docs/uv.lock.
- Remove src/pytest_bazel.
- Update python/private/pytest_test/pytest_test.bzl to depend on @dev_pip//pytest_bazel.
Allow users to specify their own pytest and pytest_bazel targets, defaulting to aliases in python/private/pytest_test pointing to @pypi.

- Add pytest and pytest_bazel aliases in python/private/pytest_test/BUILD.bazel pointing to @pypi//pytest and @pypi//pytest_bazel.
- Update python/private/pytest_test/pytest_test.bzl to accept pytest and pytest_bazel arguments and use them in deps.
- Update tests/pytest_test/BUILD.bazel to override these arguments to use @dev_pip.
- Import the automatically generated 'pypi' repo from dev_pip extension in MODULE.bazel.
- Rename aliases in python/private/pytest_test/BUILD.bazel to default_pytest and default_pytest_bazel, and add comments explaining their usage.
- Update python/private/pytest_test/pytest_test.bzl to default pytest/pytest_bazel arguments to None, convert them to Label constants inside, and update docstrings (including documenting that 'main' is not supported).
- Update tests/pytest_test/BUILD.bazel to remove overrides and make the target compatible with bzlmod only.
@rickeylev
rickeylev requested a review from aignas as a code owner July 16, 2026 20:21
@rickeylev rickeylev added the do not merge Tag that prevents merging label Jul 16, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new pytest_test rule to support running pytest tests under Bazel, along with corresponding dependency updates and integration tests. Feedback on the changes highlights several critical issues: calling pytest_bazel.main in the bootstrap template will cause an AttributeError as pytest.main should be called instead; the config_settings attribute is unsupported in pytest_test and will cause a loading-phase error; the "pypi" repository is not exported by dev_pip in MODULE.bazel; list concatenation with srcs in pytest_test.bzl will fail if srcs is a select(); template expansion can be simplified by avoiding computed_substitutions; and the bzl_library target for pytest_test is missing its srcs attribute.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread python/private/pytest_test/pytest_bootstrap_template.py
Comment thread tests/pytest_test/BUILD.bazel
Comment thread MODULE.bazel
py_test(
name = name,
main = main_file,
srcs = [bootstrap_target] + srcs,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If srcs is a select(), concatenating it as [bootstrap_target] + srcs will fail with a Starlark evaluation error because list-plus-select concatenation is not supported (only select-plus-list is supported). Reversing the order to srcs + [bootstrap_target] resolves this and makes the rule compatible with select()s.

Suggested change
srcs = [bootstrap_target] + srcs,
srcs = srcs + [bootstrap_target],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bootstrap is not needed, we can just use the main_module that already exists in the py_test rule.

Comment on lines +57 to +71
computed_subs = ctx.actions.template_dict()

computed_subs.add_joined(
"%TEST_FILES%",
depset(ctx.files.srcs),
join_with = "\n",
map_each = _map_file,
)

ctx.actions.expand_template(
output = output,
template = ctx.file._bootstrap_template,
substitutions = {},
computed_substitutions = computed_subs,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since ctx.files.srcs is already a flat list of files available at analysis time, using computed_substitutions and template_dict is unnecessary and adds extra complexity. We can simplify this by using standard substitutions with a simple string join, which is also more compatible with older Bazel versions.

    ctx.actions.expand_template(
        output = output,
        template = ctx.file._bootstrap_template,
        substitutions = {
            "%TEST_FILES%": "\n".join([f.short_path for f in ctx.files.srcs]),
        },
    )

Comment on lines +10 to +15
bzl_library(
name = "pytest_test",
deps = [
"//python:py_test",
],
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The bzl_library target is missing the srcs attribute. Please include pytest_test.bzl in srcs so that downstream bzl_library targets can correctly depend on it.

Suggested change
bzl_library(
name = "pytest_test",
deps = [
"//python:py_test",
],
)
bzl_library(
name = "pytest_test",
srcs = ["pytest_test.bzl"],
deps = [
"//python:py_test",
],
)

@aignas aignas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need a little bit of time to think how:

  • We can handle sharding related optional dependencies.
  • We can handle other features that may require extra plugins.

Since pytest_bazel is its own repo, either we have to migrate the code to rules_python or migrate the rule imple to pytest_bazel and then proxy it in rules_python. Let me think about it a little.

Other ideas/thoughts here are welcome.

py_test(
name = name,
main = main_file,
srcs = [bootstrap_target] + srcs,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bootstrap is not needed, we can just use the main_module that already exists in the py_test rule.

@rickeylev

Copy link
Copy Markdown
Collaborator Author

The bootstrap is not needed, we can just use the main_module that already exists in the py_test rule.

How so? I originally tried that, but it didn't work. I didn't see how it could work in practice -- it would have to somehow auto discover the subset of files to test, but it has no info about what that subset is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge Tag that prevents merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants