1818
1919// Add dict-like behaviour to an Exiv2 struct, e.g. PreviewProperties
2020
21+
2122// Helper functions
22- %fragment(" getset_functions" , " header" ) {
23- static PyObject* list_getset (
24- PyObject* obj, PyObject* (*conv)(PyObject*, PyGetSetDef*)) {
25- PyGetSetDef* getset = Py_TYPE (obj)->tp_getset ;
26- PyObject* result = PyList_New (0 );
27- PyObject* item = NULL ;
23+ %fragment(" struct_info_type" , " header" ) {
24+ typedef std::vector< std::string > string_list;
25+ typedef struct {
26+ string_list members;
27+ string_list aliases;
28+ } struct_info;
29+ }
30+ %fragment(" init_struct_info" , " header" , fragment=" struct_info_type" ) {
31+ static void init_struct_info (struct_info& info, swig_type_info* type) {
32+ if (!info.members .empty ())
33+ return ;
34+ PyGetSetDef* getset =
35+ ((SwigPyClientData*)type->clientdata )->pytype ->tp_getset ;
2836 while (getset->name ) {
2937 // __dict__ is also in the getset list
3038 if (getset->name [0 ] != ' _' ) {
31- item = (*conv)(obj, getset);
32- PyList_Append (result, item);
33- Py_DECREF (item);
39+ info.members .push_back (getset->name );
40+ std::string alias = getset->name ;
41+ if (alias.back () == ' _' ) {
42+ alias.pop_back ();
43+ info.aliases .push_back (alias);
44+ }
3445 }
3546 getset++;
3647 }
37- return result;
3848};
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 ;
49+ }
50+ %fragment(" get_attr_struct" , " header" , fragment=" struct_info_type" ) {
51+ static PyObject* get_attr_struct (struct_info& info, bool as_item,
52+ PyObject* obj, PyObject* name) {
53+ std::string c_name = PyUnicode_AsUTF8 (name);
54+ string_list list = info.aliases ;
55+ if (as_item && list.empty ())
56+ list = info.members ;
57+ for (size_t i = 0 ; i < list.size (); i++)
58+ if (list[i] == c_name)
59+ return PyObject_GetAttrString (obj, info.members [i].c_str ());
60+ if (as_item)
61+ return PyErr_Format (PyExc_KeyError, " '%s'" , c_name.c_str ());
62+ return PyObject_GenericGetAttr (obj, name);
6163};
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)
64+ }
65+ %fragment( " set_attr_struct " , " header " , fragment= " struct_info_type " ) {
66+ static int set_attr_struct (struct_info& info, bool as_item,
67+ PyObject* obj, PyObject* name, PyObject* value ) {
68+ std::string c_name = PyUnicode_AsUTF8 (name);
69+ string_list list = info. aliases ;
70+ if (as_item && list. empty ())
71+ list = info. members ;
72+ for ( size_t i = 0 ; i < list. size (); i++)
73+ if (list[i] == c_name)
74+ return PyObject_SetAttrString (
75+ obj, info. members [i]. c_str (), value );
76+ if (as_item) {
77+ PyErr_Format (PyExc_KeyError, " '%s' " , c_name. c_str ());
7678 return -1 ;
79+ }
80+ #if SWIG_VERSION < 0x040400
81+ if (!value)
82+ for (size_t i = 0 ; i < info.members .size (); i++)
83+ if (info.members [i] == c_name) {
84+ PyErr_Format (PyExc_TypeError, " %s.%s can not be deleted" ,
85+ Py_TYPE (obj)->tp_name , c_name.c_str ());
86+ return -1 ;
87+ }
88+ #endif
7789 return PyObject_GenericSetAttr (obj, name, value);
7890};
79- static PyObject* getset_to_value (PyObject* obj, PyGetSetDef* getset) {
80- return Py_BuildValue (" N" , getset->get (obj, getset->closure ));
81- };
82- static PyObject* getset_to_item_strip (PyObject* obj, PyGetSetDef* getset) {
83- return Py_BuildValue (" (s#N)" , getset->name , strlen (getset->name ) - 1 ,
84- getset->get (obj, getset->closure ));
85- };
86- static PyObject* getset_to_item_nostrip (PyObject* obj, PyGetSetDef* getset) {
87- return Py_BuildValue (" (sN)" , getset->name ,
88- getset->get (obj, getset->closure ));
89- };
90- static PyObject* getset_to_key_strip (PyObject* obj, PyGetSetDef* getset) {
91- return Py_BuildValue (" s#" , getset->name , strlen (getset->name ) - 1 );
92- };
93- static PyObject* getset_to_key_nostrip (PyObject* obj, PyGetSetDef* getset) {
94- return Py_BuildValue (" s" , getset->name );
95- };
96- static int set_attr_strip (PyObject* obj, PyObject* name, PyObject* value) {
97- return getset_set (obj, name, value, true , false );
91+ }
92+ %fragment(" keys_struct" , " header" , fragment=" struct_info_type" ) {
93+ static PyObject* keys_struct (struct_info& info) {
94+ string_list list = info.aliases .empty () ? info.members : info.aliases ;
95+ PyObject* result = PyTuple_New (list.size ());
96+ for (size_t i = 0 ; i < list.size (); i++)
97+ PyTuple_SET_ITEM (result, i, PyUnicode_FromString (list[i].c_str ()));
98+ return result;
9899};
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 );
100+ }
101+ %fragment(" values_struct" , " header" , fragment=" struct_info_type" ) {
102+ static PyObject* values_struct (struct_info& info, PyObject* obj) {
103+ string_list list = info.aliases .empty () ? info.members : info.aliases ;
104+ PyObject* result = PyTuple_New (list.size ());
105+ for (size_t i = 0 ; i < list.size (); i++)
106+ PyTuple_SET_ITEM (
107+ result, i, PyObject_GetAttrString (obj, info.members [i].c_str ()));
108+ return result;
102109};
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);
110+ }
111+ %fragment(" items_struct" , " header" , fragment=" struct_info_type" ) {
112+ static PyObject* items_struct (struct_info& info, PyObject* obj) {
113+ string_list list = info.aliases .empty () ? info.members : info.aliases ;
114+ PyObject* result = PyTuple_New (list.size ());
115+ for (size_t i = 0 ; i < list.size (); i++)
116+ PyTuple_SET_ITEM (result, i, Py_BuildValue (
117+ " (sN)" , list[i].c_str (),
118+ PyObject_GetAttrString (obj, info.members [i].c_str ())));
119+ return result;
109120};
110121}
111122
112- // Macro definition
123+ %define QUOTE (name)
124+ "name"
125+ %enddef
126+
127+
113128%define STRUCT_DICT(struct_type, mutable , strip_underscore)
114- %fragment(" getset_functions" );
115129// Type slots
116130%feature(" python:slot" , " tp_iter" , functype=" getiterfunc" )
117131 struct_type::__iter__;
118- %feature(" python:slot" , " mp_subscript" , functype=" binaryfunc" )
119- struct_type::__getitem__;
120132// Typemaps for slot functions
121133%typemap(default ) PyObject* value {$1 = NULL ;}
122134// Document functions
@@ -133,59 +145,86 @@ static PyObject* get_attr_strip(PyObject* obj, PyObject* name) {
133145:return: structure member values."
134146// Add functions
135147%extend struct_type {
136- #if #strip_underscore == "true"
137- PyObject* items (PyObject* py_self) {
138- return list_getset (py_self, getset_to_item_strip);
148+ %fragment (" struct_info" {struct_type});
149+ %fragment (" keys_struct" );
150+ %fragment (" values_struct" );
151+ %fragment (" items_struct" );
152+ static PyObject* keys () {
153+ init_info_%mangle (struct_type)();
154+ return keys_struct (info_%mangle (struct_type));
139155 }
140- PyObject* keys (PyObject* py_self) {
141- return list_getset (py_self, getset_to_key_strip);
156+ PyObject* values (PyObject* py_self) {
157+ init_info_%mangle (struct_type)();
158+ return values_struct (info_%mangle (struct_type), py_self);
142159 }
143- #else
144160 PyObject* items (PyObject* py_self) {
145- return list_getset (py_self, getset_to_item_nostrip);
146- }
147- PyObject* keys (PyObject* py_self) {
148- return list_getset (py_self, getset_to_key_nostrip);
161+ init_info_%mangle (struct_type)();
162+ return items_struct (info_%mangle (struct_type), py_self);
149163 }
150- #endif // strip_underscore
151- PyObject* values (PyObject* py_self) {
152- return list_getset (py_self, getset_to_value);
153- }
154- PyObject* __iter__ (PyObject* py_self) {
155- PyObject* seq =
156- %mangle (struct_type::keys)($self, py_self);
164+ static PyObject* __iter__ () {
165+ init_info_%mangle (struct_type)();
166+ PyObject* seq = keys_struct (info_%mangle (struct_type));
157167 PyObject* result = PySeqIter_New (seq);
158168 Py_DECREF (seq);
159169 return result;
160170 }
161- PyObject* __getitem__ (PyObject* py_self, PyObject* key) {
162- PyGetSetDef* getset = find_getset (
163- py_self, key, strip_underscore, true );
164- if (!getset)
165- return NULL ;
166- return getset_to_value (py_self, getset);
167- }
168171}
172+ %fragment(" struct_info" {struct_type}, " header" ,
173+ fragment=" init_struct_info" ) {
174+ static struct_info info_%mangle (struct_type);
175+ static void init_info_%mangle (struct_type)() {
176+ init_struct_info (info_%mangle (struct_type), $descriptor (struct_type*));
177+ };
178+ }
179+ %fragment(" get_item" {struct_type}, " header" ,
180+ fragment=" struct_info" {struct_type}, fragment=" get_attr_struct" ) {
181+ static PyObject* get_item_%mangle (struct_type)(PyObject* obj,
182+ PyObject* key) {
183+ init_info_%mangle (struct_type)();
184+ return get_attr_struct (info_%mangle (struct_type), true , obj, key);
185+ };
186+ }
187+ %fragment(" get_attr" {struct_type}, " header" ,
188+ fragment=" struct_info" {struct_type}, fragment=" get_attr_struct" ) {
189+ static PyObject* get_attr_%mangle (struct_type)(PyObject* obj,
190+ PyObject* name) {
191+ init_info_%mangle (struct_type)();
192+ return get_attr_struct (info_%mangle (struct_type), false , obj, name);
193+ };
194+ }
195+ %fragment(" set_item" {struct_type}, " header" ,
196+ fragment=" struct_info" {struct_type}, fragment=" set_attr_struct" ) {
197+ static int set_item_%mangle (struct_type)(
198+ PyObject* obj, PyObject* key, PyObject* value) {
199+ init_info_%mangle (struct_type)();
200+ return set_attr_struct (info_%mangle (struct_type), true , obj, key, value);
201+ };
202+ }
203+ %fragment(" set_attr" {struct_type}, " header" ,
204+ fragment=" struct_info" {struct_type}, fragment=" set_attr_struct" ) {
205+ static int set_attr_%mangle (struct_type)(
206+ PyObject* obj, PyObject* name, PyObject* value) {
207+ init_info_%mangle (struct_type)();
208+ return set_attr_struct (
209+ info_%mangle (struct_type), false , obj, name, value);
210+ };
211+ }
212+ %fragment(" get_item" {struct_type});
213+ %feature(" python:mp_subscript" ) struct_type
214+ QUOTE (get_item_%mangle(struct_type));
169215#if #strip_underscore == "true"
170- %feature(" python:tp_getattro" ) struct_type " get_attr_strip" ;
171- #endif // strip_underscore
216+ %fragment(" get_attr" {struct_type});
217+ %feature(" python:tp_getattro" ) struct_type
218+ QUOTE (get_attr_%mangle(struct_type));
219+ #endif
172220#if #mutable == "true"
173- #if #strip_underscore == "true"
174- %feature(" python:tp_setattro" ) struct_type " set_attr_strip" ;
175- #else // strip_underscore
176- #if SWIG_VERSION < 0x040400
177- %feature(" python:tp_setattro" ) struct_type " set_attr_nostrip" ;
178- #endif // SWIG_VERSION
179- #endif // strip_underscore
180- %feature(" python:slot" , " mp_ass_subscript" , functype=" objobjargproc" )
181- struct_type::__setitem__;
182- %extend struct_type {
183- PyObject* __setitem__ (PyObject* py_self, PyObject* key,
184- PyObject* value) {
185- if (getset_set (py_self, key, value, strip_underscore, true ))
186- return NULL ;
187- return SWIG_Py_Void ();
188- }
189- }
221+ %fragment(" set_item" {struct_type});
222+ %feature(" python:mp_ass_subscript" ) struct_type
223+ QUOTE (set_item_%mangle(struct_type));
224+ #if #strip_underscore == "true" || SWIG_VERSION < 0x040400
225+ %fragment(" set_attr" {struct_type});
226+ %feature(" python:tp_setattro" ) struct_type
227+ QUOTE (set_attr_%mangle(struct_type));
228+ #endif
190229#endif // mutable
191230%enddef // STRUCT_DICT
0 commit comments