-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
bpo-39385: Add an assertNoLogs context manager to unittest.TestCase (GH-18067) #18067
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
Conversation
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept this contribution by verifying everyone involved has signed the PSF contributor agreement (CLA). CLA MissingOur records indicate the following people have not signed the CLA: For legal reasons we need all the people listed to sign the CLA before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. If you have recently signed the CLA, please wait at least one business day You can check yourself to see if the CLA has been received. Thanks again for the contribution, we look forward to reviewing it! |
CLA is signed, but it needs to wait another business day. |
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.
If the feature is accepted, you will also need to update the documentation and add a blurb excerpt.
Lib/unittest/case.py
Outdated
class _AssertNoLogsContext(_AssertLogsBaseContext): | ||
"""A context manager used to implement TestCase.assertNoLogs().""" | ||
|
||
def __enter__(self): |
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.
What's the point of overriding this method here?
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.
This was to return None
instead of returning the _LoggingWatcher
instance, which is expected to be empty. I see it also does not hurt to return the watcher anyway. I will remove it.
Lib/unittest/case.py
Outdated
_AssertLogsBaseContext.__enter__(self) | ||
return None | ||
|
||
def __exit__(self, exc_type, exc_value, tb): |
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 both __exit__
could share more of there code to avoid duplication.
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 see what you mean. I pushed some changes. Hope that's better now.
To add a NEWS entry (https://devguide.python.org/committing/#what-s-new-and-news-entries), you can use https://blurb-it.herokuapp.com/ |
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.
Thank you @remilapeyre for looking into this!
Lib/unittest/case.py
Outdated
class _AssertNoLogsContext(_AssertLogsBaseContext): | ||
"""A context manager used to implement TestCase.assertNoLogs().""" | ||
|
||
def __enter__(self): |
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.
This was to return None
instead of returning the _LoggingWatcher
instance, which is expected to be empty. I see it also does not hurt to return the watcher anyway. I will remove it.
Lib/unittest/case.py
Outdated
_AssertLogsBaseContext.__enter__(self) | ||
return None | ||
|
||
def __exit__(self, exc_type, exc_value, tb): |
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 see what you mean. I pushed some changes. Hope that's better now.
Oops, haven't done that yet... on it... |
Lib/unittest/case.py
Outdated
def check_records(self): | ||
if len(self.watcher.records) > 0: | ||
self._raiseFailure( | ||
"Logs unexpected found: {!r}".format( |
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.
Thanks a lot for adding this! It's a good improvement. I think this failure message would be improved if it was Unexpected logs found: ...
or Logs unexpectedly found: ...
(probably the first)
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.
Oops, yes you are right. Thank you for spotting this.
Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst
Outdated
Show resolved
Hide resolved
@@ -1657,6 +1657,76 @@ def testAssertLogsFailureMismatchingLogger(self): | |||
with self.assertLogs('foo'): | |||
log_quux.error("1") | |||
|
|||
def testAssertLogsUnexpectedException(self): | |||
# Check unexpected exception will go through. | |||
with self.assertRaises(ZeroDivisionError): |
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.
You can use multiple context managers at once (in this test and all others):
with self.assertRaises(ZeroDivisionError), \
self.assertLogs():
raise ZeroDivisionError("Unexpected")
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.
Thanks. I realize that a lot of other existing tests in this module use nested with
, and none uses , \
. For consistencies maybe the nested with
should be kept?
Co-authored-by: Rémi Lapeyre <[email protected]>
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.
Instead of creating a base class and two subclasses for the context manager, why not just use the existing class with an additional initialization keyword parameter no_logs
, defaulting to False
? The only difference between the two cases are:
- The check for the number of collected records -- zero vs. non-zero.
- The message displayed when the assertion fails.
Instead, the class _AssertLogsContext
could get the no_logs=False
keyword parameter, which it stores in an attribute. At check time, this attribute is checked and the appropriate logic followed. The higher-level API (assertNoLogs
) remains the same, but the implementation just returns a _AssertLogsContext
called with no_logs=True
.
Wouldn't that be simpler and shorter?
Yeah that would work too. |
The only problem with |
I'm just some random person lurking around this particular issue. 😂 Thinking about this a little bit, it's a little complex. For example, let's say you called it things like: The name needs to convey The best name I can come up with is: |
How so? I see it like this -
Then
|
My reasoning for choosing |
Thank you @vsajip and @calebmarchent for your input! What is more important, I think, is the public facing API. Initially when I started with two classes, one of the main motivations was to have |
Failures can yield useful information, too. Surely if you want to assert that there were no logs, but some appear, you want (at least in some instances) to know what they are, so you can find the problem that caused them? Have I misunderstood what you're saying? Of course the failure message is there, but I mean that you might want to do something with the logs programmatically in case the basic error message isn't enough. |
I understand that there might be a use case. It is just that possible use case isn't concrete enough to me at the moment. For example, I haven't yet found myself in a situation where I would do something like this instead of using
Not adding the watcher now does not prevent the watcher to be added later. If there is a need, there can be a new issue to add the watcher to What I am proposing is that we can defer the decision as to what |
Ah, I think you meant this:
In that case, having another custom exception that subclasses |
…ythonGH-18067) Co-authored-by: Rémi Lapeyre <[email protected]>
https://bugs.python.org/issue39385
https://bugs.python.org/issue39385
Automerge-Triggered-By: @vsajip