Home | History | Annotate | Download | only in Objects
      1 // namespace object implementation
      2 
      3 #include "Python.h"
      4 #include "structmember.h"
      5 
      6 
      7 typedef struct {
      8     PyObject_HEAD
      9     PyObject *ns_dict;
     10 } _PyNamespaceObject;
     11 
     12 
     13 static PyMemberDef namespace_members[] = {
     14     {"__dict__", T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), READONLY},
     15     {NULL}
     16 };
     17 
     18 
     19 // Methods
     20 
     21 static PyObject *
     22 namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     23 {
     24     PyObject *self;
     25 
     26     assert(type != NULL && type->tp_alloc != NULL);
     27     self = type->tp_alloc(type, 0);
     28     if (self != NULL) {
     29         _PyNamespaceObject *ns = (_PyNamespaceObject *)self;
     30         ns->ns_dict = PyDict_New();
     31         if (ns->ns_dict == NULL) {
     32             Py_DECREF(ns);
     33             return NULL;
     34         }
     35     }
     36     return self;
     37 }
     38 
     39 
     40 static int
     41 namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds)
     42 {
     43     // ignore args if it's NULL or empty
     44     if (args != NULL) {
     45         Py_ssize_t argcount = PyObject_Size(args);
     46         if (argcount < 0)
     47             return -1;
     48         else if (argcount > 0) {
     49             PyErr_Format(PyExc_TypeError, "no positional arguments expected");
     50             return -1;
     51         }
     52     }
     53     if (kwds == NULL)
     54         return 0;
     55     return PyDict_Update(ns->ns_dict, kwds);
     56 }
     57 
     58 
     59 static void
     60 namespace_dealloc(_PyNamespaceObject *ns)
     61 {
     62     PyObject_GC_UnTrack(ns);
     63     Py_CLEAR(ns->ns_dict);
     64     Py_TYPE(ns)->tp_free((PyObject *)ns);
     65 }
     66 
     67 
     68 static PyObject *
     69 namespace_repr(PyObject *ns)
     70 {
     71     int i, loop_error = 0;
     72     PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL;
     73     PyObject *key;
     74     PyObject *separator, *pairsrepr, *repr = NULL;
     75     const char * name;
     76 
     77     name = (Py_TYPE(ns) == &_PyNamespace_Type) ? "namespace"
     78                                                : ns->ob_type->tp_name;
     79 
     80     i = Py_ReprEnter(ns);
     81     if (i != 0) {
     82         return i > 0 ? PyUnicode_FromFormat("%s(...)", name) : NULL;
     83     }
     84 
     85     pairs = PyList_New(0);
     86     if (pairs == NULL)
     87         goto error;
     88 
     89     d = ((_PyNamespaceObject *)ns)->ns_dict;
     90     assert(d != NULL);
     91     Py_INCREF(d);
     92 
     93     keys = PyDict_Keys(d);
     94     if (keys == NULL)
     95         goto error;
     96     if (PyList_Sort(keys) != 0)
     97         goto error;
     98 
     99     keys_iter = PyObject_GetIter(keys);
    100     if (keys_iter == NULL)
    101         goto error;
    102 
    103     while ((key = PyIter_Next(keys_iter)) != NULL) {
    104         if (PyUnicode_Check(key) && PyUnicode_GET_LENGTH(key) > 0) {
    105             PyObject *value, *item;
    106 
    107             value = PyDict_GetItem(d, key);
    108             assert(value != NULL);
    109 
    110             item = PyUnicode_FromFormat("%S=%R", key, value);
    111             if (item == NULL) {
    112                 loop_error = 1;
    113             }
    114             else {
    115                 loop_error = PyList_Append(pairs, item);
    116                 Py_DECREF(item);
    117             }
    118         }
    119 
    120         Py_DECREF(key);
    121         if (loop_error)
    122             goto error;
    123     }
    124 
    125     separator = PyUnicode_FromString(", ");
    126     if (separator == NULL)
    127         goto error;
    128 
    129     pairsrepr = PyUnicode_Join(separator, pairs);
    130     Py_DECREF(separator);
    131     if (pairsrepr == NULL)
    132         goto error;
    133 
    134     repr = PyUnicode_FromFormat("%s(%S)", name, pairsrepr);
    135     Py_DECREF(pairsrepr);
    136 
    137 error:
    138     Py_XDECREF(pairs);
    139     Py_XDECREF(d);
    140     Py_XDECREF(keys);
    141     Py_XDECREF(keys_iter);
    142     Py_ReprLeave(ns);
    143 
    144     return repr;
    145 }
    146 
    147 
    148 static int
    149 namespace_traverse(_PyNamespaceObject *ns, visitproc visit, void *arg)
    150 {
    151     Py_VISIT(ns->ns_dict);
    152     return 0;
    153 }
    154 
    155 
    156 static int
    157 namespace_clear(_PyNamespaceObject *ns)
    158 {
    159     Py_CLEAR(ns->ns_dict);
    160     return 0;
    161 }
    162 
    163 
    164 static PyObject *
    165 namespace_richcompare(PyObject *self, PyObject *other, int op)
    166 {
    167     if (PyObject_TypeCheck(self, &_PyNamespace_Type) &&
    168         PyObject_TypeCheck(other, &_PyNamespace_Type))
    169         return PyObject_RichCompare(((_PyNamespaceObject *)self)->ns_dict,
    170                                    ((_PyNamespaceObject *)other)->ns_dict, op);
    171     Py_RETURN_NOTIMPLEMENTED;
    172 }
    173 
    174 
    175 PyDoc_STRVAR(namespace_reduce__doc__, "Return state information for pickling");
    176 
    177 static PyObject *
    178 namespace_reduce(_PyNamespaceObject *ns)
    179 {
    180     PyObject *result, *args = PyTuple_New(0);
    181 
    182     if (!args)
    183         return NULL;
    184 
    185     result = PyTuple_Pack(3, (PyObject *)Py_TYPE(ns), args, ns->ns_dict);
    186     Py_DECREF(args);
    187     return result;
    188 }
    189 
    190 
    191 static PyMethodDef namespace_methods[] = {
    192     {"__reduce__", (PyCFunction)namespace_reduce, METH_NOARGS,
    193      namespace_reduce__doc__},
    194     {NULL,         NULL}  // sentinel
    195 };
    196 
    197 
    198 PyDoc_STRVAR(namespace_doc,
    199 "A simple attribute-based namespace.\n\
    200 \n\
    201 SimpleNamespace(**kwargs)");
    202 
    203 PyTypeObject _PyNamespace_Type = {
    204     PyVarObject_HEAD_INIT(&PyType_Type, 0)
    205     "types.SimpleNamespace",                    /* tp_name */
    206     sizeof(_PyNamespaceObject),                 /* tp_size */
    207     0,                                          /* tp_itemsize */
    208     (destructor)namespace_dealloc,              /* tp_dealloc */
    209     0,                                          /* tp_print */
    210     0,                                          /* tp_getattr */
    211     0,                                          /* tp_setattr */
    212     0,                                          /* tp_reserved */
    213     (reprfunc)namespace_repr,                   /* tp_repr */
    214     0,                                          /* tp_as_number */
    215     0,                                          /* tp_as_sequence */
    216     0,                                          /* tp_as_mapping */
    217     0,                                          /* tp_hash */
    218     0,                                          /* tp_call */
    219     0,                                          /* tp_str */
    220     PyObject_GenericGetAttr,                    /* tp_getattro */
    221     PyObject_GenericSetAttr,                    /* tp_setattro */
    222     0,                                          /* tp_as_buffer */
    223     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
    224         Py_TPFLAGS_BASETYPE,                    /* tp_flags */
    225     namespace_doc,                              /* tp_doc */
    226     (traverseproc)namespace_traverse,           /* tp_traverse */
    227     (inquiry)namespace_clear,                   /* tp_clear */
    228     namespace_richcompare,                      /* tp_richcompare */
    229     0,                                          /* tp_weaklistoffset */
    230     0,                                          /* tp_iter */
    231     0,                                          /* tp_iternext */
    232     namespace_methods,                          /* tp_methods */
    233     namespace_members,                          /* tp_members */
    234     0,                                          /* tp_getset */
    235     0,                                          /* tp_base */
    236     0,                                          /* tp_dict */
    237     0,                                          /* tp_descr_get */
    238     0,                                          /* tp_descr_set */
    239     offsetof(_PyNamespaceObject, ns_dict),      /* tp_dictoffset */
    240     (initproc)namespace_init,                   /* tp_init */
    241     PyType_GenericAlloc,                        /* tp_alloc */
    242     (newfunc)namespace_new,                     /* tp_new */
    243     PyObject_GC_Del,                            /* tp_free */
    244 };
    245 
    246 
    247 PyObject *
    248 _PyNamespace_New(PyObject *kwds)
    249 {
    250     PyObject *ns = namespace_new(&_PyNamespace_Type, NULL, NULL);
    251     if (ns == NULL)
    252         return NULL;
    253 
    254     if (kwds == NULL)
    255         return ns;
    256     if (PyDict_Update(((_PyNamespaceObject *)ns)->ns_dict, kwds) != 0) {
    257         Py_DECREF(ns);
    258         return NULL;
    259     }
    260 
    261     return (PyObject *)ns;
    262 }
    263