Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 96c4cbd

Browse files
uriyyoFidget-Spinnerdlax
authored
bpo-44353: Implement typing.NewType __call__ method in C (#27262)
Co-authored-by: Ken Jin <[email protected]> Co-authored-by: Denis Laxalde <[email protected]>
1 parent f1afef5 commit 96c4cbd

11 files changed

Lines changed: 137 additions & 16 deletions

File tree

Lib/test/test_typing.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@
3333
import weakref
3434
import types
3535

36+
from test.support import import_helper
3637
from test import mod_generics_cache
3738
from test import _typed_dict_helper
3839

3940

41+
py_typing = import_helper.import_fresh_module('typing', blocked=['_typing'])
42+
c_typing = import_helper.import_fresh_module('typing', fresh=['_typing'])
43+
44+
4045
class BaseTestCase(TestCase):
4146

4247
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
@@ -3673,48 +3678,75 @@ def foo(a: A) -> Optional[BaseException]:
36733678
assert foo(None) is None
36743679

36753680

3676-
class NewTypeTests(BaseTestCase):
3681+
class TestModules(TestCase):
3682+
func_names = ['_idfunc']
3683+
3684+
def test_py_functions(self):
3685+
for fname in self.func_names:
3686+
self.assertEqual(getattr(py_typing, fname).__module__, 'typing')
3687+
3688+
@skipUnless(c_typing, 'requires _typing')
3689+
def test_c_functions(self):
3690+
for fname in self.func_names:
3691+
self.assertEqual(getattr(c_typing, fname).__module__, '_typing')
3692+
3693+
3694+
class NewTypeTests:
3695+
def setUp(self):
3696+
sys.modules['typing'] = self.module
3697+
3698+
def tearDown(self):
3699+
sys.modules['typing'] = typing
36773700

36783701
def test_basic(self):
3679-
UserId = NewType('UserId', int)
3680-
UserName = NewType('UserName', str)
3702+
UserId = self.module.NewType('UserId', int)
3703+
UserName = self.module.NewType('UserName', str)
36813704
self.assertIsInstance(UserId(5), int)
36823705
self.assertIsInstance(UserName('Joe'), str)
36833706
self.assertEqual(UserId(5) + 1, 6)
36843707

36853708
def test_errors(self):
3686-
UserId = NewType('UserId', int)
3687-
UserName = NewType('UserName', str)
3709+
UserId = self.module.NewType('UserId', int)
3710+
UserName = self.module.NewType('UserName', str)
36883711
with self.assertRaises(TypeError):
36893712
issubclass(UserId, int)
36903713
with self.assertRaises(TypeError):
36913714
class D(UserName):
36923715
pass
36933716

36943717
def test_or(self):
3695-
UserId = NewType('UserId', int)
3696-
UserName = NewType('UserName', str)
3718+
UserId = self.module.NewType('UserId', int)
3719+
UserName = self.module.NewType('UserName', str)
36973720

36983721
for cls in (int, UserName):
36993722
with self.subTest(cls=cls):
3700-
self.assertEqual(UserId | cls, Union[UserId, cls])
3701-
self.assertEqual(cls | UserId, Union[cls, UserId])
3723+
self.assertEqual(UserId | cls, self.module.Union[UserId, cls])
3724+
self.assertEqual(cls | UserId, self.module.Union[cls, UserId])
37023725

3703-
self.assertEqual(get_args(UserId | cls), (UserId, cls))
3704-
self.assertEqual(get_args(cls | UserId), (cls, UserId))
3726+
self.assertEqual(self.module.get_args(UserId | cls), (UserId, cls))
3727+
self.assertEqual(self.module.get_args(cls | UserId), (cls, UserId))
37053728

37063729
def test_special_attrs(self):
3707-
UserId = NewType('UserId', int)
3730+
UserId = self.module.NewType('UserId', int)
37083731

37093732
self.assertEqual(UserId.__name__, 'UserId')
37103733
self.assertEqual(UserId.__qualname__, 'UserId')
37113734
self.assertEqual(UserId.__module__, __name__)
37123735

37133736
def test_repr(self):
3714-
UserId = NewType('UserId', int)
3737+
UserId = self.module.NewType('UserId', int)
37153738

37163739
self.assertEqual(repr(UserId), f'{__name__}.UserId')
37173740

3741+
class NewTypePythonTests(BaseTestCase, NewTypeTests):
3742+
module = py_typing
3743+
3744+
3745+
@skipUnless(c_typing, 'requires _typing')
3746+
class NewTypeCTests(BaseTestCase, NewTypeTests):
3747+
module = c_typing
3748+
3749+
37183750
class NamedTupleTests(BaseTestCase):
37193751
class NestedEmployee(NamedTuple):
37203752
name: str

Lib/typing.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@
3131
import warnings
3232
from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias
3333

34+
35+
try:
36+
from _typing import _idfunc
37+
except ImportError:
38+
def _idfunc(_, x):
39+
return x
40+
3441
# Please keep __all__ alphabetized within each category.
3542
__all__ = [
3643
# Super-special typing primitives.
@@ -2375,6 +2382,8 @@ def name_by_id(user_id: UserId) -> str:
23752382
num = UserId(5) + 1 # type: int
23762383
"""
23772384

2385+
__call__ = _idfunc
2386+
23782387
def __init__(self, name, tp):
23792388
self.__name__ = name
23802389
self.__qualname__ = name
@@ -2384,9 +2393,6 @@ def __init__(self, name, tp):
23842393
def __repr__(self):
23852394
return f'{self.__module__}.{self.__qualname__}'
23862395

2387-
def __call__(self, x):
2388-
return x
2389-
23902396
def __or__(self, other):
23912397
return Union[self, other]
23922398

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make ``NewType.__call__`` faster by implementing it in C.
2+
Patch provided by Yurii Karabas.

Modules/Setup

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ _symtable symtablemodule.c
184184
#_asyncio _asynciomodule.c # Fast asyncio Future
185185
#_json -I$(srcdir)/Include/internal -DPy_BUILD_CORE_BUILTIN _json.c # _json speedups
186186
#_statistics _statisticsmodule.c # statistics accelerator
187+
#_typing _typingmodule.c # typing accelerator
187188

188189
#unicodedata unicodedata.c -DPy_BUILD_CORE_BUILTIN # static Unicode character database
189190

Modules/_typingmodule.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* typing accelerator C extension: _typing module. */
2+
3+
#include "Python.h"
4+
#include "clinic/_typingmodule.c.h"
5+
6+
/*[clinic input]
7+
module _typing
8+
9+
[clinic start generated code]*/
10+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1db35baf1c72942b]*/
11+
12+
/* helper function to make typing.NewType.__call__ method faster */
13+
14+
/*[clinic input]
15+
_typing._idfunc -> object
16+
17+
x: object
18+
/
19+
20+
[clinic start generated code]*/
21+
22+
static PyObject *
23+
_typing__idfunc(PyObject *module, PyObject *x)
24+
/*[clinic end generated code: output=63c38be4a6ec5f2c input=49f17284b43de451]*/
25+
{
26+
Py_INCREF(x);
27+
return x;
28+
}
29+
30+
31+
static PyMethodDef typing_methods[] = {
32+
_TYPING__IDFUNC_METHODDEF
33+
{NULL, NULL, 0, NULL}
34+
};
35+
36+
PyDoc_STRVAR(typing_doc,
37+
"Accelerators for the typing module.\n");
38+
39+
static struct PyModuleDef_Slot _typingmodule_slots[] = {
40+
{0, NULL}
41+
};
42+
43+
static struct PyModuleDef typingmodule = {
44+
PyModuleDef_HEAD_INIT,
45+
"_typing",
46+
typing_doc,
47+
0,
48+
typing_methods,
49+
_typingmodule_slots,
50+
NULL,
51+
NULL,
52+
NULL
53+
};
54+
55+
PyMODINIT_FUNC
56+
PyInit__typing(void)
57+
{
58+
return PyModuleDef_Init(&typingmodule);
59+
}

Modules/clinic/_typingmodule.c.h

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PC/config.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern PyObject* PyInit__sha256(void);
2424
extern PyObject* PyInit__sha512(void);
2525
extern PyObject* PyInit__sha3(void);
2626
extern PyObject* PyInit__statistics(void);
27+
extern PyObject* PyInit__typing(void);
2728
extern PyObject* PyInit__blake2(void);
2829
extern PyObject* PyInit_time(void);
2930
extern PyObject* PyInit__thread(void);
@@ -104,6 +105,7 @@ struct _inittab _PyImport_Inittab[] = {
104105
{"_blake2", PyInit__blake2},
105106
{"time", PyInit_time},
106107
{"_thread", PyInit__thread},
108+
{"_typing", PyInit__typing},
107109
{"_statistics", PyInit__statistics},
108110
#ifdef WIN32
109111
{"msvcrt", PyInit_msvcrt},

PCbuild/pythoncore.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@
363363
<ClCompile Include="..\Modules\symtablemodule.c" />
364364
<ClCompile Include="..\Modules\_threadmodule.c" />
365365
<ClCompile Include="..\Modules\_tracemalloc.c" />
366+
<ClCompile Include="..\Modules\_typingmodule.c" />
366367
<ClCompile Include="..\Modules\timemodule.c" />
367368
<ClCompile Include="..\Modules\xxsubtype.c" />
368369
<ClCompile Include="..\Modules\_xxsubinterpretersmodule.c" />

PCbuild/pythoncore.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,9 @@
698698
<ClCompile Include="..\Modules\_statisticsmodule.c">
699699
<Filter>Modules</Filter>
700700
</ClCompile>
701+
<ClCompile Include="..\Modules\_typingmodule.c">
702+
<Filter>Modules</Filter>
703+
</ClCompile>
701704
<ClCompile Include="..\Modules\_struct.c">
702705
<Filter>Modules</Filter>
703706
</ClCompile>

Python/stdlib_module_names.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static const char* _Py_stdlib_module_names[] = {
8181
"_threading_local",
8282
"_tkinter",
8383
"_tracemalloc",
84+
"_typing",
8485
"_uuid",
8586
"_warnings",
8687
"_weakref",

0 commit comments

Comments
 (0)