summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/cextension/immutabledict.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/cextension/immutabledict.c')
-rw-r--r--lib/sqlalchemy/cextension/immutabledict.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/lib/sqlalchemy/cextension/immutabledict.c b/lib/sqlalchemy/cextension/immutabledict.c
new file mode 100644
index 000000000..2a19cf3ad
--- /dev/null
+++ b/lib/sqlalchemy/cextension/immutabledict.c
@@ -0,0 +1,475 @@
+/*
+immuatbledict.c
+Copyright (C) 2020 the SQLAlchemy authors and contributors <see AUTHORS file>
+
+This module is part of SQLAlchemy and is released under
+the MIT License: http://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;
+
+
+
+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_GetItem((PyObject *)self->dict, key);
+
+ if (value == 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 didnt 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 *default_value = Py_None;
+
+ if (!PyArg_UnpackTuple(args, "key", 1, 2, &key, &default_value)) {
+ return NULL;
+ }
+
+
+ return PyObject_CallMethod(self->dict, "get", "OO", key, default_value);
+}
+
+static PyObject *
+ImmutableDict_keys(ImmutableDict *self)
+{
+ return PyObject_CallMethod(self->dict, "keys", "");
+}
+
+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_CallMethod(self->dict, "__iter__", "");
+}
+
+static PyObject *
+ImmutableDict_items(ImmutableDict *self)
+{
+ return PyObject_CallMethod(self->dict, "items", "");
+}
+
+static PyObject *
+ImmutableDict_values(ImmutableDict *self)
+{
+ return PyObject_CallMethod(self->dict, "values", "");
+
+}
+
+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
+}