Skip to content

Commit d9d03cc

Browse files
Reworked exiv2 data struct handling
1 parent 48a717d commit d9d03cc

22 files changed

+2773
-3508
lines changed

src/interface/shared/struct_dict.i

Lines changed: 160 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -18,105 +18,117 @@
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

Comments
 (0)