X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,31 +776,32 @@ def test_union_parameter_chaining(self):
self.assertEqual((list[T] | list[S])[int, int], list[int])

def test_union_parameter_substitution(self):
def eq(actual, expected):
def eq(actual, expected, typed=True):
self.assertEqual(actual, expected)
self.assertIs(type(actual), type(expected))
if typed:
self.assertIs(type(actual), type(expected))

T = typing.TypeVar('T')
S = typing.TypeVar('S')
NT = typing.NewType('NT', str)
x = int | T | bytes

eq(x[str], int | str | bytes)
eq(x[list[int]], int | list[int] | bytes)
eq(x[str], int | str | bytes, typed=False)
eq(x[list[int]], int | list[int] | bytes, typed=False)
eq(x[typing.List], int | typing.List | bytes)
eq(x[typing.List[int]], int | typing.List[int] | bytes)
eq(x[typing.Hashable], int | typing.Hashable | bytes)
eq(x[collections.abc.Hashable],
int | collections.abc.Hashable | bytes)
int | collections.abc.Hashable | bytes, typed=False)
eq(x[typing.Callable[[int], str]],
int | typing.Callable[[int], str] | bytes)
eq(x[collections.abc.Callable[[int], str]],
int | collections.abc.Callable[[int], str] | bytes)
int | collections.abc.Callable[[int], str] | bytes, typed=False)
eq(x[typing.Tuple[int, str]], int | typing.Tuple[int, str] | bytes)
eq(x[typing.Literal['none']], int | typing.Literal['none'] | bytes)
eq(x[str | list], int | str | list | bytes)
eq(x[str | list], int | str | list | bytes, typed=False)
eq(x[typing.Union[str, list]], typing.Union[int, str, list, bytes])
eq(x[str | int], int | str | bytes)
eq(x[str | int], int | str | bytes, typed=False)
eq(x[typing.Union[str, int]], typing.Union[int, str, bytes])
eq(x[NT], int | NT | bytes)
eq(x[S], int | S | bytes)
Expand Down Expand Up @@ -829,9 +830,9 @@ def test_union_from_args(self):
with self.assertRaisesRegex(ValueError, r"args must be not empty"):
types.Union._from_args(())

alias = types.Union._from_args((int, str, T))
alias = types.Union._from_args((int, list[T], None))

self.assertEqual(alias.__args__, (int, str, T))
self.assertEqual(alias.__args__, (int, list[T], type(None)))
self.assertEqual(alias.__parameters__, (T,))

result = types.Union._from_args((int,))
Expand Down Expand Up @@ -894,7 +895,6 @@ def test_or_type_repr(self):
assert repr(int | None) == "int | None"
assert repr(int | type(None)) == "int | None"
assert repr(int | typing.GenericAlias(list, int)) == "int | list[int]"
assert repr(int | typing.TypeVar('T')) == "int | ~T"

def test_or_type_operator_with_genericalias(self):
a = list[int]
Expand Down Expand Up @@ -939,9 +939,9 @@ def __module__(self):
TypeVar = BadMeta('TypeVar', (), {})
_SpecialForm = BadMeta('_SpecialForm', (), {})
# Crashes in Issue44483
with self.assertRaises(ZeroDivisionError):
with self.assertRaises((TypeError, ZeroDivisionError)):
str | TypeVar()
with self.assertRaises(ZeroDivisionError):
with self.assertRaises((TypeError, ZeroDivisionError)):
str | _SpecialForm()

@cpython_only
Expand Down
6 changes: 6 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ def __reduce__(self):
def __call__(self, *args, **kwds):
raise TypeError(f"Cannot instantiate {self!r}")

def __or__(self, other):
return Union[self, other]

def __ror__(self, other):
return Union[other, self]

def __instancecheck__(self, obj):
raise TypeError(f"{self} cannot be used with isinstance()")

Expand Down
88 changes: 7 additions & 81 deletions Objects/unionobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,31 +115,6 @@ union_subclasscheck(PyObject *self, PyObject *instance)
Py_RETURN_FALSE;
}

static int
is_typing_module(PyObject *obj)
{
_Py_IDENTIFIER(__module__);
PyObject *module;
if (_PyObject_LookupAttrId(obj, &PyId___module__, &module) < 0) {
return -1;
}
int is_typing = (module != NULL &&
PyUnicode_Check(module) &&
_PyUnicode_EqualToASCIIString(module, "typing"));
Py_XDECREF(module);
return is_typing;
}

static int
is_typing_name(PyObject *obj, const char *name)
{
PyTypeObject *type = Py_TYPE(obj);
if (strcmp(type->tp_name, name) != 0) {
return 0;
}
return is_typing_module((PyObject *)type);
}

static PyObject *
union_richcompare(PyObject *a, PyObject *b, int op)
{
Expand Down Expand Up @@ -251,52 +226,13 @@ dedup_and_flatten_args(PyObject* args)
return new_args;
}

static int
is_typevar(PyObject *obj)
{
return is_typing_name(obj, "TypeVar");
}

static int
is_special_form(PyObject *obj)
{
return is_typing_name(obj, "_SpecialForm");
}

static int
is_new_type(PyObject *obj)
{
PyTypeObject *type = Py_TYPE(obj);
if (type != &PyFunction_Type) {
return 0;
}
return is_typing_module(obj);
}

// Emulates short-circuiting behavior of the ``||`` operator
// while also checking negative values.
#define CHECK_RES(res) { \
int result = res; \
if (result) { \
return result; \
} \
}

// Returns 1 on true, 0 on false, and -1 on error.
static int
is_unionable(PyObject *obj)
{
if (obj == Py_None ||
return (obj == Py_None ||
PyType_Check(obj) ||
_PyGenericAlias_Check(obj) ||
_PyUnion_Check(obj))
{
return 1;
}
CHECK_RES(is_typevar(obj));
CHECK_RES(is_new_type(obj));
CHECK_RES(is_special_form(obj));
return 0;
_PyUnion_Check(obj));
}

static int
Expand All @@ -305,12 +241,9 @@ is_args_unionable(PyObject *args)
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *arg = PyTuple_GET_ITEM(args, iarg);
int is_arg_unionable = is_unionable(arg);
if (is_arg_unionable <= 0) {
if (is_arg_unionable == 0) {
PyErr_Format(PyExc_TypeError,
"Each union argument must be a type, got %.100R", arg);
}
if (!is_unionable(arg)) {
PyErr_Format(PyExc_TypeError,
"Each union argument must be a type, got %.100R", arg);
return 0;
}
}
Expand All @@ -320,14 +253,7 @@ is_args_unionable(PyObject *args)
PyObject *
_Py_union_type_or(PyObject* self, PyObject* other)
{
int r = is_unionable(self);
if (r > 0) {
r = is_unionable(other);
}
if (r < 0) {
return NULL;
}
if (!r) {
if (!is_unionable(self) || !is_unionable(other)) {
Py_RETURN_NOTIMPLEMENTED;
}

Expand Down Expand Up @@ -465,7 +391,7 @@ union_from_args(PyObject *cls, PyObject *args)
return NULL;
}

if (is_args_unionable(args) <= 0) {
if (!is_args_unionable(args)) {
return NULL;
}

Expand Down
X Tutup