-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fix handle repr error with showlocals and verbose output #5133
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
Fix handle repr error with showlocals and verbose output #5133
Conversation
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.
How does this change the display style in case of an error value?
Codecov Report
@@ Coverage Diff @@
## master #5133 +/- ##
==========================================
+ Coverage 96.05% 96.07% +0.01%
==========================================
Files 114 114
Lines 25784 25812 +28
Branches 2550 2550
==========================================
+ Hits 24768 24798 +30
+ Misses 705 704 -1
+ Partials 311 310 -1
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #5133 +/- ##
==========================================
+ Coverage 96.06% 96.06% +<.01%
==========================================
Files 114 114
Lines 25790 25825 +35
Branches 2550 2550
==========================================
+ Hits 24776 24810 +34
- Misses 704 705 +1
Partials 310 310
Continue to review full report at Codecov.
|
nevermind, i missed a key part of the invocation site, its fine |
src/_pytest/_io/saferepr.py
Outdated
try: | ||
# Try the vanilla repr and make sure that the result is a string | ||
return call(x, *args) | ||
except Exception: |
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 trackback is not used, just use except Exception as e
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.
Just to make it clear, I have not changed this part, I've moved it from _callhelper
. I thought exc_info
was used to get the class of an exception. Would it be ok to use e.__class__.__name__
instead?
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.
@RonnyPfannschmidt Can you have a look, please? With e.__class__.__name__
it looks like
try:
exc_name = e.__class__.__name__
except Exception:
exc_name = "unknown"
try:
exc_info = str(e)
except Exception:
exc_info = "unknown"
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.
Makes sense. I'd use as exc
though.
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 for working on this. Please see my comments.
src/_pytest/_code/code.py
Outdated
@@ -676,7 +679,7 @@ def repr_locals(self, locals): | |||
if self.truncate_locals: | |||
str_repr = self._saferepr(value) | |||
else: | |||
str_repr = pprint.pformat(value) | |||
str_repr = self._safeformat(value) |
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.
Why not use it directly?
I.e. not via _safeformat
method - I see that this is done for _saferepr
, but I do not think it is good to follow.
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.
Yeah, I've just followed the way it has been called. I will change this to a direct call of safeformat
and saferepr
. Thank you.
src/_pytest/_io/saferepr.py
Outdated
import sys | ||
|
||
from six.moves import reprlib | ||
|
||
|
||
def _safe_format_call(call, x, *args): |
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 should be renamed to something more explicit like _call_and_format_exception
.
src/_pytest/_io/saferepr.py
Outdated
try: | ||
# Try the vanilla repr and make sure that the result is a string | ||
return call(x, *args) | ||
except Exception: |
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.
Makes sense. I'd use as exc
though.
src/_pytest/_io/saferepr.py
Outdated
try: | ||
exc_name = exc.__class__.__name__ | ||
except Exception: | ||
exc_name = "unknown" |
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 is not covered in tests (https://codecov.io/gh/pytest-dev/pytest/compare/9eac4733c56cd1bac8abb6639e19976710fee139...4024a121e446f348c5ea839e002f3362b8c4978a/diff#D1-13).
And I think it is not possible to get an exception via this, is it? (/cc @asottile)
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.
sadly it is:
class CryParty(object):
@property
def __class__(self):
raise TypeError('boom!')
>>> CryParty()
<__main__.CryParty object at 0x7f2e3c41b8d0>
>>> CryParty().__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "t.py", line 4, in __class__
raise TypeError('boom!')
TypeError: boom!
>>> type(CryParty()).__name__
'CryParty'
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.
Awesome! Thank you so much! I saw a decrease in the coverage, and I was looking without any luck for the whole evening to find a way to raise an exception with __class__
. So I was even going to remove try .. except
block.
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.
looks good! maybe add a test with my broken __class__
example, and switch to type(...).__name__
since we don't need to support old-style-classes for exceptions (py27+)
src/_pytest/_io/saferepr.py
Outdated
try: | ||
exc_name = exc.__class__.__name__ | ||
except Exception: | ||
exc_name = "unknown" |
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.
sadly it is:
class CryParty(object):
@property
def __class__(self):
raise TypeError('boom!')
>>> CryParty()
<__main__.CryParty object at 0x7f2e3c41b8d0>
>>> CryParty().__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "t.py", line 4, in __class__
raise TypeError('boom!')
TypeError: boom!
>>> type(CryParty()).__name__
'CryParty'
testing/test_session.py
Outdated
@@ -134,6 +134,30 @@ def test_implicit_bad_repr1(self): | |||
!= -1 | |||
) | |||
|
|||
def test_broken_repr_with(self, testdir): |
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.
with_showlocals_verbose perhaps?
I have not found any way for |
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 is great!
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.
Awesome.
Please squash the commits and force-push.
b9dbe2c
to
bc00d0f
Compare
Thank you! |
Fixes #5089