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
logging: some optimizations #7214
logging: some optimizations #7214
Conversation
The test failure is legitimate, so this needs some more work. |
363db7e
to
895a7f8
Compare
This makes things type-safe.
This avoids a little bit of overhead, and makes the code a bit clearer too.
Avoid the slight overhead of contextmanager.
… create Previously, a LoggingCaptureHandler was instantiated for each test's setup/call/teardown/logstart/logfinish/logreport which turns out to be expensive. Instead, only keep one instance and reset it between runs.
The logstart/logreport/logfinish hooks don't need the stuff in _runtest_for: - All of the item-conditional sections are gone. - Setting up log_file_handler is sufficiently covered by the one in the pytest_runtestloop hook, since these hooks don't have access to the caplog fixture. - The `log_cli_handler.set_when` can move inline.
895a7f8
to
4c2004a
Compare
I fixed the test failure, and now it's also faster than the previous version (updated PR description), so it's ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! Performance optimisations often involve less readable code, but this reads like a pleasant refactoring that happens to make things faster too 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job @bluetech. The usage of nested context managers were one of things that I really disliked when I integrated the plugin into pytest-dev/pytest
. You have improved this a lot, thx!
log_handler.reset() | ||
item._store[catch_log_handler_key] = log_handler | ||
item._store[catch_log_records_key][when] = log_handler.records | ||
|
||
if self.log_file_handler is not None: | ||
with catching_logs(self.log_file_handler, level=self.log_file_level): | ||
yield | ||
else: | ||
yield |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bluetech I like the usage of yield-from
in this PR! WDYT about converting this if self.log_file_handler
block (and also the block in pytest_collection
) also into a generator from which you yield? Unfortunately I can't come up with a good name for this generator. What about yield from self.forward_logs_to_logfile()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make it mostly go away! But if not, I'll try your suggestion.
if self.print_logs: | ||
# Add a captured log section to the report. | ||
log = log_handler.stream.getvalue().strip() | ||
item.add_report_section(when, "log", log) | ||
|
||
@pytest.hookimpl(hookwrapper=True) | ||
def pytest_runtest_setup(self, item): | ||
with self._runtest_for(item, "setup"): | ||
yield | ||
empty = {} # type: Dict[str, List[logging.LogRecord]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you can split up the type definiton of catch_log_records_key
into two:
catch_log_records_map
catch_log_records_key
where the latter is defined as Store[catch_log_records_key]
. Thus you can use empty = {} # type catch_log_records_map
, here. TBH I don't know if this really works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These I think will also go away eventually. They are used for the plugin<->fixture interaction, but I think we should refactor this to be more like what the capture plugin does (for #7162).
Thanks for the reviews @Zac-HD and @thisch! Upon looking at logging a bit more, and looking at the open issues/PRs, I think that the main change here ("reuse LoggingCaptureHandler") might have some interactions with them, so I decided to defer it for a bit. Instead, I took the other changes from here (and some additional ones) into a new PR #7224. Once/if it's merged, I will submit a few more changes that I have in mind (probably including the reuse optimization, and some changes to the fixture-plugin interaction). |
Thanks @bluetech - I really appreciate the care you're putting into this, from design to code to PR writeups 😍 Please @-mention me in the new PR once tests are passing and I'll give it a priority review! |
Please see the commit messages for details.
On this benchmark:
Before (only relevant entries shown):
After: