8000 bpo-12029: Exception handling should match subclasses by mbmccoy · Pull Request #6461 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-12029: Exception handling should match subclasses #6461

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

Closed

Conversation

mbmccoy
Copy link
@mbmccoy mbmccoy commented Apr 13, 2018

Exception handling should match subclasses, not subtypes

Example

from abc import ABC

class MyException(Exception, ABC):
    pass

class OtherException(Exception):
    pass

MyException.register(OtherException)

try:
    raise OtherException
except MyException:
    print("Correct: Caught MyException")
except Exception:
    print("Wrong: Caught something else")
    
# "Wrong: Caught something else"

Background and evidence of bug-ness

Issue 2534 [1] (10 years ago!) introduced the behavior, but only in the Python 3 patch [2]. During code review, the correct function call was used [3], but the function's name got switched in the final python3 patch without any comment.

The current Python 2 code uses PyObject_IsSubclass, and produces the correct behavior in the example above (using __metaclass__ = ABCMeta, of course). This leads me to strongly suspect that this is a bug, not a feature. The note below regarding unittest for further evidence that this code has eight legs.

Given the ancient nature of this bug, it affects all versions of python3.

[1] https://bugs.python.org/issue2534
[2] https://bugs.python.org/file11257/isinstance3k-2.patch
[3] https://codereview.appspot.com/483/diff/1/21#newcode114
[4] https://github.com/python/cpython/blob/2.7/Python/errors.c#L119

Solution

Coming very soon in a PR on Github, but in short, we do the following:

  1. Switch PyType_IsSubtype to PyObject_IsSubclass.
  2. Revert the changes made to remove “dead code” in https://bugs.python.org/issue31091. The code was dead because the wrong function was used—the PR left only the bug.
  3. Add tests. Note that unittest’s self.assertRaises function uses issubclass and does not alert to this bug. (Different symptom, same cause.)

-Mike

https://bugs.python.org/issue33271

https://bugs.python.org/issue12029

@the-knights-who-say-ni
Copy link

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue.

Thanks again to your contribution and we look forward to looking at it!

@mbmccoy mbmccoy force-pushed the exceptions-check-subclasss-33271 branch from 206c1b9 to 9f931da Compare April 13, 2018 18:13
@mbmccoy mbmccoy changed the title bpo-33271: Exception handling should match subtypes bpo-33271: Exception handling should match subclasses Apr 13, 2018
@mbmccoy
Copy link
Author
mbmccoy commented Apr 13, 2018

@the-knights-who-say-ni I've signed the bpo CLA. Can an admin re-run the check? Nevermind, it hasn't gone through yet.

@serhiy-storchaka serhiy-storchaka changed the title bpo-33271: Exception handling should match subclasses bpo-12029: Exception handling should match subclasses Apr 13, 2018
@mbmccoy mbmccoy force-pushed the exceptions-check-subclasss-33271 branch 2 times, most recently from 602b2ce to 76e17c9 Compare April 13, 2018 21:28
@mbmccoy
Copy link
Author
mbmccoy commented Apr 17, 2018

@the-knights-who-say-ni can I get a re-run of the CLA script? I'm verified now.

@mbmccoy mbmccoy force-pushed the exceptions-check-subclasss-33271 branch from 76e17c9 to 24153ce Compare April 17, 2018 04:21
@mbmccoy
Copy link
Author
mbmccoy commented May 19, 2018

Ok, I've run a bench mark to test the performance hit. For the benchmark code below that tests the "long path" in exception handling, I'm seeing a 1.4% slowdown over 100 million runs. To be precise, I'm seeing

master bpo-12029 (this branch) % change
0.533µs 0.540µs 1.4%

Here's a graph that batches the runs into 100000 trial each and shows the variability in runs:

slowdown

The slowdown, while real, is not particularly significant: There are many cases where the code in master takes longer to run due simply to OS noise.

You can reproduce this analysis via the code:

import time
import json

time_us = 0
num_runs = 1000
num_tests_per_run = 100000
stats = []

for n in range(num_runs):
    start_time = time.time()
    for i in range(num_tests_per_run):
        try:
            {}["a"]
        except TypeError:
            pass
        except ValueError:
            pass
        except KeyError:
            pass

    end_time = time.time()
    time_us = (end_time - start_time) / num_tests_per_run * 1e6
    stats.append(time_us)

    time.sleep(1e-4)  # Make less sensitive to initial OS state

print("Took {} µs per test".format(sum(stats)/len(stats)))

import os
filename = os.path.abspath("stats-{}.json".format(time.time()))
print("Writing to file {}".format(filename))
with open(filename, 'w') as fp:
    json.dump(stats, fp)

@VeNoMouS
Copy link

2 years on, still no fix 😢

@merwok
Copy link
Member
merwok commented Mar 21, 2022

Rejected in favour of a doc note, see ticket and #32027

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0