Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Call PyErr_NormalizeException for exceptions #1265

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

Merged
merged 17 commits into from
Dec 21, 2020
Merged

Call PyErr_NormalizeException for exceptions #1265

merged 17 commits into from
Dec 21, 2020

Conversation

slide
Copy link
Contributor

@slide slide commented Oct 15, 2020

See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_NormalizeException

What does this implement/fix? Explain your changes.

This fixes cases where some exceptions are "unnormalized" (see https://docs.python.org/3/c-api/exceptions.html#c.PyErr_NormalizeException). Calling PyErr_NormalizeException will normalize the exception so that any calls to the traceback module functions correctly.

Does this close any currently open issues?

#1190

Checklist

Check all those that are applicable and complete.

  • Make sure to include one or more tests for your change
  • Add yourself to AUTHORS
  • Updated the CHANGELOG

@lostmsu
Copy link
Member

lostmsu commented Oct 15, 2020

@slide tbh, I think it would be better to be fixed by checking if __cause__ attribute exists or available via some other means.

I am not sure if the PyErr_NormalizeException always does the right thing. I have two issues with it:

  1. What if the exception class has __init__ method, that performs something totally not expected when a value is passed to it? For example, tries to do int(val), and the exception value happens to be something, that causes that to raise another exception?
  2. If we do PyErr_NormalizeException, and completely discard the original values, subsequent PythonException.Restore would not return the error state to be exactly the same as before PythonException.

@slide
Copy link
Contributor Author

slide commented Oct 15, 2020

This function is called in PyErr_Print, which is why I thought it would be safe. Would you rather the call be moved to the Format method itself?

@lostmsu
Copy link
Member

lostmsu commented Oct 15, 2020

Can you clarify on behavior of PyErr_NormalizeException in regards to reference counts? Does it decref objects passed to it? Do the objects it returns have their refcount > 0? For example, if it can under some circumstances decref the objects passed to it, then you have to ensure to incref them, otherwise pointers in PythonException will become invalid.

A test for scenario 1 is still required (e.g. when exception class has __init__ that throws an exception of its own). If that throws in Format, what should we do in that case?

@codecov-io
Copy link

codecov-io commented Oct 15, 2020

Codecov Report

Merging #1265 (04c8b66) into master (c81c3c3) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1265   +/-   ##
=======================================
  Coverage   87.97%   87.97%           
=======================================
  Files           1        1           
  Lines         291      291           
=======================================
  Hits          256      256           
  Misses         35       35           
Flag Coverage Δ
setup_linux 65.29% <ø> (ø)
setup_windows 74.22% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c81c3c3...04c8b66. Read the comment docs.

@slide
Copy link
Contributor Author

slide commented Oct 15, 2020

The reference count stuff is something I tend to struggle with in regards to C# vs. Python stuff. :-) From looking at the source it looks like things would only be DECREF'd in case of an error or if the value is updated to be the correct value (https://github.com/python/cpython/blob/98c4433a81a4cd88c7438adbee1f2aa486188ca3/Python/errors.c#L353 and https://github.com/python/cpython/blob/98c4433a81a4cd88c7438adbee1f2aa486188ca3/Python/errors.c#L336).

In the case of an error, it looks like PyErr_NormalizeException will chain the new exception and try and normalize it. https://github.com/python/cpython/blob/98c4433a81a4cd88c7438adbee1f2aa486188ca3/Python/errors.c#L366. So, it seems like case 1 is covered internally in the normalization process. I can add something though just to make sure.

@lostmsu
Copy link
Member

lostmsu commented Oct 16, 2020

From my reading the code you linked, it looks like you need to incref pointers before sending them to _PyErr_NormalizeException, and then after the function you will own whatever is returned, so no need to incref after.

For example, they decref value here, and then replace it with fixed_value, which is a new reference from _PyErr_CreateException.

@slide
Copy link
Contributor Author

slide commented Nov 10, 2020

I have NOT abandoned this PR, just had some work stuff come up. I will revisit for item 1 above shortly.

@slide
Copy link
Contributor Author

slide commented Dec 11, 2020

I ran the same code on C Python 3.7.7 interpreter and got this, so the behavior matches:

Python 3.7.7 (tags/v3.7.7:d7c567b08f, Mar 10 2020, 10:41:24) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class TestException(NameError):
...     def __init__(self, val):
...             super().__init__(val)
...             x = int(val)
...
>>> raise TestException('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
ValueError: invalid literal for int() with base 10: 'foo'

@lostmsu
Copy link
Member

lostmsu commented Dec 13, 2020

@slide I don't think the new test with __init__ actually uses normalization. What you need is to do PyErr_NormalizeException(TestException, "hello", null).

@slide
Copy link
Contributor Author

slide commented Dec 13, 2020

You're absolutely right, I was thinking of something else, this makes complete sense.

@slide
Copy link
Contributor Author

slide commented Dec 21, 2020

closing and reopening to get a new build

@slide slide closed this Dec 21, 2020
@slide slide reopened this Dec 21, 2020
@lostmsu lostmsu merged commit a0d1a3d into pythonnet:master Dec 21, 2020
@slide
Copy link
Contributor Author

slide commented Dec 21, 2020

Thanks!

@slide slide deleted the normalize_exceptions branch December 21, 2020 21:05
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.

4 participants