diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 1bdab125dc6ef0..ece0f2847545fa 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -3,6 +3,7 @@ import dataclasses import enum import inspect +from re import I import sys import unittest @@ -2886,6 +2887,14 @@ class B(A): ... h = 1 self.assertEqual(h, 1) + def test_patma_union_type(self): + IntOrStr = int | str + x = 0 + match x: + case IntOrStr(): + x = 1 + self.assertEqual(x, 1) + class TestSyntaxErrors(unittest.TestCase): @@ -3361,6 +3370,31 @@ class A: w = 0 self.assertIsNone(w) + def test_union_type_postional_subpattern(self): + IntOrStr = int | str + x = 1 + w = None + with self.assertRaises(TypeError): + match x: + case IntOrStr(x): + w = 0 + self.assertEqual(x, 1) + self.assertIsNone(w) + + def test_union_type_keyword_subpattern(self): + @dataclasses.dataclass + class Point2: + x: int + y: int + EitherPoint = Point | Point2 + x = Point(x=1, y=2) + w = None + with self.assertRaises(TypeError): + match x: + case EitherPoint(x=1, y=2): + w = 0 + self.assertIsNone(w) + class TestValueErrors(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-16-12.gh-issue-118524.B4rIYi.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-16-12.gh-issue-118524.B4rIYi.rst new file mode 100644 index 00000000000000..0986d8c3120440 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-02-20-16-12.gh-issue-118524.B4rIYi.rst @@ -0,0 +1 @@ +Since Python 3.10, it was possible to use unions as the second argument to ``isinstance``. Now, unions can also be used as match patterns. However, no sub-patterns can be used for unions; only the basic ``isinstance`` function is available. diff --git a/Python/ceval.c b/Python/ceval.c index 59498bc826e941..a33a3798f0ca80 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -29,6 +29,7 @@ #include "pycore_sysmodule.h" // _PySys_Audit() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "pycore_typeobject.h" // _PySuper_Lookup() +#include "pycore_unionobject.h" // _PyUnion_Check() #include "pycore_uop_ids.h" // Uops #include "pycore_pyerrors.h" @@ -460,8 +461,8 @@ PyObject* _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs) { - if (!PyType_Check(type)) { - const char *e = "called match pattern must be a class"; + if (!PyType_Check(type) && !_PyUnion_Check(type)) { + const char *e = "called match pattern must be a class or a union"; _PyErr_Format(tstate, PyExc_TypeError, e); return NULL; } @@ -470,6 +471,16 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, if (PyObject_IsInstance(subject, type) <= 0) { return NULL; } + // Subpatterns are not supported for union types: + if (_PyUnion_Check(type)) { + // Return error if any positional or keyword arguments are given: + if (nargs || PyTuple_GET_SIZE(kwargs)) { + const char *e = "union types do not support sub-patterns"; + _PyErr_Format(tstate, PyExc_TypeError, e); + return NULL; + } + return PyTuple_New(0); + } // So far so good: PyObject *seen = PySet_New(NULL); if (seen == NULL) {