Skip to content

Commit d103c0f

Browse files
Added a data() method to exiv2.BasicIo
This returns a simple context manager object that provides a memoryview object when it is used in a with statement.
1 parent fb5e0f8 commit d103c0f

File tree

5 files changed

+1353
-66
lines changed

5 files changed

+1353
-66
lines changed

USAGE.rst

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ Doing so will invalidate the memoryview and may cause a segmentation fault:
390390
buf.alloc(128)
391391
print(bytes(data)) # Prints random values, may segfault
392392
393+
A good way to ensure the memoryview_ object is ephemeral is to use it in a ``with`` statement:
394+
395+
.. code:: python
396+
397+
buf = exiv2.DataBuf(b'fred')
398+
with buf.data() as data:
399+
print(bytes(data)) # Prints b'fred'
400+
393401
Buffer interface
394402
----------------
395403

@@ -439,7 +447,17 @@ A Python `context manager`_ can be used to ensure that the ``open()`` and ``mmap
439447
with get_file_data(image) as data:
440448
rsp = requests.post(url, files={'file': io.BytesIO(data)})
441449
442-
The ``exiv2.BasicIo`` Python type exposes a `buffer interface`_ which is a lot easier to use.
450+
Since python-exiv2 v0.18.0 the ``exiv2.BasicIo.data()`` method provides a context manager that calls ``open``, ``mmap``, ``munmap``, and ``close`` for you:
451+
452+
.. code:: python
453+
454+
# after setting some metadata
455+
image.writeMetadata()
456+
exiv_io = image.io()
457+
with exiv_io.data() as data:
458+
rsp = requests.post(url, files={'file': io.BytesIO(data)})
459+
460+
The ``exiv2.BasicIo`` Python type also exposes a `buffer interface`_.
443461
It allows the ``exiv2.BasicIo`` object to be used anywhere that a `bytes-like object`_ is expected:
444462

445463
.. code:: python

src/interface/basicio.i

Lines changed: 70 additions & 1 deletion
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) 2022-24 Jim Easterbrook [email protected]
3+
// Copyright (C) 2022-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
@@ -167,6 +167,75 @@ static void release_ptr(Exiv2::BasicIo* self) {
167167
}
168168
EXPOSE_OBJECT_BUFFER(Exiv2::BasicIo, true, true)
169169

170+
// Wrapper class to provide a context manager for Exiv2::BasicIo::mmap
171+
%feature("docstring") DataContext "Data context manager.
172+
173+
A simple context manager for *mmap* / *munmap* data access. The
174+
*__enter__* method returns a :py:class:`memoryview` of the data."
175+
%ignore DataContext::DataContext;
176+
%inline %{
177+
class DataContext {
178+
private:
179+
Exiv2::BasicIo* parent;
180+
bool isWriteable;
181+
bool mapped;
182+
public:
183+
DataContext(Exiv2::BasicIo* parent, bool isWriteable) :
184+
parent(parent), isWriteable(isWriteable), mapped(false) {};
185+
~DataContext() {
186+
if (mapped) {
187+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
188+
parent->munmap();
189+
SWIG_PYTHON_THREAD_END_ALLOW;
190+
parent->close();
191+
}
192+
};
193+
PyObject* __enter__() {
194+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
195+
int error = parent->open();
196+
if (error) {
197+
SWIG_PYTHON_THREAD_END_ALLOW;
198+
return PyErr_Format(
199+
PyExc_RuntimeError, "open() returned %d", error);
200+
}
201+
char* ptr = (char*)parent->mmap(isWriteable);
202+
SWIG_PYTHON_THREAD_END_ALLOW;
203+
mapped = true;
204+
return PyMemoryView_FromMemory(
205+
ptr, ptr ? parent->size() : 0,
206+
isWriteable ? PyBUF_WRITE : PyBUF_READ);
207+
};
208+
bool __exit__(PyObject* exc_type, PyObject* exc_val, PyObject* exc_tb) {
209+
if (mapped) {
210+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
211+
parent->munmap();
212+
mapped = false;
213+
parent->close();
214+
SWIG_PYTHON_THREAD_END_ALLOW;
215+
}
216+
return false;
217+
};
218+
};
219+
%}
220+
221+
// Add Exiv2::BasicIo::data() method to return a context manager
222+
%feature("docstring") Exiv2::BasicIo::data "Return a data context manager.
223+
224+
This allows easy access to the data using a ``with`` statement.
225+
The context manager calls *mmap* when the context is entered and
226+
*munmap* when the context is exited.
227+
:type isWriteable: bool, optional
228+
:param isWriteable: Set to true if the data should be writeable
229+
(default is false).
230+
:rtype: object
231+
:return: A context manager"
232+
%newobject Exiv2::BasicIo::data;
233+
%extend Exiv2::BasicIo {
234+
DataContext* data(bool isWriteable) {
235+
return new DataContext($self, isWriteable);
236+
}
237+
}
238+
170239
// Make enum more Pythonic
171240
DEFINE_CLASS_ENUM(BasicIo, Position, "Seek starting positions.",
172241
"beg", Exiv2::BasicIo::beg,

0 commit comments

Comments
 (0)