Skip to content

Commit 2d1dd75

Browse files
Added wrapper class for Metadatum& references
These can be invalidated, just like iterators can be, so need a wrapper to enable protection from segfaults.
1 parent fa0cbb7 commit 2d1dd75

18 files changed

Lines changed: 21240 additions & 8498 deletions

src/interface/exif.i

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
%include "shared/buffers.i"
2828
%include "shared/containers.i"
2929
%include "shared/data_iterator.i"
30+
%include "shared/metadatum_reference.i"
3031
%include "shared/keep_reference.i"
3132
%include "shared/windows.i"
3233

@@ -52,10 +53,12 @@ KEEP_REFERENCE_EX(Exiv2::ExifThumb*, args)
5253

5354
INPUT_BUFFER_RO(const Exiv2::byte* buf, BUFLEN_T size)
5455

55-
EXTEND_METADATUM(Exiv2::Exifdatum)
56+
EXTEND_METADATUM(Exifdatum)
5657

5758
DATA_ITERATOR(ExifData, Exifdatum)
5859

60+
METADATUM_REFERENCE(Exifdatum)
61+
5962
// Get the current (or default if not set) type id of a datum
6063
%fragment("get_type_id"{Exiv2::Exifdatum}, "header") {
6164
static Exiv2::TypeId get_type_id(Exiv2::Exifdatum* datum) {

src/interface/iptc.i

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
%include "shared/preamble.i"
2727
%include "shared/containers.i"
2828
%include "shared/data_iterator.i"
29+
%include "shared/metadatum_reference.i"
2930

3031
%include "stdint.i"
3132
%include "std_string.i"
@@ -38,10 +39,12 @@ IMPORT_ENUM(TypeId)
3839
// Catch all C++ exceptions
3940
EXCEPTION()
4041

41-
EXTEND_METADATUM(Exiv2::Iptcdatum)
42+
EXTEND_METADATUM(Iptcdatum)
4243

4344
DATA_ITERATOR(IptcData, Iptcdatum)
4445

46+
METADATUM_REFERENCE(Iptcdatum)
47+
4548
// Get the current (or default if not set) type id of a datum
4649
%fragment("get_type_id"{Exiv2::Iptcdatum}, "header") {
4750
static Exiv2::TypeId get_type_id(Exiv2::Iptcdatum* datum) {

src/interface/metadatum.i

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,19 @@ EXTEND_KEY(Exiv2::Key);
5757
// Macro for Metadatum subclasses
5858
%define EXTEND_METADATUM(datum_type)
5959
// Ignore overloaded default parameter version
60-
%ignore datum_type::write(std::ostream &) const;
60+
%ignore Exiv2::datum_type::write(std::ostream &) const;
6161
// Keep a reference to Metadatum when calling value()
6262
KEEP_REFERENCE(const Exiv2::Value&)
6363
// Keep a reference to any object that returns a reference to a datum.
64-
KEEP_REFERENCE(datum_type&)
64+
KEEP_REFERENCE(Exiv2::datum_type&)
6565
// Set the datum's value from a Python object. The datum's current or default
6666
// type is used to create an Exiv2::Value object (via Python) from the Python
6767
// object.
68-
%fragment("set_value_from_py"{datum_type}, "header",
69-
fragment="get_type_object", fragment="get_type_id"{datum_type}) {
70-
static PyObject* set_value_from_py(datum_type* datum, PyObject* py_value) {
68+
%fragment("set_value_from_py"{Exiv2::datum_type}, "header",
69+
fragment="get_type_object",
70+
fragment="get_type_id"{Exiv2::datum_type}) {
71+
static PyObject* set_value_from_py(Exiv2::datum_type* datum,
72+
PyObject* py_value) {
7173
swig_type_info* ty_info = get_type_object(get_type_id(datum));
7274
SwigPyClientData *cl_data = (SwigPyClientData*)ty_info->clientdata;
7375
// Call type object to invoke constructor
@@ -89,7 +91,7 @@ static PyObject* set_value_from_py(datum_type* datum, PyObject* py_value) {
8991
return SWIG_Py_Void();
9092
};
9193
}
92-
%extend datum_type {
94+
%extend Exiv2::datum_type {
9395
// Extend Metadatum to allow getting value as a specific type.
9496
Exiv2::Value::SMART_PTR getValue(Exiv2::TypeId as_type) {
9597
// deprecated since 2023-12-07
@@ -104,7 +106,7 @@ static PyObject* set_value_from_py(datum_type* datum, PyObject* py_value) {
104106
// Set the value from a Python object. The datum's current or default
105107
// type is used to create an Exiv2::Value object (via Python) from the
106108
// Python object.
107-
%fragment("set_value_from_py"{datum_type});
109+
%fragment("set_value_from_py"{Exiv2::datum_type});
108110
PyObject* setValue(PyObject* py_value) {
109111
return set_value_from_py($self, py_value);
110112
}
@@ -120,8 +122,8 @@ static PyObject* set_value_from_py(datum_type* datum, PyObject* py_value) {
120122
std::string toString(BUFLEN_T i) const { return self->toString(i); }
121123
}
122124
// Deprecate some methods since 2025-08-25
123-
DEPRECATE_FUNCTION(datum_type::copy, true)
124-
DEPRECATE_FUNCTION(datum_type::write, true)
125+
DEPRECATE_FUNCTION(Exiv2::datum_type::copy, true)
126+
DEPRECATE_FUNCTION(Exiv2::datum_type::write, true)
125127
%enddef // EXTEND_METADATUM
126128

127129
// Deprecate some base class methods since 2025-08-25
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* python-exiv2 - Python interface to libexiv2
2+
* http://github.com/jim-easterbrook/python-exiv2
3+
* Copyright (C) 2025 Jim Easterbrook jim@jim-easterbrook.me.uk
4+
*
5+
* This file is part of python-exiv2.
6+
*
7+
* python-exiv2 is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* python-exiv2 is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with python-exiv2. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
22+
// Macro to wrap metadatum references so they can be invalidated
23+
%define METADATUM_REFERENCE(datum_type)
24+
%feature("python:slot", "tp_str", functype="reprfunc")
25+
datum_type##_pointer::__str__;
26+
%noexception datum_type##_pointer::operator==;
27+
%noexception datum_type##_pointer::operator!=;
28+
%noexception Exiv2::datum_type::operator==;
29+
%noexception Exiv2::datum_type::operator!=;
30+
%ignore datum_type##_pointer::##datum_type##_pointer;
31+
%ignore datum_type##_pointer::size;
32+
%ignore datum_type##_pointer::operator*;
33+
%ignore datum_type##_pointer::_invalidate;
34+
%feature("docstring") datum_type##_pointer "
35+
Python wrapper for an :class:`" #datum_type "` reference. It has most of
36+
the methods of :class:`" #datum_type "` allowing easy access to the
37+
data it points to."
38+
%inline %{
39+
class datum_type##_pointer {
40+
private:
41+
Exiv2::datum_type* ptr;
42+
bool invalidated;
43+
public:
44+
datum_type##_pointer(Exiv2::datum_type* ptr) {
45+
this->ptr = ptr;
46+
invalidated = false;
47+
}
48+
// Dereference operator gives Python access to all datum methods
49+
Exiv2::datum_type* operator->() const {
50+
if (invalidated)
51+
throw std::runtime_error("datum_type pointer is invalid");
52+
return ptr;
53+
}
54+
Exiv2::datum_type* operator*() const { return ptr; }
55+
bool operator==(const Exiv2::datum_type &other) const {
56+
return &other == ptr;
57+
}
58+
bool operator!=(const Exiv2::datum_type &other) const {
59+
return &other != ptr;
60+
}
61+
std::string __str__() {
62+
if (invalidated)
63+
return "invalid pointer";
64+
return "pointer<" + ptr->key() + ": " + ptr->print() + ">";
65+
}
66+
// Invalidate pointer unilaterally
67+
void _invalidate() { invalidated = true; }
68+
// Invalidate pointer if what it points to has been deleted
69+
bool _invalidate(Exiv2::datum_type* deleted) {
70+
if (deleted == ptr)
71+
invalidated = true;
72+
return invalidated;
73+
}
74+
// Provide size() C++ method for buffer size check
75+
size_t size() {
76+
if (invalidated)
77+
return 0;
78+
return ptr->size();
79+
}
80+
};
81+
%}
82+
%extend Exiv2::datum_type {
83+
bool operator==(const Exiv2::datum_type &other) const {
84+
return &other == self;
85+
}
86+
bool operator!=(const Exiv2::datum_type &other) const {
87+
return &other != self;
88+
}
89+
}
90+
%typemap(in) const Exiv2::datum_type& {
91+
datum_type##_pointer* tmp = NULL;
92+
if (SWIG_IsOK(SWIG_ConvertPtr(
93+
$input, (void**)&tmp, $descriptor(datum_type##_pointer*), 0)))
94+
$1 = **tmp;
95+
else {
96+
$typemap(in, Exiv2::datum_type&)
97+
}
98+
}
99+
%typemap(out) Exiv2::datum_type& {
100+
$result = SWIG_NewPointerObj(
101+
SWIG_as_voidptr(new datum_type##_pointer($1)),
102+
$descriptor(datum_type##_pointer*), SWIG_POINTER_OWN);
103+
}
104+
%enddef // METADATUM_REFERENCE

src/interface/xmp.i

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
%include "shared/preamble.i"
2727
%include "shared/containers.i"
2828
%include "shared/data_iterator.i"
29+
%include "shared/metadatum_reference.i"
2930

3031
%include "stdint.i"
3132
%include "std_string.i"
@@ -38,10 +39,12 @@ IMPORT_ENUM(TypeId)
3839
// Catch all C++ exceptions
3940
EXCEPTION()
4041

41-
EXTEND_METADATUM(Exiv2::Xmpdatum)
42+
EXTEND_METADATUM(Xmpdatum)
4243

4344
DATA_ITERATOR(XmpData, Xmpdatum)
4445

46+
METADATUM_REFERENCE(Xmpdatum)
47+
4548
// Get the current (or default if not set) type id of a datum
4649
%fragment("get_type_id"{Exiv2::Xmpdatum}, "header") {
4750
static Exiv2::TypeId get_type_id(Exiv2::Xmpdatum* datum) {

0 commit comments

Comments
 (0)