Skip to content

Commit 9c59f4a

Browse files
Added strip_underscore option to STRUCT_DICT macro
This simplifies things a bit by treating C structs with underscores appended to the field names differently to ones without.
1 parent 44b224a commit 9c59f4a

File tree

17 files changed

+274
-445
lines changed

17 files changed

+274
-445
lines changed

src/interface/datasets.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ EXTEND_KEY(Exiv2::IptcKey);
4646
LIST_POINTER(const Exiv2::DataSet*, Exiv2::DataSet, number_ != 0xffff)
4747

4848
// Give Exiv2::DataSet dict-like behaviour
49-
STRUCT_DICT(Exiv2::DataSet, false)
49+
STRUCT_DICT(Exiv2::DataSet, false, true)
5050

5151
// Structs are all static data
5252
%ignore Exiv2::IptcDataSets::IptcDataSets;

src/interface/preview.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ RETURN_VIEW(Exiv2::byte* pData, arg1->size(), PyBUF_READ,
9393
Exiv2::PreviewImage::pData)
9494

9595
// Give Exiv2::PreviewProperties dict-like behaviour
96-
STRUCT_DICT(Exiv2::PreviewProperties, false)
96+
STRUCT_DICT(Exiv2::PreviewProperties, false, true)
9797

9898
%immutable Exiv2::PreviewProperties::mimeType_;
9999
%immutable Exiv2::PreviewProperties::extension_;

src/interface/properties.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ LIST_POINTER(const Exiv2::XmpPropertyInfo* xmpPropertyInfo_,
8383
Exiv2::XmpPropertyInfo, name_)
8484

8585
// Give Exiv2::XmpPropertyInfo dict-like behaviour
86-
STRUCT_DICT(Exiv2::XmpPropertyInfo, false)
86+
STRUCT_DICT(Exiv2::XmpPropertyInfo, false, true)
8787

8888
// Give Exiv2::XmpNsInfo dict-like behaviour
89-
STRUCT_DICT(Exiv2::XmpNsInfo, false)
89+
STRUCT_DICT(Exiv2::XmpNsInfo, false, true)
9090

9191
// Structs are all static data
9292
%ignore Exiv2::XmpPropertyInfo::XmpPropertyInfo;

