bpo-42195: Ensure consistency of Callable's __args__ in collections.abc and typing (GH-23060)
This commit is contained in:
parent
43c4fb6c90
commit
463c7d3d14
|
@ -10,6 +10,10 @@ from abc import ABCMeta, abstractmethod
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
GenericAlias = type(list[int])
|
GenericAlias = type(list[int])
|
||||||
|
EllipsisType = type(...)
|
||||||
|
def _f(): pass
|
||||||
|
FunctionType = type(_f)
|
||||||
|
del _f
|
||||||
|
|
||||||
__all__ = ["Awaitable", "Coroutine",
|
__all__ = ["Awaitable", "Coroutine",
|
||||||
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
|
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
|
||||||
|
@ -409,6 +413,69 @@ class Collection(Sized, Iterable, Container):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
class _CallableGenericAlias(GenericAlias):
|
||||||
|
""" Represent `Callable[argtypes, resulttype]`.
|
||||||
|
|
||||||
|
This sets ``__args__`` to a tuple containing the flattened``argtypes``
|
||||||
|
followed by ``resulttype``.
|
||||||
|
|
||||||
|
Example: ``Callable[[int, str], float]`` sets ``__args__`` to
|
||||||
|
``(int, str, float)``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __new__(cls, origin, args):
|
||||||
|
return cls.__create_ga(origin, args)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __create_ga(cls, origin, args):
|
||||||
|
if not isinstance(args, tuple) or len(args) != 2:
|
||||||
|
raise TypeError(
|
||||||
|
"Callable must be used as Callable[[arg, ...], result].")
|
||||||
|
t_args, t_result = args
|
||||||
|
if isinstance(t_args, list):
|
||||||
|
ga_args = tuple(t_args) + (t_result,)
|
||||||
|
# This relaxes what t_args can be on purpose to allow things like
|
||||||
|
# PEP 612 ParamSpec. Responsibility for whether a user is using
|
||||||
|
# Callable[...] properly is deferred to static type checkers.
|
||||||
|
else:
|
||||||
|
ga_args = args
|
||||||
|
return super().__new__(cls, origin, ga_args)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if len(self.__args__) == 2 and self.__args__[0] is Ellipsis:
|
||||||
|
return super().__repr__()
|
||||||
|
return (f'collections.abc.Callable'
|
||||||
|
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
|
||||||
|
f'{_type_repr(self.__args__[-1])}]')
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
args = self.__args__
|
||||||
|
if not (len(args) == 2 and args[0] is Ellipsis):
|
||||||
|
args = list(args[:-1]), args[-1]
|
||||||
|
return _CallableGenericAlias, (Callable, args)
|
||||||
|
|
||||||
|
|
||||||
|
def _type_repr(obj):
|
||||||
|
"""Return the repr() of an object, special-casing types (internal helper).
|
||||||
|
|
||||||
|
Copied from :mod:`typing` since collections.abc
|
||||||
|
shouldn't depend on that module.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, GenericAlias):
|
||||||
|
return repr(obj)
|
||||||
|
if isinstance(obj, type):
|
||||||
|
if obj.__module__ == 'builtins':
|
||||||
|
return obj.__qualname__
|
||||||
|
return f'{obj.__module__}.{obj.__qualname__}'
|
||||||
|
if obj is Ellipsis:
|
||||||
|
return '...'
|
||||||
|
if isinstance(obj, FunctionType):
|
||||||
|
return obj.__name__
|
||||||
|
return repr(obj)
|
||||||
|
|
||||||
|
|
||||||
class Callable(metaclass=ABCMeta):
|
class Callable(metaclass=ABCMeta):
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
@ -423,7 +490,7 @@ class Callable(metaclass=ABCMeta):
|
||||||
return _check_methods(C, "__call__")
|
return _check_methods(C, "__call__")
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
__class_getitem__ = classmethod(GenericAlias)
|
__class_getitem__ = classmethod(_CallableGenericAlias)
|
||||||
|
|
||||||
|
|
||||||
### SETS ###
|
### SETS ###
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
from _collections_abc import *
|
from _collections_abc import *
|
||||||
from _collections_abc import __all__
|
from _collections_abc import __all__
|
||||||
|
from _collections_abc import _CallableGenericAlias
|
||||||
|
|
|
@ -62,7 +62,6 @@ class BaseTest(unittest.TestCase):
|
||||||
Iterable, Iterator,
|
Iterable, Iterator,
|
||||||
Reversible,
|
Reversible,
|
||||||
Container, Collection,
|
Container, Collection,
|
||||||
Callable,
|
|
||||||
Mailbox, _PartialFile,
|
Mailbox, _PartialFile,
|
||||||
ContextVar, Token,
|
ContextVar, Token,
|
||||||
Field,
|
Field,
|
||||||
|
@ -307,6 +306,63 @@ class BaseTest(unittest.TestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
GenericAlias(bad=float)
|
GenericAlias(bad=float)
|
||||||
|
|
||||||
|
def test_subclassing_types_genericalias(self):
|
||||||
|
class SubClass(GenericAlias): ...
|
||||||
|
alias = SubClass(list, int)
|
||||||
|
class Bad(GenericAlias):
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
super().__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
self.assertEqual(alias, list[int])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Bad(list, int, bad=int)
|
||||||
|
|
||||||
|
def test_abc_callable(self):
|
||||||
|
# A separate test is needed for Callable since it uses a subclass of
|
||||||
|
# GenericAlias.
|
||||||
|
alias = Callable[[int, str], float]
|
||||||
|
with self.subTest("Testing subscription"):
|
||||||
|
self.assertIs(alias.__origin__, Callable)
|
||||||
|
self.assertEqual(alias.__args__, (int, str, float))
|
||||||
|
self.assertEqual(alias.__parameters__, ())
|
||||||
|
|
||||||
|
with self.subTest("Testing instance checks"):
|
||||||
|
self.assertIsInstance(alias, GenericAlias)
|
||||||
|
|
||||||
|
with self.subTest("Testing weakref"):
|
||||||
|
self.assertEqual(ref(alias)(), alias)
|
||||||
|
|
||||||
|
with self.subTest("Testing pickling"):
|
||||||
|
s = pickle.dumps(alias)
|
||||||
|
loaded = pickle.loads(s)
|
||||||
|
self.assertEqual(alias.__origin__, loaded.__origin__)
|
||||||
|
self.assertEqual(alias.__args__, loaded.__args__)
|
||||||
|
self.assertEqual(alias.__parameters__, loaded.__parameters__)
|
||||||
|
|
||||||
|
with self.subTest("Testing TypeVar substitution"):
|
||||||
|
C1 = Callable[[int, T], T]
|
||||||
|
C2 = Callable[[K, T], V]
|
||||||
|
C3 = Callable[..., T]
|
||||||
|
self.assertEqual(C1[str], Callable[[int, str], str])
|
||||||
|
self.assertEqual(C2[int, float, str], Callable[[int, float], str])
|
||||||
|
self.assertEqual(C3[int], Callable[..., int])
|
||||||
|
|
||||||
|
with self.subTest("Testing type erasure"):
|
||||||
|
class C1(Callable):
|
||||||
|
def __call__(self):
|
||||||
|
return None
|
||||||
|
a = C1[[int], T]
|
||||||
|
self.assertIs(a().__class__, C1)
|
||||||
|
self.assertEqual(a().__orig_class__, C1[[int], T])
|
||||||
|
|
||||||
|
# bpo-42195
|
||||||
|
with self.subTest("Testing collections.abc.Callable's consistency "
|
||||||
|
"with typing.Callable"):
|
||||||
|
c1 = typing.Callable[[int, str], dict]
|
||||||
|
c2 = Callable[[int, str], dict]
|
||||||
|
self.assertEqual(c1.__args__, c2.__args__)
|
||||||
|
self.assertEqual(hash(c1.__args__), hash(c2.__args__))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -717,14 +717,16 @@ class TypesTests(unittest.TestCase):
|
||||||
a = list[int]
|
a = list[int]
|
||||||
b = list[str]
|
b = list[str]
|
||||||
c = dict[float, str]
|
c = dict[float, str]
|
||||||
|
class SubClass(types.GenericAlias): ...
|
||||||
|
d = SubClass(list, float)
|
||||||
# equivalence with typing.Union
|
# equivalence with typing.Union
|
||||||
self.assertEqual(a | b | c, typing.Union[a, b, c])
|
self.assertEqual(a | b | c | d, typing.Union[a, b, c, d])
|
||||||
# de-duplicate
|
# de-duplicate
|
||||||
self.assertEqual(a | c | b | b | a | c, a | b | c)
|
self.assertEqual(a | c | b | b | a | c | d | d, a | b | c | d)
|
||||||
# order shouldn't matter
|
# order shouldn't matter
|
||||||
self.assertEqual(a | b, b | a)
|
self.assertEqual(a | b | d, b | a | d)
|
||||||
self.assertEqual(repr(a | b | c),
|
self.assertEqual(repr(a | b | c | d),
|
||||||
"list[int] | list[str] | dict[float, str]")
|
"list[int] | list[str] | dict[float, str] | list[float]")
|
||||||
|
|
||||||
class BadType(type):
|
class BadType(type):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
|
|
@ -446,14 +446,6 @@ class CallableTests(BaseTestCase):
|
||||||
type(c)()
|
type(c)()
|
||||||
|
|
||||||
def test_callable_wrong_forms(self):
|
def test_callable_wrong_forms(self):
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
Callable[[...], int]
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
Callable[(), int]
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
Callable[[()], int]
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
Callable[[int, 1], 2]
|
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Callable[int]
|
Callable[int]
|
||||||
|
|
||||||
|
@ -1807,10 +1799,9 @@ class GenericTests(BaseTestCase):
|
||||||
def test_extended_generic_rules_subclassing(self):
|
def test_extended_generic_rules_subclassing(self):
|
||||||
class T1(Tuple[T, KT]): ...
|
class T1(Tuple[T, KT]): ...
|
||||||
class T2(Tuple[T, ...]): ...
|
class T2(Tuple[T, ...]): ...
|
||||||
class C1(Callable[[T], T]): ...
|
class C1(typing.Container[T]):
|
||||||
class C2(Callable[..., int]):
|
def __contains__(self, item):
|
||||||
def __call__(self):
|
return False
|
||||||
return None
|
|
||||||
|
|
||||||
self.assertEqual(T1.__parameters__, (T, KT))
|
self.assertEqual(T1.__parameters__, (T, KT))
|
||||||
self.assertEqual(T1[int, str].__args__, (int, str))
|
self.assertEqual(T1[int, str].__args__, (int, str))
|
||||||
|
@ -1824,10 +1815,9 @@ class GenericTests(BaseTestCase):
|
||||||
## T2[int, str]
|
## T2[int, str]
|
||||||
|
|
||||||
self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]')
|
self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]')
|
||||||
self.assertEqual(C2.__parameters__, ())
|
self.assertEqual(C1.__parameters__, (T,))
|
||||||
self.assertIsInstance(C2(), collections.abc.Callable)
|
self.assertIsInstance(C1(), collections.abc.Container)
|
||||||
self.assertIsSubclass(C2, collections.abc.Callable)
|
self.assertIsSubclass(C1, collections.abc.Container)
|
||||||
self.assertIsSubclass(C1, collections.abc.Callable)
|
|
||||||
self.assertIsInstance(T1(), tuple)
|
self.assertIsInstance(T1(), tuple)
|
||||||
self.assertIsSubclass(T2, tuple)
|
self.assertIsSubclass(T2, tuple)
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
|
@ -1861,10 +1851,6 @@ class GenericTests(BaseTestCase):
|
||||||
class MyTup(Tuple[T, T]): ...
|
class MyTup(Tuple[T, T]): ...
|
||||||
self.assertIs(MyTup[int]().__class__, MyTup)
|
self.assertIs(MyTup[int]().__class__, MyTup)
|
||||||
self.assertEqual(MyTup[int]().__orig_class__, MyTup[int])
|
self.assertEqual(MyTup[int]().__orig_class__, MyTup[int])
|
||||||
class MyCall(Callable[..., T]):
|
|
||||||
def __call__(self): return None
|
|
||||||
self.assertIs(MyCall[T]().__class__, MyCall)
|
|
||||||
self.assertEqual(MyCall[T]().__orig_class__, MyCall[T])
|
|
||||||
class MyDict(typing.Dict[T, T]): ...
|
class MyDict(typing.Dict[T, T]): ...
|
||||||
self.assertIs(MyDict[int]().__class__, MyDict)
|
self.assertIs(MyDict[int]().__class__, MyDict)
|
||||||
self.assertEqual(MyDict[int]().__orig_class__, MyDict[int])
|
self.assertEqual(MyDict[int]().__orig_class__, MyDict[int])
|
||||||
|
|
|
@ -120,6 +120,16 @@ __all__ = [
|
||||||
# namespace, but excluded from __all__ because they might stomp on
|
# namespace, but excluded from __all__ because they might stomp on
|
||||||
# legitimate imports of those modules.
|
# legitimate imports of those modules.
|
||||||
|
|
||||||
|
|
||||||
|
def _type_convert(arg):
|
||||||
|
"""For converting None to type(None), and strings to ForwardRef."""
|
||||||
|
if arg is None:
|
||||||
|
return type(None)
|
||||||
|
if isinstance(arg, str):
|
||||||
|
return ForwardRef(arg)
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
def _type_check(arg, msg, is_argument=True):
|
def _type_check(arg, msg, is_argument=True):
|
||||||
"""Check that the argument is a type, and return it (internal helper).
|
"""Check that the argument is a type, and return it (internal helper).
|
||||||
|
|
||||||
|
@ -136,10 +146,7 @@ def _type_check(arg, msg, is_argument=True):
|
||||||
if is_argument:
|
if is_argument:
|
||||||
invalid_generic_forms = invalid_generic_forms + (ClassVar, Final)
|
invalid_generic_forms = invalid_generic_forms + (ClassVar, Final)
|
||||||
|
|
||||||
if arg is None:
|
arg = _type_convert(arg)
|
||||||
return type(None)
|
|
||||||
if isinstance(arg, str):
|
|
||||||
return ForwardRef(arg)
|
|
||||||
if (isinstance(arg, _GenericAlias) and
|
if (isinstance(arg, _GenericAlias) and
|
||||||
arg.__origin__ in invalid_generic_forms):
|
arg.__origin__ in invalid_generic_forms):
|
||||||
raise TypeError(f"{arg} is not valid as type argument")
|
raise TypeError(f"{arg} is not valid as type argument")
|
||||||
|
@ -900,13 +907,13 @@ class _CallableType(_SpecialGenericAlias, _root=True):
|
||||||
raise TypeError("Callable must be used as "
|
raise TypeError("Callable must be used as "
|
||||||
"Callable[[arg, ...], result].")
|
"Callable[[arg, ...], result].")
|
||||||
args, result = params
|
args, result = params
|
||||||
if args is Ellipsis:
|
# This relaxes what args can be on purpose to allow things like
|
||||||
params = (Ellipsis, result)
|
# PEP 612 ParamSpec. Responsibility for whether a user is using
|
||||||
else:
|
# Callable[...] properly is deferred to static type checkers.
|
||||||
if not isinstance(args, list):
|
if isinstance(args, list):
|
||||||
raise TypeError(f"Callable[args, result]: args must be a list."
|
|
||||||
f" Got {args}")
|
|
||||||
params = (tuple(args), result)
|
params = (tuple(args), result)
|
||||||
|
else:
|
||||||
|
params = (args, result)
|
||||||
return self.__getitem_inner__(params)
|
return self.__getitem_inner__(params)
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
|
@ -916,8 +923,9 @@ class _CallableType(_SpecialGenericAlias, _root=True):
|
||||||
result = _type_check(result, msg)
|
result = _type_check(result, msg)
|
||||||
if args is Ellipsis:
|
if args is Ellipsis:
|
||||||
return self.copy_with((_TypingEllipsis, result))
|
return self.copy_with((_TypingEllipsis, result))
|
||||||
msg = "Callable[[arg, ...], result]: each arg must be a type."
|
if not isinstance(args, tuple):
|
||||||
args = tuple(_type_check(arg, msg) for arg in args)
|
args = (args,)
|
||||||
|
args = tuple(_type_convert(arg) for arg in args)
|
||||||
params = args + (result,)
|
params = args + (result,)
|
||||||
return self.copy_with(params)
|
return self.copy_with(params)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
The ``__args__`` of the parameterized generics for :data:`typing.Callable`
|
||||||
|
and :class:`collections.abc.Callable` are now consistent. The ``__args__``
|
||||||
|
for :class:`collections.abc.Callable` are now flattened while
|
||||||
|
:data:`typing.Callable`'s have not changed. To allow this change,
|
||||||
|
:class:`types.GenericAlias` can now be subclassed and
|
||||||
|
``collections.abc.Callable``'s ``__class_getitem__`` will now return a subclass
|
||||||
|
of ``types.GenericAlias``. Tests for typing were also updated to not subclass
|
||||||
|
things like ``Callable[..., T]`` as that is not a valid base class. Finally,
|
||||||
|
both ``Callable``s no longer validate their ``argtypes``, in
|
||||||
|
``Callable[[argtypes], resulttype]`` to prepare for :pep:`612`. Patch by Ken Jin.
|
||||||
|
|
|
@ -429,8 +429,8 @@ ga_getattro(PyObject *self, PyObject *name)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
ga_richcompare(PyObject *a, PyObject *b, int op)
|
ga_richcompare(PyObject *a, PyObject *b, int op)
|
||||||
{
|
{
|
||||||
if (!Py_IS_TYPE(a, &Py_GenericAliasType) ||
|
if (!PyObject_TypeCheck(a, &Py_GenericAliasType) ||
|
||||||
!Py_IS_TYPE(b, &Py_GenericAliasType) ||
|
!PyObject_TypeCheck(b, &Py_GenericAliasType) ||
|
||||||
(op != Py_EQ && op != Py_NE))
|
(op != Py_EQ && op != Py_NE))
|
||||||
{
|
{
|
||||||
Py_RETURN_NOTIMPLEMENTED;
|
Py_RETURN_NOTIMPLEMENTED;
|
||||||
|
@ -564,6 +564,29 @@ static PyGetSetDef ga_properties[] = {
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* A helper function to create GenericAlias' args tuple and set its attributes.
|
||||||
|
* Returns 1 on success, 0 on failure.
|
||||||
|
*/
|
||||||
|
static inline int
|
||||||
|
setup_ga(gaobject *alias, PyObject *origin, PyObject *args) {
|
||||||
|
if (!PyTuple_Check(args)) {
|
||||||
|
args = PyTuple_Pack(1, args);
|
||||||
|
if (args == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(origin);
|
||||||
|
alias->origin = origin;
|
||||||
|
alias->args = args;
|
||||||
|
alias->parameters = NULL;
|
||||||
|
alias->weakreflist = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
|
@ -575,7 +598,15 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
}
|
}
|
||||||
PyObject *origin = PyTuple_GET_ITEM(args, 0);
|
PyObject *origin = PyTuple_GET_ITEM(args, 0);
|
||||||
PyObject *arguments = PyTuple_GET_ITEM(args, 1);
|
PyObject *arguments = PyTuple_GET_ITEM(args, 1);
|
||||||
return Py_GenericAlias(origin, arguments);
|
gaobject *self = (gaobject *)type->tp_alloc(type, 0);
|
||||||
|
if (self == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!setup_ga(self, origin, arguments)) {
|
||||||
|
type->tp_free((PyObject *)self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (PyObject *)self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyNumberMethods ga_as_number = {
|
static PyNumberMethods ga_as_number = {
|
||||||
|
@ -600,7 +631,7 @@ PyTypeObject Py_GenericAliasType = {
|
||||||
.tp_hash = ga_hash,
|
.tp_hash = ga_hash,
|
||||||
.tp_call = ga_call,
|
.tp_call = ga_call,
|
||||||
.tp_getattro = ga_getattro,
|
.tp_getattro = ga_getattro,
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
|
||||||
.tp_traverse = ga_traverse,
|
.tp_traverse = ga_traverse,
|
||||||
.tp_richcompare = ga_richcompare,
|
.tp_richcompare = ga_richcompare,
|
||||||
.tp_weaklistoffset = offsetof(gaobject, weakreflist),
|
.tp_weaklistoffset = offsetof(gaobject, weakreflist),
|
||||||
|
@ -615,27 +646,14 @@ PyTypeObject Py_GenericAliasType = {
|
||||||
PyObject *
|
PyObject *
|
||||||
Py_GenericAlias(PyObject *origin, PyObject *args)
|
Py_GenericAlias(PyObject *origin, PyObject *args)
|
||||||
{
|
{
|
||||||
if (!PyTuple_Check(args)) {
|
|
||||||
args = PyTuple_Pack(1, args);
|
|
||||||
if (args == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
gaobject *alias = PyObject_GC_New(gaobject, &Py_GenericAliasType);
|
gaobject *alias = PyObject_GC_New(gaobject, &Py_GenericAliasType);
|
||||||
if (alias == NULL) {
|
if (alias == NULL) {
|
||||||
Py_DECREF(args);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (!setup_ga(alias, origin, args)) {
|
||||||
Py_INCREF(origin);
|
PyObject_GC_Del((PyObject *)alias);
|
||||||
alias->origin = origin;
|
return NULL;
|
||||||
alias->args = args;
|
}
|
||||||
alias->parameters = NULL;
|
|
||||||
alias->weakreflist = NULL;
|
|
||||||
_PyObject_GC_TRACK(alias);
|
_PyObject_GC_TRACK(alias);
|
||||||
return (PyObject *)alias;
|
return (PyObject *)alias;
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,8 +237,8 @@ dedup_and_flatten_args(PyObject* args)
|
||||||
PyObject* i_element = PyTuple_GET_ITEM(args, i);
|
PyObject* i_element = PyTuple_GET_ITEM(args, i);
|
||||||
for (Py_ssize_t j = i + 1; j < arg_length; j++) {
|
for (Py_ssize_t j = i + 1; j < arg_length; j++) {
|
||||||
PyObject* j_element = PyTuple_GET_ITEM(args, j);
|
PyObject* j_element = PyTuple_GET_ITEM(args, j);
|
||||||
int is_ga = Py_TYPE(i_element) == &Py_GenericAliasType &&
|
int is_ga = PyObject_TypeCheck(i_element, &Py_GenericAliasType) &&
|
||||||
Py_TYPE(j_element) == &Py_GenericAliasType;
|
PyObject_TypeCheck(j_element, &Py_GenericAliasType);
|
||||||
// RichCompare to also deduplicate GenericAlias types (slower)
|
// RichCompare to also deduplicate GenericAlias types (slower)
|
||||||
is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ)
|
is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ)
|
||||||
: i_element == j_element;
|
: i_element == j_element;
|
||||||
|
@ -296,7 +296,7 @@ is_unionable(PyObject *obj)
|
||||||
is_new_type(obj) ||
|
is_new_type(obj) ||
|
||||||
is_special_form(obj) ||
|
is_special_form(obj) ||
|
||||||
PyType_Check(obj) ||
|
PyType_Check(obj) ||
|
||||||
type == &Py_GenericAliasType ||
|
PyObject_TypeCheck(obj, &Py_GenericAliasType) ||
|
||||||
type == &_Py_UnionType);
|
type == &_Py_UnionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue