Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitabf4bfe

Browse files
committed
gh-92810: Return __subclasses__clause back
1 parent7960480 commitabf4bfe

File tree

4 files changed

+222
-50
lines changed

4 files changed

+222
-50
lines changed

‎Lib/_py_abc.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from_weakrefsetimportWeakSet
2+
fromweakrefimportWeakKeyDictionary
23

34

45
defget_cache_token():
@@ -31,6 +32,7 @@ class ABCMeta(type):
3132
# Note: this counter is private. Use `abc.get_cache_token()` for
3233
# external code.
3334
_abc_invalidation_counter=0
35+
_abc_issubclass_context=WeakKeyDictionary()
3436

3537
def__new__(mcls,name,bases,namespace,/,**kwargs):
3638
cls=super().__new__(mcls,name,bases,namespace,**kwargs)
@@ -65,21 +67,7 @@ def register(cls, subclass):
6567
ifissubclass(cls,subclass):
6668
# This would create a cycle, which is bad for the algorithm below
6769
raiseRuntimeError("Refusing to create an inheritance cycle")
68-
69-
# Actual registration
7070
cls._abc_registry.add(subclass)
71-
72-
# Recursively register the subclass in all ABC bases, to avoid recursive lookups.
73-
# >>> class Ancestor1(ABC): pass
74-
# >>> class Ancestor2(Ancestor1): pass
75-
# >>> class Other: pass
76-
# >>> Ancestor2.register(Other) # same result for Ancestor1.register(Other)
77-
# >>> issubclass(Other, Ancestor2) is True
78-
# >>> issubclass(Other, Ancestor1) is True
79-
forpclsincls.__mro__:
80-
ifhasattr(pcls,"_abc_registry"):
81-
pcls._abc_registry.add(subclass)
82-
8371
# Invalidate negative cache
8472
ABCMeta._abc_invalidation_counter+=1
8573
returnsubclass
@@ -152,6 +140,26 @@ def __subclasscheck__(cls, subclass):
152140
ifissubclass(subclass,rcls):
153141
cls._abc_cache.add(subclass)
154142
returnTrue
155-
# No dice; update negative cache
156-
cls._abc_negative_cache.add(subclass)
143+
144+
# Check if it's a subclass of a subclass (recursive)
145+
forsclsincls.__subclasses__():
146+
# If inside recursive issubclass check, avoid adding classes to any cache because this
147+
# may drastically increase memory usage.
148+
# Unfortunately, issubclass/__subclasscheck__ don't accept third argument with context,
149+
# so using global context within ABCMeta.
150+
# This is done only on first method call, others will use cached result.
151+
scls_context=ABCMeta._abc_issubclass_context.setdefault(scls,WeakSet())
152+
try:
153+
scls_context.add(cls)
154+
result=issubclass(subclass,scls)
155+
finally:
156+
scls_context.remove(cls)
157+
158+
ifresult:
159+
ifnotABCMeta._abc_issubclass_context.get(cls,None):
160+
cls._abc_cache.add(subclass)
161+
returnTrue
162+
163+
ifnotABCMeta._abc_issubclass_context.get(cls,None):
164+
cls._abc_negative_cache.add(subclass)
157165
returnFalse

‎Lib/test/test_abc.py

Lines changed: 166 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -270,29 +270,100 @@ def x(self):
270270
classC(metaclass=meta):
271271
pass
272272

