diff options
author | Federico Caselli <cfederico87@gmail.com> | 2021-01-01 16:09:01 +0100 |
---|---|---|
committer | Federico Caselli <cfederico87@gmail.com> | 2021-12-17 21:29:05 +0100 |
commit | 76fa211620de167b76846f0e5db5b64b8756ad48 (patch) | |
tree | c435dbf6585b3758dc78ee82bf114e162a25d0e1 /lib/sqlalchemy/cextension | |
parent | 3543fcc9c9601e81560d055ceadaea05c75815c0 (diff) | |
download | sqlalchemy-workflow_test_cython.tar.gz |
Replace c extension with cython versions.workflow_test_cython
Re-implement c version immutabledict / processors / resultproxy / utils with cython.
Performance is in general in par or better than the c version
Added a collection module that has cython version of OrderedSet and IdentitySet
Added a new test/perf file to compare the implementations.
Run ``python test/perf/compiled_extensions.py all`` to execute the comparison test.
See results here: https://docs.google.com/document/d/1nOcDGojHRtXEkuy4vNXcW_XOJd9gqKhSeALGG3kYr6A/edit?usp=sharing
Fixes: #7256
Change-Id: I2930ef1894b5048210384728118e586e813f6a76
Signed-off-by: Federico Caselli <cfederico87@gmail.com>
Diffstat (limited to 'lib/sqlalchemy/cextension')
-rw-r--r-- | lib/sqlalchemy/cextension/immutabledict.c | 526 | ||||
-rw-r--r-- | lib/sqlalchemy/cextension/processors.c | 508 | ||||
-rw-r--r-- | lib/sqlalchemy/cextension/resultproxy.c | 1033 |
3 files changed, 0 insertions, 2067 deletions
diff --git a/lib/sqlalchemy/cextension/immutabledict.c b/lib/sqlalchemy/cextension/immutabledict.c deleted file mode 100644 index 1188dcd2b..000000000 --- a/lib/sqlalchemy/cextension/immutabledict.c +++ /dev/null @@ -1,526 +0,0 @@ -/* -immuatbledict.c -Copyright (C) 2005-2021 the SQLAlchemy authors and contributors <see AUTHORS file> - -This module is part of SQLAlchemy and is released under -the MIT License: https://www.opensource.org/licenses/mit-license.php -*/ - -#include <Python.h> - -#define MODULE_NAME "cimmutabledict" -#define MODULE_DOC "immutable dictionary implementation" - - -typedef struct { - PyObject_HEAD - PyObject *dict; -} ImmutableDict; - -static PyTypeObject ImmutableDictType; - - -#if PY_MAJOR_VERSION < 3 -/* For Python 2.7, VENDORED from cPython: https://github.com/python/cpython/commit/1c496178d2c863f135bd4a43e32e0f099480cd06 - This function was added to Python 2.7.12 as an underscore function. - - Variant of PyDict_GetItem() that doesn't suppress exceptions. - This returns NULL *with* an exception set if an exception occurred. - It returns NULL *without* an exception set if the key wasn't present. -*/ -PyObject * -PyDict_GetItemWithError(PyObject *op, PyObject *key) -{ - long hash; - PyDictObject *mp = (PyDictObject *)op; - PyDictEntry *ep; - if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); - return NULL; - } - if (!PyString_CheckExact(key) || - (hash = ((PyStringObject *) key)->ob_shash) == -1) - { - hash = PyObject_Hash(key); - if (hash == -1) { - return NULL; - } - } - - ep = (mp->ma_lookup)(mp, key, hash); - if (ep == NULL) { - return NULL; - } - return ep->me_value; -} -#endif - -static PyObject * - -ImmutableDict_new(PyTypeObject *type, PyObject *args, PyObject *kw) - -{ - ImmutableDict *new_obj; - PyObject *arg_dict = NULL; - PyObject *our_dict; - - if (!PyArg_UnpackTuple(args, "ImmutableDict", 0, 1, &arg_dict)) { - return NULL; - } - - if (arg_dict != NULL && PyDict_CheckExact(arg_dict)) { - // going on the unproven theory that doing PyDict_New + PyDict_Update - // is faster than just calling CallObject, as we do below to - // accommodate for other dictionary argument forms - our_dict = PyDict_New(); - if (our_dict == NULL) { - return NULL; - } - - if (PyDict_Update(our_dict, arg_dict) == -1) { - Py_DECREF(our_dict); - return NULL; - } - } - else { - // for other calling styles, let PyDict figure it out - our_dict = PyObject_Call((PyObject *) &PyDict_Type, args, kw); - } - - new_obj = PyObject_GC_New(ImmutableDict, &ImmutableDictType); - if (new_obj == NULL) { - Py_DECREF(our_dict); - return NULL; - } - new_obj->dict = our_dict; - PyObject_GC_Track(new_obj); - - return (PyObject *)new_obj; - -} - - -Py_ssize_t -ImmutableDict_length(ImmutableDict *self) -{ - return PyDict_Size(self->dict); -} - -static PyObject * -ImmutableDict_subscript(ImmutableDict *self, PyObject *key) -{ - PyObject *value; -#if PY_MAJOR_VERSION >= 3 - PyObject *err_bytes; -#endif - - value = PyDict_GetItemWithError(self->dict, key); - - if (value == NULL) { - if (PyErr_Occurred() != NULL) { - // there was an error while hashing the key - return NULL; - } -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsUTF8String(key); - if (err_bytes == NULL) - return NULL; - PyErr_Format(PyExc_KeyError, "%s", PyBytes_AS_STRING(err_bytes)); -#else - PyErr_Format(PyExc_KeyError, "%s", PyString_AsString(key)); -#endif - return NULL; - } - - Py_INCREF(value); - - return value; -} - - -static void -ImmutableDict_dealloc(ImmutableDict *self) -{ - PyObject_GC_UnTrack(self); - Py_XDECREF(self->dict); - PyObject_GC_Del(self); -} - - -static PyObject * -ImmutableDict_reduce(ImmutableDict *self) -{ - return Py_BuildValue("O(O)", Py_TYPE(self), self->dict); -} - - -static PyObject * -ImmutableDict_repr(ImmutableDict *self) -{ - return PyUnicode_FromFormat("immutabledict(%R)", self->dict); -} - - -static PyObject * -ImmutableDict_union(PyObject *self, PyObject *args, PyObject *kw) -{ - PyObject *arg_dict, *new_dict; - - ImmutableDict *new_obj; - - if (!PyArg_UnpackTuple(args, "ImmutableDict", 0, 1, &arg_dict)) { - return NULL; - } - - if (!PyDict_CheckExact(arg_dict)) { - // if we didn't get a dict, and got lists of tuples or - // keyword args, make a dict - arg_dict = PyObject_Call((PyObject *) &PyDict_Type, args, kw); - if (arg_dict == NULL) { - return NULL; - } - } - else { - // otherwise we will use the dict as is - Py_INCREF(arg_dict); - } - - if (PyDict_Size(arg_dict) == 0) { - Py_DECREF(arg_dict); - Py_INCREF(self); - return self; - } - - new_dict = PyDict_New(); - if (new_dict == NULL) { - Py_DECREF(arg_dict); - return NULL; - } - - if (PyDict_Update(new_dict, ((ImmutableDict *)self)->dict) == -1) { - Py_DECREF(arg_dict); - Py_DECREF(new_dict); - return NULL; - } - - if (PyDict_Update(new_dict, arg_dict) == -1) { - Py_DECREF(arg_dict); - Py_DECREF(new_dict); - return NULL; - } - - Py_DECREF(arg_dict); - - new_obj = PyObject_GC_New(ImmutableDict, Py_TYPE(self)); - if (new_obj == NULL) { - Py_DECREF(new_dict); - return NULL; - } - - new_obj->dict = new_dict; - - PyObject_GC_Track(new_obj); - - return (PyObject *)new_obj; -} - - -static PyObject * -ImmutableDict_merge_with(PyObject *self, PyObject *args) -{ - PyObject *element, *arg, *new_dict = NULL; - - ImmutableDict *new_obj; - - Py_ssize_t num_args = PyTuple_Size(args); - Py_ssize_t i; - - for (i=0; i<num_args; i++) { - element = PyTuple_GetItem(args, i); - - if (element == NULL) { - Py_XDECREF(new_dict); - return NULL; - } - else if (element == Py_None) { - // none was passed, skip it - continue; - } - - if (!PyDict_CheckExact(element)) { - // not a dict, try to make a dict - - arg = PyTuple_Pack(1, element); - - element = PyObject_CallObject((PyObject *) &PyDict_Type, arg); - - Py_DECREF(arg); - - if (element == NULL) { - Py_XDECREF(new_dict); - return NULL; - } - } - else { - Py_INCREF(element); - if (PyDict_Size(element) == 0) { - continue; - } - } - - // initialize a new dictionary only if we receive data that - // is not empty. otherwise we return self at the end. - if (new_dict == NULL) { - - new_dict = PyDict_New(); - if (new_dict == NULL) { - Py_DECREF(element); - return NULL; - } - - if (PyDict_Update(new_dict, ((ImmutableDict *)self)->dict) == -1) { - Py_DECREF(element); - Py_DECREF(new_dict); - return NULL; - } - } - - if (PyDict_Update(new_dict, element) == -1) { - Py_DECREF(element); - Py_DECREF(new_dict); - return NULL; - } - - Py_DECREF(element); - } - - - if (new_dict != NULL) { - new_obj = PyObject_GC_New(ImmutableDict, Py_TYPE(self)); - if (new_obj == NULL) { - Py_DECREF(new_dict); - return NULL; - } - - new_obj->dict = new_dict; - PyObject_GC_Track(new_obj); - return (PyObject *)new_obj; - } - else { - Py_INCREF(self); - return self; - } - -} - - -static PyObject * -ImmutableDict_get(ImmutableDict *self, PyObject *args) -{ - PyObject *key; - PyObject *value; - PyObject *default_value = Py_None; - - if (!PyArg_UnpackTuple(args, "key", 1, 2, &key, &default_value)) { - return NULL; - } - - value = PyDict_GetItemWithError(self->dict, key); - - if (value == NULL) { - if (PyErr_Occurred() != NULL) { - // there was an error while hashing the key - return NULL; - } else { - // return default - Py_INCREF(default_value); - return default_value; - } - } else { - Py_INCREF(value); - return value; - } -} - -static PyObject * -ImmutableDict_keys(ImmutableDict *self) -{ - return PyDict_Keys(self->dict); -} - -static int -ImmutableDict_traverse(ImmutableDict *self, visitproc visit, void *arg) -{ - Py_VISIT(self->dict); - return 0; -} - -static PyObject * -ImmutableDict_richcompare(ImmutableDict *self, PyObject *other, int op) -{ - return PyObject_RichCompare(self->dict, other, op); -} - -static PyObject * -ImmutableDict_iter(ImmutableDict *self) -{ - return PyObject_GetIter(self->dict); -} - -static PyObject * -ImmutableDict_items(ImmutableDict *self) -{ - return PyDict_Items(self->dict); -} - -static PyObject * -ImmutableDict_values(ImmutableDict *self) -{ - return PyDict_Values(self->dict); -} - -static PyObject * -ImmutableDict_contains(ImmutableDict *self, PyObject *key) -{ - int ret; - - ret = PyDict_Contains(self->dict, key); - - if (ret == 1) Py_RETURN_TRUE; - else if (ret == 0) Py_RETURN_FALSE; - else return NULL; -} - -static PyMethodDef ImmutableDict_methods[] = { - {"union", (PyCFunction) ImmutableDict_union, METH_VARARGS | METH_KEYWORDS, - "provide a union of this dictionary with the given dictionary-like arguments"}, - {"merge_with", (PyCFunction) ImmutableDict_merge_with, METH_VARARGS, - "provide a union of this dictionary with those given"}, - {"keys", (PyCFunction) ImmutableDict_keys, METH_NOARGS, - "return dictionary keys"}, - - {"__contains__",(PyCFunction)ImmutableDict_contains, METH_O, - "test a member for containment"}, - - {"items", (PyCFunction) ImmutableDict_items, METH_NOARGS, - "return dictionary items"}, - {"values", (PyCFunction) ImmutableDict_values, METH_NOARGS, - "return dictionary values"}, - {"get", (PyCFunction) ImmutableDict_get, METH_VARARGS, - "get a value"}, - {"__reduce__", (PyCFunction)ImmutableDict_reduce, METH_NOARGS, - "Pickle support method."}, - {NULL}, -}; - - -static PyMappingMethods ImmutableDict_as_mapping = { - (lenfunc)ImmutableDict_length, /* mp_length */ - (binaryfunc)ImmutableDict_subscript, /* mp_subscript */ - 0 /* mp_ass_subscript */ -}; - - - - -static PyTypeObject ImmutableDictType = { - PyVarObject_HEAD_INIT(NULL, 0) - "sqlalchemy.cimmutabledict.immutabledict", /* tp_name */ - sizeof(ImmutableDict), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)ImmutableDict_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)ImmutableDict_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - &ImmutableDict_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC , /* tp_flags */ - "immutable dictionary", /* tp_doc */ - (traverseproc)ImmutableDict_traverse, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)ImmutableDict_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)ImmutableDict_iter, /* tp_iter */ - 0, /* tp_iternext */ - ImmutableDict_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - ImmutableDict_new, /* tp_new */ - 0, /* tp_free */ -}; - - - - - -static PyMethodDef module_methods[] = { - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif - - -#if PY_MAJOR_VERSION >= 3 - -static struct PyModuleDef module_def = { - PyModuleDef_HEAD_INIT, - MODULE_NAME, - MODULE_DOC, - -1, - module_methods -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC -PyInit_cimmutabledict(void) - -#else - -#define INITERROR return - -PyMODINIT_FUNC -initcimmutabledict(void) - -#endif - -{ - PyObject *m; - - if (PyType_Ready(&ImmutableDictType) < 0) - INITERROR; - - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&module_def); -#else - m = Py_InitModule3(MODULE_NAME, module_methods, MODULE_DOC); -#endif - if (m == NULL) - INITERROR; - - Py_INCREF(&ImmutableDictType); - PyModule_AddObject(m, "immutabledict", (PyObject *)&ImmutableDictType); - -#if PY_MAJOR_VERSION >= 3 - return m; -#endif -} diff --git a/lib/sqlalchemy/cextension/processors.c b/lib/sqlalchemy/cextension/processors.c deleted file mode 100644 index 8c031b70a..000000000 --- a/lib/sqlalchemy/cextension/processors.c +++ /dev/null @@ -1,508 +0,0 @@ -/* -processors.c -Copyright (C) 2010-2021 the SQLAlchemy authors and contributors <see AUTHORS file> -Copyright (C) 2010-2011 Gaetan de Menten gdementen@gmail.com - -This module is part of SQLAlchemy and is released under -the MIT License: https://www.opensource.org/licenses/mit-license.php -*/ - -#include <Python.h> -#include <datetime.h> - -#define MODULE_NAME "cprocessors" -#define MODULE_DOC "Module containing C versions of data processing functions." - -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif - -static PyObject * -int_to_boolean(PyObject *self, PyObject *arg) -{ - int l = 0; - PyObject *res; - - if (arg == Py_None) - Py_RETURN_NONE; - - l = PyObject_IsTrue(arg); - if (l == 0) { - res = Py_False; - } else if (l == 1) { - res = Py_True; - } else { - return NULL; - } - - Py_INCREF(res); - return res; -} - -static PyObject * -to_str(PyObject *self, PyObject *arg) -{ - if (arg == Py_None) - Py_RETURN_NONE; - - return PyObject_Str(arg); -} - -static PyObject * -to_float(PyObject *self, PyObject *arg) -{ - if (arg == Py_None) - Py_RETURN_NONE; - - return PyNumber_Float(arg); -} - -static PyObject * -str_to_datetime(PyObject *self, PyObject *arg) -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *bytes; - PyObject *err_bytes; -#endif - const char *str; - int numparsed; - unsigned int year, month, day, hour, minute, second, microsecond = 0; - PyObject *err_repr; - - if (arg == Py_None) - Py_RETURN_NONE; - -#if PY_MAJOR_VERSION >= 3 - bytes = PyUnicode_AsASCIIString(arg); - if (bytes == NULL) - str = NULL; - else - str = PyBytes_AS_STRING(bytes); -#else - str = PyString_AsString(arg); -#endif - if (str == NULL) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse datetime string '%.200s' " - "- value is not a string.", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse datetime string '%.200s' " - "- value is not a string.", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - - /* microseconds are optional */ - /* - TODO: this is slightly less picky than the Python version which would - not accept "2000-01-01 00:00:00.". I don't know which is better, but they - should be coherent. - */ - numparsed = sscanf(str, "%4u-%2u-%2u %2u:%2u:%2u.%6u", &year, &month, &day, - &hour, &minute, &second, µsecond); -#if PY_MAJOR_VERSION >= 3 - Py_DECREF(bytes); -#endif - if (numparsed < 6) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse datetime string: %.200s", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse datetime string: %.200s", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - return PyDateTime_FromDateAndTime(year, month, day, - hour, minute, second, microsecond); -} - -static PyObject * -str_to_time(PyObject *self, PyObject *arg) -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *bytes; - PyObject *err_bytes; -#endif - const char *str; - int numparsed; - unsigned int hour, minute, second, microsecond = 0; - PyObject *err_repr; - - if (arg == Py_None) - Py_RETURN_NONE; - -#if PY_MAJOR_VERSION >= 3 - bytes = PyUnicode_AsASCIIString(arg); - if (bytes == NULL) - str = NULL; - else - str = PyBytes_AS_STRING(bytes); -#else - str = PyString_AsString(arg); -#endif - if (str == NULL) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; - -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse time string '%.200s' - value is not a string.", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse time string '%.200s' - value is not a string.", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - - /* microseconds are optional */ - /* - TODO: this is slightly less picky than the Python version which would - not accept "00:00:00.". I don't know which is better, but they should be - coherent. - */ - numparsed = sscanf(str, "%2u:%2u:%2u.%6u", &hour, &minute, &second, - µsecond); -#if PY_MAJOR_VERSION >= 3 - Py_DECREF(bytes); -#endif - if (numparsed < 3) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse time string: %.200s", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse time string: %.200s", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - return PyTime_FromTime(hour, minute, second, microsecond); -} - -static PyObject * -str_to_date(PyObject *self, PyObject *arg) -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *bytes; - PyObject *err_bytes; -#endif - const char *str; - int numparsed; - unsigned int year, month, day; - PyObject *err_repr; - - if (arg == Py_None) - Py_RETURN_NONE; - -#if PY_MAJOR_VERSION >= 3 - bytes = PyUnicode_AsASCIIString(arg); - if (bytes == NULL) - str = NULL; - else - str = PyBytes_AS_STRING(bytes); -#else - str = PyString_AsString(arg); -#endif - if (str == NULL) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse date string '%.200s' - value is not a string.", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse date string '%.200s' - value is not a string.", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - - numparsed = sscanf(str, "%4u-%2u-%2u", &year, &month, &day); -#if PY_MAJOR_VERSION >= 3 - Py_DECREF(bytes); -#endif - if (numparsed != 3) { - err_repr = PyObject_Repr(arg); - if (err_repr == NULL) - return NULL; -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(err_repr); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_ValueError, - "Couldn't parse date string: %.200s", - PyBytes_AS_STRING(err_bytes)); - Py_DECREF(err_bytes); -#else - PyErr_Format( - PyExc_ValueError, - "Couldn't parse date string: %.200s", - PyString_AsString(err_repr)); -#endif - Py_DECREF(err_repr); - return NULL; - } - return PyDate_FromDate(year, month, day); -} - - -/*********** - * Structs * - ***********/ - -typedef struct { - PyObject_HEAD - PyObject *type; - PyObject *format; -} DecimalResultProcessor; - - - - -/************************** - * DecimalResultProcessor * - **************************/ - -static int -DecimalResultProcessor_init(DecimalResultProcessor *self, PyObject *args, - PyObject *kwds) -{ - PyObject *type, *format; - -#if PY_MAJOR_VERSION >= 3 - if (!PyArg_ParseTuple(args, "OU", &type, &format)) -#else - if (!PyArg_ParseTuple(args, "OS", &type, &format)) -#endif - return -1; - - Py_INCREF(type); - self->type = type; - - Py_INCREF(format); - self->format = format; - - return 0; -} - -static PyObject * -DecimalResultProcessor_process(DecimalResultProcessor *self, PyObject *value) -{ - PyObject *str, *result, *args; - - if (value == Py_None) - Py_RETURN_NONE; - - /* Decimal does not accept float values directly */ - /* SQLite can also give us an integer here (see [ticket:2432]) */ - /* XXX: starting with Python 3.1, we could use Decimal.from_float(f), - but the result wouldn't be the same */ - - args = PyTuple_Pack(1, value); - if (args == NULL) - return NULL; - -#if PY_MAJOR_VERSION >= 3 - str = PyUnicode_Format(self->format, args); -#else - str = PyString_Format(self->format, args); -#endif - - Py_DECREF(args); - if (str == NULL) - return NULL; - - result = PyObject_CallFunctionObjArgs(self->type, str, NULL); - Py_DECREF(str); - return result; -} - -static void -DecimalResultProcessor_dealloc(DecimalResultProcessor *self) -{ - Py_XDECREF(self->type); - Py_XDECREF(self->format); -#if PY_MAJOR_VERSION >= 3 - Py_TYPE(self)->tp_free((PyObject*)self); -#else - self->ob_type->tp_free((PyObject*)self); -#endif -} - -static PyMethodDef DecimalResultProcessor_methods[] = { - {"process", (PyCFunction)DecimalResultProcessor_process, METH_O, - "The value processor itself."}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject DecimalResultProcessorType = { - PyVarObject_HEAD_INIT(NULL, 0) - "sqlalchemy.DecimalResultProcessor", /* tp_name */ - sizeof(DecimalResultProcessor), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)DecimalResultProcessor_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "DecimalResultProcessor objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - DecimalResultProcessor_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)DecimalResultProcessor_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -static PyMethodDef module_methods[] = { - {"int_to_boolean", int_to_boolean, METH_O, - "Convert an integer to a boolean."}, - {"to_str", to_str, METH_O, - "Convert any value to its string representation."}, - {"to_float", to_float, METH_O, - "Convert any value to its floating point representation."}, - {"str_to_datetime", str_to_datetime, METH_O, - "Convert an ISO string to a datetime.datetime object."}, - {"str_to_time", str_to_time, METH_O, - "Convert an ISO string to a datetime.time object."}, - {"str_to_date", str_to_date, METH_O, - "Convert an ISO string to a datetime.date object."}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif - - -#if PY_MAJOR_VERSION >= 3 - -static struct PyModuleDef module_def = { - PyModuleDef_HEAD_INIT, - MODULE_NAME, - MODULE_DOC, - -1, - module_methods -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC -PyInit_cprocessors(void) - -#else - -#define INITERROR return - -PyMODINIT_FUNC -initcprocessors(void) - -#endif - -{ - PyObject *m; - - DecimalResultProcessorType.tp_new = PyType_GenericNew; - if (PyType_Ready(&DecimalResultProcessorType) < 0) - INITERROR; - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&module_def); -#else - m = Py_InitModule3(MODULE_NAME, module_methods, MODULE_DOC); -#endif - if (m == NULL) - INITERROR; - - PyDateTime_IMPORT; - - Py_INCREF(&DecimalResultProcessorType); - PyModule_AddObject(m, "DecimalResultProcessor", - (PyObject *)&DecimalResultProcessorType); - -#if PY_MAJOR_VERSION >= 3 - return m; -#endif -} diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c deleted file mode 100644 index 99b2d36f3..000000000 --- a/lib/sqlalchemy/cextension/resultproxy.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* -resultproxy.c -Copyright (C) 2010-2021 the SQLAlchemy authors and contributors <see AUTHORS file> -Copyright (C) 2010-2011 Gaetan de Menten gdementen@gmail.com - -This module is part of SQLAlchemy and is released under -the MIT License: https://www.opensource.org/licenses/mit-license.php -*/ - -#include <Python.h> - -#define MODULE_NAME "cresultproxy" -#define MODULE_DOC "Module containing C versions of core ResultProxy classes." - -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -typedef Py_ssize_t (*lenfunc)(PyObject *); -#define PyInt_FromSsize_t(x) PyInt_FromLong(x) -typedef intargfunc ssizeargfunc; -#endif - -#if PY_MAJOR_VERSION < 3 - -// new typedef in Python 3 -typedef long Py_hash_t; - -// from pymacro.h, new in Python 3.2 -#if defined(__GNUC__) || defined(__clang__) -# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) -#else -# define Py_UNUSED(name) _unused_ ## name -#endif - -#endif - - -/*********** - * Structs * - ***********/ - -typedef struct { - PyObject_HEAD - PyObject *parent; - PyObject *row; - PyObject *keymap; - long key_style; -} BaseRow; - - -static PyObject *sqlalchemy_engine_row = NULL; -static PyObject *sqlalchemy_engine_result = NULL; - - -static int KEY_INTEGER_ONLY = 0; -static int KEY_OBJECTS_ONLY = 1; - -/**************** - * BaseRow * - ****************/ - -static PyObject * -safe_rowproxy_reconstructor(PyObject *self, PyObject *args) -{ - PyObject *cls, *state, *tmp; - BaseRow *obj; - - if (!PyArg_ParseTuple(args, "OO", &cls, &state)) - return NULL; - - obj = (BaseRow *)PyObject_CallMethod(cls, "__new__", "O", cls); - if (obj == NULL) - return NULL; - - tmp = PyObject_CallMethod((PyObject *)obj, "__setstate__", "O", state); - if (tmp == NULL) { - Py_DECREF(obj); - return NULL; - } - Py_DECREF(tmp); - - if (obj->parent == NULL || obj->row == NULL || - obj->keymap == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "__setstate__ for BaseRow subclasses must set values " - "for parent, row, and keymap"); - Py_DECREF(obj); - return NULL; - } - - return (PyObject *)obj; -} - -static int -BaseRow_init(BaseRow *self, PyObject *args, PyObject *kwds) -{ - PyObject *parent, *keymap, *row, *processors, *key_style; - Py_ssize_t num_values, num_processors; - PyObject **valueptr, **funcptr, **resultptr; - PyObject *func, *result, *processed_value, *values_fastseq; - - if (!PyArg_UnpackTuple(args, "BaseRow", 5, 5, - &parent, &processors, &keymap, &key_style, &row)) - return -1; - - Py_INCREF(parent); - self->parent = parent; - - values_fastseq = PySequence_Fast(row, "row must be a sequence"); - if (values_fastseq == NULL) - return -1; - - num_values = PySequence_Length(values_fastseq); - - - if (processors != Py_None) { - num_processors = PySequence_Size(processors); - if (num_values != num_processors) { - PyErr_Format(PyExc_RuntimeError, - "number of values in row (%d) differ from number of column " - "processors (%d)", - (int)num_values, (int)num_processors); - return -1; - } - - } else { - num_processors = -1; - } - - result = PyTuple_New(num_values); - if (result == NULL) - return -1; - - if (num_processors != -1) { - valueptr = PySequence_Fast_ITEMS(values_fastseq); - funcptr = PySequence_Fast_ITEMS(processors); - resultptr = PySequence_Fast_ITEMS(result); - while (--num_values >= 0) { - func = *funcptr; - if (func != Py_None) { - processed_value = PyObject_CallFunctionObjArgs( - func, *valueptr, NULL); - if (processed_value == NULL) { - Py_DECREF(values_fastseq); - Py_DECREF(result); - return -1; - } - *resultptr = processed_value; - } else { - Py_INCREF(*valueptr); - *resultptr = *valueptr; - } - valueptr++; - funcptr++; - resultptr++; - } - } else { - valueptr = PySequence_Fast_ITEMS(values_fastseq); - resultptr = PySequence_Fast_ITEMS(result); - while (--num_values >= 0) { - Py_INCREF(*valueptr); - *resultptr = *valueptr; - valueptr++; - resultptr++; - } - } - - Py_DECREF(values_fastseq); - self->row = result; - - if (!PyDict_CheckExact(keymap)) { - PyErr_SetString(PyExc_TypeError, "keymap must be a dict"); - return -1; - } - Py_INCREF(keymap); - self->keymap = keymap; - self->key_style = PyLong_AsLong(key_style); - - // observation: because we have not implemented our own new method, - // cPython is apparently already calling PyObject_GC_Track for us. - // We assume it also called PyObject_GC_New since prior to #5348 we - // were already relying upon it to call PyObject_New, and we have now - // set Py_TPFLAGS_HAVE_GC. - - return 0; -} - -static int -BaseRow_traverse(BaseRow *self, visitproc visit, void *arg) -{ - Py_VISIT(self->parent); - Py_VISIT(self->row); - Py_VISIT(self->keymap); - return 0; -} - -/* We need the reduce method because otherwise the default implementation - * does very weird stuff for pickle protocol 0 and 1. It calls - * BaseRow.__new__(Row_instance) upon *pickling*. - */ -static PyObject * -BaseRow_reduce(PyObject *self) -{ - PyObject *method, *state; - PyObject *module, *reconstructor, *cls; - - method = PyObject_GetAttrString(self, "__getstate__"); - if (method == NULL) - return NULL; - - state = PyObject_CallObject(method, NULL); - Py_DECREF(method); - if (state == NULL) - return NULL; - - if (sqlalchemy_engine_row == NULL) { - module = PyImport_ImportModule("sqlalchemy.engine.row"); - if (module == NULL) - return NULL; - sqlalchemy_engine_row = module; - } - - reconstructor = PyObject_GetAttrString(sqlalchemy_engine_row, "rowproxy_reconstructor"); - if (reconstructor == NULL) { - Py_DECREF(state); - return NULL; - } - - cls = PyObject_GetAttrString(self, "__class__"); - if (cls == NULL) { - Py_DECREF(reconstructor); - Py_DECREF(state); - return NULL; - } - - return Py_BuildValue("(N(NN))", reconstructor, cls, state); -} - -static PyObject * -BaseRow_filter_on_values(BaseRow *self, PyObject *filters) -{ - PyObject *module, *row_class, *new_obj, *key_style; - - if (sqlalchemy_engine_row == NULL) { - module = PyImport_ImportModule("sqlalchemy.engine.row"); - if (module == NULL) - return NULL; - sqlalchemy_engine_row = module; - } - - // TODO: do we want to get self.__class__ instead here? I'm not sure - // how to use METH_VARARGS and then also get the BaseRow struct - // at the same time - row_class = PyObject_GetAttrString(sqlalchemy_engine_row, "Row"); - - key_style = PyLong_FromLong(self->key_style); - - new_obj = PyObject_CallFunction( - row_class, "OOOOO", self->parent, filters, self->keymap, - key_style, self->row); - Py_DECREF(key_style); - Py_DECREF(row_class); - if (new_obj == NULL) { - return NULL; - } - - return new_obj; - -} - -static void -BaseRow_dealloc(BaseRow *self) -{ - PyObject_GC_UnTrack(self); - Py_XDECREF(self->parent); - Py_XDECREF(self->row); - Py_XDECREF(self->keymap); - PyObject_GC_Del(self); - -} - -static PyObject * -BaseRow_valuescollection(PyObject *values, int astuple) -{ - PyObject *result; - - if (astuple) { - result = PySequence_Tuple(values); - } else { - result = PySequence_List(values); - } - if (result == NULL) - return NULL; - - return result; -} - -static PyListObject * -BaseRow_values_impl(BaseRow *self) -{ - return (PyListObject *)BaseRow_valuescollection(self->row, 0); -} - -static Py_hash_t -BaseRow_hash(BaseRow *self) -{ - return PyObject_Hash(self->row); -} - -static PyObject * -BaseRow_iter(BaseRow *self) -{ - PyObject *values, *result; - - values = BaseRow_valuescollection(self->row, 1); - if (values == NULL) - return NULL; - - result = PyObject_GetIter(values); - Py_DECREF(values); - if (result == NULL) - return NULL; - - return result; -} - -static Py_ssize_t -BaseRow_length(BaseRow *self) -{ - return PySequence_Length(self->row); -} - -static PyObject * -BaseRow_getitem(BaseRow *self, Py_ssize_t i) -{ - PyObject *value; - PyObject *row; - - row = self->row; - - // row is a Tuple - value = PyTuple_GetItem(row, i); - - if (value == NULL) - return NULL; - - Py_INCREF(value); - - return value; -} - -static PyObject * -BaseRow_getitem_by_object(BaseRow *self, PyObject *key, int asmapping) -{ - PyObject *record, *indexobject; - long index; - int key_fallback = 0; - - // we want to raise TypeError for slice access on a mapping. - // Py3 will do this with PyDict_GetItemWithError, Py2 will do it - // with PyObject_GetItem. However in the Python2 case the object - // protocol gets in the way for reasons not entirely clear, so - // detect slice we have a key error and raise directly. - - record = PyDict_GetItem((PyObject *)self->keymap, key); - - if (record == NULL) { - if (PySlice_Check(key)) { - PyErr_Format(PyExc_TypeError, "can't use slices for mapping access"); - return NULL; - } - record = PyObject_CallMethod(self->parent, "_key_fallback", - "OO", key, Py_None); - if (record == NULL) - return NULL; - - key_fallback = 1; // boolean to indicate record is a new reference - } - - indexobject = PyTuple_GetItem(record, 0); - if (indexobject == NULL) - return NULL; - - if (key_fallback) { - Py_DECREF(record); - } - - if (indexobject == Py_None) { - PyObject *tmp; - - tmp = PyObject_CallMethod(self->parent, "_raise_for_ambiguous_column_name", "(O)", record); - if (tmp == NULL) { - return NULL; - } - Py_DECREF(tmp); - - return NULL; - } - -#if PY_MAJOR_VERSION >= 3 - index = PyLong_AsLong(indexobject); -#else - index = PyInt_AsLong(indexobject); -#endif - if ((index == -1) && PyErr_Occurred()) - /* -1 can be either the actual value, or an error flag. */ - return NULL; - - return BaseRow_getitem(self, index); - -} - -static PyObject * -BaseRow_subscript_impl(BaseRow *self, PyObject *key, int asmapping) -{ - PyObject *values; - PyObject *result; - long index; - PyObject *tmp; - -#if PY_MAJOR_VERSION < 3 - if (PyInt_CheckExact(key)) { - if (self->key_style == KEY_OBJECTS_ONLY) { - // TODO: being very lazy with error catching here - PyErr_Format(PyExc_KeyError, "%s", PyString_AsString(PyObject_Repr(key))); - return NULL; - } - index = PyInt_AS_LONG(key); - - // support negative indexes. We can also call PySequence_GetItem, - // but here we can stay with the simpler tuple protocol - // rather than the sequence protocol which has to check for - // __getitem__ methods etc. - if (index < 0) - index += (long)BaseRow_length(self); - return BaseRow_getitem(self, index); - } else -#endif - - if (PyLong_CheckExact(key)) { - if (self->key_style == KEY_OBJECTS_ONLY) { -#if PY_MAJOR_VERSION < 3 - // TODO: being very lazy with error catching here - PyErr_Format(PyExc_KeyError, "%s", PyString_AsString(PyObject_Repr(key))); -#else - PyErr_Format(PyExc_KeyError, "%R", key); -#endif - return NULL; - } - index = PyLong_AsLong(key); - if ((index == -1) && PyErr_Occurred() != NULL) - /* -1 can be either the actual value, or an error flag. */ - return NULL; - - // support negative indexes. We can also call PySequence_GetItem, - // but here we can stay with the simpler tuple protocol - // rather than the sequence protocol which has to check for - // __getitem__ methods etc. - if (index < 0) - index += (long)BaseRow_length(self); - return BaseRow_getitem(self, index); - - } else if (PySlice_Check(key) && self->key_style != KEY_OBJECTS_ONLY) { - values = PyObject_GetItem(self->row, key); - if (values == NULL) - return NULL; - - result = BaseRow_valuescollection(values, 1); - Py_DECREF(values); - return result; - } - else if (!asmapping && self->key_style == KEY_INTEGER_ONLY) { - tmp = PyObject_CallMethod(self->parent, "_raise_for_nonint", "(O)", key); - if (tmp == NULL) { - return NULL; - } - Py_DECREF(tmp); - return NULL; - } else { - return BaseRow_getitem_by_object(self, key, asmapping); - } -} - -static PyObject * -BaseRow_subscript(BaseRow *self, PyObject *key) -{ - return BaseRow_subscript_impl(self, key, 0); -} - -static PyObject * -BaseRow_subscript_mapping(BaseRow *self, PyObject *key) -{ - if (self->key_style == KEY_INTEGER_ONLY) { - return BaseRow_subscript_impl(self, key, 0); - } - else { - return BaseRow_subscript_impl(self, key, 1); - } -} - - -static PyObject * -BaseRow_getattro(BaseRow *self, PyObject *name) -{ - PyObject *tmp; -#if PY_MAJOR_VERSION >= 3 - PyObject *err_bytes; -#endif - - if (!(tmp = PyObject_GenericGetAttr((PyObject *)self, name))) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) - return NULL; - PyErr_Clear(); - } - else - return tmp; - - tmp = BaseRow_subscript_impl(self, name, 1); - - if (tmp == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { - -#if PY_MAJOR_VERSION >= 3 - err_bytes = PyUnicode_AsASCIIString(name); - if (err_bytes == NULL) - return NULL; - PyErr_Format( - PyExc_AttributeError, - "Could not locate column in row for column '%.200s'", - PyBytes_AS_STRING(err_bytes) - ); -#else - PyErr_Format( - PyExc_AttributeError, - "Could not locate column in row for column '%.200s'", - PyString_AsString(name) - ); -#endif - return NULL; - } - return tmp; -} - -/*********************** - * getters and setters * - ***********************/ - -static PyObject * -BaseRow_getparent(BaseRow *self, void *closure) -{ - Py_INCREF(self->parent); - return self->parent; -} - -static int -BaseRow_setparent(BaseRow *self, PyObject *value, void *closure) -{ - PyObject *module, *cls; - - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, - "Cannot delete the 'parent' attribute"); - return -1; - } - - if (sqlalchemy_engine_result == NULL) { - module = PyImport_ImportModule("sqlalchemy.engine.result"); - if (module == NULL) - return -1; - sqlalchemy_engine_result = module; - } - - cls = PyObject_GetAttrString(sqlalchemy_engine_result, "ResultMetaData"); - if (cls == NULL) - return -1; - - if (PyObject_IsInstance(value, cls) != 1) { - PyErr_SetString(PyExc_TypeError, - "The 'parent' attribute value must be an instance of " - "ResultMetaData"); - return -1; - } - Py_DECREF(cls); - Py_XDECREF(self->parent); - Py_INCREF(value); - self->parent = value; - - return 0; -} - -static PyObject * -BaseRow_getrow(BaseRow *self, void *closure) -{ - Py_INCREF(self->row); - return self->row; -} - -static int -BaseRow_setrow(BaseRow *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, - "Cannot delete the 'row' attribute"); - return -1; - } - - if (!PySequence_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The 'row' attribute value must be a sequence"); - return -1; - } - - Py_XDECREF(self->row); - Py_INCREF(value); - self->row = value; - - return 0; -} - - - -static PyObject * -BaseRow_getkeymap(BaseRow *self, void *closure) -{ - Py_INCREF(self->keymap); - return self->keymap; -} - -static int -BaseRow_setkeymap(BaseRow *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, - "Cannot delete the 'keymap' attribute"); - return -1; - } - - if (!PyDict_CheckExact(value)) { - PyErr_SetString(PyExc_TypeError, - "The 'keymap' attribute value must be a dict"); - return -1; - } - - Py_XDECREF(self->keymap); - Py_INCREF(value); - self->keymap = value; - - return 0; -} - -static PyObject * -BaseRow_getkeystyle(BaseRow *self, void *closure) -{ - PyObject *result; - - result = PyLong_FromLong(self->key_style); - Py_INCREF(result); - return result; -} - - -static int -BaseRow_setkeystyle(BaseRow *self, PyObject *value, void *closure) -{ - if (value == NULL) { - - PyErr_SetString( - PyExc_TypeError, - "Cannot delete the 'key_style' attribute"); - return -1; - } - - if (!PyLong_CheckExact(value)) { - PyErr_SetString( - PyExc_TypeError, - "The 'key_style' attribute value must be an integer"); - return -1; - } - - self->key_style = PyLong_AsLong(value); - - return 0; -} - -static PyGetSetDef BaseRow_getseters[] = { - {"_parent", - (getter)BaseRow_getparent, (setter)BaseRow_setparent, - "ResultMetaData", - NULL}, - {"_data", - (getter)BaseRow_getrow, (setter)BaseRow_setrow, - "processed data list", - NULL}, - {"_keymap", - (getter)BaseRow_getkeymap, (setter)BaseRow_setkeymap, - "Key to (obj, index) dict", - NULL}, - {"_key_style", - (getter)BaseRow_getkeystyle, (setter)BaseRow_setkeystyle, - "Return the key style", - NULL}, - {NULL} -}; - -static PyMethodDef BaseRow_methods[] = { - {"_values_impl", (PyCFunction)BaseRow_values_impl, METH_NOARGS, - "Return the values represented by this BaseRow as a list."}, - {"__reduce__", (PyCFunction)BaseRow_reduce, METH_NOARGS, - "Pickle support method."}, - {"_get_by_key_impl", (PyCFunction)BaseRow_subscript, METH_O, - "implement mapping-like getitem as well as sequence getitem"}, - {"_get_by_key_impl_mapping", (PyCFunction)BaseRow_subscript_mapping, METH_O, - "implement mapping-like getitem as well as sequence getitem"}, - {"_filter_on_values", (PyCFunction)BaseRow_filter_on_values, METH_O, - "return a new Row with per-value filters applied to columns"}, - - {NULL} /* Sentinel */ -}; - -// currently, the sq_item hook is not used by Python except for slices, -// because we also implement subscript_mapping which seems to intercept -// integers. Ideally, when there -// is a complete separation of "row" from "mapping", we can make -// two separate types here so that one has only sq_item and the other -// has only mp_subscript. -static PySequenceMethods BaseRow_as_sequence = { - (lenfunc)BaseRow_length, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - (ssizeargfunc)BaseRow_getitem, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - 0, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ -}; - -static PyMappingMethods BaseRow_as_mapping = { - (lenfunc)BaseRow_length, /* mp_length */ - (binaryfunc)BaseRow_subscript_mapping, /* mp_subscript */ - 0 /* mp_ass_subscript */ -}; - -static PyTypeObject BaseRowType = { - PyVarObject_HEAD_INIT(NULL, 0) - "sqlalchemy.cresultproxy.BaseRow", /* tp_name */ - sizeof(BaseRow), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)BaseRow_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &BaseRow_as_sequence, /* tp_as_sequence */ - &BaseRow_as_mapping, /* tp_as_mapping */ - (hashfunc)BaseRow_hash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - (getattrofunc)BaseRow_getattro,/* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "BaseRow is a abstract base class for Row", /* tp_doc */ - (traverseproc)BaseRow_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)BaseRow_iter, /* tp_iter */ - 0, /* tp_iternext */ - BaseRow_methods, /* tp_methods */ - 0, /* tp_members */ - BaseRow_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)BaseRow_init, /* tp_init */ - 0, /* tp_alloc */ - 0 /* tp_new */ -}; - - - -/* _tuplegetter function ************************************************/ -/* -retrieves segments of a row as tuples. - -mostly like operator.itemgetter but calls a fixed method instead, -returns tuple every time. - -*/ - -typedef struct { - PyObject_HEAD - Py_ssize_t nitems; - PyObject *item; -} tuplegetterobject; - -static PyTypeObject tuplegetter_type; - -static PyObject * -tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - tuplegetterobject *tg; - PyObject *item; - Py_ssize_t nitems; - - if (!_PyArg_NoKeywords("tuplegetter", kwds)) - return NULL; - - nitems = PyTuple_GET_SIZE(args); - item = args; - - tg = PyObject_GC_New(tuplegetterobject, &tuplegetter_type); - if (tg == NULL) - return NULL; - - Py_INCREF(item); - tg->item = item; - tg->nitems = nitems; - PyObject_GC_Track(tg); - return (PyObject *)tg; -} - -static void -tuplegetter_dealloc(tuplegetterobject *tg) -{ - PyObject_GC_UnTrack(tg); - Py_XDECREF(tg->item); - PyObject_GC_Del(tg); -} - -static int -tuplegetter_traverse(tuplegetterobject *tg, visitproc visit, void *arg) -{ - Py_VISIT(tg->item); - return 0; -} - -static PyObject * -tuplegetter_call(tuplegetterobject *tg, PyObject *args, PyObject *kw) -{ - PyObject *row_or_tuple, *result; - Py_ssize_t i, nitems=tg->nitems; - int has_row_method; - - assert(PyTuple_CheckExact(args)); - - // this is a tuple, however if its a BaseRow subclass we want to - // call specific methods to bypass the pure python LegacyRow.__getitem__ - // method for now - row_or_tuple = PyTuple_GET_ITEM(args, 0); - - has_row_method = PyObject_HasAttrString(row_or_tuple, "_get_by_key_impl_mapping"); - - assert(PyTuple_Check(tg->item)); - assert(PyTuple_GET_SIZE(tg->item) == nitems); - - result = PyTuple_New(nitems); - if (result == NULL) - return NULL; - - for (i=0 ; i < nitems ; i++) { - PyObject *item, *val; - item = PyTuple_GET_ITEM(tg->item, i); - - if (has_row_method) { - val = PyObject_CallMethod(row_or_tuple, "_get_by_key_impl_mapping", "O", item); - } - else { - val = PyObject_GetItem(row_or_tuple, item); - } - - if (val == NULL) { - Py_DECREF(result); - return NULL; - } - PyTuple_SET_ITEM(result, i, val); - } - return result; -} - -static PyObject * -tuplegetter_repr(tuplegetterobject *tg) -{ - PyObject *repr; - const char *reprfmt; - - int status = Py_ReprEnter((PyObject *)tg); - if (status != 0) { - if (status < 0) - return NULL; - return PyUnicode_FromFormat("%s(...)", Py_TYPE(tg)->tp_name); - } - - reprfmt = tg->nitems == 1 ? "%s(%R)" : "%s%R"; - repr = PyUnicode_FromFormat(reprfmt, Py_TYPE(tg)->tp_name, tg->item); - Py_ReprLeave((PyObject *)tg); - return repr; -} - -static PyObject * -tuplegetter_reduce(tuplegetterobject *tg, PyObject *Py_UNUSED(ignored)) -{ - return PyTuple_Pack(2, Py_TYPE(tg), tg->item); -} - -PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); - -static PyMethodDef tuplegetter_methods[] = { - {"__reduce__", (PyCFunction)tuplegetter_reduce, METH_NOARGS, - reduce_doc}, - {NULL} -}; - -PyDoc_STRVAR(tuplegetter_doc, -"tuplegetter(item, ...) --> tuplegetter object\n\ -\n\ -Return a callable object that fetches the given item(s) from its operand\n\ -and returns them as a tuple.\n"); - -static PyTypeObject tuplegetter_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "sqlalchemy.engine.util.tuplegetter", /* tp_name */ - sizeof(tuplegetterobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)tuplegetter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)tuplegetter_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)tuplegetter_call, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - tuplegetter_doc, /* tp_doc */ - (traverseproc)tuplegetter_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - tuplegetter_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - tuplegetter_new, /* tp_new */ - 0, /* tp_free */ -}; - - - -static PyMethodDef module_methods[] = { - {"safe_rowproxy_reconstructor", safe_rowproxy_reconstructor, METH_VARARGS, - "reconstruct a Row instance from its pickled form."}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif - - -#if PY_MAJOR_VERSION >= 3 - -static struct PyModuleDef module_def = { - PyModuleDef_HEAD_INIT, - MODULE_NAME, - MODULE_DOC, - -1, - module_methods -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC -PyInit_cresultproxy(void) - -#else - -#define INITERROR return - -PyMODINIT_FUNC -initcresultproxy(void) - -#endif - -{ - PyObject *m; - - BaseRowType.tp_new = PyType_GenericNew; - if (PyType_Ready(&BaseRowType) < 0) - INITERROR; - - if (PyType_Ready(&tuplegetter_type) < 0) - INITERROR; - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&module_def); -#else - m = Py_InitModule3(MODULE_NAME, module_methods, MODULE_DOC); -#endif - if (m == NULL) - INITERROR; - - Py_INCREF(&BaseRowType); - PyModule_AddObject(m, "BaseRow", (PyObject *)&BaseRowType); - - Py_INCREF(&tuplegetter_type); - PyModule_AddObject(m, "tuplegetter", (PyObject *)&tuplegetter_type); - -#if PY_MAJOR_VERSION >= 3 - return m; -#endif -} |