mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +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 | ||||
| 
 | ||||
|         self.assertIsInstance(C(), C) | ||||
|         with self.assertRaises(TypeError): | ||||
|             C(42) | ||||
| 
 | ||||
|         T = TypeVar('T') | ||||
| 
 | ||||
|         class PG(Protocol[T]): pass | ||||
|  | @ -759,6 +762,8 @@ class PG(Protocol[T]): pass | |||
|         class CG(PG[T]): pass | ||||
| 
 | ||||
|         self.assertIsInstance(CG[int](), CG) | ||||
|         with self.assertRaises(TypeError): | ||||
|             CG[int](42) | ||||
| 
 | ||||
|     def test_cannot_instantiate_abstract(self): | ||||
|         @runtime_checkable | ||||
|  | @ -1194,6 +1199,37 @@ def __init__(self): | |||
| 
 | ||||
|         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): | ||||
|         T = TypeVar('T') | ||||
|         S = TypeVar('S') | ||||
|  |  | |||
|  | @ -1080,8 +1080,7 @@ def _is_callable_members_only(cls): | |||
| 
 | ||||
| 
 | ||||
| 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(): | ||||
|  | @ -1210,6 +1209,15 @@ def _proto_hook(other): | |||
| 
 | ||||
|         # We have nothing more to do for non-protocols... | ||||
|         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 | ||||
| 
 | ||||
|         # ... 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)