src/interface/shared/struct_dict.i

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// python-exiv2 - Python interface to libexiv2
22
// http://github.com/jim-easterbrook/python-exiv2
3-
// Copyright (C) 2024 Jim Easterbrook [email protected]
3+
// Copyright (C) 2024-25 Jim Easterbrook [email protected]
44
//
55
// This program is free software: you can redistribute it and/or modify
66
// it under the terms of the GNU General Public License as published by
@@ -35,41 +35,33 @@ static PyObject* list_getset(
3535
}
3636
return result;
3737
};
38-
static PyGetSetDef* find_getset(PyObject* obj, const char* name) {
39-
size_t len = strlen(name);
40-
PyGetSetDef* getset = obj->ob_type->tp_getset;
41-
while (getset->name) {
42-
size_t cmp_len = strlen(getset->name);
43-
if (getset->name[cmp_len-1] == '_')
44-
cmp_len--;
45-
if ((cmp_len == len) && (strncmp(getset->name, name, len) == 0))
46-
return getset;
47-
getset++;
48-
}
49-
PyErr_Format(
50-
PyExc_KeyError, "'%s' not in '%s'", name, obj->ob_type->tp_name);
51-
return NULL;
38+
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
39+
return Py_BuildValue("N", getset->get(obj, getset->closure));
5240
};
53-
static PyObject* getset_to_item(PyObject* obj, PyGetSetDef* getset) {
54-
size_t len = strlen(getset->name);
55-
if (getset->name[len-1] == '_')
56-
len--;
57-
return Py_BuildValue("(s#N)", getset->name, len,
41+
}
42+
%fragment("getset_functions_strip", "header",
43+
fragment="getset_functions") {
44+
static PyObject* getset_to_item_strip(PyObject* obj, PyGetSetDef* getset) {
45+
return Py_BuildValue("(s#N)", getset->name, strlen(getset->name) - 1,
5846
getset->get(obj, getset->closure));
5947
};
60-
static PyObject* getset_to_key(PyObject* obj, PyGetSetDef* getset) {
61-
size_t len = strlen(getset->name);
62-
if (getset->name[len-1] == '_')
63-
len--;
64-
return Py_BuildValue("s#", getset->name, len);
48+
static PyObject* getset_to_key_strip(PyObject* obj, PyGetSetDef* getset) {
49+
return Py_BuildValue("s#", getset->name, strlen(getset->name) - 1);
6550
};
66-
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
67-
return Py_BuildValue("N", getset->get(obj, getset->closure));
51+
}
52+
%fragment("getset_functions_nostrip", "header",
53+
fragment="getset_functions") {
54+
static PyObject* getset_to_item_nostrip(PyObject* obj, PyGetSetDef* getset) {
55+
return Py_BuildValue("(sN)", getset->name,
56+
getset->get(obj, getset->closure));
57+
};
58+
static PyObject* getset_to_key_nostrip(PyObject* obj, PyGetSetDef* getset) {
59+
return Py_BuildValue("s", getset->name);
6860
};
6961
}
7062

7163
// Macro definition
72-
%define STRUCT_DICT(struct_type, mutable)
64+
%define STRUCT_DICT(struct_type, mutable, strip_underscore)
7365
// Type slots
7466
%feature("python:slot", "tp_iter", functype="getiterfunc")
7567
struct_type::__iter__;
@@ -92,13 +84,23 @@ static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
9284
:return: structure member values."
9385
// Add functions
9486
%extend struct_type {
95-
%fragment("getset_functions");
87+
#if #strip_underscore == "true"
88+
%fragment("getset_functions_strip");
9689
PyObject* items(PyObject* py_self) {
97-
return list_getset(py_self, getset_to_item);
90+
return list_getset(py_self, getset_to_item_strip);
9891
}
9992
PyObject* keys(PyObject* py_self) {
100-
return list_getset(py_self, getset_to_key);
93+
return list_getset(py_self, getset_to_key_strip);
94+
}
95+
#else
96+
%fragment("getset_functions_nostrip");
97+
PyObject* items(PyObject* py_self) {
98+
return list_getset(py_self, getset_to_item_nostrip);
10199
}
100+
PyObject* keys(PyObject* py_self) {
101+
return list_getset(py_self, getset_to_key_nostrip);
102+
}
103+
#endif // strip_underscore
102104
PyObject* values(PyObject* py_self) {
103105
return list_getset(py_self, getset_to_value);
104106
}
@@ -110,10 +112,11 @@ static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
110112
return result;
111113
}
112114
PyObject* __getitem__(PyObject* py_self, const std::string& key) {
113-
PyGetSetDef* getset = find_getset(py_self, key.c_str());
114-
if (!getset)
115-
return NULL;
116-
return getset->get(py_self, getset->closure);
115+
#if #strip_underscore == "true"
116+
return PyObject_GetAttrString(py_self, (key + '_').c_str());
117+
#else
118+
return PyObject_GetAttrString(py_self, key.c_str());
119+
#endif // strip_underscore
117120
}
118121
}
119122
#if #mutable == "true"
@@ -122,17 +125,17 @@ static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
122125
%extend struct_type {
123126
PyObject* __setitem__(PyObject* py_self, const std::string& key,
124127
PyObject* value) {
125-
PyGetSetDef* getset = find_getset(py_self, key.c_str());
126-
if (!getset)
127-
return NULL;
128128
if (!value)
129129
return PyErr_Format(PyExc_TypeError,
130130
"%s['%s'] can not be deleted", py_self->ob_type->tp_name,
131131
key.c_str());
132-
if (!getset->set)
133-
return PyErr_Format(PyExc_TypeError, "%s['%s'] is read-only",
134-
py_self->ob_type->tp_name, key.c_str());
135-
if (getset->set(py_self, value, getset->closure) != 0)
132+
#if #strip_underscore == "true"
133+
int error = PyObject_SetAttrString(
134+
py_self, (key + '_').c_str(), value);
135+
#else
136+
int error = PyObject_SetAttrString(py_self, key.c_str(), value);
137+
#endif // strip_underscore
138+
if (error)
136139
return NULL;
137140
return SWIG_Py_Void();
138141
}

src/interface/tags.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ LIST_POINTER(const Exiv2::GroupInfo*, Exiv2::GroupInfo, tagList_)
9898
LIST_POINTER(const Exiv2::TagInfo*, Exiv2::TagInfo, tag_ != 0xFFFF)
9999

100100
// Give Exiv2::GroupInfo dict-like behaviour
101-
STRUCT_DICT(Exiv2::GroupInfo, false)
101+
STRUCT_DICT(Exiv2::GroupInfo, false, true)
102102

103103
// Give Exiv2::TagInfo dict-like behaviour
104-
STRUCT_DICT(Exiv2::TagInfo, false)
104+
STRUCT_DICT(Exiv2::TagInfo, false, true)
105105

106106
// Wrapper class for TagListFct function pointer
107107
#ifndef SWIGIMPORTED

src/interface/value.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ VALUE_SUBCLASS(Exiv2::ValueType<item_type>, type_name)
334334
%enddef // VALUETYPE
335335

336336
// Give Date and Time structs some dict-like behaviour
337-
STRUCT_DICT(Exiv2::DateValue::Date, true)
338-
STRUCT_DICT(Exiv2::TimeValue::Time, true)
337+
STRUCT_DICT(Exiv2::DateValue::Date, true, false)
338+
STRUCT_DICT(Exiv2::TimeValue::Time, true, false)
339339

340340
%extend Exiv2::DateValue {
341341
// Allow DateValue to be constructed from a Date

src/swig-0_27_7/datasets_wrap.cxx

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4377,36 +4377,17 @@ static PyObject* list_getset(
43774377
}
43784378
return result;
43794379
};
4380-
static PyGetSetDef* find_getset(PyObject* obj, const char* name) {
4381-
size_t len = strlen(name);
4382-
PyGetSetDef* getset = obj->ob_type->tp_getset;
4383-
while (getset->name) {
4384-
size_t cmp_len = strlen(getset->name);
4385-
if (getset->name[cmp_len-1] == '_')
4386-
cmp_len--;
4387-
if ((cmp_len == len) && (strncmp(getset->name, name, len) == 0))
4388-
return getset;
4389-
getset++;
4390-
}
4391-
PyErr_Format(
4392-
PyExc_KeyError, "'%s' not in '%s'", name, obj->ob_type->tp_name);
4393-
return NULL;
4380+
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
4381+
return Py_BuildValue("N", getset->get(obj, getset->closure));
43944382
};
4395-
static PyObject* getset_to_item(PyObject* obj, PyGetSetDef* getset) {
4396-
size_t len = strlen(getset->name);
4397-
if (getset->name[len-1] == '_')
4398-
len--;
4399-
return Py_BuildValue("(s#N)", getset->name, len,
4383+
4384+
4385+
static PyObject* getset_to_item_strip(PyObject* obj, PyGetSetDef* getset) {
4386+
return Py_BuildValue("(s#N)", getset->name, strlen(getset->name) - 1,
44004387
getset->get(obj, getset->closure));
44014388
};
4402-
static PyObject* getset_to_key(PyObject* obj, PyGetSetDef* getset) {
4403-
size_t len = strlen(getset->name);
4404-
if (getset->name[len-1] == '_')
4405-
len--;
4406-
return Py_BuildValue("s#", getset->name, len);
4407-
};
4408-
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
4409-
return Py_BuildValue("N", getset->get(obj, getset->closure));
4389+
static PyObject* getset_to_key_strip(PyObject* obj, PyGetSetDef* getset) {
4390+
return Py_BuildValue("s#", getset->name, strlen(getset->name) - 1);
44104391
};
44114392

44124393

@@ -4469,10 +4450,10 @@ static PyObject* py_from_enum_Exiv2_TypeId(long value) {
44694450
};
44704451

44714452
SWIGINTERN PyObject *Exiv2_DataSet_items(Exiv2::DataSet *self,PyObject *py_self){
4472-
return list_getset(py_self, getset_to_item);
4453+
return list_getset(py_self, getset_to_item_strip);
44734454
}
44744455
SWIGINTERN PyObject *Exiv2_DataSet_keys(Exiv2::DataSet *self,PyObject *py_self){
4475-
return list_getset(py_self, getset_to_key);
4456+
return list_getset(py_self, getset_to_key_strip);
44764457
}
44774458
SWIGINTERN PyObject *Exiv2_DataSet_values(Exiv2::DataSet *self,PyObject *py_self){
44784459
return list_getset(py_self, getset_to_value);
@@ -4599,10 +4580,11 @@ SWIG_AsPtr_std_string (PyObject * obj, std::string **val)
45994580
}
46004581

46014582
SWIGINTERN PyObject *Exiv2_DataSet___getitem__(Exiv2::DataSet *self,PyObject *py_self,std::string const &key){
4602-
PyGetSetDef* getset = find_getset(py_self, key.c_str());
4603-
if (!getset)
4604-
return NULL;
4605-
return getset->get(py_self, getset->closure);
4583+
4584+
return PyObject_GetAttrString(py_self, (key + '_').c_str());
4585+
4586+
4587+
46064588
}
46074589

46084590
#include <limits.h>

src/swig-0_27_7/preview_wrap.cxx

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5472,36 +5472,17 @@ static PyObject* list_getset(
54725472
}
54735473
return result;
54745474
};
5475-
static PyGetSetDef* find_getset(PyObject* obj, const char* name) {
5476-
size_t len = strlen(name);
5477-
PyGetSetDef* getset = obj->ob_type->tp_getset;
5478-
while (getset->name) {
5479-
size_t cmp_len = strlen(getset->name);
5480-
if (getset->name[cmp_len-1] == '_')
5481-
cmp_len--;
5482-
if ((cmp_len == len) && (strncmp(getset->name, name, len) == 0))
5483-
return getset;
5484-
getset++;
5485-
}
5486-
PyErr_Format(
5487-
PyExc_KeyError, "'%s' not in '%s'", name, obj->ob_type->tp_name);
5488-
return NULL;
5475+
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
5476+
return Py_BuildValue("N", getset->get(obj, getset->closure));
54895477
};
5490-
static PyObject* getset_to_item(PyObject* obj, PyGetSetDef* getset) {
5491-
size_t len = strlen(getset->name);
5492-
if (getset->name[len-1] == '_')
5493-
len--;
5494-
return Py_BuildValue("(s#N)", getset->name, len,
5478+
5479+
5480+
static PyObject* getset_to_item_strip(PyObject* obj, PyGetSetDef* getset) {
5481+
return Py_BuildValue("(s#N)", getset->name, strlen(getset->name) - 1,
54955482
getset->get(obj, getset->closure));
54965483
};
5497-
static PyObject* getset_to_key(PyObject* obj, PyGetSetDef* getset) {
5498-
size_t len = strlen(getset->name);
5499-
if (getset->name[len-1] == '_')
5500-
len--;
5501-
return Py_BuildValue("s#", getset->name, len);
5502-
};
5503-
static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) {
5504-
return Py_BuildValue("N", getset->get(obj, getset->closure));
5484+
static PyObject* getset_to_key_strip(PyObject* obj, PyGetSetDef* getset) {
5485+
return Py_BuildValue("s#", getset->name, strlen(getset->name) - 1);
55055486
};
55065487

55075488

@@ -5526,10 +5507,10 @@ SWIGINTERNINLINE PyObject*
55265507
}
55275508

55285509
SWIGINTERN PyObject *Exiv2_PreviewProperties_items(Exiv2::PreviewProperties *self,PyObject *py_self){
5529-
return list_getset(py_self, getset_to_item);
5510+
return list_getset(py_self, getset_to_item_strip);
55305511
}
55315512
SWIGINTERN PyObject *Exiv2_PreviewProperties_keys(Exiv2::PreviewProperties *self,PyObject *py_self){
5532-
return list_getset(py_self, getset_to_key);
5513+
return list_getset(py_self, getset_to_key_strip);
55335514
}
55345515
SWIGINTERN PyObject *Exiv2_PreviewProperties_values(Exiv2::PreviewProperties *self,PyObject *py_self){
55355516
return list_getset(py_self, getset_to_value);
@@ -5656,10 +5637,11 @@ SWIG_AsPtr_std_string (PyObject * obj, std::string **val)
56565637
}
56575638

56585639
SWIGINTERN PyObject *Exiv2_PreviewProperties___getitem__(Exiv2::PreviewProperties *self,PyObject *py_self,std::string const &key){
5659-
PyGetSetDef* getset = find_getset(py_self, key.c_str());
5660-
if (!getset)
5661-
return NULL;
5662-
return getset->get(py_self, getset->closure);
5640+
5641+
return PyObject_GetAttrString(py_self, (key + '_').c_str());
5642+
5643+
5644+
56635645
}
56645646

56655647
#define SWIG_From_long PyInt_FromLong

0 commit comments

Comments
 (0)