Nuitka
The Python compiler
Loading...
Searching...
No Matches
InspectPatcher.c
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
8/* This file is included from another C file, help IDEs to still parse it on its own. */
9#ifdef __IDE_ONLY__
10#include "nuitka/prelude.h"
11#endif
12
13#if PYTHON_VERSION >= 0x300
14static PyObject *module_inspect;
15#if PYTHON_VERSION >= 0x350
16static PyObject *module_types;
17#endif
18
19static char *kw_list_object[] = {(char *)"object", NULL};
20
21// spell-checker: ignore getgeneratorstate, getcoroutinestate
22
23static PyObject *old_getgeneratorstate = NULL;
24
25static PyObject *_inspect_getgeneratorstate_replacement(PyObject *self, PyObject *args, PyObject *kwds) {
26 PyObject *object;
27
28 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:getgeneratorstate", kw_list_object, &object, NULL)) {
29 return NULL;
30 }
31
32 CHECK_OBJECT(object);
33
34 if (Nuitka_Generator_Check(object)) {
35 struct Nuitka_GeneratorObject *generator = (struct Nuitka_GeneratorObject *)object;
36
37 if (generator->m_running) {
38 return PyObject_GetAttrString(module_inspect, "GEN_RUNNING");
39 } else if (generator->m_status == status_Finished) {
40 return PyObject_GetAttrString(module_inspect, "GEN_CLOSED");
41 } else if (generator->m_status == status_Unused) {
42 return PyObject_GetAttrString(module_inspect, "GEN_CREATED");
43 } else {
44 return PyObject_GetAttrString(module_inspect, "GEN_SUSPENDED");
45 }
46 } else {
47 return old_getgeneratorstate->ob_type->tp_call(old_getgeneratorstate, args, kwds);
48 }
49}
50
51#if PYTHON_VERSION >= 0x350
52static PyObject *old_getcoroutinestate = NULL;
53
54static PyObject *_inspect_getcoroutinestate_replacement(PyObject *self, PyObject *args, PyObject *kwds) {
55 PyObject *object;
56
57 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:getcoroutinestate", kw_list_object, &object, NULL)) {
58 return NULL;
59 }
60
61 if (Nuitka_Coroutine_Check(object)) {
62 struct Nuitka_CoroutineObject *coroutine = (struct Nuitka_CoroutineObject *)object;
63
64 if (coroutine->m_running) {
65 return PyObject_GetAttrString(module_inspect, "CORO_RUNNING");
66 } else if (coroutine->m_status == status_Finished) {
67 return PyObject_GetAttrString(module_inspect, "CORO_CLOSED");
68 } else if (coroutine->m_status == status_Unused) {
69 return PyObject_GetAttrString(module_inspect, "CORO_CREATED");
70 } else {
71 return PyObject_GetAttrString(module_inspect, "CORO_SUSPENDED");
72 }
73 } else {
74 return old_getcoroutinestate->ob_type->tp_call(old_getcoroutinestate, args, kwds);
75 }
76}
77
78static PyObject *old_types_coroutine = NULL;
79
80static char *kw_list_coroutine[] = {(char *)"func", NULL};
81
82static PyObject *_types_coroutine_replacement(PyObject *self, PyObject *args, PyObject *kwds) {
83 PyObject *func;
84
85 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:coroutine", kw_list_coroutine, &func, NULL)) {
86 return NULL;
87 }
88
89 if (Nuitka_Function_Check(func)) {
90 struct Nuitka_FunctionObject *function = (struct Nuitka_FunctionObject *)func;
91
92 if (function->m_code_object->co_flags & CO_GENERATOR) {
93 function->m_code_object->co_flags |= 0x100;
94 }
95 }
96
97 return old_types_coroutine->ob_type->tp_call(old_types_coroutine, args, kwds);
98}
99
100#endif
101
102#endif
103
104#if PYTHON_VERSION >= 0x300
105static PyMethodDef _method_def_inspect_getgeneratorstate_replacement = {
106 "getgeneratorstate", (PyCFunction)_inspect_getgeneratorstate_replacement, METH_VARARGS | METH_KEYWORDS, NULL};
107
108#if PYTHON_VERSION >= 0x350
109static PyMethodDef _method_def_inspect_getcoroutinestate_replacement = {
110 "getcoroutinestate", (PyCFunction)_inspect_getcoroutinestate_replacement, METH_VARARGS | METH_KEYWORDS, NULL};
111
112static PyMethodDef _method_def_types_coroutine_replacement = {"coroutine", (PyCFunction)_types_coroutine_replacement,
113 METH_VARARGS | METH_KEYWORDS, NULL};
114
115#endif
116
117#if PYTHON_VERSION >= 0x3c0
118
119static char *kw_list_depth[] = {(char *)"depth", NULL};
120
121static bool Nuitka_FrameIsCompiled(_PyInterpreterFrame *frame) {
122 return ((frame->frame_obj != NULL) && Nuitka_Frame_Check((PyObject *)frame->frame_obj));
123}
124
125static bool Nuitka_FrameIsIncomplete(_PyInterpreterFrame *frame) {
126 bool r = _PyFrame_IsIncomplete(frame);
127
128 return r;
129}
130
131static PyObject *orig_sys_getframemodulename = NULL;
132
133static PyObject *_sys_getframemodulename_replacement(PyObject *self, PyObject *args, PyObject *kwds) {
134 PyObject *depth_arg = NULL;
135
136 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:_getframemodulename", kw_list_depth, &depth_arg)) {
137 return NULL;
138 }
139
140 PyObject *index_value = Nuitka_Number_IndexAsLong(depth_arg ? depth_arg : const_int_0);
141
142 if (unlikely(index_value == NULL)) {
143 return NULL;
144 }
145
146 Py_ssize_t depth_ssize = PyLong_AsSsize_t(index_value);
147
148 Py_DECREF(index_value);
149
150 PyThreadState *tstate = _PyThreadState_GET();
151
152 _PyInterpreterFrame *frame = CURRENT_TSTATE_INTERPRETER_FRAME(tstate);
153 while ((frame != NULL) && ((Nuitka_FrameIsIncomplete(frame)) || depth_ssize-- > 0)) {
154 frame = frame->previous;
155 }
156
157 if ((frame != NULL) && (Nuitka_FrameIsCompiled(frame))) {
158 PyObject *frame_globals = PyObject_GetAttrString((PyObject *)frame->frame_obj, "f_globals");
159
160 PyObject *result = LOOKUP_ATTRIBUTE(tstate, frame_globals, const_str_plain___name__);
161 Py_DECREF(frame_globals);
162
163 return result;
164 }
165
166 return CALL_FUNCTION_WITH_SINGLE_ARG(tstate, orig_sys_getframemodulename, depth_arg);
167}
168
169// spell-checker: ignore getframemodulename
170static PyMethodDef _method_def_sys_getframemodulename_replacement = {
171 "getcoroutinestate", (PyCFunction)_sys_getframemodulename_replacement, METH_VARARGS | METH_KEYWORDS, NULL};
172
173#endif
174
175/* Replace inspect functions with ones that handle compiles types too. */
176void patchInspectModule(PyThreadState *tstate) {
177 static bool is_done = false;
178 if (is_done) {
179 return;
180 }
181
182 CHECK_OBJECT(dict_builtin);
183
184#if PYTHON_VERSION >= 0x300
185#if _NUITKA_EXE_MODE && !_NUITKA_STANDALONE_MODE
186 // May need to import the "site" module, because otherwise the patching can
187 // fail with it being unable to load it (yet)
188 if (Py_NoSiteFlag == 0) {
189 PyObject *site_module =
190 IMPORT_MODULE5(tstate, const_str_plain_site, Py_None, Py_None, const_tuple_empty, const_int_0);
191
192 if (site_module == NULL) {
193 // Ignore "ImportError", having a "site" module is not a must.
194 CLEAR_ERROR_OCCURRED(tstate);
195 }
196 }
197#endif
198
199 // TODO: Change this into an import hook that is executed after it is imported.
200 module_inspect = IMPORT_MODULE5(tstate, const_str_plain_inspect, Py_None, Py_None, const_tuple_empty, const_int_0);
201
202 if (module_inspect == NULL) {
203 PyErr_PrintEx(0);
204 Py_Exit(1);
205 }
206 CHECK_OBJECT(module_inspect);
207
208 // Patch "inspect.getgeneratorstate" unless it is already patched.
209 old_getgeneratorstate = PyObject_GetAttrString(module_inspect, "getgeneratorstate");
210 CHECK_OBJECT(old_getgeneratorstate);
211
212 PyObject *inspect_getgeneratorstate_replacement =
213 PyCFunction_New(&_method_def_inspect_getgeneratorstate_replacement, NULL);
214 CHECK_OBJECT(inspect_getgeneratorstate_replacement);
215
216 PyObject_SetAttrString(module_inspect, "getgeneratorstate", inspect_getgeneratorstate_replacement);
217
218#if PYTHON_VERSION >= 0x350
219 // Patch "inspect.getcoroutinestate" unless it is already patched.
220 old_getcoroutinestate = PyObject_GetAttrString(module_inspect, "getcoroutinestate");
221 CHECK_OBJECT(old_getcoroutinestate);
222
223 if (PyFunction_Check(old_getcoroutinestate)) {
224 PyObject *inspect_getcoroutinestate_replacement =
225 PyCFunction_New(&_method_def_inspect_getcoroutinestate_replacement, NULL);
226 CHECK_OBJECT(inspect_getcoroutinestate_replacement);
227
228 PyObject_SetAttrString(module_inspect, "getcoroutinestate", inspect_getcoroutinestate_replacement);
229 }
230
231 module_types = IMPORT_MODULE5(tstate, const_str_plain_types, Py_None, Py_None, const_tuple_empty, const_int_0);
232
233 if (module_types == NULL) {
234 PyErr_PrintEx(0);
235 Py_Exit(1);
236 }
237 CHECK_OBJECT(module_types);
238
239 // Patch "types.coroutine" unless it is already patched.
240 old_types_coroutine = PyObject_GetAttrString(module_types, "coroutine");
241 CHECK_OBJECT(old_types_coroutine);
242
243 if (PyFunction_Check(old_types_coroutine)) {
244 PyObject *types_coroutine_replacement = PyCFunction_New(&_method_def_types_coroutine_replacement, NULL);
245 CHECK_OBJECT(types_coroutine_replacement);
246
247 PyObject_SetAttrString(module_types, "coroutine", types_coroutine_replacement);
248 }
249
250 static char const *wrapper_enhancement_code = "\n\
251import types\n\
252_old_GeneratorWrapper = types._GeneratorWrapper\n\
253class GeneratorWrapperEnhanced(_old_GeneratorWrapper):\n\
254 def __init__(self, gen):\n\
255 _old_GeneratorWrapper.__init__(self, gen)\n\
256\n\
257 if hasattr(gen, 'gi_code'):\n\
258 if gen.gi_code.co_flags & 0x0020:\n\
259 self._GeneratorWrapper__isgen = True\n\
260\n\
261types._GeneratorWrapper = GeneratorWrapperEnhanced\n"
262#if PYTHON_VERSION >= 0x3b0
263 "\
264import inspect\n\
265_old_get_code_position = inspect._get_code_position\n\
266def _get_code_position(code, instruction_index):\n\
267 try:\n\
268 return _old_get_code_position(code, instruction_index)\n\
269 except StopIteration:\n\
270 return None, None, None, None\n\
271inspect._get_code_position=_get_code_position\n\
272"
273#endif
274 ;
275
276 PyObject *wrapper_enhancement_code_object = Py_CompileString(wrapper_enhancement_code, "<exec>", Py_file_input);
277 CHECK_OBJECT(wrapper_enhancement_code_object);
278
279 {
280 NUITKA_MAY_BE_UNUSED PyObject *module =
281 PyImport_ExecCodeModule("nuitka_types_patch", wrapper_enhancement_code_object);
282 CHECK_OBJECT(module);
283
284 NUITKA_MAY_BE_UNUSED bool bool_res = Nuitka_DelModuleString(tstate, "nuitka_types_patch");
285 assert(bool_res != false);
286 }
287
288#endif
289
290#endif
291
292#if PYTHON_VERSION >= 0x3c0
293 orig_sys_getframemodulename = Nuitka_SysGetObject("_getframemodulename");
294
295 PyObject *sys_getframemodulename_replacement =
296 PyCFunction_New(&_method_def_sys_getframemodulename_replacement, NULL);
297 CHECK_OBJECT(sys_getframemodulename_replacement);
298
299 Nuitka_SysSetObject("_getframemodulename", sys_getframemodulename_replacement);
300#endif
301
302 is_done = true;
303}
304#endif
305
306static richcmpfunc original_PyType_tp_richcompare = NULL;
307
308static PyObject *Nuitka_type_tp_richcompare(PyObject *a, PyObject *b, int op) {
309 if (likely(op == Py_EQ || op == Py_NE)) {
310 if (a == (PyObject *)&Nuitka_Function_Type) {
311 a = (PyObject *)&PyFunction_Type;
312 } else if (a == (PyObject *)&Nuitka_Method_Type) {
313 a = (PyObject *)&PyMethod_Type;
314 } else if (a == (PyObject *)&Nuitka_Generator_Type) {
315 a = (PyObject *)&PyGen_Type;
316#if PYTHON_VERSION >= 0x350
317 } else if (a == (PyObject *)&Nuitka_Coroutine_Type) {
318 a = (PyObject *)&PyCoro_Type;
319#endif
320#if PYTHON_VERSION >= 0x360
321 } else if (a == (PyObject *)&Nuitka_Asyncgen_Type) {
322 a = (PyObject *)&PyAsyncGen_Type;
323#endif
324 }
325
326 if (b == (PyObject *)&Nuitka_Function_Type) {
327 b = (PyObject *)&PyFunction_Type;
328 } else if (b == (PyObject *)&Nuitka_Method_Type) {
329 b = (PyObject *)&PyMethod_Type;
330 } else if (b == (PyObject *)&Nuitka_Generator_Type) {
331 b = (PyObject *)&PyGen_Type;
332#if PYTHON_VERSION >= 0x350
333 } else if (b == (PyObject *)&Nuitka_Coroutine_Type) {
334 b = (PyObject *)&PyCoro_Type;
335#endif
336#if PYTHON_VERSION >= 0x360
337 } else if (b == (PyObject *)&Nuitka_Asyncgen_Type) {
338 b = (PyObject *)&PyAsyncGen_Type;
339#endif
340 }
341 }
342
343 CHECK_OBJECT(a);
344 CHECK_OBJECT(b);
345
346 assert(original_PyType_tp_richcompare);
347
348 return original_PyType_tp_richcompare(a, b, op);
349}
350
351void patchTypeComparison(void) {
352 if (original_PyType_tp_richcompare == NULL) {
353 original_PyType_tp_richcompare = PyType_Type.tp_richcompare;
354 PyType_Type.tp_richcompare = Nuitka_type_tp_richcompare;
355 }
356}
357
358#include "nuitka/freelists.h"
359
360// Freelist setup
361#define MAX_TRACEBACK_FREE_LIST_COUNT 1000
362static PyTracebackObject *free_list_tracebacks = NULL;
363static int free_list_tracebacks_count = 0;
364
365// Create a traceback for a given frame, using a free list hacked into the
366// existing type.
367PyTracebackObject *MAKE_TRACEBACK(struct Nuitka_FrameObject *frame, int lineno) {
368#if 0
369 PRINT_STRING("MAKE_TRACEBACK: Enter");
370 PRINT_ITEM((PyObject *)frame);
371 PRINT_NEW_LINE();
372
373 dumpFrameStack();
374#endif
375
376 CHECK_OBJECT(frame);
377 if (lineno == 0) {
378 lineno = frame->m_frame.f_lineno;
379 }
380 assert(lineno != 0);
381
382 PyTracebackObject *result;
383
384 allocateFromFreeListFixed(free_list_tracebacks, PyTracebackObject, PyTraceBack_Type);
385
386 result->tb_next = NULL;
387 result->tb_frame = (PyFrameObject *)frame;
388 Py_INCREF(frame);
389
390 result->tb_lasti = -1;
391 result->tb_lineno = lineno;
392
393 Nuitka_GC_Track(result);
394
395 return result;
396}
397
398static void Nuitka_tb_dealloc(PyTracebackObject *tb) {
399 // Need to use official method as it checks for recursion.
400 Nuitka_GC_UnTrack(tb);
401
402#if 0
403#if PYTHON_VERSION >= 0x380
404 Py_TRASHCAN_BEGIN(tb, Nuitka_tb_dealloc);
405#else
406 Py_TRASHCAN_SAFE_BEGIN(tb);
407#endif
408#endif
409
410 Py_XDECREF(tb->tb_next);
411 Py_XDECREF(tb->tb_frame);
412
413 releaseToFreeList(free_list_tracebacks, tb, MAX_TRACEBACK_FREE_LIST_COUNT);
414
415#if 0
416#if PYTHON_VERSION >= 0x380
417 Py_TRASHCAN_END;
418#else
419 Py_TRASHCAN_SAFE_END(tb);
420#endif
421#endif
422}
423
424void patchTracebackDealloc(void) { PyTraceBack_Type.tp_dealloc = (destructor)Nuitka_tb_dealloc; }
425
426// Part of "Nuitka", an optimizing Python compiler that is compatible and
427// integrates with CPython, but also works on its own.
428//
429// Licensed under the Apache License, Version 2.0 (the "License");
430// you may not use this file except in compliance with the License.
431// You may obtain a copy of the License at
432//
433// http://www.apache.org/licenses/LICENSE-2.0
434//
435// Unless required by applicable law or agreed to in writing, software
436// distributed under the License is distributed on an "AS IS" BASIS,
437// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
438// See the License for the specific language governing permissions and
439// limitations under the License.
Definition compiled_frame.h:117
Definition compiled_function.h:22
Definition compiled_generator.h:41