FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-105144: abc: Suppress errors raised by unrelated other subclasses by JelleZijlstra · Pull Request #105159 · python/cpython · GitHub

/ cpython Public
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .py  (2) .rst  (1) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
18 changes: 12 additions & 6 deletions Lib/_py_abc.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,20 @@ def __subclasscheck__(cls, subclass):
return True
# Check if it's a subclass of a registered class (recursive)
for rcls in cls._abc_registry:
if issubclass(subclass, rcls):
cls._abc_cache.add(subclass)
return True
try:
if issubclass(subclass, rcls):
cls._abc_cache.add(subclass)
return True
except Exception:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

In the C code I actually suppress all exceptions, not just Exception. Not sure what the right thing to do is.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I'd say suppressing all exceptions would definitely be bad -- that would mean that we'd be suppressing e.g. KeyboardInterrupt, and SystemExit.

Honestly, I feel pretty uncomfortable about suppressing all subclasses of Exception -- could we just do TypeError? If I had a typo in a custom __subclasscheck__ method, I'm not sure I'd want e.g. the resulting AttributeError to be silenced because of the fact that ABCMeta was being used somewhere

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

You'd still see the error if you use your broken class directly. What this PR changes is that you'll no longer see the error if you use issubclass() checks on a separate class that inherits from the same ABC.

pass
# Check if it's a subclass of a subclass (recursive)
for scls in cls.__subclasses__():
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True
try:
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True
except Exception:
pass
# No dice; update negative cache
cls._abc_negative_cache.add(subclass)
return False
38 changes: 36 additions & 2 deletions Lib/test/test_abc.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -435,17 +435,22 @@ class C:
None,
lambda x: [],
lambda: 42,
lambda: [42],
]

for i, func in enumerate(bogus_subclasses):
class S(metaclass=abc_ABCMeta):
__subclasses__ = func

with self.subTest(i=i):
with self.subTest(i=i, func=func):
with self.assertRaises(TypeError):
issubclass(int, S)

# If __subclasses__ contains non-classes, we suppress the error instead.
class S(metaclass=abc_ABCMeta):
__subclasses__ = lambda: [42]

self.assertIs(issubclass(int, S), False)

# Also check that issubclass() propagates exceptions raised by
# __subclasses__.
exc_msg = "exception from __subclasses__"
Expand All @@ -459,6 +464,35 @@ class S(metaclass=abc_ABCMeta):
with self.assertRaisesRegex(Exception, exc_msg):
issubclass(int, S)

def test_subclass_with_broken_subclasscheck(self):
class A(metaclass=abc_ABCMeta):
pass

class Unrelated: pass

self.assertIs(issubclass(Unrelated, A), False)

class BrokenMeta(abc_ABCMeta):
is_broken = True
def __subclasscheck__(cls, subclass):
if not BrokenMeta.is_broken:
return super().__subclasscheck__(subclass)
raise Exception("broken")

class Broken(A, metaclass=BrokenMeta):
pass

self.assertIs(issubclass(Unrelated, A), False)

class RegisteredBroken(metaclass=BrokenMeta):
pass

BrokenMeta.is_broken = False
A.register(RegisteredBroken)
BrokenMeta.is_broken = True

self.assertIs(issubclass(Unrelated, A), False)

def test_subclasshook(self):
class A(metaclass=abc.ABCMeta):
@classmethod
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Subclass checks on :class:`abc.ABC` subclasses no longer propagate errors
raised by subclass checks on unrelated base classes of the ABC. Patch by
Jelle Zijlstra.
6 changes: 3 additions & 3 deletions Modules/_abc.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,8 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
goto end;
}
if (r < 0) {
goto end;
// Ignore subclasses that throw on issubclass().
PyErr_Clear();
}
}

Expand Down Expand Up @@ -856,8 +857,7 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
int r = PyObject_IsSubclass(subclass, rkey);
Py_DECREF(rkey);
if (r < 0) {
ret = -1;
break;
PyErr_Clear();
}
if (r > 0) {
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
Expand Down

Back | FazBrowse Home | New Git URL