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 --debug option to run V2 tests interactively. #8827

Merged
merged 20 commits into from Jan 9, 2020

Conversation

codealchemy
Copy link
Contributor

Problem:

  • Users are unable to run V2 tests interactively for debugging purposes

Solution:

  • Add a --debug option to the test console rule, which runs a single test target (will error otherwise) interactively.

Result:

  • Users can run a single test target in an interactive session that will support debugging / break points.

output_pytest_requirements_pex = 'pytest-with-requirements.pex'

resolved_requirements_pex = await Get[Pex](
CreatePex(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a fair amount of duplication between this and the python_test_runner rule - we would be able to leverage create_pex_from_target_closure to replace much of this (the only mod it would need is the ability to pass the additional_requirements seen on l51).

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

create_pex_from_target_closure is now in master, FWIW.

Copy link
Sponsor Member

@stuhood stuhood Dec 20, 2019

Choose a reason for hiding this comment

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

The need to duplicate the test runner was one of the motivations for the design from https://docs.google.com/document/d/1Hn73YlhTPROlULTMa_3A-Fdv7hAPiHFII8rvXri5l7E/edit?disco=AAAAD2DjLRk : basically, having a "foreground=True" flag. cc @gshuflin

Before continuing with this implementation, it would be really good to confirm that adding that flag would not be a net simplification over needing to duplicate rules in non-composable ways like this.

Copy link
Contributor

Choose a reason for hiding this comment

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

@stuhood @codealchemy n.b. some of these comments were brought up in the context of #8855

The rules necessary to implement --debug should be composeable with the exiting run_python_test rule that yields a TestResult up to here:

request = resolved_requirements_pex.create_execute_request(

This line of code returns an ExecuteProcessRequest and then runs it. The --debug version should take this same ExecuteProcessRequest and extract the fields from it that are relevant to running an InteractiveProcessRequest.

create_execute_request in hermetic_pex.py returns an ExecuteProcessRequest that has the fields:

      argv=argv,
      input_files=input_files,
      description=description,
      env=hermetic_env

which are implemented on InteractiveProcessRequest (at least with the above PR which implements input_files), as well as a timeout (which is not currently implemented on InteractiveProcessRequest, but would be easy to do).

It was suggested that InteractiveProcessRequest should perhaps change to just wrap the ExecuteProcessRequest type, rather than duplicate some of its fields. If we go that route, than that makes this --debug rule ever-so-slightly easier to write because then the --debug flag can control whether or not the rule graph asks for the EPR directly or asks for an InteractiveProcessRequest that wraps the EPR.

But either way, this test-debug rule can be implemented by abstraction all of the run_python_test except for the last few lines into a new rule, and then composing that rule with additional rules that get us the new behavior we want, i.e. the ability to get either an ExecuteProcessRequest or InteractiveProcessRequest based on --debug.

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

Before continuing with this implementation, it would be really good to confirm that adding that flag would not be a net simplification over needing to duplicate rules in non-composable ways like this.

I don't have a sense of how much work this would be to change. If it's significant, I think I'd prefer to get this in ~as-is (in terms of the overall design, even with the duplication), so we can start working with it, and refactor it to use a proposed --foreground flag in a follow-up, once we better understand the use case and its consequences.

As far as I can tell tests are the only place where this problem arises, so it may end up being the case that the duplication is actually OK, or even better. It may make sense to start off with it and see.

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell tests are the only place where this problem arises, so it may end up being the case that the duplication is actually OK, or even better. It may make sense to start off with it and see.

+1. Don't generalize until we have enough use cases to have a solid understanding of what the generalization needs to support.

There is a fair amount of duplication between this and the python_test_runner rule - we would be able to leverage create_pex_from_target_closure to replace much of this

I'm still concerned about the impact of caching on this. Currently, we produce a requirements PEX with no sources in it. That Pex has a high cache hit rate. Instead, create_pex_from_target_closure would include the source files in the generated Pex so the cache would no longer be useful between different targets as they will always have different source files.

--

Instead, consider adding this debugger implementation directly in python_test_runner.py and deduplicating there, similar to what we do for Black and Isort: https://github.com/pantsbuild/pants/blob/master/src/python/pants/backend/python/lint/black/rules.py and https://github.com/pantsbuild/pants/blob/master/src/python/pants/backend/python/lint/isort/rules.py.

Until after merged_input_files = await Get(Digest, ...), everything seems duplicated from a quick scan. Introduce a new PytestSetup dataclass that contains the requirements Pex, merged input files, and CLI args, and then have the rules test and debug request a PytestSetup and feed it into an EPR and IPR, respectively.

Happy to pair on this with you.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I started prototyping the interactive=True flag, and so far it's pretty straightforward. Broken patch here: master...twitter:stuhood/process-execute-foreground

If this PR really needs to land to support a usecase you folks have asap, then I won't block. But please make it easy to deprecate the duplicated code later, as I expect that the foreground patch could be polished in a few more hours of work.

Copy link
Contributor

Choose a reason for hiding this comment

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

@stuhood isn't this what InteractiveProcessRunner was meant to do, with the additional constraint that we side-step having to think about how EPR does caching by requiring that InteractiveProcessRunner only execute from console_rules?

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Yes. Except, again, InteractiveProcessRunner requires changes like this PR, which duplicate a lot of code. There would not be any code duplication in the case of foreground=True, because it could be used arbitrarily deep in the rule graph.

@codealchemy codealchemy marked this pull request as ready for review December 19, 2019 02:12
Add a MockOptions in the engine util for mocking out rule option values as well as a smoke test running the test rule with debug enabled.
src/python/pants/testutil/engine/util.py Outdated Show resolved Hide resolved
src/python/pants/rules/core/test.py Outdated Show resolved Hide resolved
src/python/pants/rules/core/test.py Outdated Show resolved Hide resolved
src/python/pants/rules/core/test.py Outdated Show resolved Hide resolved
output_pytest_requirements_pex = 'pytest-with-requirements.pex'

resolved_requirements_pex = await Get[Pex](
CreatePex(
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell tests are the only place where this problem arises, so it may end up being the case that the duplication is actually OK, or even better. It may make sense to start off with it and see.

+1. Don't generalize until we have enough use cases to have a solid understanding of what the generalization needs to support.

There is a fair amount of duplication between this and the python_test_runner rule - we would be able to leverage create_pex_from_target_closure to replace much of this

I'm still concerned about the impact of caching on this. Currently, we produce a requirements PEX with no sources in it. That Pex has a high cache hit rate. Instead, create_pex_from_target_closure would include the source files in the generated Pex so the cache would no longer be useful between different targets as they will always have different source files.

--

Instead, consider adding this debugger implementation directly in python_test_runner.py and deduplicating there, similar to what we do for Black and Isort: https://github.com/pantsbuild/pants/blob/master/src/python/pants/backend/python/lint/black/rules.py and https://github.com/pantsbuild/pants/blob/master/src/python/pants/backend/python/lint/isort/rules.py.

Until after merged_input_files = await Get(Digest, ...), everything seems duplicated from a quick scan. Introduce a new PytestSetup dataclass that contains the requirements Pex, merged input files, and CLI args, and then have the rules test and debug request a PytestSetup and feed it into an EPR and IPR, respectively.

Happy to pair on this with you.

codealchemy and others added 4 commits December 26, 2019 12:13
- Update help text.
- Move MockOptions out of test utils for now.
- Drop fingerprint for options (not needed in V2 world)
Eric-Arellano pushed a commit that referenced this pull request Jan 2, 2020
### Problem

#8811 extracted pex creation to its own rule. There remains a fair amount of duplication in how the pex is created in the `python_test_runner`.

### Solution

Allow passing `additional_requirements` in CreatePexFromTargetClosure, which enables the `python_test_runner` to leverage that rule for pex creation.

📝 `include_sources` was added as well to permit creating a pex _without_ the source files from targets - this helps improve cache hits when running tests since we only want to include 3rd party deps. 

### Result

Less duplicate code and a path forward for #8827 (creating a pex for debugging tests).
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.

Much smaller now! Yay!

src/python/pants/rules/core/test.py Outdated Show resolved Hide resolved
fast_* is no longer used across pants, and using `test` alone caused issues when running pytest - this feels like a good middle-ground
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.

Thanks Alex! This is pretty clean now and a very useful feature.

Copy link
Contributor

@illicitonion illicitonion left a comment

Choose a reason for hiding this comment

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

Looks good - thanks for cleaning up all the duplication!

Just one comment about the tests :)

fast_test,
rule_args=[console, (addr,)],
run_tests,
rule_args=[console, options, (addr,)],
mock_gets=[
MockGet(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you conditionally set mock_gets to either the debug or non-debug Gets based on the value of debug so that we validate in the tests that we don't call into the wrong ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This definitely feels like it'd be helpful here, though AFAICT we can't (currently) condition mock_gets in tests as run_rule uses task_rule.input_gets, which itself is a flat list put together in the _RuleVisitor of all Gets in a rule body (and doesn't account for conditionals).

Copy link
Contributor

@TansyArron TansyArron left a comment

Choose a reason for hiding this comment

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

💯

@Eric-Arellano Eric-Arellano merged commit 2741f02 into pantsbuild:master Jan 9, 2020
@codealchemy codealchemy deleted the python-debug-test-option branch January 9, 2020 00:48
@stuhood
Copy link
Sponsor Member

stuhood commented Jan 9, 2020

Sorry that I didn't get around to reviewing this in time. Opened #8923 for the followup.

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

Successfully merging this pull request may close these issues.

None yet

7 participants