mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-44806: Fix __init__ in subclasses of protocols (GH-27545) (GH-27559)
Non-protocol subclasses of protocol ignore now the __init__ method
inherited from protocol base classes.
(cherry picked from commit 043cd60abe)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									4817c14395
								
							
						
					
					
						commit
						0f6a7739df
					
				
					 3 changed files with 48 additions and 2 deletions
				
			
		|  | @ -745,6 +745,9 @@ class P(Protocol): pass | ||||||
|         class C(P): pass |         class C(P): pass | ||||||
| 
 | 
 | ||||||
|         self.assertIsInstance(C(), C) |         self.assertIsInstance(C(), C) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             C(42) | ||||||
|  | 
 | ||||||
|         T = TypeVar('T') |         T = TypeVar('T') | ||||||
| 
 | 
 | ||||||
|         class PG(Protocol[T]): pass |         class PG(Protocol[T]): pass | ||||||
|  | @ -759,6 +762,8 @@ class PG(Protocol[T]): pass | ||||||
|         class CG(PG[T]): pass |         class CG(PG[T]): pass | ||||||
| 
 | 
 | ||||||
|         self.assertIsInstance(CG[int](), CG) |         self.assertIsInstance(CG[int](), CG) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             CG[int](42) | ||||||
| 
 | 
 | ||||||
|     def test_cannot_instantiate_abstract(self): |     def test_cannot_instantiate_abstract(self): | ||||||
|         @runtime_checkable |         @runtime_checkable | ||||||
|  | @ -1194,6 +1199,37 @@ def __init__(self): | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(C[int]().test, 'OK') |         self.assertEqual(C[int]().test, 'OK') | ||||||
| 
 | 
 | ||||||
|  |         class B: | ||||||
|  |             def __init__(self): | ||||||
|  |                 self.test = 'OK' | ||||||
|  | 
 | ||||||
|  |         class D1(B, P[T]): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(D1[int]().test, 'OK') | ||||||
|  | 
 | ||||||
|  |         class D2(P[T], B): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(D2[int]().test, 'OK') | ||||||
|  | 
 | ||||||
|  |     def test_new_called(self): | ||||||
|  |         T = TypeVar('T') | ||||||
|  | 
 | ||||||
|  |         class P(Protocol[T]): pass | ||||||
|  | 
 | ||||||
|  |         class C(P[T]): | ||||||
|  |             def __new__(cls, *args): | ||||||
|  |                 self = super().__new__(cls, *args) | ||||||
|  |                 self.test = 'OK' | ||||||
|  |                 return self | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(C[int]().test, 'OK') | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             C[int](42) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             C[int](a=42) | ||||||
|  | 
 | ||||||
|     def test_protocols_bad_subscripts(self): |     def test_protocols_bad_subscripts(self): | ||||||
|         T = TypeVar('T') |         T = TypeVar('T') | ||||||
|         S = TypeVar('S') |         S = TypeVar('S') | ||||||
|  |  | ||||||
|  | @ -1080,8 +1080,7 @@ def _is_callable_members_only(cls): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _no_init(self, *args, **kwargs): | def _no_init(self, *args, **kwargs): | ||||||
|     if type(self)._is_protocol: |     raise TypeError('Protocols cannot be instantiated') | ||||||
|         raise TypeError('Protocols cannot be instantiated') |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _allow_reckless_class_cheks(): | def _allow_reckless_class_cheks(): | ||||||
|  | @ -1210,6 +1209,15 @@ def _proto_hook(other): | ||||||
| 
 | 
 | ||||||
|         # We have nothing more to do for non-protocols... |         # We have nothing more to do for non-protocols... | ||||||
|         if not cls._is_protocol: |         if not cls._is_protocol: | ||||||
|  |             if cls.__init__ == _no_init: | ||||||
|  |                 for base in cls.__mro__: | ||||||
|  |                     init = base.__dict__.get('__init__', _no_init) | ||||||
|  |                     if init != _no_init: | ||||||
|  |                         cls.__init__ = init | ||||||
|  |                         break | ||||||
|  |                 else: | ||||||
|  |                     # should not happen | ||||||
|  |                     cls.__init__ = object.__init__ | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         # ... otherwise check consistency of bases, and prohibit instantiation. |         # ... otherwise check consistency of bases, and prohibit instantiation. | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Non-protocol subclasses of :class:`typing.Protocol` ignore now the | ||||||
|  | ``__init__`` method inherited from protocol base classes. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)