Repository navigation
Adds a feature to control how directories created by the tmp_path fixture are kept. - #10442
Conversation
tmp_path fixture are kept.
tmp_path fixture are kept.tmp_path fixture are kept.
nicoddemus
left a comment
There was a problem hiding this comment.
Thanks @yusuke-kadowaki for the contribution.
We also need to update the tmpdir.rst docs with the new options, and mention the configuration variables (as I requested them to be changed from command-line options to conf variables) in reference.rst.
| ), | ||
| ) | ||
|
|
||
| group.addoption( |
There was a problem hiding this comment.
Let's make those configuration variables instead, seems like something users will configure once instead of passing them in the command-line all the time.
There was a problem hiding this comment.
Done.
I was confused with command line options and configuration options.
| _trace = attr.ib() | ||
| _basetemp = attr.ib(type=Optional[Path]) | ||
| _retention_count = attr.ib(type=int) | ||
| _retention_policy = attr.ib(type=str) |
There was a problem hiding this comment.
| _retention_policy = attr.ib(type=str) | |
| _retention_policy = attr.ib(type=Literal["none", "failed", "all"]) |
We should probably make a TypeAlias for that and reuse it everywhere it is declared.
There was a problem hiding this comment.
I made this.
RetentionType = Literal["all", "failed", "none"]| rmtree(passed_dir) | ||
|
|
||
| # Register to remove the base directory before starting cleanup_numbered_dir | ||
| atexit.register(cleanup_passed_dir, tmp_path_factory._basetemp) |
There was a problem hiding this comment.
I would prefer to explicitly remove them at this point instead of relying on atexit.
pytest might be embedded, for example as part of user script or IDE, which might not actually exit the interpreter for a awhile.
There was a problem hiding this comment.
Done.
Is there any reason for using atexit to execute cleanup_numbered_dir in the first place? That makes it really difficult to test the behavior.
| @@ -0,0 +1 @@ | |||
| Added `--tmp-path-retention-count` and `--tmp-path-retention-policy` options to control how directories created by the `tmp_path` fixture are kept. | |||
There was a problem hiding this comment.
We should also mention explicitly that the default has changed to remove all directories if all tests passed.
| tmp_path_factory: TempPathFactory = session.config._tmp_path_factory | ||
| if tmp_path_factory._basetemp is None: | ||
| return | ||
| policy = tmp_path_factory._retention_policy |
There was a problem hiding this comment.
I was under the impression that the failed policy would remove all passed tests, even if some tests failed.
Here it will remove all directories only if all tests pass.
Or is that handled somewhere else that I am missing?
There was a problem hiding this comment.
Here it will remove all directories only if all tests pass.
This is actually how I implemented it. And I added some implementation to match the behavior you mentioned.
0abbf8c
|
Only the Windows tests are failing but I'm not familiar with Windows so I need some help. My first guess is we need to stick to |
nicoddemus
left a comment
There was a problem hiding this comment.
Thanks @yusuke-kadowaki!
Left a few comments.
@RonnyPfannschmidt would also appreciate your review given your familiarity with this code.
| @@ -0,0 +1,2 @@ | |||
| Added `--tmp-path-retention-count` and `--tmp-path-retention-policy` options to control how directories created by the `tmp_path` fixture are kept. | |||
There was a problem hiding this comment.
| Added `--tmp-path-retention-count` and `--tmp-path-retention-policy` options to control how directories created by the `tmp_path` fixture are kept. | |
| Added :confval:`tmp_path_retention_count` and :confval:`tmp_path_retention_policy` configuration options to control how directories created by the :fixture:`tmp_path` fixture are kept. |
| @@ -0,0 +1,2 @@ | |||
| Added `--tmp-path-retention-count` and `--tmp-path-retention-policy` options to control how directories created by the `tmp_path` fixture are kept. | |||
| The default behavior has changed to remove all directories if all tests passed. | |||
There was a problem hiding this comment.
| The default behavior has changed to remove all directories if all tests passed. | |
| The default behavior has changed to keep only directories for failed tests, equivalent to `tmp_path_retention_policy="failed"`. |
| tmp_path_factory: TempPathFactory = request.session.config._tmp_path_factory # type: ignore | ||
| policy = tmp_path_factory._retention_policy | ||
| if policy == "failed" and request.node.result_call.passed: | ||
| rmtree(path) |
There was a problem hiding this comment.
| rmtree(path) | |
| # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, | |
| # permissions, etc, in which case we ignore it. | |
| rmtree(path, ignore_errors=True) |
| def pytest_runtest_makereport(item, call): | ||
| outcome = yield | ||
| result = outcome.get_result() | ||
| setattr(item, "result_" + result.when, result) |
There was a problem hiding this comment.
Let us make this private to not encourage users and plugins to depend on it.
| setattr(item, "result_" + result.when, result) | |
| setattr(item, "_tmp_path_result_" + result.when, result) |
| # Register to remove the base directory before starting cleanup_numbered_dir | ||
| passed_dir = tmp_path_factory._basetemp | ||
| if passed_dir.exists(): | ||
| rmtree(passed_dir) |
There was a problem hiding this comment.
| rmtree(passed_dir) | |
| # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, | |
| # permissions, etc, in which case we ignore it. | |
| rmtree(path, ignore_errors=True) |
| elif name == "tmp_path_retention_policy": | ||
| return "failed" | ||
| else: | ||
| assert False |
| basetemp = tmp_path_factory._basetemp | ||
| if basetemp is None: | ||
| return | ||
| for left_dir in basetemp.iterdir(): |
There was a problem hiding this comment.
Lets move this block of code to a function in pathlib.py and reuse it in cleanup_numbered_dir. 👍
| pytester.inline_run(p) | ||
| root = pytester._test_tmproot | ||
| for child in root.iterdir(): | ||
| base_dir = filter(lambda x: not x.is_symlink(), child.iterdir()) |
There was a problem hiding this comment.
Don't we want to ensure the symlinks are removed too?
| base_dir = filter(lambda x: not x.is_symlink(), child.iterdir()) | |
| base_dir = list(child.iterdir()) |
There was a problem hiding this comment.
This symlink will be deleted by cleanup_numbered_dir after the test finishes because it's triggered by atexit. So you cannot really test it here.
Added comments about it here instead.
|
Sorry I accidentally removed @RonnyPfannschmidt from reviewers and cannot get it back. |
|
Thanks @yusuke-kadowaki, great work! |
|
@RonnyPfannschmidt |
|
@nicoddemus |
|
Let's go ahead and merge this, @RonnyPfannschmidt can always take a look later and we can do a follow up if needed. 👍 |
|
Thanks @yusuke-kadowaki! |
|
thanks for landing this, this pr is good, i just dont have bandwidth for deep reviews for family reasons |
|
Thank you for the reviews and also maintaining this great library! |
| def pytest_runtest_makereport(item, call): | ||
| outcome = yield | ||
| result = outcome.get_result() | ||
| setattr(item, "_tmp_path_result_" + result.when, result) |
There was a problem hiding this comment.
Those aren't guaranteed to exist if a phase wasn't run, see #10502.
But also, didn't we introduce the .stash stuff to avoid having to monkeypatch random attributes on items? It seems like we should use that here, no? Search for e.g. caplog_records_key in the codebase to see how this is done elsewhere, including storing things based on when.
There was a problem hiding this comment.
But also, didn't we introduce the .stash stuff to avoid having to monkeypatch random attributes on items? It seems like we should use that here, no?
Thanks for catching this @The-Compiler, completely missed that on my review. 👍
closes #8141
I recognize that the tests are insufficient but I couldn't find a good reference. Is there any integration tests that actually make sure only 3 directories remain after executing a test that uses
tmp_dirfixture for multiple times? I assume no and it's probably because it's difficult to deal withatexit.register?