273+
deftest_isinstance_direct_inheritance(self):
274+
classA(metaclass=abc_ABCMeta):
275+
pass
276+
classB(A):
277+
pass
278+
classC(A):
279+
pass
280+
a=A()
281+
b=B()
282+
c=C()
283+
# trigger caching
284+
for_inrange(2):
285+
self.assertIsInstance(a,A)
286+
self.assertIsInstance(a, (A,))
287+
self.assertNotIsInstance(a,B)
288+
self.assertNotIsInstance(a, (B,))
289+
self.assertNotIsInstance(a,C)
290+
self.assertNotIsInstance(a, (C,))
291+
292+
self.assertIsInstance(b,B)
293+
self.assertIsInstance(b, (B,))
294+
self.assertIsInstance(b,A)
295+
self.assertIsInstance(b, (A,))
296+
self.assertNotIsInstance(b,C)
297+
self.assertNotIsInstance(b, (C,))
298+
299+
self.assertIsInstance(c,C)
300+
self.assertIsInstance(c, (C,))
301+
self.assertIsInstance(c,A)
302+
self.assertIsInstance(c, (A,))
303+
self.assertNotIsInstance(c,B)
304+
self.assertNotIsInstance(c, (B,))
305+
306+
self.assertIsSubclass(B,A)
307+
self.assertIsSubclass(B, (A,))
308+
self.assertIsSubclass(C,A)
309+
self.assertIsSubclass(C, (A,))
310+
self.assertNotIsSubclass(B,C)
311+
self.assertNotIsSubclass(B, (C,))
312+
self.assertNotIsSubclass(C,B)
313+
self.assertNotIsSubclass(C, (B,))
314+
self.assertNotIsSubclass(A,B)
315+
self.assertNotIsSubclass(A, (B,))
316+
self.assertNotIsSubclass(A,C)
317+
self.assertNotIsSubclass(A, (C,))
318+
273319
deftest_registration_basics(self):
274320
classA(metaclass=abc_ABCMeta):
275321
pass
276322
classB(object):
277323
pass
324+
a=A()
278325
b=B()
279-
self.assertNotIsSubclass(B,A)
280-
self.assertNotIsSubclass(B, (A,))
281-
self.assertNotIsInstance(b,A)
282-
self.assertNotIsInstance(b, (A,))
326+
327+
# trigger caching
328+
for_inrange(2):
329+
self.assertNotIsSubclass(B,A)
330+
self.assertNotIsSubclass(B, (A,))
331+
self.assertNotIsInstance(b,A)
332+
self.assertNotIsInstance(b, (A,))
333+
334+
self.assertNotIsSubclass(A,B)
335+
self.assertNotIsSubclass(A, (B,))
336+
self.assertNotIsInstance(a,B)
337+
self.assertNotIsInstance(a, (B,))
338+
283339
B1=A.register(B)
284-
self.assertIsSubclass(B,A)
285-
self.assertIsSubclass(B, (A,))
286-
self.assertIsInstance(b,A)
287-
self.assertIsInstance(b, (A,))
288-
self.assertIs(B1,B)
340+
# trigger caching
341+
for_inrange(2):
342+
self.assertIsSubclass(B,A)
343+
self.assertIsSubclass(B, (A,))
344+
self.assertIsInstance(b,A)
345+
self.assertIsInstance(b, (A,))
346+
self.assertIs(B1,B)
347+
348+
self.assertNotIsSubclass(A,B)
349+
self.assertNotIsSubclass(A, (B,))
350+
self.assertNotIsInstance(a,B)
351+
self.assertNotIsInstance(a, (B,))
352+
289353
classC(B):
290354
pass
291355
c=C()
292-
self.assertIsSubclass(C,A)
293-
self.assertIsSubclass(C, (A,))
294-
self.assertIsInstance(c,A)
295-
self.assertIsInstance(c, (A,))
356+
# trigger caching
357+
for_inrange(2):
358+
self.assertIsSubclass(C,A)
359+
self.assertIsSubclass(C, (A,))
360+
self.assertIsInstance(c,A)
361+
self.assertIsInstance(c, (A,))
362+
363+
self.assertNotIsSubclass(A,C)
364+
self.assertNotIsSubclass(A, (C,))
365+
self.assertNotIsInstance(a,C)
366+
self.assertNotIsInstance(a, (C,))
296367

297368
deftest_register_as_class_deco(self):
298369
classA(metaclass=abc_ABCMeta):
@@ -377,41 +448,75 @@ class A(metaclass=abc_ABCMeta):
377448
pass
378449
self.assertIsSubclass(A,A)
379450
self.assertIsSubclass(A, (A,))
451+
380452
classB(metaclass=abc_ABCMeta):
381453
pass
382454
self.assertNotIsSubclass(A,B)
383455
self.assertNotIsSubclass(A, (B,))
384456
self.assertNotIsSubclass(B,A)
385457
self.assertNotIsSubclass(B, (A,))
458+
386459
classC(metaclass=abc_ABCMeta):
387460
pass
388461
A.register(B)
389462
classB1(B):
390463
pass
391-
self.assertIsSubclass(B1,A)
392-
self.assertIsSubclass(B1, (A,))
464+
# trigger caching
465+
for_inrange(2):
466+
self.assertIsSubclass(B1,A)
467+
self.assertIsSubclass(B1, (A,))
468+
393469
classC1(C):
394470
pass
395471
B1.register(C1)
396-
self.assertNotIsSubclass(C,B)
397-
self.assertNotIsSubclass(C, (B,))
398-
self.assertNotIsSubclass(C,B1)
399-
self.assertNotIsSubclass(C, (B1,))
400-
self.assertIsSubclass(C1,A)
401-
self.assertIsSubclass(C1, (A,))
402-
self.assertIsSubclass(C1,B)
403-
self.assertIsSubclass(C1, (B,))
404-
self.assertIsSubclass(C1,B1)
405-
self.assertIsSubclass(C1, (B1,))
472+
# trigger caching
473+
for_inrange(2):
474+
self.assertNotIsSubclass(C,B)
475+
self.assertNotIsSubclass(C, (B,))
476+
self.assertNotIsSubclass(C,B1)
477+
self.assertNotIsSubclass(C, (B1,))
478+
self.assertIsSubclass(C1,A)
479+
self.assertIsSubclass(C1, (A,))
480+
self.assertIsSubclass(C1,B)
481+
self.assertIsSubclass(C1, (B,))
482+
self.assertIsSubclass(C1,B1)
483+
self.assertIsSubclass(C1, (B1,))
484+
406485
C1.register(int)
407486
classMyInt(int):
408487
pass
409-
self.assertIsSubclass(MyInt,A)
410-
self.assertIsSubclass(MyInt, (A,))
411-
self.assertIsInstance(42,A)
412-
self.assertIsInstance(42, (A,))
488+
# trigger caching
489+
for_inrange(2):
490+
self.assertIsSubclass(MyInt,A)
491+
self.assertIsSubclass(MyInt, (A,))
492+
self.assertIsInstance(42,A)
493+
self.assertIsInstance(42, (A,))
413494

