From c0c60f4ad612a580ef6dc0e0c4b6658e31e9f681 Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Fri, 2 Mar 2012 12:49:27 +0100 Subject: [PATCH 1/8] Started workin on a C implementation (got __setattr__ so far). --- bunch/_bunchmodule.c | 127 +++++++++++++++++++++++++++++++++++++++++++ setup.py | 5 +- 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 bunch/_bunchmodule.c diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c new file mode 100644 index 0000000..8a52c3c --- /dev/null +++ b/bunch/_bunchmodule.c @@ -0,0 +1,127 @@ +/* + MIT License + (c) 2012 Dariusz Suchojad +*/ + +#include + +#ifndef _BUNCH_SUCCESS +#define _BUNCH_SUCCESS 0 +#endif + +typedef struct { + PyDictObject HEAD; +} Bunch; + + +static PyMethodDef Bunch_methods[] = { + {NULL, NULL}, +}; + +static int +Bunch_init(Bunch *self, PyObject *args, PyObject *kwds) +{ + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + return 0; +}; + +static int +Bunch_setattr(PyObject *self, char *name, PyObject *value) +{ + PyObject *_name = NULL; + int result = 0; + + if(PyObject_HasAttrString(self, name)) { + _name = PyString_FromString(name); + if(_name == NULL) { + (void)PyErr_Format(PyExc_MemoryError, "Could not create PyString from char *"); + return -1; + } + result = PyObject_GenericSetAttr(self, _name, value); + Py_DECREF(_name); + + /* Unfortunately, the docs don't clearly say 0 will will be returned + by PyObject_GenericSetAttr on success so we need this 'if' below. + */ + if(result == _BUNCH_SUCCESS) { + return _BUNCH_SUCCESS; + } + else { + return -1; + } + } + + else { + result = PyDict_SetItemString(self, name, value); + if(result == _BUNCH_SUCCESS) { + return _BUNCH_SUCCESS; + } + else { + (void)PyErr_Format(PyExc_AttributeError, "\%s", name); + return -1; + } + } + +} + + +static PyTypeObject BunchType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "bunch._bunch.Bunch", /* tp_name */ + sizeof(Bunch), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + (setattrfunc)Bunch_setattr, /* 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 */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Bunch_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)Bunch_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyMODINIT_FUNC +init_bunch(void) +{ + PyObject *m; + + BunchType.tp_base = &PyDict_Type; + if (PyType_Ready(&BunchType) < 0) + return; + + m = Py_InitModule3("_bunch", NULL, "Bunch module (C implementation)"); + if (m == NULL) + return; + + Py_INCREF(&BunchType); + PyModule_AddObject(m, "Bunch", (PyObject *) &BunchType); +} diff --git a/setup.py b/setup.py index b10f09e..c8242bd 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import sys, os, re from os.path import dirname, abspath, join -from setuptools import setup +from setuptools import setup, Extension HERE = abspath(dirname(__file__)) @@ -15,6 +15,8 @@ [ line.strip() for line in package_file if '__version__' in line ].pop(0) ) +_bunch = Extension('_bunch', sources = ['bunch/_bunchmodule.c']) + setup( name = "bunch", @@ -27,6 +29,7 @@ author_email = "dsc@less.ly", packages = ['bunch',], + ext_modules = [_bunch], keywords = ['bunch', 'dict', 'mapping', 'container', 'collection'], classifiers = [ From cf949bb10ff1f78ffd5d918b3a58c4f43e7c4b3d Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Fri, 2 Mar 2012 22:21:36 +0100 Subject: [PATCH 2/8] Added a SimpleBunch C extension type for speeding the most common operations up. --- bunch/__init__.py | 6 ++++ bunch/_bunchmodule.c | 69 +++++++++++++++++++++++++------------------- setup.py | 42 +++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 33 deletions(-) diff --git a/bunch/__init__.py b/bunch/__init__.py index 2dc7869..b7312e6 100644 --- a/bunch/__init__.py +++ b/bunch/__init__.py @@ -401,6 +401,12 @@ def fromYAML(*args, **kwargs): pass +# SimpleBunch might now be available, fall back on regular Bunch in that case. +try: + from _bunch import Bunch as SimpleBunch +except ImportError: + SimpleBunch = Bunch + if __name__ == "__main__": import doctest doctest.testmod() diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index 8a52c3c..17f856c 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -13,7 +13,6 @@ typedef struct { PyDictObject HEAD; } Bunch; - static PyMethodDef Bunch_methods[] = { {NULL, NULL}, }; @@ -26,43 +25,53 @@ Bunch_init(Bunch *self, PyObject *args, PyObject *kwds) return 0; }; +/* + __setattr__ +*/ static int Bunch_setattr(PyObject *self, char *name, PyObject *value) { - PyObject *_name = NULL; int result = 0; - if(PyObject_HasAttrString(self, name)) { - _name = PyString_FromString(name); - if(_name == NULL) { - (void)PyErr_Format(PyExc_MemoryError, "Could not create PyString from char *"); - return -1; - } - result = PyObject_GenericSetAttr(self, _name, value); - Py_DECREF(_name); - - /* Unfortunately, the docs don't clearly say 0 will will be returned - by PyObject_GenericSetAttr on success so we need this 'if' below. - */ - if(result == _BUNCH_SUCCESS) { - return _BUNCH_SUCCESS; - } - else { - return -1; - } + result = PyDict_SetItemString(self, name, value); + if(result == _BUNCH_SUCCESS) { + return _BUNCH_SUCCESS; + } + else { + (void)PyErr_Format(PyExc_AttributeError, "(Bunch_setattr) \%s", name); + return -1; + } +} + +/* + __getattr__ +*/ +static PyObject * +Bunch_getattr(PyObject *self, const char *name) +{ + PyObject *_name = NULL; + PyObject *_attr = NULL; + + _name = PyString_FromString(name); + if(_name == NULL) { + (void)PyErr_Format(PyExc_MemoryError, "(Bunch_getattr) Could not create PyString from char *"); + return NULL; } + _attr = PyObject_GenericGetAttr(self, _name); + Py_XDECREF(_name); + + if(_attr != NULL) { + return _attr; + } else { - result = PyDict_SetItemString(self, name, value); - if(result == _BUNCH_SUCCESS) { - return _BUNCH_SUCCESS; - } - else { - (void)PyErr_Format(PyExc_AttributeError, "\%s", name); - return -1; + _attr = PyDict_GetItemString(self, name); + if(_attr == NULL) { + (void)PyErr_Format(PyExc_AttributeError, "(Bunch_getattr) \%s", name); + return NULL; } + return _attr; } - } @@ -74,7 +83,7 @@ static PyTypeObject BunchType = { 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ - 0, /* tp_getattr */ + (getattrfunc)Bunch_getattr, /* tp_getattr */ (setattrfunc)Bunch_setattr, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ @@ -88,7 +97,7 @@ static PyTypeObject BunchType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/setup.py b/setup.py index c8242bd..8fc3e63 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,8 @@ # -*- coding: utf-8 -*- import sys, os, re from os.path import dirname, abspath, join -from setuptools import setup, Extension +from distutils.command.build_ext import build_ext +from setuptools import setup, Extension, Command HERE = abspath(dirname(__file__)) @@ -15,8 +16,42 @@ [ line.strip() for line in package_file if '__version__' in line ].pop(0) ) -_bunch = Extension('_bunch', sources = ['bunch/_bunchmodule.c']) +# BuildFailed and ve_build_ext taken from simplejson (MIT License) +# https://github.com/simplejson/simplejson/blob/master/setup.py +# Python 3.x introduces a flag to let the extension harmlessly fail to compile +# but we're doing 2.x + +class BuildFailed(Exception): + pass + +class ve_build_ext(build_ext): + # This class allows C extension building to fail. + + def run(self): + try: + build_ext.run(self) + except DistutilsPlatformError, x: + raise BuildFailed() + + def build_extension(self, ext): + try: + build_ext.build_extension(self, ext) + except ext_errors, x: + raise BuildFailed() + +_simple_bunch_systems = ['linux'] +for system in _simple_bunch_systems: + if system in sys.platform: + kw = dict( + ext_modules = [ + Extension('_bunch', sources = ['bunch/_bunchmodule.c']) + ], + cmdclass=dict(build_ext=ve_build_ext), + ) + break +else: + kw = {} setup( name = "bunch", @@ -29,7 +64,6 @@ author_email = "dsc@less.ly", packages = ['bunch',], - ext_modules = [_bunch], keywords = ['bunch', 'dict', 'mapping', 'container', 'collection'], classifiers = [ @@ -47,4 +81,6 @@ ], # download_url = "http://pypi.python.org/packages/source/b/bunch/bunch-%s.tar.gz" % __version__, license = 'MIT', + + **kw ) From bcd3315c9a28f692bcf376af2e71e3e3007a2b56 Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 03:19:30 +0100 Subject: [PATCH 3/8] Merely checking the system will suffice, no need for any new classes. --- setup.py | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/setup.py b/setup.py index 8fc3e63..8fca588 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,7 @@ # -*- coding: utf-8 -*- import sys, os, re from os.path import dirname, abspath, join -from distutils.command.build_ext import build_ext -from setuptools import setup, Extension, Command +from setuptools import setup, Extension HERE = abspath(dirname(__file__)) @@ -16,30 +15,6 @@ [ line.strip() for line in package_file if '__version__' in line ].pop(0) ) - -# BuildFailed and ve_build_ext taken from simplejson (MIT License) -# https://github.com/simplejson/simplejson/blob/master/setup.py -# Python 3.x introduces a flag to let the extension harmlessly fail to compile -# but we're doing 2.x - -class BuildFailed(Exception): - pass - -class ve_build_ext(build_ext): - # This class allows C extension building to fail. - - def run(self): - try: - build_ext.run(self) - except DistutilsPlatformError, x: - raise BuildFailed() - - def build_extension(self, ext): - try: - build_ext.build_extension(self, ext) - except ext_errors, x: - raise BuildFailed() - _simple_bunch_systems = ['linux'] for system in _simple_bunch_systems: if system in sys.platform: @@ -47,7 +22,6 @@ def build_extension(self, ext): ext_modules = [ Extension('_bunch', sources = ['bunch/_bunchmodule.c']) ], - cmdclass=dict(build_ext=ve_build_ext), ) break else: From e93635b39f5c78b4ef58dda4b14c85d0d5ee980d Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 03:21:45 +0100 Subject: [PATCH 4/8] Started working on a version which closer resembles the Python counterpart. --- bunch/_bunchmodule.c | 129 ++++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index 17f856c..beee031 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -29,17 +29,55 @@ Bunch_init(Bunch *self, PyObject *args, PyObject *kwds) __setattr__ */ static int -Bunch_setattr(PyObject *self, char *name, PyObject *value) +Bunch_setattro(PyObject *self, PyObject *_k, PyObject *_v) { - int result = 0; + /* Python code is: - result = PyDict_SetItemString(self, name, value); - if(result == _BUNCH_SUCCESS) { - return _BUNCH_SUCCESS; + try: + object.__getattribute__(self, k) # 1) + except AttributeError: + try: + self[k] = v # 2) + except: + raise AttributeError(k) # 3) + else: + object.__setattr__(self, k, v) # 4) + */ + + char *k = PyString_AsString(_k); + + PyObject *__builtin__ = PyImport_AddModule("__builtin__"); + PyObject *object = PyObject_GetAttrString(__builtin__, "object"); /* New ref */ + + /* 1) Try getting ahold of an attribute first .. + */ + PyObject *ret = PyObject_CallMethod(object, "__getattribute__", "Os", self, k); /* New ref */ + Py_XDECREF(object); + + PyObject *err_occurred = PyErr_Occurred(); + + if(err_occurred) { + Py_XDECREF(ret); + if(PyErr_ExceptionMatches(PyExc_AttributeError)) { + + /* 2) .. ignore AttributeError and attempt to insert a key .. */ + PyErr_Clear(); + int set_item_result = PyDict_SetItem(self, _k, _v); + if(set_item_result == 0) { + return 0; + } + else { + /* 3) .. give up with an AttributeError */ + (void)PyErr_Format(PyExc_AttributeError, "\%s", k); + return -1; + } + } + else { + return -1; + } } else { - (void)PyErr_Format(PyExc_AttributeError, "(Bunch_setattr) \%s", name); - return -1; + return 0; } } @@ -47,31 +85,56 @@ Bunch_setattr(PyObject *self, char *name, PyObject *value) __getattr__ */ static PyObject * -Bunch_getattr(PyObject *self, const char *name) +Bunch_getattro(PyObject *self, PyObject *_k) { - PyObject *_name = NULL; - PyObject *_attr = NULL; - - _name = PyString_FromString(name); - if(_name == NULL) { - (void)PyErr_Format(PyExc_MemoryError, "(Bunch_getattr) Could not create PyString from char *"); - return NULL; - } + /* Python code is: - _attr = PyObject_GenericGetAttr(self, _name); - Py_XDECREF(_name); + try: + return object.__getattribute__(self, k) # 1) + except AttributeError: + try: + return self[k] # 2) + except KeyError: + raise AttributeError(k) # 3) + */ - if(_attr != NULL) { - return _attr; - } - else { - _attr = PyDict_GetItemString(self, name); - if(_attr == NULL) { - (void)PyErr_Format(PyExc_AttributeError, "(Bunch_getattr) \%s", name); + char *k = PyString_AsString(_k); + + PyObject *__builtin__ = PyImport_AddModule("__builtin__"); + PyObject *object = PyObject_GetAttrString(__builtin__, "object"); + + /* 1) Try getting ahold of an attribute first .. + */ + PyObject *ret = PyObject_CallMethod(object, "__getattribute__", "Os", self, k); /* New ref */ + Py_XDECREF(object); + + PyObject *err_occurred = PyErr_Occurred(); + + if(err_occurred) { + Py_XDECREF(ret); + if(PyErr_ExceptionMatches(PyExc_AttributeError)) { + + /* 2) .. ignore AttributeError and see if there's a key of that name .. */ + PyErr_Clear(); + PyObject *item = PyDict_GetItem(self, _k); /* Borrowed ref */ + if(item != NULL) { + Py_INCREF(item); + return item; + } + else { + /* 3) .. give up with an AttributeError */ + (void)PyErr_Format(PyExc_AttributeError, "\%s", k); + return NULL; + } + } + else { return NULL; } - return _attr; } + else { + return ret; + } + } @@ -83,8 +146,8 @@ static PyTypeObject BunchType = { 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)Bunch_getattr, /* tp_getattr */ - (setattrfunc)Bunch_setattr, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ @@ -93,8 +156,8 @@ static PyTypeObject BunchType = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ + (getattrofunc)Bunch_getattro, /* tp_getattro */ + (setattrofunc)Bunch_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ @@ -124,12 +187,14 @@ init_bunch(void) PyObject *m; BunchType.tp_base = &PyDict_Type; - if (PyType_Ready(&BunchType) < 0) + if (PyType_Ready(&BunchType) < 0) { return; + } m = Py_InitModule3("_bunch", NULL, "Bunch module (C implementation)"); - if (m == NULL) + if (m == NULL) { return; + } Py_INCREF(&BunchType); PyModule_AddObject(m, "Bunch", (PyObject *) &BunchType); From 1bd5c1ca71b9163b4caa961a751a936f78a2af5c Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 03:56:58 +0100 Subject: [PATCH 5/8] Store reference to object.__getattribute__ and object.__setattr__ in a Bunch instance itself (freed in a dealloc function). --- bunch/_bunchmodule.c | 62 ++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index beee031..8c52c9f 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -11,6 +11,8 @@ typedef struct { PyDictObject HEAD; + PyObject *object__getattribute__; + PyObject *object__setattr__; } Bunch; static PyMethodDef Bunch_methods[] = { @@ -20,11 +22,45 @@ static PyMethodDef Bunch_methods[] = { static int Bunch_init(Bunch *self, PyObject *args, PyObject *kwds) { - if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) { return -1; + } + + PyObject *__builtin__ = PyImport_AddModule("__builtin__"); + + PyObject *object = PyObject_GetAttrString(__builtin__, "object"); /* New ref */ + if(object == NULL) { + return -1; + } + + PyObject *__getattribute__ = PyObject_GetAttrString(object, "__getattribute__"); /* New ref */ + if(__getattribute__ == NULL) { + Py_XDECREF(object); + return -1; + } + + PyObject *__setattr__ = PyObject_GetAttrString(object, "__setattr__"); /* New ref */ + if(__setattr__ == NULL) { + Py_XDECREF(object); + Py_XDECREF(__getattribute__); + return -1; + } + + self->object__getattribute__ = __getattribute__; + self->object__setattr__ = __setattr__; + + Py_XDECREF(object); return 0; }; +static void +Bunch_dealloc(Bunch *self) +{ + Py_XDECREF(self->object__getattribute__); + Py_XDECREF(self->object__setattr__); + ((PyObject *)self)->ob_type->tp_free(self); +} + /* __setattr__ */ @@ -44,16 +80,9 @@ Bunch_setattro(PyObject *self, PyObject *_k, PyObject *_v) object.__setattr__(self, k, v) # 4) */ - char *k = PyString_AsString(_k); - - PyObject *__builtin__ = PyImport_AddModule("__builtin__"); - PyObject *object = PyObject_GetAttrString(__builtin__, "object"); /* New ref */ - /* 1) Try getting ahold of an attribute first .. */ - PyObject *ret = PyObject_CallMethod(object, "__getattribute__", "Os", self, k); /* New ref */ - Py_XDECREF(object); - + PyObject *ret = PyObject_CallFunctionObjArgs(((Bunch *)self)->object__getattribute__, self, _k, NULL); PyObject *err_occurred = PyErr_Occurred(); if(err_occurred) { @@ -68,7 +97,7 @@ Bunch_setattro(PyObject *self, PyObject *_k, PyObject *_v) } else { /* 3) .. give up with an AttributeError */ - (void)PyErr_Format(PyExc_AttributeError, "\%s", k); + (void)PyErr_Format(PyExc_AttributeError, "\%s", PyString_AsString(_k)); return -1; } } @@ -98,16 +127,9 @@ Bunch_getattro(PyObject *self, PyObject *_k) raise AttributeError(k) # 3) */ - char *k = PyString_AsString(_k); - - PyObject *__builtin__ = PyImport_AddModule("__builtin__"); - PyObject *object = PyObject_GetAttrString(__builtin__, "object"); - /* 1) Try getting ahold of an attribute first .. */ - PyObject *ret = PyObject_CallMethod(object, "__getattribute__", "Os", self, k); /* New ref */ - Py_XDECREF(object); - + PyObject *ret = PyObject_CallFunctionObjArgs(((Bunch *)self)->object__getattribute__, self, _k, NULL); PyObject *err_occurred = PyErr_Occurred(); if(err_occurred) { @@ -123,7 +145,7 @@ Bunch_getattro(PyObject *self, PyObject *_k) } else { /* 3) .. give up with an AttributeError */ - (void)PyErr_Format(PyExc_AttributeError, "\%s", k); + (void)PyErr_Format(PyExc_AttributeError, "\%s", PyString_AsString(_k)); return NULL; } } @@ -144,7 +166,7 @@ static PyTypeObject BunchType = { "bunch._bunch.Bunch", /* tp_name */ sizeof(Bunch), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + Bunch_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ From b165440067f7363c7e88c86a55c143945572631d Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 04:04:45 +0100 Subject: [PATCH 6/8] Added the step 4) in getattro function - object.__setattr__(self, k, v) --- bunch/_bunchmodule.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index 8c52c9f..4be4a92 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -80,10 +80,13 @@ Bunch_setattro(PyObject *self, PyObject *_k, PyObject *_v) object.__setattr__(self, k, v) # 4) */ + PyObject *ret = NULL; + PyObject *err_occurred = NULL; + /* 1) Try getting ahold of an attribute first .. */ - PyObject *ret = PyObject_CallFunctionObjArgs(((Bunch *)self)->object__getattribute__, self, _k, NULL); - PyObject *err_occurred = PyErr_Occurred(); + ret = PyObject_CallFunctionObjArgs(((Bunch *)self)->object__getattribute__, self, _k, NULL); + err_occurred = PyErr_Occurred(); if(err_occurred) { Py_XDECREF(ret); @@ -96,16 +99,25 @@ Bunch_setattro(PyObject *self, PyObject *_k, PyObject *_v) return 0; } else { - /* 3) .. give up with an AttributeError */ + /* 3) .. give up with an AttributeError .. */ (void)PyErr_Format(PyExc_AttributeError, "\%s", PyString_AsString(_k)); return -1; } } else { + /* Some exception but not an AttributeError */ return -1; } } else { + /* 4) .. no error so we actually have an atrribute of the name we tried in 1) */ + ret = PyObject_CallFunctionObjArgs(((Bunch *)self)->object__setattr__, self, _k, _v, NULL); + err_occurred = PyErr_Occurred(); + + if(err_occurred) { + Py_XDECREF(ret); + return -1; + } return 0; } } From 6fbf7612048711ff278c77eeaf56b32ca100e6bf Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 04:07:26 +0100 Subject: [PATCH 7/8] Removed unused #define's --- bunch/_bunchmodule.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index 4be4a92..fb296e5 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -5,10 +5,6 @@ #include -#ifndef _BUNCH_SUCCESS -#define _BUNCH_SUCCESS 0 -#endif - typedef struct { PyDictObject HEAD; PyObject *object__getattribute__; From 42c6d3634e23c41efdfb837f2ebbf177e8d4e0e6 Mon Sep 17 00:00:00 2001 From: Dariusz Suchojad Date: Mon, 5 Mar 2012 04:09:34 +0100 Subject: [PATCH 8/8] Fixed a missing cast to destructor so it all compiles without warnings now. --- bunch/_bunchmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bunch/_bunchmodule.c b/bunch/_bunchmodule.c index fb296e5..49d8e06 100644 --- a/bunch/_bunchmodule.c +++ b/bunch/_bunchmodule.c @@ -174,7 +174,7 @@ static PyTypeObject BunchType = { "bunch._bunch.Bunch", /* tp_name */ sizeof(Bunch), /* tp_basicsize */ 0, /* tp_itemsize */ - Bunch_dealloc, /* tp_dealloc */ + (destructor)Bunch_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */