diff options
| author | Floris Bruynooghe <flub@devork.be> | 2019-11-17 17:41:35 +0100 |
|---|---|---|
| committer | David Bremner <david@tethera.net> | 2019-12-03 08:12:30 -0400 |
| commit | e2df30f7a98f91543d0b3561dbb366eb4b3d812c (patch) | |
| tree | 7b1bb0c60d8723e9e58158b151afbb5d0989c56f /bindings/python-cffi/notdb/_base.py | |
| parent | a950aa28449feef76246ad2b64224fd72e2e574c (diff) | |
Rename package to notmuch2
This is based on a previous discussion on the list where this was more
or less seen as the least-bad option.
Diffstat (limited to 'bindings/python-cffi/notdb/_base.py')
| -rw-r--r-- | bindings/python-cffi/notdb/_base.py | 238 |
1 files changed, 0 insertions, 238 deletions
diff --git a/bindings/python-cffi/notdb/_base.py b/bindings/python-cffi/notdb/_base.py deleted file mode 100644 index acb64413..00000000 --- a/bindings/python-cffi/notdb/_base.py +++ /dev/null @@ -1,238 +0,0 @@ -import abc -import collections.abc - -from notdb import _capi as capi -from notdb import _errors as errors - - -__all__ = ['NotmuchObject', 'BinString'] - - -class NotmuchObject(metaclass=abc.ABCMeta): - """Base notmuch object syntax. - - This base class exists to define the memory management handling - required to use the notmuch library. It is meant as an interface - definition rather than a base class, though you can use it as a - base class to ensure you don't forget part of the interface. It - only concerns you if you are implementing this package itself - rather then using it. - - libnotmuch uses a hierarchical memory allocator, where freeing the - memory of a parent object also frees the memory of all child - objects. To make this work seamlessly in Python this package - keeps references to parent objects which makes them stay alive - correctly under normal circumstances. When an object finally gets - deleted the :meth:`__del__` method will be called to free the - memory. - - However during some peculiar situations, e.g. interpreter - shutdown, it is possible for the :meth:`__del__` method to have - been called, whele there are still references to an object. This - could result in child objects asking their memeory to be freed - after the parent has already freed the memory, making things - rather unhappy as double frees are not taken lightly in C. To - handle this case all objects need to follow the same protocol to - destroy themselves, see :meth:`destroy`. - - Once an object has been destroyed trying to use it should raise - the :exc:`ObjectDestroyedError` exception. For this see also the - convenience :class:`MemoryPointer` descriptor in this module which - can be used as a pointer to libnotmuch memory. - """ - - @abc.abstractmethod - def __init__(self, parent, *args, **kwargs): - """Create a new object. - - Other then for the toplevel :class:`Database` object - constructors are only ever called by internal code and not by - the user. Per convention their signature always takes the - parent object as first argument. Feel free to make the rest - of the signature match the object's requirement. The object - needs to keep a reference to the parent, so it can check the - parent is still alive. - """ - - @property - @abc.abstractmethod - def alive(self): - """Whether the object is still alive. - - This indicates whether the object is still alive. The first - thing this needs to check is whether the parent object is - still alive, if it is not then this object can not be alive - either. If the parent is alive then it depends on whether the - memory for this object has been freed yet or not. - """ - - def __del__(self): - self._destroy() - - @abc.abstractmethod - def _destroy(self): - """Destroy the object, freeing all memory. - - This method needs to destory the object on the - libnotmuch-level. It must ensure it's not been destroyed by - it's parent object yet before doing so. It also must be - idempotent. - """ - - -class MemoryPointer: - """Data Descriptor to handle accessing libnotmuch pointers. - - Most :class:`NotmuchObject` instances will have one or more CFFI - pointers to C-objects. Once an object is destroyed this pointer - should no longer be used and a :exc:`ObjectDestroyedError` - exception should be raised on trying to access it. This - descriptor simplifies implementing this, allowing the creation of - an attribute which can be assigned to, but when accessed when the - stored value is *None* it will raise the - :exc:`ObjectDestroyedError` exception:: - - class SomeOjb: - _ptr = MemoryPointer() - - def __init__(self, ptr): - self._ptr = ptr - - def destroy(self): - somehow_free(self._ptr) - self._ptr = None - - def do_something(self): - return some_libnotmuch_call(self._ptr) - """ - - def __get__(self, instance, owner): - try: - val = getattr(instance, self.attr_name, None) - except AttributeError: - # We're not on 3.6+ and self.attr_name does not exist - self.__set_name__(instance, 'dummy') - val = getattr(instance, self.attr_name, None) - if val is None: - raise errors.ObjectDestroyedError() - return val - - def __set__(self, instance, value): - try: - setattr(instance, self.attr_name, value) - except AttributeError: - # We're not on 3.6+ and self.attr_name does not exist - self.__set_name__(instance, 'dummy') - setattr(instance, self.attr_name, value) - - def __set_name__(self, instance, name): - self.attr_name = '_memptr_{}_{:x}'.format(name, id(instance)) - - -class BinString(str): - """A str subclass with binary data. - - Most data in libnotmuch should be valid ASCII or valid UTF-8. - However since it is a C library these are represented as - bytestrings intead which means on an API level we can not - guarantee that decoding this to UTF-8 will both succeed and be - lossless. This string type converts bytes to unicode in a lossy - way, but also makes the raw bytes available. - - This object is a normal unicode string for most intents and - purposes, but you can get the original bytestring back by calling - ``bytes()`` on it. - """ - - def __new__(cls, data, encoding='utf-8', errors='ignore'): - if not isinstance(data, bytes): - data = bytes(data, encoding=encoding) - strdata = str(data, encoding=encoding, errors=errors) - inst = super().__new__(cls, strdata) - inst._bindata = data - return inst - - @classmethod - def from_cffi(cls, cdata): - """Create a new string from a CFFI cdata pointer.""" - return cls(capi.ffi.string(cdata)) - - def __bytes__(self): - return self._bindata - - -class NotmuchIter(NotmuchObject, collections.abc.Iterator): - """An iterator for libnotmuch iterators. - - It is tempting to use a generator function instead, but this would - not correctly respect the :class:`NotmuchObject` memory handling - protocol and in some unsuspecting cornercases cause memory - trouble. You probably want to sublcass this in order to wrap the - value returned by :meth:`__next__`. - - :param parent: The parent object. - :type parent: NotmuchObject - :param iter_p: The CFFI pointer to the C iterator. - :type iter_p: cffi.cdata - :param fn_destory: The CFFI notmuch_*_destroy function. - :param fn_valid: The CFFI notmuch_*_valid function. - :param fn_get: The CFFI notmuch_*_get function. - :param fn_next: The CFFI notmuch_*_move_to_next function. - """ - _iter_p = MemoryPointer() - - def __init__(self, parent, iter_p, - *, fn_destroy, fn_valid, fn_get, fn_next): - self._parent = parent - self._iter_p = iter_p - self._fn_destroy = fn_destroy - self._fn_valid = fn_valid - self._fn_get = fn_get - self._fn_next = fn_next - - def __del__(self): - self._destroy() - - @property - def alive(self): - if not self._parent.alive: - return False - try: - self._iter_p - except errors.ObjectDestroyedError: - return False - else: - return True - - def _destroy(self): - if self.alive: - try: - self._fn_destroy(self._iter_p) - except errors.ObjectDestroyedError: - pass - self._iter_p = None - - def __iter__(self): - """Return the iterator itself. - - Note that as this is an iterator and not a container this will - not return a new iterator. Thus any elements already consumed - will not be yielded by the :meth:`__next__` method anymore. - """ - return self - - def __next__(self): - if not self._fn_valid(self._iter_p): - self._destroy() - raise StopIteration() - obj_p = self._fn_get(self._iter_p) - self._fn_next(self._iter_p) - return obj_p - - def __repr__(self): - try: - self._iter_p - except errors.ObjectDestroyedError: - return '<NotmuchIter (exhausted)>' - else: - return '<NotmuchIter>' |
