gh-103763: Implement PEP 695 (#103764)

This implements PEP 695, Type Parameter Syntax. It adds support for:

- Generic functions (def func[T](): ...)
- Generic classes (class X[T](): ...)
- Type aliases (type X = ...)
- New scoping when the new syntax is used within a class body
- Compiler and interpreter changes to support the new syntax and scoping rules 

Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: Eric Traut <eric@traut.com>
Co-authored-by: Larry Hastings <larry@hastings.org>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Jelle Zijlstra 2023-05-15 20:36:23 -07:00 committed by GitHub
parent fdafdc235e
commit 24d8b88420
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 11405 additions and 5469 deletions

View file

@ -48,11 +48,8 @@ from test import mod_generics_cache
from test import _typed_dict_helper
py_typing = import_helper.import_fresh_module('typing', blocked=['_typing'])
c_typing = import_helper.import_fresh_module('typing', fresh=['_typing'])
CANNOT_SUBCLASS_TYPE = 'Cannot subclass special typing classes'
NOT_A_BASE_TYPE = "type 'typing.%s' is not an acceptable base type"
CANNOT_SUBCLASS_INSTANCE = 'Cannot subclass an instance of %s'
@ -430,7 +427,7 @@ class TypeVarTests(BaseTestCase):
self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str))
def test_cannot_subclass(self):
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVar'):
class V(TypeVar): pass
T = TypeVar("T")
with self.assertRaisesRegex(TypeError,
@ -446,6 +443,9 @@ class TypeVarTests(BaseTestCase):
TypeVar('X', bound=Union)
with self.assertRaises(TypeError):
TypeVar('X', str, float, bound=Employee)
with self.assertRaisesRegex(TypeError,
r"Bound must be a type\. Got \(1, 2\)\."):
TypeVar('X', bound=(1, 2))
def test_missing__name__(self):
# See bpo-39942
@ -1161,7 +1161,7 @@ class TypeVarTupleTests(BaseTestCase):
self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]')
def test_cannot_subclass(self):
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVarTuple'):
class C(TypeVarTuple): pass
Ts = TypeVarTuple('Ts')
with self.assertRaisesRegex(TypeError,
@ -6444,34 +6444,27 @@ class TypeTests(BaseTestCase):
class TestModules(TestCase):
func_names = ['_idfunc']
def test_py_functions(self):
for fname in self.func_names:
self.assertEqual(getattr(py_typing, fname).__module__, 'typing')
@skipUnless(c_typing, 'requires _typing')
def test_c_functions(self):
for fname in self.func_names:
self.assertEqual(getattr(c_typing, fname).__module__, '_typing')
self.assertEqual(getattr(typing, fname).__module__, '_typing')
class NewTypeTests:
class NewTypeTests(BaseTestCase):
def cleanup(self):
for f in self.module._cleanups:
for f in typing._cleanups:
f()
@classmethod
def setUpClass(cls):
sys.modules['typing'] = cls.module
global UserId
UserId = cls.module.NewType('UserId', int)
cls.UserName = cls.module.NewType(cls.__qualname__ + '.UserName', str)
UserId = typing.NewType('UserId', int)
cls.UserName = typing.NewType(cls.__qualname__ + '.UserName', str)
@classmethod
def tearDownClass(cls):
global UserId
del UserId
del cls.UserName
sys.modules['typing'] = typing
def tearDown(self):
self.cleanup()
@ -6491,11 +6484,11 @@ class NewTypeTests:
def test_or(self):
for cls in (int, self.UserName):
with self.subTest(cls=cls):
self.assertEqual(UserId | cls, self.module.Union[UserId, cls])
self.assertEqual(cls | UserId, self.module.Union[cls, UserId])
self.assertEqual(UserId | cls, typing.Union[UserId, cls])
self.assertEqual(cls | UserId, typing.Union[cls, UserId])
self.assertEqual(self.module.get_args(UserId | cls), (UserId, cls))
self.assertEqual(self.module.get_args(cls | UserId), (cls, UserId))
self.assertEqual(typing.get_args(UserId | cls), (UserId, cls))
self.assertEqual(typing.get_args(cls | UserId), (cls, UserId))
def test_special_attrs(self):
self.assertEqual(UserId.__name__, 'UserId')
@ -6516,7 +6509,7 @@ class NewTypeTests:
f'{__name__}.{self.__class__.__qualname__}.UserName')
def test_pickle(self):
UserAge = self.module.NewType('UserAge', float)
UserAge = typing.NewType('UserAge', float)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(proto=proto):
pickled = pickle.dumps(UserId, proto)
@ -6548,15 +6541,6 @@ class NewTypeTests:
...
class NewTypePythonTests(NewTypeTests, BaseTestCase):
module = py_typing
@skipUnless(c_typing, 'requires _typing')
class NewTypeCTests(NewTypeTests, BaseTestCase):
module = c_typing
class NamedTupleTests(BaseTestCase):
class NestedEmployee(NamedTuple):
name: str
@ -8170,11 +8154,11 @@ class ParamSpecTests(BaseTestCase):
self.assertEqual(C2[Concatenate[T, P2]].__parameters__, (T, P2))
def test_cannot_subclass(self):
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'ParamSpec'):
class C(ParamSpec): pass
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'ParamSpecArgs'):
class C(ParamSpecArgs): pass
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'ParamSpecKwargs'):
class C(ParamSpecKwargs): pass
P = ParamSpec('P')
with self.assertRaisesRegex(TypeError,