diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a99344e94c3098..1d8ff330bc18b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -94,9 +94,9 @@ repos: description: Check C++ code style using cpplint.py. entry: bash ./tools/codestyle/cpplint_pre_commit.hook language: system - files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$ + files: \.(cc|cxx|cpp|cu|h|hpp|hxx)$ args: - - --extensions=c,cc,cxx,cpp,cu,cuh,h,hpp,hxx,kps + - --extensions=cc,cxx,cpp,cu,cuh,h,hpp,hxx,kps - --filter=-readability/fn_size,-build/include_what_you_use,-build/c++11,-whitespace/parens - --quiet # Exclude third-party libraries diff --git a/paddle/fluid/pybind/CMakeLists.txt b/paddle/fluid/pybind/CMakeLists.txt index 22e48b0b6a075c..1a919956a2c302 100755 --- a/paddle/fluid/pybind/CMakeLists.txt +++ b/paddle/fluid/pybind/CMakeLists.txt @@ -144,7 +144,8 @@ set(PYBIND_SRCS custom_device_py.cc xpu_streams_py.cc jit.cc - auto_parallel_py.cc) + auto_parallel_py.cc + eval_frame.c) if(WITH_CUSTOM_DEVICE) set(PYBIND_DEPS ${PYBIND_DEPS} custom_device_common_op_registry) diff --git a/paddle/fluid/pybind/eval_frame.c b/paddle/fluid/pybind/eval_frame.c new file mode 100644 index 00000000000000..0254e1dce1f0a8 --- /dev/null +++ b/paddle/fluid/pybind/eval_frame.c @@ -0,0 +1,577 @@ +/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/fluid/pybind/eval_frame.h" + +#include +#include + +#if PY_VERSION_HEX < 0x030b0000 +#include +#endif +#if PY_VERSION_HEX >= 0x030b0000 +#include +#include +#define Py_BUILD_CORE // internal/pycore_opcode.h need this macro +#define NEED_OPCODE_TABLES // To get _PyOpcode_Caches and _PyOpcode_Deopt +#include +#undef NEED_OPCODE_TABLES +#undef Py_BUILD_CORE +#include +#endif + +#include +#include + +#if PY_VERSION_HEX >= 0x030b0000 +// To avoid the error: undefined symbol: _PyFrame_GetFrameObject, all we need is +// to redefine this function based source code in python3.11. The advantage is +// that we don't need any modification in eval_frame functions. +typedef _PyInterpreterFrame FrameObject; +#define CALL_STAT_INC(name) ((void)0) + +int Internal_PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame); +static int Internal_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, + int opcode, + int oparg); +int Internal_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame); + +// clang-format off +// Define a proxy PyObject to access _PyInterpreterFrame's properties. +// It will be passed as an argument to the eval frame's callback. +typedef struct PyInterpreterFrameProxy { + PyObject_HEAD + _PyInterpreterFrame *frame; +} PyInterpreterFrameProxy; +// clang-format on + +#define DECLARE_PROXY_PROPERTY(name) \ + static PyObject *PyInterpreterFrameProxy_property_##name( \ + PyInterpreterFrameProxy *self, void *closure) { \ + Py_XINCREF(self->frame->name); \ + return (PyObject *)self->frame->name; \ + } + +// clang-format off +#define REGISTER_PROXY_PROPERTY(name) \ + { #name, (getter)PyInterpreterFrameProxy_property_##name, NULL, NULL, NULL } +// clang-format on + +DECLARE_PROXY_PROPERTY(f_code) +DECLARE_PROXY_PROPERTY(f_locals) +DECLARE_PROXY_PROPERTY(f_globals) +DECLARE_PROXY_PROPERTY(f_builtins) + +// Refer to +// https://github.com/python/cpython/blob/9414ddf91898892f3f6a672ae946931ee4b3ceb7/Objects/frameobject.c#L953-L961 +static PyObject *PyInterpreterFrameProxy_method_repr( + PyInterpreterFrameProxy *self) { + int lineno = Internal_PyInterpreterFrame_GetLine(self->frame); + PyCodeObject *code = self->frame->f_code; + return PyUnicode_FromFormat( + "", + self, + code->co_filename, + lineno, + code->co_name); +} + +static PyGetSetDef PyInterpreterFrameProxy_properties[] = { + REGISTER_PROXY_PROPERTY(f_code), + REGISTER_PROXY_PROPERTY(f_locals), + REGISTER_PROXY_PROPERTY(f_globals), + REGISTER_PROXY_PROPERTY(f_builtins), + {NULL} /* Sentinel */ +}; + +// clang-format off +static PyTypeObject PyInterpreterFrameProxyType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "paddle.framework.core.PyInterpreterFrameProxy", + .tp_doc = PyDoc_STR("A proxy object for _PyInterpreterFrame, " + "it's only define all properties we need."), + .tp_repr = (reprfunc)PyInterpreterFrameProxy_method_repr, + .tp_basicsize = sizeof(PyInterpreterFrameProxy), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_getset = PyInterpreterFrameProxy_properties, +}; +// clang-format on + +PyInterpreterFrameProxy *PyInterpreterFrameProxy_New( + _PyInterpreterFrame *frame) { + PyTypeObject *type = &PyInterpreterFrameProxyType; + PyInterpreterFrameProxy *self = + (PyInterpreterFrameProxy *)type->tp_alloc(type, 0); + if (!self) { + // VLOG(7) << "Failed to allocate PyInterpreterFrameProxy"; + return NULL; + } + self->frame = frame; + return self; +} + +// We copy some cpython internal API from cpython project. +// To avoid name conflict, we use "Internal_" prefix to mark them. +int Internal_PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame) { + int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + return PyCode_Addr2Line(frame->f_code, addr); +} + +static int Internal_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, + int opcode, + int oparg) { + // This only works when opcode is a non-quickened form: + assert(_PyOpcode_Deopt[opcode] == opcode); + int check_oparg = 0; + for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); + instruction < frame->prev_instr; + instruction++) { + int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)]; + check_oparg |= _Py_OPARG(*instruction); + if (check_opcode == opcode && check_oparg == oparg) { + return 1; + } + if (check_opcode == EXTENDED_ARG) { + check_oparg <<= 8; + } else { + check_oparg = 0; + } + instruction += _PyOpcode_Caches[check_opcode]; + } + return 0; +} + +int Internal_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { + /* Merge fast locals into f->f_locals */ + PyObject *locals; + PyObject **fast; + PyCodeObject *co; + locals = frame->f_locals; + if (locals == NULL) { + locals = frame->f_locals = PyDict_New(); + if (locals == NULL) return -1; + } + co = frame->f_code; + fast = _PyFrame_GetLocalsArray(frame); + // COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt + // here: + int lasti = _PyInterpreterFrame_LASTI(frame); + if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) { + /* Free vars have not been initialized -- Do that */ + PyCodeObject *co = frame->f_code; + PyObject *closure = frame->f_func->func_closure; + int offset = co->co_nlocals + co->co_nplaincellvars; + for (int i = 0; i < co->co_nfreevars; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + Py_INCREF(o); + frame->localsplus[offset + i] = o; + } + // COPY_FREE_VARS doesn't have inline CACHEs, either: + frame->prev_instr = _PyCode_CODE(frame->f_code); + } + for (int i = 0; i < co->co_nlocalsplus; i++) { + _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); + + /* If the namespace is unoptimized, then one of the + following cases applies: + 1. It does not contain free variables, because it + uses import * or is a top-level namespace. + 2. It is a class namespace. + We don't want to accidentally copy free variables + into the locals dict used by the class. + */ + if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) { + continue; + } + + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + PyObject *value = fast[i]; + if (frame->stacktop) { + if (kind & CO_FAST_FREE) { + // The cell was set by COPY_FREE_VARS. + assert(value != NULL && PyCell_Check(value)); + value = PyCell_GET(value); + } else if (kind & CO_FAST_CELL) { + // Note that no *_DEREF ops can happen before MAKE_CELL + // executes. So there's no need to duplicate the work + // that MAKE_CELL would otherwise do later, if it hasn't + // run yet. + if (value != NULL) { + if (PyCell_Check(value) && + Internal_PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { + // (likely) MAKE_CELL must have executed already. + value = PyCell_GET(value); + } + // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL), + // with the initial value set when the frame was created... + // (unlikely) ...or it was set to some initial value by + // an earlier call to PyFrame_LocalsToFast(). + } + } + } else { + assert(value == NULL); + } + if (value == NULL) { + if (PyObject_DelItem(locals, name) != 0) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + } else { + return -1; + } + } + } else { + if (PyObject_SetItem(locals, name, value) != 0) { + return -1; + } + } + } + return 0; +} + +#else +typedef PyFrameObject FrameObject; +#endif + +#ifdef _WIN32 +#define unlikely(x) (x) +#else +#define unlikely(x) __builtin_expect((x), 0) +#endif + +// Use static variable to save customed eval hook. +static Py_tss_t eval_frame_callback_key = {0, 0}; + +inline static PyObject *eval_frame_callback_get() { + void *result = PyThread_tss_get(&eval_frame_callback_key); + if (unlikely(result == NULL)) { + Py_RETURN_NONE; + } else { + return (PyObject *)result; + } +} + +inline static void eval_frame_callback_set(PyObject *obj) { + PyThread_tss_set(&eval_frame_callback_key, obj); +} + +// call python default eval frame to interpret current frame. +inline static PyObject *eval_frame_default(PyThreadState *tstate, + FrameObject *frame, + int throw_flag) { +#if PY_VERSION_HEX >= 0x03090000 + if (tstate == NULL) { + tstate = PyThreadState_GET(); + } + return _PyEval_EvalFrameDefault(tstate, frame, throw_flag); +#else + return _PyEval_EvalFrameDefault(frame, throw_flag); +#endif +} + +#if PY_VERSION_HEX >= 0x030b0000 + +inline static PyObject *eval_custom_code_py311_plus(PyThreadState *tstate, + FrameObject *frame, + PyCodeObject *code, + int throw_flag) { + Py_ssize_t nlocalsplus_new = code->co_nlocalsplus; + Py_ssize_t nlocalsplus_old = frame->f_code->co_nlocalsplus; + // Create a new PyInterpreterFrame. Refer to CALL. + // PyInterpreterFrame has a head section calls "specials". It follows + // a contiguous section containing localplus and interpreter stack space. + size_t size = nlocalsplus_new + code->co_stacksize + FRAME_SPECIALS_SIZE; + CALL_STAT_INC(frames_pushed); + _PyInterpreterFrame *shadow = + (_PyInterpreterFrame *)malloc(sizeof(PyObject *) * size); + if (shadow == NULL) { + // VLOG(7) << "Failed to allocate memory for shadow frame."; + return NULL; + } + // Create a new function object from code object. Refer to MAKE_FUNCTION. + PyFunctionObject *func = + (PyFunctionObject *)PyFunction_New((PyObject *)code, frame->f_globals); + Py_XINCREF(frame->f_func->func_closure); + func->func_closure = frame->f_func->func_closure; + _PyFrame_InitializeSpecials(shadow, func, NULL, code->co_nlocalsplus); + + PyObject **fastlocals_old = frame->localsplus; + PyObject **fastlocals_new = shadow->localsplus; + + for (Py_ssize_t i = 0; i < nlocalsplus_new; ++i) { + fastlocals_new[i] = NULL; + } + + // The namemap to map the name to index in new frame localsplus. + PyObject *namemap = PyDict_New(); + if (namemap == NULL) { + // VLOG(7) << "Failed to create namemap."; + free(shadow); + return NULL; + } + for (Py_ssize_t i = 0; i < nlocalsplus_new; ++i) { + PyObject *name = PyTuple_GET_ITEM(code->co_localsplusnames, i); + PyObject *index = PyLong_FromSize_t(i); + PyDict_SetItem(namemap, name, index); + } + for (Py_ssize_t i = 0; i < nlocalsplus_old; ++i) { + PyObject *name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, i); + PyObject *index = PyDict_GetItem(namemap, name); + if (index == NULL) { + continue; + } + Py_XINCREF(fastlocals_old[i]); + fastlocals_new[PyLong_AsSize_t(index)] = fastlocals_old[i]; + } + + PyObject *result = eval_frame_default(tstate, shadow, throw_flag); + free(shadow); + Py_DECREF(namemap); + return result; +} + +#else + +inline static PyObject *eval_custom_code_py310_minus(PyThreadState *tstate, + FrameObject *frame, + PyCodeObject *code, + int throw_flag) { + Py_ssize_t ncells = 0; + Py_ssize_t nfrees = 0; + Py_ssize_t nlocals_new = code->co_nlocals; + Py_ssize_t nlocals_old = frame->f_code->co_nlocals; + + ncells = PyTuple_GET_SIZE(code->co_cellvars); + nfrees = PyTuple_GET_SIZE(code->co_freevars); + + PyFrameObject *shadow = PyFrame_New(tstate, code, frame->f_globals, NULL); + if (shadow == NULL) { + return NULL; + } + + PyObject **fastlocals_old = frame->f_localsplus; + PyObject **fastlocals_new = shadow->f_localsplus; + + for (Py_ssize_t i = 0; i < nlocals_old; i++) { + Py_XINCREF(fastlocals_old[i]); + fastlocals_new[i] = fastlocals_old[i]; + } + + for (Py_ssize_t i = 0; i < ncells + nfrees; i++) { + Py_XINCREF(fastlocals_old[nlocals_old + i]); + fastlocals_new[nlocals_new + i] = fastlocals_old[nlocals_old + i]; + } + + PyObject *result = eval_frame_default(tstate, shadow, throw_flag); + Py_DECREF(shadow); + return result; +} + +#endif + +// Start a new frame and run code in this frame. +// Execute a piece of code by default frame-hook. +inline static PyObject *eval_custom_code(PyThreadState *tstate, + FrameObject *frame, + PyCodeObject *code, + int throw_flag) { +#if PY_VERSION_HEX >= 0x030b0000 + return eval_custom_code_py311_plus(tstate, frame, code, throw_flag); +#else + return eval_custom_code_py310_minus(tstate, frame, code, throw_flag); +#endif +} + +static PyObject *_custom_eval_frame(PyThreadState *tstate, + FrameObject *frame, + int throw_flag, + PyObject *callback) { +// https://peps.python.org/pep-0558/#fast-locals-proxy-implementation-details +// https://devguide.python.org/internals/interpreter/#all-sorts-of-variables +#if PY_VERSION_HEX >= 0x030b0000 + if (frame->owner == FRAME_OWNED_BY_GENERATOR) { + return eval_frame_default(tstate, frame, throw_flag); + } + // PyFrame_FastToLocalsWithError receives a PyFrameObject, but if we created a + // PyFrameObject from a PyInterpreterFrame, it will changes the original + // PyInterpreterFrame and causes a Segmentation Fault when Fallback to run + // original frame. So we pass a PyInterpreterFrame to + // _PyFrame_FastToLocalsWithError directly. But this is an internal API, so we + // copy many code from CPython project into our project. + if (Internal_PyFrame_FastToLocalsWithError(frame) < 0) { +#else + if (PyFrame_FastToLocalsWithError(frame) < 0) { +#endif + return NULL; + } + + // NOTE:(xiongkun): Handle GeneratorExit exception: (Spend a day) + // In Python, gen close is also a Python function call that will enter this + // function with GeneratorExit set, which will cause the PyObject_CallObject + // raise SystemError. So we disable the custom behavior for GeneratorExit. def + // func(): + // iter = iter([1, 2, 3]) + // for i in iter: + // return i # <--- Early return, cause a GeneratorExit thrown, + // # <--- which Cause the PyObject_CallObject raise + // SystemError. + if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) { + return eval_frame_default(tstate, frame, throw_flag); + } + + // We don't run the current custom_eval_frame behavior for guards. + // So we temporarily set the callback to Py_None to drive the correct behavior + // in the shim. + eval_frame_callback_set(Py_None); + +#if PY_VERSION_HEX >= 0x030b0000 + PyObject *args = Py_BuildValue("(O)", PyInterpreterFrameProxy_New(frame)); +#else + PyObject *args = Py_BuildValue("(O)", frame); +#endif + PyObject *result = PyObject_CallObject(callback, args); + Py_DECREF(args); + // VLOG(7) << "After call eval_frame_function and decrease frame."; + // class CustomCode(Protocal): + // code: CodeType | None + // disable_eval_frame: bool + // result: CustomCode + if (result == NULL) { + // internal exception + // VLOG(7) << "Error happened."; + return NULL; + } else { + // NOTE: Cache is not supported now + PyCodeObject *code = (PyCodeObject *)PyObject_GetAttrString(result, "code"); + PyObject *disable_eval_frame = + PyObject_GetAttrString(result, "disable_eval_frame"); + PyObject *out; + // VLOG(7) << "Start eval new frame and code."; + if (disable_eval_frame != Py_True) { + // Re-enable custom behavior + eval_frame_callback_set(callback); + if ((PyObject *)code != Py_None) { + out = eval_custom_code(tstate, frame, code, throw_flag); + } else { + out = eval_frame_default(tstate, frame, throw_flag); + } + } else { + if ((PyObject *)code != Py_None) { + out = eval_custom_code(tstate, frame, code, throw_flag); + } else { + out = eval_frame_default(tstate, frame, throw_flag); + } + // Re-enable custom behavior + eval_frame_callback_set(callback); + } + Py_DECREF(result); + Py_DECREF(code); + return out; + } +} + +static PyObject *_custom_eval_frame_shim(PyThreadState *tstate, + FrameObject *frame, + int throw_flag) { + PyObject *callback = eval_frame_callback_get(); + + if (callback == Py_None) { + return eval_frame_default(tstate, frame, throw_flag); + } + + return _custom_eval_frame(tstate, frame, throw_flag, callback); +} + +#if PY_VERSION_HEX >= 0x03090000 +static PyObject *custom_eval_frame_shim(PyThreadState *tstate, + FrameObject *frame, + int throw_flag) { + return _custom_eval_frame_shim(tstate, frame, throw_flag); +} +#else +static PyObject *custom_eval_frame_shim(FrameObject *frame, int throw_flag) { + PyThreadState *tstate = PyThreadState_GET(); + return _custom_eval_frame_shim(tstate, frame, throw_flag); +} +#endif + +static PyObject *set_eval_frame(PyObject *new_callback, PyThreadState *tstate) { + // Change the eval frame callback and return the old one + // - None: disables: disable custom callback. + // - Python callable(): enables custom callback. + // NOTE: Cache is not supported now + PyObject *old_callback = eval_frame_callback_get(); + +#if PY_VERSION_HEX >= 0x03090000 + _PyFrameEvalFunction old_eval_frame = + _PyInterpreterState_GetEvalFrameFunc(tstate->interp); +#else + // Function pointer. + _PyFrameEvalFunction old_eval_frame = tstate->interp->eval_frame; +#endif + + // NOTE: multi-threading is not supported now + if (old_callback != Py_None && new_callback == Py_None) { + if (old_eval_frame != &_PyEval_EvalFrameDefault) { + // VLOG(7) << "set _PyEval_EvalFrameDefault"; +#if PY_VERSION_HEX >= 0x03090000 + _PyInterpreterState_SetEvalFrameFunc(tstate->interp, + &_PyEval_EvalFrameDefault); +#else + tstate->interp->eval_frame = &_PyEval_EvalFrameDefault; +#endif + } + } else if (old_callback == Py_None && new_callback != Py_None) { + if (old_eval_frame != &custom_eval_frame_shim) { + // VLOG(7) << "set custom_eval_frame_shim"; +#if PY_VERSION_HEX >= 0x03090000 + _PyInterpreterState_SetEvalFrameFunc(tstate->interp, + &custom_eval_frame_shim); +#else + tstate->interp->eval_frame = &custom_eval_frame_shim; +#endif + } + } + + Py_INCREF(new_callback); + eval_frame_callback_set(new_callback); + + return old_callback; +} + +PyObject *set_eval_frame_py(PyObject *callback) { + if (callback != Py_None && !PyCallable_Check(callback)) { + // VLOG(7) << "callback is not a callable or none, invalid arguments."; + Py_INCREF(Py_None); + return Py_None; + } + return set_eval_frame(callback, PyThreadState_GET()); +} + +PyMODINIT_FUNC PyInit__eval_frame() { + PyThread_tss_create(&eval_frame_callback_key); + // VLOG(7) << "Set PyThread_tss_create return: " << result; + + Py_INCREF(Py_None); + eval_frame_callback_set(Py_None); + +#if PY_VERSION_HEX >= 0x030b0000 + if (PyType_Ready(&PyInterpreterFrameProxyType) < 0) { + // VLOG(7) << "PyInterpreterFrameProxyType has not been ready!"; + } + Py_INCREF(&PyInterpreterFrameProxyType); +#endif + + return NULL; +} diff --git a/paddle/fluid/pybind/eval_frame.h b/paddle/fluid/pybind/eval_frame.h new file mode 100644 index 00000000000000..383d2d7a5bc003 --- /dev/null +++ b/paddle/fluid/pybind/eval_frame.h @@ -0,0 +1,118 @@ +/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// see https://bugs.python.org/issue35886 +// If py_version==3.8.*, we need to redefine _PyEvalFrameFunc and the +// related functions and structs. + +#if PY_VERSION_HEX >= 0x03080000 && PY_VERSION_HEX < 0x3090000 + +typedef PyObject *(*_PyFrameEvalFunction)(struct _frame *, int); + +struct _warnings_runtime_state { + /* Both 'filters' and 'onceregistry' can be set in warnings.py; + get_warnings_attr() will reset these variables accordingly. */ + PyObject *filters; /* List */ + PyObject *once_registry; /* Dict */ + PyObject *default_action; /* String */ + long filters_version; // NOLINT +}; + +struct _is { + struct _is *next; + struct _ts *tstate_head; + + int64_t id; + int64_t id_refcount; + int requires_idref; + PyThread_type_lock id_mutex; + + int finalizing; + + PyObject *modules; + PyObject *modules_by_index; + PyObject *sysdict; + PyObject *builtins; + PyObject *importlib; + + /* Used in Python/sysmodule.c. */ + int check_interval; + + /* Used in Modules/_threadmodule.c. */ + long num_threads; // NOLINT + /* Support for runtime thread stack size tuning. + A value of 0 means using the platform's default stack size + or the size specified by the THREAD_STACK_SIZE macro. */ + /* Used in Python/thread.c. */ + size_t pythread_stacksize; + + PyObject *codec_search_path; + PyObject *codec_search_cache; + PyObject *codec_error_registry; + int codecs_initialized; + + /* fs_codec.encoding is initialized to NULL. + Later, it is set to a non-NULL string by _PyUnicode_InitEncodings(). */ + struct { + char *encoding; /* Filesystem encoding (encoded to UTF-8) */ + char *errors; /* Filesystem errors (encoded to UTF-8) */ + _Py_error_handler error_handler; + } fs_codec; + + PyConfig config; +#ifdef HAVE_DLOPEN + int dlopenflags; +#endif + + PyObject *dict; /* Stores per-interpreter state */ + + PyObject *builtins_copy; + PyObject *import_func; + /* Initialized to PyEval_EvalFrameDefault(). */ + _PyFrameEvalFunction eval_frame; + + Py_ssize_t co_extra_user_count; + freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; + +#ifdef HAVE_FORK + PyObject *before_forkers; + PyObject *after_forkers_parent; + PyObject *after_forkers_child; +#endif + /* AtExit module */ + void (*pyexitfunc)(PyObject *); + PyObject *pyexitmodule; + + uint64_t tstate_next_unique_id; + + struct _warnings_runtime_state warnings; + + PyObject *audit_hooks; +}; + +#endif + +PyObject *set_eval_frame_py(PyObject *callback); +PyMODINIT_FUNC PyInit__eval_frame(); + +#ifdef __cplusplus +} +#endif diff --git a/paddle/fluid/pybind/jit.cc b/paddle/fluid/pybind/jit.cc index 8d4d62f738211e..09e194bf0b7c8e 100644 --- a/paddle/fluid/pybind/jit.cc +++ b/paddle/fluid/pybind/jit.cc @@ -13,36 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/fluid/pybind/jit.h" - -#include -#include - -#if PY_VERSION_HEX < 0x030b0000 -#include -#endif -#if PY_VERSION_HEX >= 0x030b0000 -#include -#include -#define Py_BUILD_CORE // internal/pycore_opcode.h need this macro -#define NEED_OPCODE_TABLES // To get _PyOpcode_Caches and _PyOpcode_Deopt -#include -#undef NEED_OPCODE_TABLES -#undef Py_BUILD_CORE -#include -#endif - -#include -#include - +#include "glog/logging.h" #include "paddle/fluid/framework/variable.h" #include "paddle/fluid/imperative/layer.h" -#include "paddle/fluid/platform/place.h" - -#include "glog/logging.h" #include "paddle/fluid/jit/function.h" #include "paddle/fluid/jit/function_schema.h" #include "paddle/fluid/jit/layer.h" #include "paddle/fluid/jit/serializer.h" +#include "paddle/fluid/platform/place.h" +#include "paddle/fluid/pybind/eval_frame.h" #include "paddle/utils/pybind.h" namespace py = pybind11; @@ -50,535 +29,6 @@ namespace py = pybind11; namespace paddle { namespace pybind { -#if PY_VERSION_HEX >= 0x030b0000 -// To avoid the error: undefined symbol: _PyFrame_GetFrameObject, all we need is -// to redefine this function based source code in python3.11. The advantage is -// that we don't need any modification in eval_frame functions. -typedef _PyInterpreterFrame FrameObject; -#define CALL_STAT_INC(name) ((void)0) - -int Internal_PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame); -static int Internal_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, - int opcode, - int oparg); -int Internal_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame); - -// clang-format off -// Define a proxy PyObject to access _PyInterpreterFrame's properties. -// It will be passed as an argument to the eval frame's callback. -typedef struct PyInterpreterFrameProxy { - PyObject_HEAD - _PyInterpreterFrame *frame; -} PyInterpreterFrameProxy; -// clang-format on - -#define DECLARE_PROXY_PROPERTY(name) \ - static PyObject *PyInterpreterFrameProxy_property_##name( \ - PyInterpreterFrameProxy *self, void *closure) { \ - Py_XINCREF(self->frame->name); \ - return reinterpret_cast(self->frame->name); \ - } - -// clang-format off -#define REGISTER_PROXY_PROPERTY(name) \ - { \ - #name, (getter)PyInterpreterFrameProxy_property_##name, nullptr, nullptr, \ - nullptr \ - } -// clang-format on - -DECLARE_PROXY_PROPERTY(f_code) -DECLARE_PROXY_PROPERTY(f_locals) -DECLARE_PROXY_PROPERTY(f_globals) -DECLARE_PROXY_PROPERTY(f_builtins) - -// Refer to -// https://github.com/python/cpython/blob/9414ddf91898892f3f6a672ae946931ee4b3ceb7/Objects/frameobject.c#L953-L961 -static PyObject *PyInterpreterFrameProxy_method_repr( - PyInterpreterFrameProxy *self) { - int lineno = Internal_PyInterpreterFrame_GetLine(self->frame); - PyCodeObject *code = self->frame->f_code; - return PyUnicode_FromFormat( - "", - self, - code->co_filename, - lineno, - code->co_name); -} - -static PyGetSetDef PyInterpreterFrameProxy_properties[] = { - REGISTER_PROXY_PROPERTY(f_code), - REGISTER_PROXY_PROPERTY(f_locals), - REGISTER_PROXY_PROPERTY(f_globals), - REGISTER_PROXY_PROPERTY(f_builtins), - {nullptr} /* Sentinel */ -}; - -// clang-format off -static PyTypeObject PyInterpreterFrameProxyType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "paddle.framework.core.PyInterpreterFrameProxy", - .tp_doc = PyDoc_STR("A proxy object for _PyInterpreterFrame, " - "it's only define all properties we need."), - .tp_repr = reinterpret_cast(PyInterpreterFrameProxy_method_repr), - .tp_basicsize = sizeof(PyInterpreterFrameProxy), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_getset = PyInterpreterFrameProxy_properties, -}; -// clang-format on - -PyInterpreterFrameProxy *PyInterpreterFrameProxy_New( - _PyInterpreterFrame *frame) { - PyTypeObject *type = &PyInterpreterFrameProxyType; - PyInterpreterFrameProxy *self = - reinterpret_cast(type->tp_alloc(type, 0)); - if (!self) { - VLOG(7) << "Failed to allocate PyInterpreterFrameProxy"; - return nullptr; - } - self->frame = frame; - return self; -} - -// We copy some cpython internal API from cpython project. -// To avoid name conflict, we use "Internal_" prefix to mark them. -int Internal_PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame) { - int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); - return PyCode_Addr2Line(frame->f_code, addr); -} - -static int Internal_PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, - int opcode, - int oparg) { - // This only works when opcode is a non-quickened form: - assert(_PyOpcode_Deopt[opcode] == opcode); - int check_oparg = 0; - for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); - instruction < frame->prev_instr; - instruction++) { - int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)]; - check_oparg |= _Py_OPARG(*instruction); - if (check_opcode == opcode && check_oparg == oparg) { - return 1; - } - if (check_opcode == EXTENDED_ARG) { - check_oparg <<= 8; - } else { - check_oparg = 0; - } - instruction += _PyOpcode_Caches[check_opcode]; - } - return 0; -} - -int Internal_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { - /* Merge fast locals into f->f_locals */ - PyObject *locals; - PyObject **fast; - PyCodeObject *co; - locals = frame->f_locals; - if (locals == NULL) { - locals = frame->f_locals = PyDict_New(); - if (locals == NULL) return -1; - } - co = frame->f_code; - fast = _PyFrame_GetLocalsArray(frame); - // COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt - // here: - int lasti = _PyInterpreterFrame_LASTI(frame); - if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) { - /* Free vars have not been initialized -- Do that */ - PyCodeObject *co = frame->f_code; - PyObject *closure = frame->f_func->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; - for (int i = 0; i < co->co_nfreevars; ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i); - Py_INCREF(o); - frame->localsplus[offset + i] = o; - } - // COPY_FREE_VARS doesn't have inline CACHEs, either: - frame->prev_instr = _PyCode_CODE(frame->f_code); - } - for (int i = 0; i < co->co_nlocalsplus; i++) { - _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); - - /* If the namespace is unoptimized, then one of the - following cases applies: - 1. It does not contain free variables, because it - uses import * or is a top-level namespace. - 2. It is a class namespace. - We don't want to accidentally copy free variables - into the locals dict used by the class. - */ - if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) { - continue; - } - - PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); - PyObject *value = fast[i]; - if (frame->stacktop) { - if (kind & CO_FAST_FREE) { - // The cell was set by COPY_FREE_VARS. - assert(value != NULL && PyCell_Check(value)); - value = PyCell_GET(value); - } else if (kind & CO_FAST_CELL) { - // Note that no *_DEREF ops can happen before MAKE_CELL - // executes. So there's no need to duplicate the work - // that MAKE_CELL would otherwise do later, if it hasn't - // run yet. - if (value != NULL) { - if (PyCell_Check(value) && - Internal_PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { - // (likely) MAKE_CELL must have executed already. - value = PyCell_GET(value); - } - // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL), - // with the initial value set when the frame was created... - // (unlikely) ...or it was set to some initial value by - // an earlier call to PyFrame_LocalsToFast(). - } - } - } else { - assert(value == NULL); - } - if (value == NULL) { - if (PyObject_DelItem(locals, name) != 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Clear(); - } else { - return -1; - } - } - } else { - if (PyObject_SetItem(locals, name, value) != 0) { - return -1; - } - } - } - return 0; -} - -#else -typedef PyFrameObject FrameObject; -#endif - -#define unlikely(x) __builtin_expect((x), 0) - -// Use static variable to save customed eval hook. -static Py_tss_t eval_frame_callback_key = {0, 0}; - -inline static PyObject *eval_frame_callback_get() { - void *result = PyThread_tss_get(&eval_frame_callback_key); - if (unlikely(result == nullptr)) { - Py_RETURN_NONE; - } else { - return reinterpret_cast(result); - } -} - -inline static void eval_frame_callback_set(PyObject *obj) { - PyThread_tss_set(&eval_frame_callback_key, obj); -} - -// call python default eval frame to interpret current frame. -inline static PyObject *eval_frame_default(PyThreadState *tstate, - FrameObject *frame, - int throw_flag) { -#if PY_VERSION_HEX >= 0x03090000 - if (tstate == nullptr) { - tstate = PyThreadState_GET(); - } - return _PyEval_EvalFrameDefault(tstate, frame, throw_flag); -#else - return _PyEval_EvalFrameDefault(frame, throw_flag); -#endif -} - -#if PY_VERSION_HEX >= 0x030b0000 - -inline static PyObject *eval_custom_code_py311_plus(PyThreadState *tstate, - FrameObject *frame, - PyCodeObject *code, - int throw_flag) { - // Create a new PyInterpreterFrame. Refer to CALL. - // PyInterpreterFrame has a head section calls "specials". It follows - // a contiguous section containing localplus and interpreter stack space. - size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; - CALL_STAT_INC(frames_pushed); - _PyInterpreterFrame *shadow = reinterpret_cast<_PyInterpreterFrame *>( - malloc(sizeof(PyObject *) * size)); - if (shadow == nullptr) { - VLOG(7) << "Failed to allocate memory for shadow frame."; - return nullptr; - } - // Create a new function object from code object. Refer to MAKE_FUNCTION. - PyFunctionObject *func = reinterpret_cast( - PyFunction_New(reinterpret_cast(code), frame->f_globals)); - _PyFrame_InitializeSpecials(shadow, func, nullptr, code->co_nlocalsplus); - - PyObject **fastlocals_old = frame->localsplus; - PyObject **fastlocals_new = shadow->localsplus; - - for (size_t i = 0; i < code->co_nlocalsplus; ++i) { - fastlocals_new[i] = nullptr; - } - - // The namemap to map the name to index in new frame localsplus. - PyObject *namemap = PyDict_New(); - if (namemap == nullptr) { - VLOG(7) << "Failed to create namemap."; - free(shadow); - return nullptr; - } - for (size_t i = 0; i < code->co_nlocalsplus; ++i) { - PyObject *name = PyTuple_GET_ITEM(code->co_localsplusnames, i); - PyObject *index = PyLong_FromSize_t(i); - PyDict_SetItem(namemap, name, index); - } - for (size_t i = 0; i < frame->f_code->co_nlocalsplus; ++i) { - PyObject *name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, i); - PyObject *index = PyDict_GetItem(namemap, name); - if (index == nullptr) { - continue; - } - Py_XINCREF(fastlocals_old[i]); - fastlocals_new[PyLong_AsSize_t(index)] = fastlocals_old[i]; - } - - PyObject *result = eval_frame_default(tstate, shadow, throw_flag); - free(shadow); - Py_DECREF(namemap); - return result; -} - -#else - -inline static PyObject *eval_custom_code_py310_minus(PyThreadState *tstate, - FrameObject *frame, - PyCodeObject *code, - int throw_flag) { - Py_ssize_t ncells = 0; - Py_ssize_t nfrees = 0; - Py_ssize_t nlocals_new = code->co_nlocals; - Py_ssize_t nlocals_old = frame->f_code->co_nlocals; - - ncells = PyTuple_GET_SIZE(code->co_cellvars); - nfrees = PyTuple_GET_SIZE(code->co_freevars); - - PyFrameObject *shadow = PyFrame_New(tstate, code, frame->f_globals, nullptr); - if (shadow == nullptr) { - return nullptr; - } - - PyObject **fastlocals_old = frame->f_localsplus; - PyObject **fastlocals_new = shadow->f_localsplus; - - for (Py_ssize_t i = 0; i < nlocals_old; i++) { - Py_XINCREF(fastlocals_old[i]); - fastlocals_new[i] = fastlocals_old[i]; - } - - for (Py_ssize_t i = 0; i < ncells + nfrees; i++) { - Py_XINCREF(fastlocals_old[nlocals_old + i]); - fastlocals_new[nlocals_new + i] = fastlocals_old[nlocals_old + i]; - } - - PyObject *result = eval_frame_default(tstate, shadow, throw_flag); - Py_DECREF(shadow); - return result; -} - -#endif - -// Start a new frame and run code in this frame. -// Execute a piece of code by default frame-hook. -inline static PyObject *eval_custom_code(PyThreadState *tstate, - FrameObject *frame, - PyCodeObject *code, - int throw_flag) { -#if PY_VERSION_HEX >= 0x030b0000 - return eval_custom_code_py311_plus(tstate, frame, code, throw_flag); -#else - return eval_custom_code_py310_minus(tstate, frame, code, throw_flag); -#endif -} - -static PyObject *_custom_eval_frame(PyThreadState *tstate, - FrameObject *frame, - int throw_flag, - PyObject *callback) { -// https://peps.python.org/pep-0558/#fast-locals-proxy-implementation-details -// https://devguide.python.org/internals/interpreter/#all-sorts-of-variables -#if PY_VERSION_HEX >= 0x030b0000 - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { - return eval_frame_default(tstate, frame, throw_flag); - } - // PyFrame_FastToLocalsWithError receives a PyFrameObject, but if we created a - // PyFrameObject from a PyInterpreterFrame, it will changes the original - // PyInterpreterFrame and causes a Segmentation Fault when Fallback to run - // original frame. So we pass a PyInterpreterFrame to - // _PyFrame_FastToLocalsWithError directly. But this is an internal API, so we - // copy many code from CPython project into our project. - if (Internal_PyFrame_FastToLocalsWithError(frame) < 0) { -#else - if (PyFrame_FastToLocalsWithError(frame) < 0) { -#endif - return nullptr; - } - - // NOTE:(xiongkun): Handle GeneratorExit exception: (Spend a day) - // In Python, gen close is also a Python function call that will enter this - // function with GeneratorExit set, which will cause the PyObject_CallObject - // raise SystemError. So we disable the custom behavior for GeneratorExit. def - // func(): - // iter = iter([1, 2, 3]) - // for i in iter: - // return i # <--- Early return, cause a GeneratorExit thrown, - // # <--- which Cause the PyObject_CallObject raise - // SystemError. - if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) { - return eval_frame_default(tstate, frame, throw_flag); - } - - // We don't run the current custom_eval_frame behavior for guards. - // So we temporarily set the callback to Py_None to drive the correct behavior - // in the shim. - eval_frame_callback_set(Py_None); - -#if PY_VERSION_HEX >= 0x030b0000 - PyObject *args = Py_BuildValue("(O)", PyInterpreterFrameProxy_New(frame)); -#else - PyObject *args = Py_BuildValue("(O)", frame); -#endif - PyObject *result = PyObject_CallObject(callback, args); - Py_DECREF(args); - VLOG(7) << "After call eval_frame_function and decrease frame."; - // class CustomCode(Protocal): - // code: CodeType | None - // disable_eval_frame: bool - // result: CustomCode - if (result == nullptr) { - // internal exception - VLOG(7) << "Error happened."; - return nullptr; - } else { - // NOTE: Cache is not supported now - PyCodeObject *code = reinterpret_cast( - PyObject_GetAttrString(result, "code")); - PyObject *disable_eval_frame = - PyObject_GetAttrString(result, "disable_eval_frame"); - PyObject *out; - VLOG(7) << "Start eval new frame and code."; - if (disable_eval_frame != Py_True) { - // Re-enable custom behavior - eval_frame_callback_set(callback); - if (reinterpret_cast(code) != Py_None) { - out = eval_custom_code(tstate, frame, code, throw_flag); - } else { - out = eval_frame_default(tstate, frame, throw_flag); - } - } else { - if (reinterpret_cast(code) != Py_None) { - out = eval_custom_code(tstate, frame, code, throw_flag); - } else { - out = eval_frame_default(tstate, frame, throw_flag); - } - // Re-enable custom behavior - eval_frame_callback_set(callback); - } - Py_DECREF(result); - Py_DECREF(code); - return out; - } -} - -static PyObject *_custom_eval_frame_shim(PyThreadState *tstate, - FrameObject *frame, - int throw_flag) { - PyObject *callback = eval_frame_callback_get(); - - if (callback == Py_None) { - return eval_frame_default(tstate, frame, throw_flag); - } - - return _custom_eval_frame(tstate, frame, throw_flag, callback); -} - -#if PY_VERSION_HEX >= 0x03090000 -static PyObject *custom_eval_frame_shim(PyThreadState *tstate, - FrameObject *frame, - int throw_flag) { - return _custom_eval_frame_shim(tstate, frame, throw_flag); -} -#else -static PyObject *custom_eval_frame_shim(FrameObject *frame, int throw_flag) { - PyThreadState *tstate = PyThreadState_GET(); - return _custom_eval_frame_shim(tstate, frame, throw_flag); -} -#endif - -static PyObject *set_eval_frame(PyObject *new_callback, PyThreadState *tstate) { - // Change the eval frame callback and return the old one - // - None: disables: disable custom callback. - // - Python callable(): enables custom callback. - // NOTE: Cache is not supported now - PyObject *old_callback = eval_frame_callback_get(); - -#if PY_VERSION_HEX >= 0x03090000 - auto *old_eval_frame = _PyInterpreterState_GetEvalFrameFunc(tstate->interp); -#else - // Function pointer. - _PyFrameEvalFunction old_eval_frame = tstate->interp->eval_frame; -#endif - - // NOTE: multi-threading is not supported now - if (old_callback != Py_None && new_callback == Py_None) { - if (old_eval_frame != &_PyEval_EvalFrameDefault) { - VLOG(7) << "set _PyEval_EvalFrameDefault"; -#if PY_VERSION_HEX >= 0x03090000 - _PyInterpreterState_SetEvalFrameFunc(tstate->interp, - &_PyEval_EvalFrameDefault); -#else - tstate->interp->eval_frame = &_PyEval_EvalFrameDefault; -#endif - } - } else if (old_callback == Py_None && new_callback != Py_None) { - if (old_eval_frame != &custom_eval_frame_shim) { - VLOG(7) << "set custom_eval_frame_shim"; -#if PY_VERSION_HEX >= 0x03090000 - _PyInterpreterState_SetEvalFrameFunc(tstate->interp, - &custom_eval_frame_shim); -#else - tstate->interp->eval_frame = &custom_eval_frame_shim; -#endif - } - } - - Py_INCREF(new_callback); - eval_frame_callback_set(new_callback); - - return old_callback; -} - -static PyObject *set_eval_frame_py(PyObject *callback) { - if (callback != Py_None && !PyCallable_Check(callback)) { - VLOG(7) << "callback is not a callable or none, invalid arguments."; - RETURN_PY_NONE - } - return set_eval_frame(callback, PyThreadState_GET()); -} - -PyMODINIT_FUNC PyInit__eval_frame() { - int result = PyThread_tss_create(&eval_frame_callback_key); - VLOG(7) << "Set PyThread_tss_create return: " << result; - - Py_INCREF(Py_None); - eval_frame_callback_set(Py_None); - - return nullptr; -} - PyTypeObject *g_jit_function_pytype = nullptr; using Variable = paddle::framework::Variable; @@ -620,12 +70,6 @@ void BindEvalFrame(pybind11::module *m) { return obj; }, py::arg("callback")); -#if PY_VERSION_HEX >= 0x030b0000 - if (PyType_Ready(&PyInterpreterFrameProxyType) < 0) { - VLOG(7) << "PyInterpreterFrameProxyType has not been ready!"; - } - Py_INCREF(&PyInterpreterFrameProxyType); -#endif } } // namespace pybind diff --git a/paddle/fluid/pybind/jit.h b/paddle/fluid/pybind/jit.h index 2d1a2e08d1e89c..0472967ef1907b 100644 --- a/paddle/fluid/pybind/jit.h +++ b/paddle/fluid/pybind/jit.h @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include // Avoid a problem with copysign defined in pyconfig.h on Windows. #ifdef copysign #undef copysign @@ -22,97 +21,6 @@ limitations under the License. */ #include "pybind11/pybind11.h" #include "pybind11/stl.h" -// see https://bugs.python.org/issue35886 -// If py_version==3.8.*, we need to redefine _PyEvalFrameFunc and the -// related functions and structs. - -#if PY_VERSION_HEX >= 0x03080000 && PY_VERSION_HEX < 0x3090000 - -typedef PyObject *(*_PyFrameEvalFunction)(struct _frame *, int); - -struct _warnings_runtime_state { - /* Both 'filters' and 'onceregistry' can be set in warnings.py; - get_warnings_attr() will reset these variables accordingly. */ - PyObject *filters; /* List */ - PyObject *once_registry; /* Dict */ - PyObject *default_action; /* String */ - long filters_version; // NOLINT -}; - -struct _is { - struct _is *next; - struct _ts *tstate_head; - - int64_t id; - int64_t id_refcount; - int requires_idref; - PyThread_type_lock id_mutex; - - int finalizing; - - PyObject *modules; - PyObject *modules_by_index; - PyObject *sysdict; - PyObject *builtins; - PyObject *importlib; - - /* Used in Python/sysmodule.c. */ - int check_interval; - - /* Used in Modules/_threadmodule.c. */ - long num_threads; // NOLINT - /* Support for runtime thread stack size tuning. - A value of 0 means using the platform's default stack size - or the size specified by the THREAD_STACK_SIZE macro. */ - /* Used in Python/thread.c. */ - size_t pythread_stacksize; - - PyObject *codec_search_path; - PyObject *codec_search_cache; - PyObject *codec_error_registry; - int codecs_initialized; - - /* fs_codec.encoding is initialized to NULL. - Later, it is set to a non-NULL string by _PyUnicode_InitEncodings(). */ - struct { - char *encoding; /* Filesystem encoding (encoded to UTF-8) */ - char *errors; /* Filesystem errors (encoded to UTF-8) */ - _Py_error_handler error_handler; - } fs_codec; - - PyConfig config; -#ifdef HAVE_DLOPEN - int dlopenflags; -#endif - - PyObject *dict; /* Stores per-interpreter state */ - - PyObject *builtins_copy; - PyObject *import_func; - /* Initialized to PyEval_EvalFrameDefault(). */ - _PyFrameEvalFunction eval_frame; - - Py_ssize_t co_extra_user_count; - freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; - -#ifdef HAVE_FORK - PyObject *before_forkers; - PyObject *after_forkers_parent; - PyObject *after_forkers_child; -#endif - /* AtExit module */ - void (*pyexitfunc)(PyObject *); - PyObject *pyexitmodule; - - uint64_t tstate_next_unique_id; - - struct _warnings_runtime_state warnings; - - PyObject *audit_hooks; -}; - -#endif - namespace paddle { namespace pybind { diff --git a/test/dygraph_to_static/test_pylayer.py b/test/dygraph_to_static/test_pylayer.py index ee2d1248e5f634..fca9534c2cd301 100644 --- a/test/dygraph_to_static/test_pylayer.py +++ b/test/dygraph_to_static/test_pylayer.py @@ -14,13 +14,19 @@ """Tests for PyLayer of Dynamic-to-Static. Only test simple cases here.""" +import sys +from pathlib import Path + +sys.path.append( + str(Path(__file__).absolute().parent.parent.joinpath("legacy_test")) +) import os import tempfile import unittest import numpy as np -from legacy_test.test_jit_save_load import train +from test_jit_save_load import train import paddle from paddle.autograd.py_layer import PyLayer