Skip to content
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

Add support for batched pytest execution #17385

Merged
merged 13 commits into from Oct 28, 2022

Conversation

danxmoran
Copy link
Contributor

@danxmoran danxmoran commented Oct 28, 2022

Closes #14941

It's sometimes safe to run multiple python_tests within a single pytest process, and doing so can give significant wins by allowing reuse of expensive test setup/teardown logic. To allow users to opt into this behavior we introduce a new batch_compatibility_tag field on python_test, with semantics:

  • If unset, the test is assumed to be incompatible with all others and will run it a dedicated pytest process
  • If set and != the value on some other python_test, the test is explicitly incompatible with the other and is guaranteed to not run in the same pytest process
  • If set and == the value on some other python_test, the tests are explicitly compatible and may run in the same pytest process

Compatible tests may not end up in the same pytest batch if:

  • There are "too many" compatible tests in a partition, as determined by [test].batch_size
  • Compatible tests have some incompatibility in Pants metadata (i.e. different resolve or extra_env_vars)

When tests with the same batch_compatibility_tag have incompatibilities in some other Pants metadata, the custom partitioning rule will split them into separate partitions. We'd previously discussed raising an error in this case when calling the field batch_key/batch_tag, but IMO that approach will have a worse UX - this way users can set a high-level batch_compatibility_tag using __defaults__ and then have things continue to Just Work as they add/remove extra_env_vars and parameterize resolves on lower-level targets.

All the existing tests for the logic changed here are integration tests,
so I'll need to wait until the entire batching implementation is done to
check if the new logic works... but this at least makes the types line
up.
Same as coverage, the tests for the change logic are all integration
tests so we'll need to wait until the end to see if this works
properly...
The custom partitioner still creates one partition per field-set, but
the runner rule should now be capable of testing an arbitrary number of
input field-sets. As part of this, I've defined the metadata type for
`pytest` partitions and shifted some logic for calculating metadata
fields from the runner rule into the partitioner rule.
If two `python_test` targets have the same value for
`batch_compatibility_tag`, then they are eligible to be batched into the
same `pytest` process. They might _not_ be batched in the same process
if:

    * There are "too many" compatible tests as determined by the
      `[test].batch_size` config option
    * Compatible tests have some incompatible Pants metadata (i.e.
      different `extra_env_vars` or `xdist_concurrency`)

Tests that don't set the new field are assumed to be incompatible with
all others, and continue to run one-target-per-process.
Improve batching hit-rate when the vars are the same, but listed in a
different order.
Ensure that the expected results don't change in the presence of
batching.
@danxmoran danxmoran marked this pull request as ready for review October 28, 2022 18:44
Copy link
Sponsor Member

@stuhood stuhood left a comment

Choose a reason for hiding this comment

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

Looks great! Thanks a lot.

src/python/pants/backend/python/target_types.py Outdated Show resolved Hide resolved
src/python/pants/backend/python/goals/pytest_runner.py Outdated Show resolved Hide resolved
src/python/pants/backend/python/goals/pytest_runner.py Outdated Show resolved Hide resolved
Only calculate it if needed.
Make sure coverage collection works in both cases
I think ideally the plugin wouldn't need to think about this, but for
now the core `test` logic will raise an error if a partition spans
multiple environments so we should do our best not to get into that
situation...
@stuhood stuhood enabled auto-merge (squash) October 28, 2022 20:24
auto-merge was automatically disabled October 28, 2022 20:24

Head branch was pushed to by a user without write access

@Eric-Arellano Eric-Arellano enabled auto-merge (squash) October 28, 2022 20:28
@Eric-Arellano Eric-Arellano merged commit d39ad4b into pantsbuild:main Oct 28, 2022
@benjyw
Copy link
Sponsor Contributor

benjyw commented Oct 28, 2022

🎉

@danxmoran danxmoran deleted the danxmoran/batch-pytest branch October 31, 2022 12:54
Copy link
Contributor

@Eric-Arellano Eric-Arellano left a comment

Choose a reason for hiding this comment

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

Neat!

if concurrency is None:
contents = await Get(DigestContents, Digest, field_set_source_files.snapshot.digest)
concurrency = _count_pytest_tests(contents)
xdist_concurrency = concurrency

timeout_seconds: int | None = None
for field_set in request.field_sets:
timeout = field_set.timeout.calculate_from_global_options(test_subsystem, pytest)
Copy link
Contributor

Choose a reason for hiding this comment

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

It probably would be good to update the help for the timeout field to mention this summing behavior

Comment on lines +907 to +908
* Compatible tests have some incompatibility in Pants metadata (i.e. different \
`resolve`s or `extra_env_vars`).
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts on adding logs in this situation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not opposed - in our repo we don't (yet) use the Pants features that cause this sort of incompatibility so I'm not sure how to judge the noisiness vs. usefulness

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe debug level to start

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

Successfully merging this pull request may close these issues.

Run pytest on multiple files the pytest-way
4 participants