Skip to content

Commit db8b6e4

Browse files
Deprecate use of BasicIo objects
1 parent ede9d04 commit db8b6e4

8 files changed

Lines changed: 65 additions & 19 deletions

File tree

CHANGELOG.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ You should have received a copy of the GNU General Public License
1616
along with this program. If not, see
1717
<http://www.gnu.org/licenses/>.
1818

19+
Changes in v0.20.0:
20+
1/ Deprecate all use of BasicIo objects. Please let me know if this is a
21+
problem.
22+
2/ Initialise XMP toolkit on startup and deprecate XmpParser.initialize
23+
3/ Improved thread safety.
24+
1925
Changes in v0.19.2:
2026
1/ Binary wheels incorporate libexiv2 v0.28.9
2127

USAGE.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ In python-exiv2 the ``data`` and ``size`` parameters are replaced with a single
399399
The buffered data isn't actually read until ``Image::readMetadata`` is called, so python-exiv2 stores a reference to the buffer to stop the user accidentally deleting it.
400400

401401
When ``Image::writeMetadata`` is called exiv2 allocates a new block of memory to store the modified data.
402-
The ``Image::io`` method returns an `Exiv2::BasicIo`_ object that provides access to this data.
402+
The deprecated ``Image::io`` method returns an `Exiv2::BasicIo`_ object that provides access to this data.
403403

404404
The ``BasicIo::mmap`` and ``BasicIo::munmap`` methods allow access to the image file data without unnecessary copying.
405405
However they are rather error prone, crashing your Python program with a segmentation fault if anything goes wrong.

src/interface/image.i

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ WINDOWS_PATH(const std::string& path)
131131
%typemap(default) bool enable {$1 = true;}
132132
%ignore Exiv2::enableBMFF();
133133

134+
// Deprecate methods that return a BasicIo object
135+
// Deprecated since 2026-09-01
136+
DEPRECATE_FUNCTION(Exiv2::Image::io, true)
137+
DEPRECATE_FUNCTION(Exiv2::ImageFactory::checkType, true)
138+
DEPRECATE_FUNCTION(Exiv2::ImageFactory::createIo, true)
139+
DEPRECATE_FUNCTION(Exiv2::ImageFactory::getType(BasicIo&), true)
140+
134141
// Extend ImageFactory to allow creation of a MemIo from a buffer
135142
%feature("docstring") Exiv2::ImageFactory::createIo "
136143
*Overload 1:*

src/swig-0_27_7/image_wrap.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6402,6 +6402,7 @@ SWIGINTERN PyObject *_wrap_Image_io(PyObject *self, PyObject *args) {
64026402
#endif
64036403
}
64046404
{
6405+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::Image::io", 1);
64056406
try {
64066407
result = (Exiv2::BasicIo *) &((Exiv2::Image const *)arg1)->io();
64076408
}
@@ -6816,6 +6817,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_0(PyObject *self, Py_ssiz
68166817
#endif
68176818
}
68186819
{
6820+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
68196821
try {
68206822
result = Exiv2::ImageFactory::createIo((std::string const &)*arg1,arg2);
68216823
}
@@ -7251,6 +7253,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_getType__SWIG_2(PyObject *self, Py_ssize
72517253
}
72527254
arg1 = reinterpret_cast< Exiv2::BasicIo * >(argp1);
72537255
{
7256+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::getType(BasicIo&)", 1);
72547257
try {
72557258
result = (int)Exiv2::ImageFactory::getType(*arg1);
72567259
}
@@ -7409,6 +7412,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_checkType(PyObject *self, PyObject *args
74097412
}
74107413
arg3 = static_cast< bool >(val3);
74117414
{
7415+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::checkType", 1);
74127416
try {
74137417
result = (bool)Exiv2::ImageFactory::checkType(arg1,*arg2,arg3);
74147418
}
@@ -7448,6 +7452,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_1(PyObject *self, Py_ssiz
74487452
arg2 = (long) buff->len;
74497453
}
74507454
{
7455+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
74517456
try {
74527457
result = Exiv2_ImageFactory_createIo__SWIG_1((unsigned char const *)arg1,arg2);
74537458
}

src/swig-0_28_7/image_wrap.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6456,6 +6456,7 @@ SWIGINTERN PyObject *_wrap_Image_io(PyObject *self, PyObject *args) {
64566456
#endif
64576457
}
64586458
{
6459+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::Image::io", 1);
64596460
try {
64606461
result = (Exiv2::BasicIo *) &((Exiv2::Image const *)arg1)->io();
64616462
}
@@ -6870,6 +6871,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_0(PyObject *self, Py_ssiz
68706871
#endif
68716872
}
68726873
{
6874+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
68736875
try {
68746876
result = Exiv2::ImageFactory::createIo((std::string const &)*arg1,arg2);
68756877
}
@@ -7309,6 +7311,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_getType__SWIG_2(PyObject *self, Py_ssize
73097311
}
73107312
arg1 = reinterpret_cast< Exiv2::BasicIo * >(argp1);
73117313
{
7314+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::getType(BasicIo&)", 1);
73127315
try {
73137316
result = (Exiv2::ImageType)Exiv2::ImageFactory::getType(*arg1);
73147317
}
@@ -7467,6 +7470,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_checkType(PyObject *self, PyObject *args
74677470
}
74687471
arg3 = static_cast< bool >(val3);
74697472
{
7473+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::checkType", 1);
74707474
try {
74717475
result = (bool)Exiv2::ImageFactory::checkType(arg1,*arg2,arg3);
74727476
}
@@ -7506,6 +7510,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_1(PyObject *self, Py_ssiz
75067510
arg2 = (size_t) buff->len;
75077511
}
75087512
{
7513+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
75097514
try {
75107515
result = Exiv2_ImageFactory_createIo__SWIG_1((unsigned char const *)arg1,SWIG_STD_MOVE(*(&arg2)));
75117516
}

src/swig-0_28_9/image_wrap.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6456,6 +6456,7 @@ SWIGINTERN PyObject *_wrap_Image_io(PyObject *self, PyObject *args) {
64566456
#endif
64576457
}
64586458
{
6459+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::Image::io", 1);
64596460
try {
64606461
result = (Exiv2::BasicIo *) &((Exiv2::Image const *)arg1)->io();
64616462
}
@@ -6870,6 +6871,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_0(PyObject *self, Py_ssiz
68706871
#endif
68716872
}
68726873
{
6874+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
68736875
try {
68746876
result = Exiv2::ImageFactory::createIo((std::string const &)*arg1,arg2);
68756877
}
@@ -7309,6 +7311,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_getType__SWIG_2(PyObject *self, Py_ssize
73097311
}
73107312
arg1 = reinterpret_cast< Exiv2::BasicIo * >(argp1);
73117313
{
7314+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::getType(BasicIo&)", 1);
73127315
try {
73137316
result = (Exiv2::ImageType)Exiv2::ImageFactory::getType(*arg1);
73147317
}
@@ -7467,6 +7470,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_checkType(PyObject *self, PyObject *args
74677470
}
74687471
arg3 = static_cast< bool >(val3);
74697472
{
7473+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::checkType", 1);
74707474
try {
74717475
result = (bool)Exiv2::ImageFactory::checkType(arg1,*arg2,arg3);
74727476
}
@@ -7506,6 +7510,7 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo__SWIG_1(PyObject *self, Py_ssiz
75067510
arg2 = (size_t) buff->len;
75077511
}
75087512
{
7513+
PyErr_WarnEx(PyExc_DeprecationWarning, "Python scripts should not need to call ""Exiv2::ImageFactory::createIo", 1);
75097514
try {
75107515
result = Exiv2_ImageFactory_createIo__SWIG_1((unsigned char const *)arg1,SWIG_STD_MOVE(*(&arg2)));
75117516
}

tests/test_basicio.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def setUpClass(cls):
5555
def test_CurlIo(self):
5656
https_image = ('https://raw.githubusercontent.com/jim-easterbrook'
5757
'/python-exiv2/main/tests/image_02.jpg')
58-
io = exiv2.ImageFactory.createIo(https_image)
58+
with self.assertWarns(DeprecationWarning):
59+
io = exiv2.ImageFactory.createIo(https_image)
5960
self.assertIsInstance(io, exiv2.BasicIo)
6061
self.assertEqual(io.ioType(), 'CurlIo')
6162
self.assertEqual(io.error(), False)
@@ -79,7 +80,8 @@ def test_CurlIo(self):
7980
'EXV_ENABLE_FILESYSTEM is off')
8081
def test_FileIo(self):
8182
# most functions are tested in test_MemIo
82-
io = exiv2.ImageFactory.createIo(self.image_path)
83+
with self.assertWarns(DeprecationWarning):
84+
io = exiv2.ImageFactory.createIo(self.image_path)
8385
self.assertIsInstance(io, exiv2.BasicIo)
8486
self.assertEqual(io.ioType(), 'FileIo')
8587
self.assertEqual(io.error(), False)
@@ -94,7 +96,8 @@ def test_FileIo(self):
9496

9597
def test_MemIo(self):
9698
# empty buffer
97-
io = exiv2.ImageFactory.createIo(b'')
99+
with self.assertWarns(DeprecationWarning):
100+
io = exiv2.ImageFactory.createIo(b'')
98101
self.assertIsInstance(io, exiv2.BasicIo)
99102
self.assertEqual(io.ioType(), 'MemIo')
100103
self.assertEqual(io.size(), 0)
@@ -139,7 +142,8 @@ def test_MemIo(self):
139142
with self.assertRaises(IndexError):
140143
view[0] = 0
141144
# non-empty buffer
142-
io = exiv2.ImageFactory.createIo(self.data)
145+
with self.assertWarns(DeprecationWarning):
146+
io = exiv2.ImageFactory.createIo(self.data)
143147
self.assertIsInstance(io, exiv2.BasicIo)
144148
self.assertEqual(io.ioType(), 'MemIo')
145149
self.assertEqual(io.error(), False)
@@ -253,7 +257,8 @@ def test_ref_counts(self):
253257
if count > 1000000:
254258
# self.data is "immortal"
255259
return
256-
io = exiv2.ImageFactory.createIo(self.data)
260+
with self.assertWarns(DeprecationWarning):
261+
io = exiv2.ImageFactory.createIo(self.data)
257262
self.assertEqual(sys.getrefcount(self.data), count + 1)
258263
del io
259264
self.assertEqual(sys.getrefcount(self.data), count)
@@ -272,7 +277,8 @@ def test_unicode_paths(self):
272277
tmp_path = os.path.normcase(
273278
os.path.join(tmp_dir, file_name))
274279
shutil.copyfile(self.image_path, tmp_path)
275-
io = exiv2.ImageFactory.createIo(tmp_path)
280+
with self.assertWarns(DeprecationWarning):
281+
io = exiv2.ImageFactory.createIo(tmp_path)
276282
if cp in codes:
277283
self.assertEqual(io.path(), tmp_path)
278284
else:

tests/test_image.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def test_BMFF(self):
6262
def test_Image(self):
6363
# open image in memory so we don't corrupt the file
6464
image = exiv2.ImageFactory.open(self.image_data)
65-
self.assertEqual(len(image.io()), 15125)
65+
with self.assertWarns(DeprecationWarning):
66+
self.assertEqual(len(image.io()), 15125)
6667
# test clearMetadata
6768
image.readMetadata()
6869
self.check_result(image.comment(), str, 'Created with GIMP')
@@ -77,7 +78,8 @@ def test_Image(self):
7778
self.assertEqual(len(image.xmpData()), 0)
7879
self.assertEqual(len(image.iccProfile()), 0)
7980
image.writeMetadata()
80-
self.assertEqual(len(image.io()), 6371)
81+
with self.assertWarns(DeprecationWarning):
82+
self.assertEqual(len(image.io()), 6371)
8183
# test setting individual parts
8284
image2 = exiv2.ImageFactory.open(self.image_data)
8385
image2.readMetadata()
@@ -96,7 +98,8 @@ def test_Image(self):
9698
self.assertEqual(len(image.iccProfile()), 672)
9799
image.writeMetadata()
98100
data_len = (15125, 15174)[exiv2.testVersion(0, 28, 9)]
99-
self.assertEqual(len(image.io()), data_len)
101+
with self.assertWarns(DeprecationWarning):
102+
self.assertEqual(len(image.io()), data_len)
100103
del image2
101104
# test clearing individual parts
102105
image = exiv2.ImageFactory.open(self.image_data)
@@ -148,7 +151,8 @@ def test_Image(self):
148151
self.check_result(image.iccProfileDefined(), bool, True)
149152
self.check_result(image.imageType(),
150153
exiv2.ImageType, exiv2.ImageType.jpeg)
151-
self.assertIsInstance(image.io(), exiv2.BasicIo)
154+
with self.assertWarns(DeprecationWarning):
155+
self.assertIsInstance(image.io(), exiv2.BasicIo)
152156
self.check_result(image.mimeType(), str, 'image/jpeg')
153157
self.check_result(image.pixelHeight(), int, 200)
154158
self.check_result(image.pixelWidth(), int, 200)
@@ -163,28 +167,35 @@ def test_ImageFactory(self):
163167
self.check_result(
164168
factory.checkMode(exiv2.ImageType.jpeg, exiv2.MetadataId.Exif),
165169
exiv2.AccessMode, exiv2.AccessMode.ReadWrite)
166-
io = factory.createIo(self.image_data)
170+
with self.assertWarns(DeprecationWarning):
171+
io = factory.createIo(self.image_data)
167172
with self.assertWarns(DeprecationWarning):
168173
factory.checkType(int(exiv2.ImageType.jpeg), io, False)
169-
self.check_result(
170-
factory.checkType(exiv2.ImageType.jpeg, io, False), bool, True)
174+
with self.assertWarns(DeprecationWarning):
175+
self.check_result(
176+
factory.checkType(exiv2.ImageType.jpeg, io, False), bool, True)
171177
with self.assertWarns(DeprecationWarning):
172178
factory.create(int(exiv2.ImageType.jpeg))
173179
self.assertIsInstance(
174180
factory.create(exiv2.ImageType.jpeg), exiv2.Image)
175-
self.assertIsInstance(factory.createIo(self.image_data), exiv2.BasicIo)
181+
with self.assertWarns(DeprecationWarning):
182+
self.assertIsInstance(
183+
factory.createIo(self.image_data), exiv2.BasicIo)
176184
self.check_result(factory.getType(self.image_data),
177185
exiv2.ImageType, exiv2.ImageType.jpeg)
178-
self.check_result(factory.getType(io),
179-
exiv2.ImageType, exiv2.ImageType.jpeg)
186+
with self.assertWarns(DeprecationWarning):
187+
self.check_result(factory.getType(io),
188+
exiv2.ImageType, exiv2.ImageType.jpeg)
180189
self.assertIsInstance(factory.open(self.image_data), exiv2.Image)
181190
if not exiv2.versionInfo()['EXV_ENABLE_FILESYSTEM']:
182191
self.skipTest('EXV_ENABLE_FILESYSTEM is off')
183192
with tempfile.TemporaryDirectory() as tmp_dir:
184193
temp_file = os.path.join(tmp_dir, 'image.jpg')
185194
self.assertIsInstance(
186195
factory.create(exiv2.ImageType.jpeg, temp_file), exiv2.Image)
187-
self.assertIsInstance(factory.createIo(self.image_path), exiv2.BasicIo)
196+
with self.assertWarns(DeprecationWarning):
197+
self.assertIsInstance(
198+
factory.createIo(self.image_path), exiv2.BasicIo)
188199
self.check_result(factory.getType(self.image_path),
189200
exiv2.ImageType, exiv2.ImageType.jpeg)
190201
self.assertIsInstance(factory.open(self.image_path), exiv2.Image)
@@ -210,7 +221,8 @@ def test_ref_counts(self):
210221
self.assertEqual(sys.getrefcount(image), count + 3)
211222
data.append(image.iccProfile())
212223
self.assertEqual(sys.getrefcount(image), count + 4)
213-
data.append(image.io())
224+
with self.assertWarns(DeprecationWarning):
225+
data.append(image.io())
214226
self.assertEqual(sys.getrefcount(image), count + 5)
215227
data.append(image.data())
216228
self.assertEqual(sys.getrefcount(image), count + 6)

0 commit comments

Comments
 (0)