diff options
Diffstat (limited to 'Cython/Utility/Dataclasses.c')
-rw-r--r-- | Cython/Utility/Dataclasses.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/Cython/Utility/Dataclasses.c b/Cython/Utility/Dataclasses.c new file mode 100644 index 000000000..fc6a88d94 --- /dev/null +++ b/Cython/Utility/Dataclasses.c @@ -0,0 +1,178 @@ +///////////////////// ModuleLoader.proto ////////////////////////// + +static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback_code); /* proto */ + +//////////////////// ModuleLoader /////////////////////// +//@requires: CommonStructures.c::FetchSharedCythonModule + +static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback_code) { + // We want to be able to use the contents of the standard library dataclasses module where available. + // If those objects aren't available (due to Python version) then a simple fallback is substituted + // instead, which largely just fails with a not-implemented error. + // + // The fallbacks are placed in the "shared abi module" as a convenient internal place to + // store them + + PyObject *shared_abi_module = 0, *module = 0; + + shared_abi_module = __Pyx_FetchSharedCythonABIModule(); + if (!shared_abi_module) return NULL; + + if (PyObject_HasAttrString(shared_abi_module, name)) { + PyObject* result = PyObject_GetAttrString(shared_abi_module, name); + Py_DECREF(shared_abi_module); + return result; + } + + // the best and simplest case is simply to defer to the standard library (if available) + module = PyImport_ImportModule(name); + if (!module) { + PyObject *localDict, *runValue, *builtins, *modulename; + if (!PyErr_ExceptionMatches(PyExc_ImportError)) goto bad; + PyErr_Clear(); // this is reasonably likely (especially on older versions of Python) +#if PY_MAJOR_VERSION < 3 + modulename = PyBytes_FromFormat("_cython_" CYTHON_ABI ".%s", name); +#else + modulename = PyUnicode_FromFormat("_cython_" CYTHON_ABI ".%s", name); +#endif + if (!modulename) goto bad; +#if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_CPYTHON + module = PyImport_AddModuleObject(modulename); // borrowed +#else + module = PyImport_AddModule(PyBytes_AsString(modulename)); // borrowed +#endif + Py_DECREF(modulename); + if (!module) goto bad; + Py_INCREF(module); + if (PyObject_SetAttrString(shared_abi_module, name, module) < 0) goto bad; + localDict = PyModule_GetDict(module); // borrowed + if (!localDict) goto bad; + builtins = PyEval_GetBuiltins(); // borrowed + if (!builtins) goto bad; + if (PyDict_SetItemString(localDict, "__builtins__", builtins) <0) goto bad; + + runValue = PyRun_String(fallback_code, Py_file_input, localDict, localDict); + if (!runValue) goto bad; + Py_DECREF(runValue); + } + goto shared_cleanup; + + bad: + Py_CLEAR(module); + shared_cleanup: + Py_XDECREF(shared_abi_module); + return module; +} + +///////////////////// SpecificModuleLoader.proto ////////////////////// +//@substitute: tempita + +static PyObject* __Pyx_Load_{{cname}}_Module(void); /* proto */ + + +//////////////////// SpecificModuleLoader /////////////////////// +//@requires: ModuleLoader + +static PyObject* __Pyx_Load_{{cname}}_Module(void) { + return __Pyx_LoadInternalModule("{{cname}}", {{py_code}}); +} + +//////////////////// DataclassesCallHelper.proto //////////////////////// + +static PyObject* __Pyx_DataclassesCallHelper(PyObject *callable, PyObject *kwds); /* proto */ + +//////////////////// DataclassesCallHelper //////////////////////// +//@substitute: naming + +// The signature of a few of the dataclasses module functions has +// been expanded over the years. Cython always passes the full set +// of arguments from the most recent version we know of, so needs +// to remove any arguments that don't exist on earlier versions. + +#if PY_MAJOR_VERSION >= 3 +static int __Pyx_DataclassesCallHelper_FilterToDict(PyObject *callable, PyObject *kwds, PyObject *new_kwds, PyObject *args_list, int is_kwonly) { + Py_ssize_t size, i; + size = PySequence_Size(args_list); + if (size == -1) return -1; + + for (i=0; i<size; ++i) { + PyObject *key, *value; + int setitem_result; + key = PySequence_GetItem(args_list, i); + if (!key) return -1; + + if (PyUnicode_Check(key) && ( + PyUnicode_CompareWithASCIIString(key, "self") == 0 || + // namedtuple constructor in fallback code + PyUnicode_CompareWithASCIIString(key, "_cls") == 0)) { + Py_DECREF(key); + continue; + } + + value = PyDict_GetItem(kwds, key); + if (!value) { + if (is_kwonly) { + Py_DECREF(key); + continue; + } else { + // The most likely reason for this is that Cython + // hasn't kept up to date with the Python dataclasses module. + // To be nice to our users, try not to fail, but ask them + // to report a bug so we can keep up to date. + value = Py_None; + if (PyErr_WarnFormat( + PyExc_RuntimeWarning, 1, + "Argument %S not passed to %R. This is likely a bug in Cython so please report it.", + key, callable) == -1) { + Py_DECREF(key); + return -1; + } + } + } + Py_INCREF(value); + setitem_result = PyDict_SetItem(new_kwds, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (setitem_result == -1) return -1; + } + return 0; +} +#endif + +static PyObject* __Pyx_DataclassesCallHelper(PyObject *callable, PyObject *kwds) { +#if PY_MAJOR_VERSION < 3 + // We're falling back to our full replacement anyway + return PyObject_Call(callable, $empty_tuple, kwds); +#else + PyObject *new_kwds=NULL, *result=NULL; + PyObject *inspect; + PyObject *args_list=NULL, *kwonly_args_list=NULL, *getfullargspec_result=NULL; + + // Going via inspect to work out what arguments to pass is unlikely to be the + // fastest thing ever. However, it is compatible, and only happens once + // at module-import time. + inspect = PyImport_ImportModule("inspect"); + if (!inspect) goto bad; + getfullargspec_result = PyObject_CallMethodObjArgs(inspect, PYUNICODE("getfullargspec"), callable, NULL); + Py_DECREF(inspect); + if (!getfullargspec_result) goto bad; + args_list = PyObject_GetAttrString(getfullargspec_result, "args"); + if (!args_list) goto bad; + kwonly_args_list = PyObject_GetAttrString(getfullargspec_result, "kwonlyargs"); + if (!kwonly_args_list) goto bad; + + new_kwds = PyDict_New(); + if (!new_kwds) goto bad; + + // copy over only those arguments that are in the specification + if (__Pyx_DataclassesCallHelper_FilterToDict(callable, kwds, new_kwds, args_list, 0) == -1) goto bad; + if (__Pyx_DataclassesCallHelper_FilterToDict(callable, kwds, new_kwds, kwonly_args_list, 1) == -1) goto bad; + result = PyObject_Call(callable, $empty_tuple, new_kwds); +bad: + Py_XDECREF(getfullargspec_result); + Py_XDECREF(args_list); + Py_XDECREF(kwonly_args_list); + Py_XDECREF(new_kwds); + return result; +#endif +} |