diff options
author | Yurii Karabas <1998uriyyo@gmail.com> | 2021-07-22 21:06:54 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-22 21:06:54 (GMT) |
commit | 96c4cbd96c769e92869c62ba898dd9eb670baa81 (patch) | |
tree | bf57868bec1d475c469214d42f89d29158b60244 | |
parent | f1afef5e0d93d66fbf3c9aaeab8b3b8da9617583 (diff) | |
download | cpython-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.py | 58 | ||||
-rw-r--r-- | Lib/typing.py | 12 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-07-20-18-34-16.bpo-44353.ATuYq4.rst | 2 | ||||
-rw-r--r-- | Modules/Setup | 1 | ||||
-rw-r--r-- | Modules/_typingmodule.c | 59 | ||||
-rw-r--r-- | Modules/clinic/_typingmodule.c.h | 12 | ||||
-rw-r--r-- | PC/config.c | 2 | ||||
-rw-r--r-- | PCbuild/pythoncore.vcxproj | 1 | ||||
-rw-r--r-- | PCbuild/pythoncore.vcxproj.filters | 3 | ||||
-rw-r--r-- | Python/stdlib_module_names.h | 1 | ||||
-rw-r--r-- | setup.py | 2 |
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", @@ -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 |