Nuitka
The Python compiler
Loading...
Searching...
No Matches
exception_groups.h
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_EXCEPTION_GROUPS_H__
4#define __NUITKA_EXCEPTION_GROUPS_H__
5
6// Exception group helpers for generated code and compiled code helpers.
7
8#if PYTHON_VERSION >= 0x3b0
9
10NUITKA_MAY_BE_UNUSED static void FORMAT_CLASS_CATCH_ERROR(PyThreadState *tstate) {
11 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError,
12 "catching classes that do not inherit from BaseException is not allowed");
13}
14
15NUITKA_MAY_BE_UNUSED static int CHECK_EXCEPTION_TYPE_VALID(PyThreadState *tstate, PyObject *right) {
16 if (PyTuple_Check(right)) {
17 Py_ssize_t length = PyTuple_GET_SIZE(right);
18
19 for (Py_ssize_t i = 0; i < length; i++) {
20 PyObject *exc = PyTuple_GET_ITEM(right, i);
21
22 if (!PyExceptionClass_Check(exc)) {
23 FORMAT_CLASS_CATCH_ERROR(tstate);
24 return -1;
25 }
26 }
27 } else {
28 if (!PyExceptionClass_Check(right)) {
29 FORMAT_CLASS_CATCH_ERROR(tstate);
30 return -1;
31 }
32 }
33 return 0;
34}
35
36NUITKA_MAY_BE_UNUSED static int CHECK_EXCEPTION_STAR_VALID(PyThreadState *tstate, PyObject *right) {
37 if (CHECK_EXCEPTION_TYPE_VALID(tstate, right) < 0) {
38 return -1;
39 }
40
41 // TODO: Wants to reject except *ExceptionGroup, but we would be able to
42 // statically tell that often, and then this wouldn't have to be done,
43 // but it might be code.
44 int is_subclass = 0;
45
46 if (PyTuple_Check(right)) {
47 Py_ssize_t length = PyTuple_GET_SIZE(right);
48
49 for (Py_ssize_t i = 0; i < length; i++) {
50 PyObject *exc = PyTuple_GET_ITEM(right, i);
51 is_subclass = PyObject_IsSubclass(exc, PyExc_BaseExceptionGroup);
52
53 if (is_subclass < 0) {
54 return -1;
55 }
56
57 if (is_subclass) {
58 break;
59 }
60 }
61 } else {
62 is_subclass = PyObject_IsSubclass(right, PyExc_BaseExceptionGroup);
63
64 if (is_subclass < 0) {
65 return -1;
66 }
67 }
68
69 if (is_subclass) {
70 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError,
71 "catching ExceptionGroup with except* is not allowed. Use except instead.");
72 return -1;
73 }
74
75 return 0;
76}
77
78NUITKA_MAY_BE_UNUSED static int EXCEPTION_GROUP_MATCH(PyThreadState *tstate, PyObject *exc_value, PyObject *match_type,
79 PyObject **match, PyObject **rest) {
80 // TODO: Avoid this from happening, we should not call it then.
81 if (exc_value == Py_None) {
82 *match = Py_None;
83 Py_INCREF_IMMORTAL(*match);
84 *rest = Py_None;
85 Py_INCREF_IMMORTAL(*rest);
86
87 return 0;
88 }
89
90 // If not none, must be an instance at this point.
91 assert(PyExceptionInstance_Check(exc_value));
92
93 if (PyErr_GivenExceptionMatches(exc_value, match_type)) {
94 bool is_exception_group = _PyBaseExceptionGroup_Check(exc_value);
95
96 if (is_exception_group) {
97 *match = exc_value;
98 Py_INCREF(*match);
99 } else {
100 // Old style plain exception, put it into an exception group.
101 PyObject *exception_tuple = MAKE_TUPLE1_0(tstate, exc_value);
102 PyObject *wrapped = _PyExc_CreateExceptionGroup("", exception_tuple);
103 Py_DECREF(exception_tuple);
104
105 if (unlikely(wrapped == NULL)) {
106 return -1;
107 }
108
109 *match = wrapped;
110 }
111
112 *rest = Py_None;
113 Py_INCREF_IMMORTAL(*rest);
114
115 return 0;
116 }
117
118 // exc_value does not match match_type completely, need to check for partial
119 // match if it's an exception group.
120 if (_PyBaseExceptionGroup_Check(exc_value)) {
121 PyObject *pair = CALL_METHOD_WITH_SINGLE_ARG(tstate, exc_value, const_str_plain_split, match_type);
122
123 if (pair == NULL) {
124 return -1;
125 }
126
127 // TODO: What is that split method going to be, can we then inline it. CPython
128 // asserts these, so maybe it's not free to do what it wants.
129 assert(PyTuple_CheckExact(pair));
130 assert(PyTuple_GET_SIZE(pair) == 2);
131
132 *match = PyTuple_GET_ITEM(pair, 0);
133 Py_INCREF(*match);
134
135 *rest = PyTuple_GET_ITEM(pair, 1);
136 Py_INCREF(*rest);
137
138 Py_DECREF(pair);
139 return 0;
140 }
141
142 *match = Py_None;
143 Py_INCREF_IMMORTAL(*match);
144
145 *rest = Py_None;
146 Py_INCREF_IMMORTAL(*rest);
147
148 return 0;
149}
150
151#endif
152
153#endif
154// Part of "Nuitka", an optimizing Python compiler that is compatible and
155// integrates with CPython, but also works on its own.
156//
157// Licensed under the Apache License, Version 2.0 (the "License");
158// you may not use this file except in compliance with the License.
159// You may obtain a copy of the License at
160//
161// http://www.apache.org/licenses/LICENSE-2.0
162//
163// Unless required by applicable law or agreed to in writing, software
164// distributed under the License is distributed on an "AS IS" BASIS,
165// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
166// See the License for the specific language governing permissions and
167// limitations under the License.