@@ -26,6 +26,7 @@ static PyObject* list_getset(
2626 PyObject* result = PyList_New (0 );
2727 PyObject* item = NULL ;
2828 while (getset->name ) {
29+ // __dict__ is also in the getset list
2930 if (getset->name [0 ] != ' _' ) {
3031 item = (*conv)(obj, getset);
3132 PyList_Append (result, item);
@@ -35,52 +36,82 @@ static PyObject* list_getset(
3536 }
3637 return result;
3738};
39+ static PyGetSetDef* find_getset (PyObject* obj, PyObject* name,
40+ bool strip, bool required) {
41+ if (!PyUnicode_Check (name))
42+ return NULL ;
43+ Py_ssize_t size = 0 ;
44+ const char * c_name = PyUnicode_AsUTF8AndSize (name, &size);
45+ bool truncate = strip && size > 0 && c_name[size - 1 ] != ' _' ;
46+ PyGetSetDef* getset = Py_TYPE (obj)->tp_getset ;
47+ size_t len = 0 ;
48+ while (getset->name ) {
49+ len = strlen (getset->name );
50+ if (truncate && getset->name [len - 1 ] == ' _' )
51+ len--;
52+ if (len == (size_t ) size && strncmp (getset->name , c_name, len) == 0 )
53+ return getset;
54+ getset++;
55+ }
56+ if (required)
57+ PyErr_Format (PyExc_AttributeError,
58+ " '%s' object has no attribute '%U'" ,
59+ Py_TYPE (obj)->tp_name , name);
60+ return NULL ;
61+ };
62+ static int getset_set (PyObject* obj, PyObject* name, PyObject* value,
63+ bool strip, bool required) {
64+ PyGetSetDef* getset = find_getset (obj, name, strip, required);
65+ if (getset) {
66+ #if SWIG_VERSION < 0x040400
67+ if (!value) {
68+ PyErr_Format (PyExc_TypeError,
69+ " %s.%s can not be deleted" , Py_TYPE (obj)->tp_name , getset->name );
70+ return -1 ;
71+ }
72+ #endif // SWIG_VERSION
73+ return getset->set (obj, value, getset->closure );
74+ }
75+ if (required)
76+ return -1 ;
77+ return PyObject_GenericSetAttr (obj, name, value);
78+ };
3879static PyObject* getset_to_value (PyObject* obj, PyGetSetDef* getset) {
3980 return Py_BuildValue (" N" , getset->get (obj, getset->closure ));
4081};
41- }
42- %fragment(" getset_functions_strip" , " header" ,
43- fragment=" getset_functions" ) {
4482static PyObject* getset_to_item_strip (PyObject* obj, PyGetSetDef* getset) {
4583 return Py_BuildValue (" (s#N)" , getset->name , strlen (getset->name ) - 1 ,
4684 getset->get (obj, getset->closure ));
4785};
48- static PyObject* getset_to_key_strip (PyObject* obj, PyGetSetDef* getset) {
49- return Py_BuildValue (" s#" , getset->name , strlen (getset->name ) - 1 );
50- };
51- }
52- %fragment(" getset_functions_nostrip" , " header" ,
53- fragment=" getset_functions" ) {
5486static PyObject* getset_to_item_nostrip (PyObject* obj, PyGetSetDef* getset) {
5587 return Py_BuildValue (" (sN)" , getset->name ,
5688 getset->get (obj, getset->closure ));
5789};
90+ static PyObject* getset_to_key_strip (PyObject* obj, PyGetSetDef* getset) {
91+ return Py_BuildValue (" s#" , getset->name , strlen (getset->name ) - 1 );
92+ };
5893static PyObject* getset_to_key_nostrip (PyObject* obj, PyGetSetDef* getset) {
5994 return Py_BuildValue (" s" , getset->name );
6095};
61- }
62- %fragment(" set_attr_no_delete" , " header" ) {
63- static int set_attr_no_delete (
64- PyObject* obj, PyObject* name, PyObject* value) {
65- if ((!value) && PyUnicode_Check (name)) {
66- const char * c_name = PyUnicode_AsUTF8 (name);
67- PyGetSetDef* getset = Py_TYPE (obj)->tp_getset ;
68- while (getset->name ) {
69- if (strcmp (getset->name , c_name) == 0 ) {
70- PyErr_Format (PyExc_TypeError,
71- " %s.%s can not be deleted" ,
72- Py_TYPE (obj)->tp_name , c_name);
73- return -1 ;
74- }
75- getset++;
76- }
77- }
78- return PyObject_GenericSetAttr (obj, name, value);
96+ static int set_attr_strip (PyObject* obj, PyObject* name, PyObject* value) {
97+ return getset_set (obj, name, value, true , false );
98+ };
99+ #if SWIG_VERSION < 0x040400
100+ static int set_attr_nostrip (PyObject* obj, PyObject* name, PyObject* value) {
101+ return getset_set (obj, name, value, false , false );
102+ };
103+ #endif // SWIG_VERSION
104+ static PyObject* get_attr_strip (PyObject* obj, PyObject* name) {
105+ PyGetSetDef* getset = find_getset (obj, name, true , false );
106+ if (getset)
107+ return getset_to_value (obj, getset);
108+ return PyObject_GenericGetAttr (obj, name);
79109};
80110}
81111
82112// Macro definition
83113%define STRUCT_DICT (struct_type, mutable , strip_underscore)
114+ %fragment(" getset_functions" );
84115// Type slots
85116%feature(" python:slot" , " tp_iter" , functype=" getiterfunc" )
86117 struct_type::__iter__;
@@ -104,15 +135,13 @@ static int set_attr_no_delete(
104135// Add functions
105136%extend struct_type {
106137#if #strip_underscore == "true"
107- %fragment (" getset_functions_strip" );
108138 PyObject* items (PyObject* py_self) {
109139 return list_getset (py_self, getset_to_item_strip);
110140 }
111141 PyObject* keys (PyObject* py_self) {
112142 return list_getset (py_self, getset_to_key_strip);
113143 }
114144#else
115- %fragment (" getset_functions_nostrip" );
116145 PyObject* items (PyObject* py_self) {
117146 return list_getset (py_self, getset_to_item_nostrip);
118147 }
@@ -130,33 +159,31 @@ static int set_attr_no_delete(
130159 Py_DECREF (seq);
131160 return result;
132161 }
133- PyObject* __getitem__ (PyObject* py_self, const std::string& key) {
134- # if #strip_underscore == "true"
135- return PyObject_GetAttrString ( py_self, ( key + ' _ ' ). c_str () );
136- # else
137- return PyObject_GetAttrString (py_self, key. c_str ()) ;
138- # endif // strip_underscore
162+ PyObject* __getitem__ (PyObject* py_self, PyObject* key) {
163+ PyGetSetDef* getset = find_getset (
164+ py_self, key, strip_underscore, true );
165+ if (!getset)
166+ return NULL ;
167+ return getset_to_value (py_self, getset);
139168 }
140169}
170+ #if #strip_underscore == "true"
171+ %feature(" python:tp_getattro" ) struct_type " get_attr_strip" ;
172+ #endif // strip_underscore
141173#if #mutable == "true"
142- %fragment(" set_attr_no_delete" );
143- %feature(" python:tp_setattro" ) struct_type " set_attr_no_delete" ;
174+ #if #strip_underscore == "true"
175+ %feature(" python:tp_setattro" ) struct_type " set_attr_strip" ;
176+ #else // strip_underscore
177+ #if SWIG_VERSION < 0x040400
178+ %feature(" python:tp_setattro" ) struct_type " set_attr_nostrip" ;
179+ #endif // SWIG_VERSION
180+ #endif // strip_underscore
144181%feature(" python:slot" , " mp_ass_subscript" , functype=" objobjargproc" )
145182 struct_type::__setitem__;
146183%extend struct_type {
147- PyObject* __setitem__ (PyObject* py_self, const std::string& key,
184+ PyObject* __setitem__ (PyObject* py_self, PyObject* key,
148185 PyObject* value) {
149- if (!value)
150- return PyErr_Format (PyExc_TypeError,
151- " %s['%s'] can not be deleted" , Py_TYPE (py_self)->tp_name ,
152- key.c_str ());
153- #if #strip_underscore == "true"
154- int error = PyObject_SetAttrString (
155- py_self, (key + ' _' ).c_str (), value);
156- #else
157- int error = PyObject_SetAttrString (py_self, key.c_str (), value);
158- #endif // strip_underscore
159- if (error)
186+ if (getset_set (py_self, key, value, strip_underscore, true ))
160187 return NULL ;
161188 return SWIG_Py_Void ();
162189 }
0 commit comments