414-
deftest_subclasses_bad_arguments(self):
495+
deftest_custom_subclasses(self):
496+
classA:pass
497+
classB:pass
498+
499+
classParent1(metaclass=abc_ABCMeta):
500+
@classmethod
501+
def__subclasses__(cls):
502+
return [A]
503+
504+
classParent2(metaclass=abc_ABCMeta):
505+
__subclasses__=lambda: [A]
506+
507+
# trigger caching
508+
for_inrange(2):
509+
self.assertIsInstance(A(),Parent1)
510+
self.assertIsSubclass(A,Parent1)
511+
self.assertNotIsInstance(B(),Parent1)
512+
self.assertNotIsSubclass(B,Parent1)
513+
514+
self.assertIsInstance(A(),Parent2)
515+
self.assertIsSubclass(A,Parent2)
516+
self.assertNotIsInstance(B(),Parent2)
517+
self.assertNotIsSubclass(B,Parent2)
518+
519+
deftest_issubclass_bad_arguments(self):
415520
classA(metaclass=abc_ABCMeta):
416521
pass
417522

@@ -429,6 +534,37 @@ class C:
429534
withself.assertRaises(TypeError):
430535
issubclass(C(),A)
431536

537+
# bpo-34441: Check that issubclass() doesn't crash on bogus
538+
# classes.
539+
bogus_subclasses= [
540+
None,
541+
lambdax: [],
542+
lambda:42,
543+
lambda: [42],
544+
]
545+
546+
fori,funcinenumerate(bogus_subclasses):
547+
classS(metaclass=abc_ABCMeta):
548+
__subclasses__=func
549+
550+
withself.subTest(i=i):
551+
withself.assertRaises(TypeError):
552+
issubclass(int,S)
553+
554+
# Also check that issubclass() propagates exceptions raised by
555+
# __subclasses__.
556+
classCustomError(Exception): ...
557+
exc_msg="exception from __subclasses__"
558+
559+
defraise_exc():
560+
raiseCustomError(exc_msg)
561+
562+
classS(metaclass=abc_ABCMeta):
563+
__subclasses__=raise_exc
564+
565+
withself.assertRaisesRegex(CustomError,exc_msg):
566+
issubclass(int,S)
567+
432568
deftest_subclasshook(self):
433569
classA(metaclass=abc.ABCMeta):
434570
@classmethod
@@ -491,7 +627,6 @@ def foo(self):
491627
self.assertEqual(A.__abstractmethods__,set())
492628
A()
493629

494-
495630
deftest_update_new_abstractmethods(self):
496631
classA(metaclass=abc_ABCMeta):
497632
@abc.abstractmethod

‎Lib/test/test_isinstance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,13 +351,13 @@ class B:
351351
withsupport.infinite_recursion(25):
352352
self.assertRaises(RecursionError,issubclass,X(),int)
353353

354-
deftest_override_subclasses(self):
354+
deftest_custom_subclasses_are_ignored(self):
355355
classA:pass
356356
classB:pass
357357

358358
classParent1:
359359
@classmethod
360-
def__subclasses__(self):
360+
def__subclasses__(cls):
361361
return [A,B]
362362

363363
classParent2:

‎Modules/_abc.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
763763

764764
PyObject*ok,*subclasses=NULL,*result=NULL;
765765
_abcmodule_state*state=NULL;
766+
Py_ssize_tpos;
766767
intincache;
767768
_abc_data*impl=_get_impl(module,self);
768769
if (impl==NULL) {
@@ -849,7 +850,35 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
849850
gotoend;
850851
}
851852

852-
/* No dice; update negative cache. */
853+
/* 6. Check if it's a subclass of a subclass (recursive). */
854+
subclasses=PyObject_CallMethod(self,"__subclasses__",NULL);
855+
if (subclasses==NULL) {
856+
gotoend;
857+
}
858+
if (!PyList_Check(subclasses)) {
859+
PyErr_SetString(PyExc_TypeError,"__subclasses__() must return a list");
860+
gotoend;
861+
}
862+
for (pos=0;pos<PyList_GET_SIZE(subclasses);pos++) {
863+
PyObject*scls=PyList_GetItemRef(subclasses,pos);
864+
if (scls==NULL) {
865+
gotoend;
866+
}
867+
intr=PyObject_IsSubclass(subclass,scls);
868+
Py_DECREF(scls);
869+
if (r>0) {
870+
if (_add_to_weak_set(impl,&impl->_abc_cache,subclass)<0) {
871+
gotoend;
872+
}
873+
result=Py_True;
874+
gotoend;
875+
}
876+
if (r<0) {
877+
gotoend;
878+
}
879+
}
880+
881+
/* Recursive calls lead to uncontrolled negative cache growth, avoid this */
853882
if (_add_to_weak_set(impl,&impl->_abc_negative_cache,subclass)<0) {
854883
gotoend;
855884
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp