Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersExceptions.c
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
9// This file is included from another C file, help IDEs to still parse it on
10// its own.
11#ifdef __IDE_ONLY__
12#include "nuitka/prelude.h"
13#endif
14
15void SET_CURRENT_EXCEPTION_TYPE0_FORMAT1(PyObject *exception_type, char const *format, char const *value) {
16 PyErr_Format(exception_type, format, value);
17}
18
19void SET_CURRENT_EXCEPTION_TYPE0_FORMAT2(PyObject *exception_type, char const *format, char const *value1,
20 char const *value2) {
21 PyErr_Format(exception_type, format, value1, value2);
22}
23
24void SET_CURRENT_EXCEPTION_TYPE0_FORMAT3(PyObject *exception_type, char const *format, char const *value1,
25 char const *value2, char const *value3) {
26 PyErr_Format(exception_type, format, value1, value2, value3);
27}
28
29void SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(char const *format, PyObject *mistyped) {
30 SET_CURRENT_EXCEPTION_TYPE0_FORMAT1(PyExc_TypeError, format, Py_TYPE(mistyped)->tp_name);
31}
32
33static char const *TYPE_NAME_DESC(PyObject *type) {
34 if (type == Py_None) {
35 return "None";
36 } else {
37 return Py_TYPE(type)->tp_name;
38 }
39}
40
41void SET_CURRENT_EXCEPTION_TYPE_COMPLAINT_NICE(char const *format, PyObject *mistyped) {
42 SET_CURRENT_EXCEPTION_TYPE0_FORMAT1(PyExc_TypeError, format, TYPE_NAME_DESC(mistyped));
43}
44
45void FORMAT_UNBOUND_LOCAL_ERROR(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state,
46 PyObject *variable_name) {
47#if PYTHON_VERSION < 0x300
48 char const *message = "local variable '%s' referenced before assignment";
49 PyObject *exception_value = Nuitka_String_FromFormat(message, Nuitka_String_AsString_Unchecked(variable_name));
50#elif PYTHON_VERSION < 0x3b0
51 char const *message = "local variable '%U' referenced before assignment";
52 PyObject *exception_value = Nuitka_String_FromFormat(message, variable_name);
53#else
54 char const *message = "cannot access local variable '%U' where it is not associated with a value";
55 PyObject *exception_value = Nuitka_String_FromFormat(message, variable_name);
56#endif
57
58 CHECK_OBJECT(exception_value);
59 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, PyExc_UnboundLocalError,
60 exception_value);
61}
62
63void FORMAT_UNBOUND_CLOSURE_ERROR(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state,
64 PyObject *variable_name) {
65#if PYTHON_VERSION < 0x3b0
66 char const *message = "free variable '%s' referenced before assignment in enclosing scope";
67#else
68 char const *message = "cannot access free variable '%s' where it is not associated with a value in enclosing scope";
69#endif
70
71 PyObject *exception_value = Nuitka_String_FromFormat(message, Nuitka_String_AsString_Unchecked(variable_name));
72 CHECK_OBJECT(exception_value);
73
74 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, PyExc_NameError, exception_value);
75}
76
77static PyObject *_Nuitka_Err_CreateException(PyThreadState *tstate, PyObject *exception_type, PyObject *value) {
78 PyObject *exc;
79
80 if (value == NULL || value == Py_None) {
81 exc = CALL_FUNCTION_NO_ARGS(tstate, exception_type);
82 } else if (PyTuple_Check(value)) {
83 exc = CALL_FUNCTION_WITH_POS_ARGS(tstate, exception_type, value);
84 } else {
85 exc = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, exception_type, value);
86 }
87
88 if (exc != NULL && !PyExceptionInstance_Check(exc)) {
89 PyErr_Format(PyExc_TypeError,
90 "calling %s should have returned an instance of "
91 "BaseException, not %s",
92 GET_CALLABLE_NAME(exception_type), Py_TYPE(exc)->tp_name);
93 Py_DECREF(exc);
94
95 return NULL;
96 }
97
98 return exc;
99}
100
101PyObject *MAKE_EXCEPTION_WITH_VALUE(PyThreadState *tstate, PyObject *exception_type, PyObject *value) {
102 return _Nuitka_Err_CreateException(tstate, exception_type, value);
103}
104
105// Our replacement for PyErr_NormalizeException, that however does not attempt
106// to deal with recursion, i.e. exception during normalization, we just avoid
107// the API call overhead in the normal case.
108
109#if PYTHON_VERSION >= 0x3d0
110// TODO: Merge with old branch for enhancements.
111void Nuitka_Err_NormalizeException(PyThreadState *tstate, PyObject **exc, PyObject **val, PyTracebackObject **tb) {
112 int recursion_depth = 0;
113 tstate->recursion_headroom++;
114
115 PyObject *type, *value;
116 PyTracebackObject *initial_tb;
117
118restart:
119 type = *exc;
120
121 if (type == NULL) {
122 tstate->recursion_headroom--;
123 return;
124 }
125
126 value = *val;
127
128 if (!value) {
129 Py_INCREF_IMMORTAL(Py_None);
130 value = Py_None;
131 }
132
133 if (PyExceptionClass_Check(type)) {
134 PyObject *instance_class = NULL;
135
136 int is_subclass = 0;
137
138 if (PyExceptionInstance_Check(value)) {
139 instance_class = PyExceptionInstance_Class(value);
140
141 is_subclass = PyObject_IsSubclass(instance_class, type);
142 if (is_subclass < 0) {
143 goto error;
144 }
145 }
146
147 if (!is_subclass) {
148 PyObject *fixed_value = _Nuitka_Err_CreateException(tstate, type, value);
149
150 if (fixed_value == NULL) {
151 goto error;
152 }
153
154 Py_SETREF(value, fixed_value);
155 } else if (instance_class != type) {
156 Py_SETREF(type, Py_NewRef(instance_class));
157 }
158 }
159 *exc = type;
160 *val = value;
161 tstate->recursion_headroom--;
162 return;
163
164error:
165 Py_DECREF(type);
166 Py_DECREF(value);
167 recursion_depth++;
168 if (recursion_depth == 32) {
169 _PyErr_SetString(tstate, PyExc_RecursionError,
170 "maximum recursion depth exceeded "
171 "while normalizing an exception");
172 }
173
174 initial_tb = *tb;
175
176 struct Nuitka_ExceptionPreservationItem exception_state;
177 FETCH_ERROR_OCCURRED_STATE(tstate, &exception_state);
178
179 ASSIGN_ARGS_FROM_EXCEPTION_PRESERVATION_STATE(&exception_state, exc, val, tb);
180 RELEASE_ERROR_OCCURRED_STATE(&exception_state);
181
182 assert(*exc != NULL);
183 if (initial_tb != NULL) {
184 if (*tb == NULL)
185 *tb = initial_tb;
186 else
187 Py_DECREF(initial_tb);
188 }
189 if (recursion_depth >= 32 + 2) {
190 if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) {
191 Py_FatalError("Cannot recover from MemoryErrors "
192 "while normalizing exceptions.");
193 } else {
194 Py_FatalError("Cannot recover from the recursive normalization "
195 "of an exception.");
196 }
197 }
198 goto restart;
199}
200#else
201void Nuitka_Err_NormalizeException(PyThreadState *tstate, PyObject **exc, PyObject **val, PyTracebackObject **tb) {
202 PyObject *type = *exc;
203
204 // Dealt with in NORMALIZE_EXCEPTION
205 assert(type != NULL && type != Py_None);
206
207 PyObject *value = *val;
208
209 // Allow setting the value to NULL for time savings with quick type only errors
210 if (value == NULL) {
211 value = Py_None;
212 Py_INCREF_IMMORTAL(value);
213 }
214
215 // Normalize the exception from class to instance
216 if (PyExceptionClass_Check(type)) {
217 PyObject *instance_class = NULL;
218
219 int is_subclass = 0;
220
221 if (PyExceptionInstance_Check(value)) {
222 instance_class = PyExceptionInstance_Class(value);
223
224 is_subclass = PyObject_IsSubclass(instance_class, type);
225
226 if (is_subclass < 0) {
227 goto error;
228 }
229 }
230
231 // If the value was not an instance, or is not an instance of derived
232 // type, then call it
233 if (!is_subclass) {
234 PyObject *fixed_value = _Nuitka_Err_CreateException(tstate, type, value);
235
236 if (unlikely(fixed_value == NULL)) {
237 goto error;
238 }
239
240 Py_DECREF(value);
241 value = fixed_value;
242 } else if (instance_class != type) {
243 // Switch to given type then
244 Py_INCREF(instance_class);
245 Py_DECREF(type);
246
247 type = instance_class;
248 }
249 }
250
251 *exc = type;
252 *val = value;
253
254 return;
255
256error:
257
258 Py_DECREF(type);
259 Py_DECREF(value);
260 PyTracebackObject *initial_tb = *tb;
261
262 struct Nuitka_ExceptionPreservationItem exception_state;
263 FETCH_ERROR_OCCURRED_STATE(tstate, &exception_state);
264
265 ASSIGN_ARGS_FROM_EXCEPTION_PRESERVATION_STATE(&exception_state, exc, val, tb);
266 RELEASE_ERROR_OCCURRED_STATE(&exception_state);
267
268 if (initial_tb != NULL) {
269 if (*tb == NULL) {
270 *tb = initial_tb;
271 } else {
272 Py_DECREF(initial_tb);
273 }
274 }
275
276#if PYTHON_VERSION >= 0x380
277 _PyErr_NormalizeException(tstate, exc, val, (PyObject **)tb);
278#else
279 PyErr_NormalizeException(exc, val, (PyObject **)tb);
280#endif
281}
282
283#endif
284
285// Part of "Nuitka", an optimizing Python compiler that is compatible and
286// integrates with CPython, but also works on its own.
287//
288// Licensed under the Apache License, Version 2.0 (the "License");
289// you may not use this file except in compliance with the License.
290// You may obtain a copy of the License at
291//
292// http://www.apache.org/licenses/LICENSE-2.0
293//
294// Unless required by applicable law or agreed to in writing, software
295// distributed under the License is distributed on an "AS IS" BASIS,
296// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
297// See the License for the specific language governing permissions and
298// limitations under the License.
Definition exceptions.h:712