Skip to content

Commit 8216b98

Browse files
Replaced context object with simple data() method
This is much simpler, and takes advantage of the callback used to call munmap when the memoryview is deleted.
1 parent 49bafea commit 8216b98

File tree

5 files changed

+128
-1227
lines changed

5 files changed

+128
-1227
lines changed

USAGE.rst

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -384,19 +384,29 @@ Doing so will invalidate the memoryview and may cause a segmentation fault:
384384

385385
.. code:: python
386386
387-
buf = exiv2.DataBuf(b'fred')
388-
data = buf.data()
389-
print(bytes(data)) # Prints b'fred'
390-
buf.alloc(128)
391-
print(bytes(data)) # Prints random values, may segfault
387+
>>> buf = exiv2.DataBuf(b'fred')
388+
>>> data = buf.data()
389+
>>> print(bytes(data))
390+
b'fred'
391+
>>> del buf
392+
>>> print(bytes(data))
393+
b'en_G'
392394
393-
A good way to ensure the memoryview_ object is ephemeral is to use it in a ``with`` statement:
395+
Since version 0.18.0 python-exiv2 releases the memoryview (when the memory block is resized) to prevent problems:
394396

395397
.. code:: python
396398
397-
buf = exiv2.DataBuf(b'fred')
398-
with buf.data() as data:
399-
print(bytes(data)) # Prints b'fred'
399+
>>> buf = exiv2.DataBuf(b'fred')
400+
>>> data = buf.data()
401+
>>> print(bytes(data))
402+
b'fred'
403+
>>> buf.alloc(128)
404+
>>> print(bytes(data))
405+
Traceback (most recent call last):
406+
File "<stdin>", line 1, in <module>
407+
ValueError: operation forbidden on released memoryview object
408+
409+
Unfortunately I haven't been able to make this work for memory block deletion.
400410

401411
Buffer interface
402412
----------------
@@ -425,7 +435,7 @@ The ``Image::io`` method returns an `Exiv2::BasicIo`_ object that provides acces
425435
The ``BasicIo::mmap`` method allows access to the image file data without unnecessary copying.
426436
However it is rather error prone, crashing your Python program with a segmentation fault if anything goes wrong.
427437

428-
The ``Exiv2::BasicIo`` object must be opened before calling ``mmap()``.
438+
The ``Exiv2::BasicIo`` object must be open when ``mmap()`` is called.
429439
A Python `context manager`_ can be used to ensure that the ``open()`` and ``mmap()`` calls are paired with ``munmap()`` and ``close()`` calls:
430440

431441
.. code:: python
@@ -447,15 +457,14 @@ A Python `context manager`_ can be used to ensure that the ``open()`` and ``mmap
447457
with get_file_data(image) as data:
448458
rsp = requests.post(url, files={'file': io.BytesIO(data)})
449459
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:
460+
Since v0.18.0 python-exiv2 has a ``exiv2.BasicIo.data()`` method that is easier to use:
451461

452462
.. code:: python
453463
454464
# after setting some metadata
455465
image.writeMetadata()
456466
exiv_io = image.io()
457-
with exiv_io.data() as data:
458-
rsp = requests.post(url, files={'file': io.BytesIO(data)})
467+
rsp = requests.post(url, files={'file': io.BytesIO(exiv_io.data())})
459468
460469
The ``exiv2.BasicIo`` Python type also exposes a `buffer interface`_.
461470
It allows the ``exiv2.BasicIo`` object to be used anywhere that a `bytes-like object`_ is expected:

src/interface/basicio.i

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ RETURN_VIEW_CB(Exiv2::byte* mmap, $1 ? arg1->size() : 0,
146146
};
147147
}
148148

149-
// Release memoryviews when mmap is called
149+
// Release memoryviews when mmap or data is called
150150
%typemap(check, fragment="memoryview_funcs") bool isWriteable {
151151
release_views(self);
152152
}
@@ -157,6 +157,18 @@ RETURN_VIEW_CB(Exiv2::byte* mmap, $1 ? arg1->size() : 0,
157157
release_views(self);
158158
%}
159159

160+
// Add data() method for easy access
161+
RETURN_VIEW_CB(Exiv2::byte* data, $1 ? arg1->size() : 0,
162+
arg2 ? PyBUF_WRITE : PyBUF_READ, self,)
163+
%extend Exiv2::BasicIo {
164+
Exiv2::byte* data(bool isWriteable) {
165+
if (!self->isopen())
166+
self->open();
167+
self->munmap();
168+
return self->mmap(isWriteable);
169+
};
170+
}
171+
160172
// Enable len(Exiv2::BasicIo)
161173
%feature("python:slot", "sq_length", functype="lenfunc")
162174
Exiv2::BasicIo::size;
@@ -191,64 +203,6 @@ static void release_ptr(Exiv2::BasicIo* self) {
191203
}
192204
EXPOSE_OBJECT_BUFFER(Exiv2::BasicIo, true, true)
193205

194-
// Wrapper class to provide a context manager for Exiv2::BasicIo::mmap
195-
%feature("docstring") DataContext "Data context manager.
196-
197-
A simple context manager for *mmap* / *munmap* data access. The
198-
*__enter__* method returns a :py:class:`memoryview` of the data."
199-
%ignore DataContext::DataContext;
200-
%thread DataContext::~DataContext;
201-
%inline %{
202-
class DataContext {
203-
private:
204-
Exiv2::BasicIo* parent;
205-
bool isWriteable;
206-
char* ptr;
207-
public:
208-
DataContext(Exiv2::BasicIo* parent, bool isWriteable) {
209-
ptr = NULL;
210-
this->parent = parent;
211-
this->isWriteable = isWriteable;
212-
if (parent->open())
213-
throw std::runtime_error("BasicIo.open() failed");
214-
ptr = (char*)parent->mmap(isWriteable);
215-
};
216-
~DataContext() {
217-
if (ptr) {
218-
parent->munmap();
219-
parent->close();
220-
}
221-
};
222-
PyObject* __enter__() {
223-
return PyMemoryView_FromMemory(
224-
ptr, ptr ? parent->size() : 0,
225-
isWriteable ? PyBUF_WRITE : PyBUF_READ);
226-
};
227-
bool __exit__(PyObject* exc_type, PyObject* exc_val, PyObject* exc_tb) {
228-
return false;
229-
};
230-
};
231-
%}
232-
KEEP_REFERENCE(DataContext*);
233-
234-
// Add Exiv2::BasicIo::data() method to return a context manager
235-
%feature("docstring") Exiv2::BasicIo::data "Return a data context manager.
236-
237-
This allows easy access to the data using a ``with`` statement.
238-
The context manager calls *mmap* when the context is entered and
239-
*munmap* when the context is exited.
240-
:type isWriteable: bool, optional
241-
:param isWriteable: Set to true if the data should be writeable
242-
(default is false).
243-
:rtype: object
244-
:return: A context manager"
245-
%newobject Exiv2::BasicIo::data;
246-
%extend Exiv2::BasicIo {
247-
DataContext* data(bool isWriteable) {
248-
return new DataContext($self, isWriteable);
249-
}
250-
}
251-
252206
// Make enum more Pythonic
253207
DEFINE_CLASS_ENUM(BasicIo, Position, "Seek starting positions.",
254208
"beg", Exiv2::BasicIo::beg,

0 commit comments

Comments
 (0)