summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYurii Karabas <1998uriyyo@gmail.com>2021-07-22 21:06:54 (GMT)
committerGitHub <noreply@github.com>2021-07-22 21:06:54 (GMT)
commit96c4cbd96c769e92869c62ba898dd9eb670baa81 (patch)
treebf57868bec1d475c469214d42f89d29158b60244
parentf1afef5e0d93d66fbf3c9aaeab8b3b8da9617583 (diff)
downloadcpython-96c4cbd96c769e92869c62ba898dd9eb670baa81.zip
cpython-96c4cbd96c769e92869c62ba898dd9eb670baa81.tar.gz
cpython-96c4cbd96c769e92869c62ba898dd9eb670baa81.tar.bz2
bpo-44353: Implement typing.NewType __call__ method in C (#27262)
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Co-authored-by: Denis Laxalde <denis@laxalde.org>
-rw-r--r--Lib/test/test_typing.py58
-rw-r--r--Lib/typing.py12
-rw-r--r--Misc/NEWS.d/next/Library/2021-07-20-18-34-16.bpo-44353.ATuYq4.rst2
-rw-r--r--Modules/Setup1
-rw-r--r--Modules/_typingmodule.c59
-rw-r--r--Modules/clinic/_typingmodule.c.h12
-rw-r--r--PC/config.c2
-rw-r--r--PCbuild/pythoncore.vcxproj1
-rw-r--r--PCbuild/pythoncore.vcxproj.filters3
-rw-r--r--Python/stdlib_module_names.h1
-rw-r--r--setup.py2
11 files changed, 137 insertions, 16 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index ba51b9c..6f1d4f6 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -33,10 +33,15 @@ import typing
import weakref
import types
+from test.support import import_helper
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'])
+
+
class BaseTestCase(TestCase):
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
@@ -3673,18 +3678,36 @@ class TypeTests(BaseTestCase):
assert foo(None) is None
-class NewTypeTests(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')
+
+
+class NewTypeTests:
+ def setUp(self):
+ sys.modules['typing'] = self.module
+
+ def tearDown(self):
+ sys.modules['typing'] = typing
def test_basic(self):
- UserId = NewType('UserId', int)
- UserName = NewType('UserName', str)
+ UserId = self.module.NewType('UserId', int)
+ UserName = self.module.NewType('UserName', str)
self.assertIsInstance(UserId(5), int)
self.assertIsInstance(UserName('Joe'), str)
self.assertEqual(UserId(5) + 1, 6)
def test_errors(self):
- UserId = NewType('UserId', int)
- UserName = NewType('UserName', str)
+ UserId = self.module.NewType('UserId', int)
+ UserName = self.module.NewType('UserName', str)
with self.assertRaises(TypeError):
issubclass(UserId, int)
with self.assertRaises(TypeError):
@@ -3692,29 +3715,38 @@ class NewTypeTests(BaseTestCase):
pass
def test_or(self):
- UserId = NewType('UserId', int)
- UserName = NewType('UserName', str)
+ UserId = self.module.NewType('UserId', int)
+ UserName = self.module.NewType('UserName', str)
for cls in (int, UserName):
with self.subTest(cls=cls):
- self.assertEqual(UserId | cls, Union[UserId, cls])
- self.assertEqual(cls | UserId, Union[cls, UserId])
+ self.assertEqual(UserId | cls, self.module.Union[UserId, cls])
+ self.assertEqual(cls | UserId, self.module.Union[cls, UserId])
- self.assertEqual(get_args(UserId | cls), (UserId, cls))
- self.assertEqual(get_args(cls | UserId), (cls, UserId))
+ self.assertEqual(self.module.get_args(UserId | cls), (UserId, cls))
+ self.assertEqual(self.module.get_args(cls | UserId), (cls, UserId))
def test_special_attrs(self):
- UserId = NewType('UserId', int)
+ UserId = self.module.NewType('UserId', int)
self.assertEqual(UserId.__name__, 'UserId')
self.assertEqual(UserId.__qualname__, 'UserId')
self.assertEqual(UserId.__module__, __name__)
def test_repr(self):
- UserId = NewType('UserId', int)
+ UserId = self.module.NewType('UserId', int)
self.assertEqual(repr(UserId), f'{__name__}.UserId')
+class NewTypePythonTests(BaseTestCase, NewTypeTests):
+ module = py_typing
+
+
+@skipUnless(c_typing, 'requires _typing')
+class NewTypeCTests(BaseTestCase, NewTypeTests):
+ module = c_typing
+
+
class NamedTupleTests(BaseTestCase):
class NestedEmployee(NamedTuple):
name: str
diff --git a/Lib/typing.py b/Lib/typing.py
index 1aff0a1..5c95a4d 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -31,6 +31,13 @@ import types
import warnings
from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias
+
+try:
+ from _typing import _idfunc
+except ImportError:
+ def _idfunc(_, x):
+ return x
+
# Please keep __all__ alphabetized within each category.
__all__ = [
# Super-special typing primitives.
@@ -2375,6 +2382,8 @@ class NewType:
num = UserId(5) + 1 # type: int
"""
+ __call__ = _idfunc
+
def __init__(self, name, tp):
self.__name__ = name
self.__qualname__ = name
@@ -2384,9 +2393,6 @@ class NewType:
def __repr__(self):
return f'{self.__module__}.{self.__qualname__}'
- def __call__(self, x):
- return x
-
def __or__(self, other):
return Union[self, other]
diff --git a/Misc/NEWS.d/next/Library/2021-07-20-18-34-16.bpo-44353.ATuYq4.rst b/Misc/NEWS.d/next/Library/2021-07-20-18-34-16.bpo-44353.ATuYq4.rst
new file mode 100644
index 0000000..7332770
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-07-20-18-34-16.bpo-44353.ATuYq4.rst
@@ -0,0 +1,2 @@
+Make ``NewType.__call__`` faster by implementing it in C.
+Patch provided by Yurii Karabas.
diff --git a/Modules/Setup b/Modules/Setup
index 87c6a15..5e26472 100644
--- a/Modules/Setup
+++ b/Modules/Setup
@@ -184,6 +184,7 @@ _symtable symtablemodule.c
#_asyncio _asynciomodule.c # Fast asyncio Future
#_json -I$(srcdir)/Include/internal -DPy_BUILD_CORE_BUILTIN _json.c # _json speedups
#_statistics _statisticsmodule.c # statistics accelerator
+#_typing _typingmodule.c # typing accelerator
#unicodedata unicodedata.c -DPy_BUILD_CORE_BUILTIN # static Unicode character database
diff --git a/Modules/_typingmodule.c b/Modules/_typingmodule.c
new file mode 100644
index 0000000..8b6faa6
--- /dev/null
+++ b/Modules/_typingmodule.c
@@ -0,0 +1,59 @@
+/* typing accelerator C extension: _typing module. */
+
+#include "Python.h"
+#include "clinic/_typingmodule.c.h"
+
+/*[clinic input]
+module _typing
+
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1db35baf1c72942b]*/
+
+/* helper function to make typing.NewType.__call__ method faster */
+
+/*[clinic input]
+_typing._idfunc -> object
+
+ x: object
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_typing__idfunc(PyObject *module, PyObject *x)
+/*[clinic end generated code: output=63c38be4a6ec5f2c input=49f17284b43de451]*/
+{
+ Py_INCREF(x);
+ return x;
+}
+
+
+static PyMethodDef typing_methods[] = {
+ _TYPING__IDFUNC_METHODDEF
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(typing_doc,
+"Accelerators for the typing module.\n");
+
+static struct PyModuleDef_Slot _typingmodule_slots[] = {
+ {0, NULL}
+};
+
+static struct PyModuleDef typingmodule = {
+ PyModuleDef_HEAD_INIT,
+ "_typing",
+ typing_doc,
+ 0,
+ typing_methods,
+ _typingmodule_slots,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit__typing(void)
+{
+ return PyModuleDef_Init(&typingmodule);
+}
diff --git a/Modules/clinic/_typingmodule.c.h b/Modules/clinic/_typingmodule.c.h
new file mode 100644
index 0000000..ea415e6
--- /dev/null
+++ b/Modules/clinic/_typingmodule.c.h
@@ -0,0 +1,12 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_typing__idfunc__doc__,
+"_idfunc($module, x, /)\n"
+"--\n"
+"\n");
+
+#define _TYPING__IDFUNC_METHODDEF \
+ {"_idfunc", (PyCFunction)_typing__idfunc, METH_O, _typing__idfunc__doc__},
+/*[clinic end generated code: output=e7ea2a3cb7ab301a input=a9049054013a1b77]*/
diff --git a/PC/config.c b/PC/config.c
index 87cd76d..11743ea 100644
--- a/PC/config.c
+++ b/PC/config.c
@@ -24,6 +24,7 @@ extern PyObject* PyInit__sha256(void);
extern PyObject* PyInit__sha512(void);
extern PyObject* PyInit__sha3(void);
extern PyObject* PyInit__statistics(void);
+extern PyObject* PyInit__typing(void);
extern PyObject* PyInit__blake2(void);
extern PyObject* PyInit_time(void);
extern PyObject* PyInit__thread(void);
@@ -104,6 +105,7 @@ struct _inittab _PyImport_Inittab[] = {
{"_blake2", PyInit__blake2},
{"time", PyInit_time},
{"_thread", PyInit__thread},
+ {"_typing", PyInit__typing},
{"_statistics", PyInit__statistics},
#ifdef WIN32
{"msvcrt", PyInit_msvcrt},
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 66d35e0..7cd3c21 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -363,6 +363,7 @@
<ClCompile Include="..\Modules\symtablemodule.c" />
<ClCompile Include="..\Modules\_threadmodule.c" />
<ClCompile Include="..\Modules\_tracemalloc.c" />
+ <ClCompile Include="..\Modules\_typingmodule.c" />
<ClCompile Include="..\Modules\timemodule.c" />
<ClCompile Include="..\Modules\xxsubtype.c" />
<ClCompile Include="..\Modules\_xxsubinterpretersmodule.c" />
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 22904d5..4eccf4f 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -698,6 +698,9 @@
<ClCompile Include="..\Modules\_statisticsmodule.c">
<Filter>Modules</Filter>
</ClCompile>
+ <ClCompile Include="..\Modules\_typingmodule.c">
+ <Filter>Modules</Filter>
+ </ClCompile>
<ClCompile Include="..\Modules\_struct.c">
<Filter>Modules</Filter>
</ClCompile>
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index b09b8dc..3c5f176 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -81,6 +81,7 @@ static const char* _Py_stdlib_module_names[] = {
"_threading_local",
"_tkinter",
"_tracemalloc",
+"_typing",
"_uuid",
"_warnings",
"_weakref",
diff --git a/setup.py b/setup.py
index bc2ea16..97f101a 100644
--- a/setup.py
+++ b/setup.py
@@ -957,6 +957,8 @@ class PyBuildExt(build_ext):
extra_compile_args=['-DPy_BUILD_CORE_MODULE']))
# _statistics module
self.add(Extension("_statistics", ["_statisticsmodule.c"]))
+ # _typing module
+ self.add(Extension("_typing", ["_typingmodule.c"]))
# Modules with some UNIX dependencies -- on by default:
# (If you have a really backward UNIX, select and socket may not be