| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,7 +30,7 @@ | |||
| 30 | 30 | # wrapper functions that can handle naive introspection | |
| 31 | 31 | ||
| 32 | 32 | WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', | |
| 33 | - '__annotations__') | ||
| 33 | + '__annotations__', '__type_params__') | ||
| 34 | 34 | WRAPPER_UPDATES = ('__dict__',) | |
| 35 | 35 | def update_wrapper(wrapper, | |
| 36 | 36 | wrapped, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | import textwrap | |
| 2 | 2 | import types | |
| 3 | + import typing | ||
| 3 | 4 | import unittest | |
| 4 | 5 | ||
| 5 | 6 | ||
@@ -190,6 +191,20 @@ def test___qualname__(self): | |||
| 190 | 191 | # __qualname__ must be a string | |
| 191 | 192 | self.cannot_set_attr(self.b, '__qualname__', 7, TypeError) | |
| 192 | 193 | ||
| 194 | + def test___type_params__(self): | ||
| 195 | + def generic[T](): pass | ||
| 196 | + def not_generic(): pass | ||
| 197 | + T, = generic.__type_params__ | ||
| 198 | + self.assertIsInstance(T, typing.TypeVar) | ||
| 199 | + self.assertEqual(generic.__type_params__, (T,)) | ||
| 200 | + self.assertEqual(not_generic.__type_params__, ()) | ||
| 201 | + with self.assertRaises(TypeError): | ||
| 202 | + del not_generic.__type_params__ | ||
| 203 | + with self.assertRaises(TypeError): | ||
| 204 | + not_generic.__type_params__ = 42 | ||
| 205 | + not_generic.__type_params__ = (T,) | ||
| 206 | + self.assertEqual(not_generic.__type_params__, (T,)) | ||
| 207 | + | ||
| 193 | 208 | def test___code__(self): | |
| 194 | 209 | num_one, num_two = 7, 8 | |
| 195 | 210 | def a(): pass | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -617,7 +617,7 @@ def check_wrapper(self, wrapper, wrapped, | |||
| 617 | 617 | ||
| 618 | 618 | ||
| 619 | 619 | def _default_update(self): | |
| 620 | - def f(a:'This is a new annotation'): | ||
| 620 | + def f[T](a:'This is a new annotation'): | ||
| 621 | 621 | """This is a test""" | |
| 622 | 622 | pass | |
| 623 | 623 | f.attr = 'This is also a test' | |
@@ -630,12 +630,14 @@ def wrapper(b:'This is the prior annotation'): | |||
| 630 | 630 | def test_default_update(self): | |
| 631 | 631 | wrapper, f = self._default_update() | |
| 632 | 632 | self.check_wrapper(wrapper, f) | |
| 633 | + T, = f.__type_params__ | ||
| 633 | 634 | self.assertIs(wrapper.__wrapped__, f) | |
| 634 | 635 | self.assertEqual(wrapper.__name__, 'f') | |
| 635 | 636 | self.assertEqual(wrapper.__qualname__, f.__qualname__) | |
| 636 | 637 | self.assertEqual(wrapper.attr, 'This is also a test') | |
| 637 | 638 | self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') | |
| 638 | 639 | self.assertNotIn('b', wrapper.__annotations__) | |
| 640 | + self.assertEqual(wrapper.__type_params__, (T,)) | ||
| 639 | 641 | ||
| 640 | 642 | @unittest.skipIf(sys.flags.optimize >= 2, | |
| 641 | 643 | "Docstrings are omitted with -O2 and above") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -843,5 +843,5 @@ def func[A](): | |||
| 843 | 843 | func.__type_params__ = () | |
| 844 | 844 | """ | |
| 845 | 845 | ||
| 846 | - with self.assertRaisesRegex(AttributeError, "attribute '__type_params__' of 'function' objects is not writable"): | ||
| 847 | - run_code(code) | ||
| 846 | + ns = run_code(code) | ||
| 847 | + self.assertEqual(ns["func"].__type_params__, ()) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + :func:`functools.update_wrapper` now sets the ``__type_params__`` attribute | ||
| 2 | + (added by :pep:`695`). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -665,6 +665,20 @@ func_get_type_params(PyFunctionObject *op, void *Py_UNUSED(ignored)) | |||
| 665 | 665 | return Py_NewRef(op->func_typeparams); | |
| 666 | 666 | } | |
| 667 | 667 | ||
| 668 | + static int | ||
| 669 | + func_set_type_params(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored)) | ||
| 670 | + { | ||
| 671 | + /* Not legal to del f.__type_params__ or to set it to anything | ||
| 672 | + * other than a tuple object. */ | ||
| 673 | + if (value == NULL || !PyTuple_Check(value)) { | ||
| 674 | + PyErr_SetString(PyExc_TypeError, | ||
| 675 | + "__type_params__ must be set to a tuple"); | ||
| 676 | + return -1; | ||
| 677 | + } | ||
| 678 | + Py_XSETREF(op->func_typeparams, Py_NewRef(value)); | ||
| 679 | + return 0; | ||
| 680 | + } | ||
| 681 | + | ||
| 668 | 682 | PyObject * | |
| 669 | 683 | _Py_set_function_type_params(PyThreadState *Py_UNUSED(ignored), PyObject *func, | |
| 670 | 684 | PyObject *type_params) | |
@@ -687,7 +701,8 @@ static PyGetSetDef func_getsetlist[] = { | |||
| 687 | 701 | {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, | |
| 688 | 702 | {"__name__", (getter)func_get_name, (setter)func_set_name}, | |
| 689 | 703 | {"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname}, | |
| 690 | - {"__type_params__", (getter)func_get_type_params, NULL}, | ||
| 704 | + {"__type_params__", (getter)func_get_type_params, | ||
| 705 | + (setter)func_set_type_params}, | ||
| 691 | 706 | {NULL} /* Sentinel */ | |
| 692 | 707 | }; | |
| 693 | 708 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments