9 import notmuch2._base as base
10 import notmuch2._config as config
11 import notmuch2._capi as capi
12 import notmuch2._errors as errors
13 import notmuch2._message as message
14 import notmuch2._query as querymod
15 import notmuch2._tags as tags
18 __all__ = ['Database', 'AtomicContext', 'DbRevision']
21 def _config_pathname():
22 """Return the path of the configuration file.
26 cfgfname = os.getenv('NOTMUCH_CONFIG', '~/.notmuch-config')
27 return pathlib.Path(os.path.expanduser(cfgfname))
30 class Mode(enum.Enum):
31 READ_ONLY = capi.lib.NOTMUCH_DATABASE_MODE_READ_ONLY
32 READ_WRITE = capi.lib.NOTMUCH_DATABASE_MODE_READ_WRITE
34 class ConfigFile(enum.Enum):
36 SEARCH = capi.ffi.NULL
38 class QuerySortOrder(enum.Enum):
39 OLDEST_FIRST = capi.lib.NOTMUCH_SORT_OLDEST_FIRST
40 NEWEST_FIRST = capi.lib.NOTMUCH_SORT_NEWEST_FIRST
41 MESSAGE_ID = capi.lib.NOTMUCH_SORT_MESSAGE_ID
42 UNSORTED = capi.lib.NOTMUCH_SORT_UNSORTED
45 class QueryExclude(enum.Enum):
46 TRUE = capi.lib.NOTMUCH_EXCLUDE_TRUE
47 FLAG = capi.lib.NOTMUCH_EXCLUDE_FLAG
48 FALSE = capi.lib.NOTMUCH_EXCLUDE_FALSE
49 ALL = capi.lib.NOTMUCH_EXCLUDE_ALL
52 class DecryptionPolicy(enum.Enum):
53 FALSE = capi.lib.NOTMUCH_DECRYPT_FALSE
54 TRUE = capi.lib.NOTMUCH_DECRYPT_TRUE
55 AUTO = capi.lib.NOTMUCH_DECRYPT_AUTO
56 NOSTASH = capi.lib.NOTMUCH_DECRYPT_NOSTASH
59 class Database(base.NotmuchObject):
60 """Toplevel access to notmuch.
62 A :class:`Database` can be opened read-only or read-write.
63 Modifications are not atomic by default, use :meth:`begin_atomic`
64 for atomic updates. If the underlying database has been modified
65 outside of this class a :exc:`XapianError` will be raised and the
66 instance must be closed and a new one created.
68 You can use an instance of this class as a context-manager.
70 :cvar MODE: The mode a database can be opened with, an enumeration
71 of ``READ_ONLY`` and ``READ_WRITE``
72 :cvar SORT: The sort order for search results, ``OLDEST_FIRST``,
73 ``NEWEST_FIRST``, ``MESSAGE_ID`` or ``UNSORTED``.
74 :cvar EXCLUDE: Which messages to exclude from queries, ``TRUE``,
75 ``FLAG``, ``FALSE`` or ``ALL``. See the query documentation
77 :cvar CONFIG: Control loading of config file. Enumeration of
78 ``EMPTY`` (don't load a config file), and ``SEARCH`` (search as
79 in :ref:`config_search`)
80 :cvar AddedMessage: A namedtuple ``(msg, dup)`` used by
81 :meth:`add` as return value.
82 :cvar STR_MODE_MAP: A map mapping strings to :attr:`MODE` items.
83 This is used to implement the ``ro`` and ``rw`` string
86 :ivar closed: Boolean indicating if the database is closed or
89 :param path: The directory of where the database is stored. If
90 ``None`` the location will be searched according to
92 :type path: str, bytes, os.PathLike or pathlib.Path
93 :param mode: The mode to open the database in. One of
94 :attr:`MODE.READ_ONLY` OR :attr:`MODE.READ_WRITE`. For
95 convenience you can also use the strings ``ro`` for
96 :attr:`MODE.READ_ONLY` and ``rw`` for :attr:`MODE.READ_WRITE`.
97 :type mode: :attr:`MODE` or str.
99 :param config: Where to load the configuration from, if any.
100 :type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path
101 :raises KeyError: if an unknown mode string is used.
102 :raises OSError: or subclasses if the configuration file can not
104 :raises configparser.Error: or subclasses if the configuration
105 file can not be parsed.
106 :raises NotmuchError: or subclasses for other failures.
110 SORT = QuerySortOrder
111 EXCLUDE = QueryExclude
113 AddedMessage = collections.namedtuple('AddedMessage', ['msg', 'dup'])
114 _db_p = base.MemoryPointer()
116 'ro': MODE.READ_ONLY,
117 'rw': MODE.READ_WRITE,
121 def _cfg_path_encode(path):
122 if isinstance(path,ConfigFile):
126 elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
129 path = os.fsencode(path)
133 def _db_path_encode(path):
136 elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
139 path = os.fsencode(path)
142 def __init__(self, path=None, mode=MODE.READ_ONLY, config=CONFIG.EMPTY):
143 if isinstance(mode, str):
144 mode = self.STR_MODE_MAP[mode]
147 db_pp = capi.ffi.new('notmuch_database_t **')
148 cmsg = capi.ffi.new('char**')
149 ret = capi.lib.notmuch_database_open_with_config(self._db_path_encode(path),
151 self._cfg_path_encode(config),
155 msg = capi.ffi.string(cmsg[0]).decode(errors='replace')
156 capi.lib.free(cmsg[0])
159 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
160 raise errors.NotmuchError(ret, msg)
161 self._db_p = db_pp[0]
165 def create(cls, path=None, config=ConfigFile.EMPTY):
166 """Create and open database in READ_WRITE mode.
168 This is creates a new notmuch database and returns an opened
169 instance in :attr:`MODE.READ_WRITE` mode.
171 :param path: The directory of where the database is stored.
172 If ``None`` the location will be read searched by the
173 notmuch library (see notmuch(3)::notmuch_open_with_config).
174 :type path: str, bytes or os.PathLike
176 :param config: The pathname of the notmuch configuration file.
177 :type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path
179 :raises OSError: or subclasses if the configuration file can not
181 :raises configparser.Error: or subclasses if the configuration
182 file can not be parsed.
183 :raises NotmuchError: if the config file does not have the
184 database.path setting.
185 :raises FileError: if the database already exists.
187 :returns: The newly created instance.
190 db_pp = capi.ffi.new('notmuch_database_t **')
191 cmsg = capi.ffi.new('char**')
192 ret = capi.lib.notmuch_database_create_with_config(cls._db_path_encode(path),
193 cls._cfg_path_encode(config),
197 msg = capi.ffi.string(cmsg[0]).decode(errors='replace')
198 capi.lib.free(cmsg[0])
201 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
202 raise errors.NotmuchError(ret, msg)
204 # Now close the db and let __init__ open it. Inefficient but
205 # creating is not a hot loop while this allows us to have a
207 ret = capi.lib.notmuch_database_destroy(db_pp[0])
208 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
209 raise errors.NotmuchError(ret)
210 return cls(path, cls.MODE.READ_WRITE, config=config)
213 def default_path(cfg_path=None):
214 """Return the path of the user's default database.
216 This reads the user's configuration file and returns the
217 default path of the database.
219 :param cfg_path: The pathname of the notmuch configuration file.
220 If not specified tries to use the pathname provided in the
221 :envvar:`NOTMUCH_CONFIG` environment variable and falls back
222 to :file:`~/.notmuch-config`.
223 :type cfg_path: str, bytes, os.PathLike or pathlib.Path.
225 :returns: The path of the database, which does not necessarily
228 :raises OSError: or subclasses if the configuration file can not
230 :raises configparser.Error: or subclasses if the configuration
231 file can not be parsed.
232 :raises NotmuchError: if the config file does not have the
233 database.path setting.
236 Use the ``config`` parameter to :meth:`__init__` or :meth:`__create__` instead.
239 cfg_path = _config_pathname()
240 if not hasattr(os, 'PathLike') and isinstance(cfg_path, pathlib.Path):
241 cfg_path = bytes(cfg_path)
242 parser = configparser.ConfigParser()
243 with open(cfg_path) as fp:
246 return pathlib.Path(parser.get('database', 'path'))
247 except configparser.Error:
248 raise errors.NotmuchError(
249 'No database.path setting in {}'.format(cfg_path))
258 except errors.ObjectDestroyedError:
265 ret = capi.lib.notmuch_database_destroy(self._db_p)
266 except errors.ObjectDestroyedError:
267 ret = capi.lib.NOTMUCH_STATUS_SUCCESS
270 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
271 raise errors.NotmuchError(ret)
274 """Close the notmuch database.
276 Once closed most operations will fail. This can still be
277 useful however to explicitly close a database which is opened
278 read-write as this would otherwise stop other processes from
279 reading the database while it is open.
281 :raises ObjectDestroyedError: if used after destroyed.
283 ret = capi.lib.notmuch_database_close(self._db_p)
284 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
285 raise errors.NotmuchError(ret)
291 def __exit__(self, exc_type, exc_value, traceback):
296 """The pathname of the notmuch database.
298 This is returned as a :class:`pathlib.Path` instance.
300 :raises ObjectDestroyedError: if used after destroyed.
303 return self._cache_path
304 except AttributeError:
305 ret = capi.lib.notmuch_database_get_path(self._db_p)
306 self._cache_path = pathlib.Path(os.fsdecode(capi.ffi.string(ret)))
307 return self._cache_path
311 """The database format version.
313 This is a positive integer.
315 :raises ObjectDestroyedError: if used after destroyed.
318 return self._cache_version
319 except AttributeError:
320 ret = capi.lib.notmuch_database_get_version(self._db_p)
321 self._cache_version = ret
325 def needs_upgrade(self):
326 """Whether the database should be upgraded.
328 If *True* the database can be upgraded using :meth:`upgrade`.
329 Not doing so may result in some operations raising
330 :exc:`UpgradeRequiredError`.
332 A read-only database will never be upgradable.
334 :raises ObjectDestroyedError: if used after destroyed.
336 ret = capi.lib.notmuch_database_needs_upgrade(self._db_p)
339 def upgrade(self, progress_cb=None):
340 """Upgrade the database to the latest version.
342 Upgrade the database, optionally with a progress callback
343 which should be a callable which will be called with a
344 floating point number in the range of [0.0 .. 1.0].
346 raise NotImplementedError
349 """Return a context manager to perform atomic operations.
351 The returned context manager can be used to perform atomic
352 operations on the database.
354 .. note:: Unlinke a traditional RDBMS transaction this does
355 not imply durability, it only ensures the changes are
356 performed atomically.
358 :raises ObjectDestroyedError: if used after destroyed.
360 ctx = AtomicContext(self, '_db_p')
364 """The currently committed revision in the database.
366 Returned as a ``(revision, uuid)`` namedtuple.
368 :raises ObjectDestroyedError: if used after destroyed.
370 raw_uuid = capi.ffi.new('char**')
371 rev = capi.lib.notmuch_database_get_revision(self._db_p, raw_uuid)
372 return DbRevision(rev, capi.ffi.string(raw_uuid[0]))
374 def get_directory(self, path):
375 raise NotImplementedError
377 def default_indexopts(self):
378 """Returns default index options for the database.
380 :raises ObjectDestroyedError: if used after destroyed.
382 :returns: :class:`IndexOptions`.
384 opts = capi.lib.notmuch_database_get_default_indexopts(self._db_p)
385 return IndexOptions(self, opts)
387 def add(self, filename, *, sync_flags=False, indexopts=None):
388 """Add a message to the database.
390 Add a new message to the notmuch database. The message is
391 referred to by the pathname of the maildir file. If the
392 message ID of the new message already exists in the database,
393 this adds ``pathname`` to the list of list of files for the
396 :param filename: The path of the file containing the message.
397 :type filename: str, bytes, os.PathLike or pathlib.Path.
398 :param sync_flags: Whether to sync the known maildir flags to
399 notmuch tags. See :meth:`Message.flags_to_tags` for
401 :type sync_flags: bool
402 :param indexopts: The indexing options, see
403 :meth:`default_indexopts`. Leave as `None` to use the
404 default options configured in the database.
405 :type indexopts: :class:`IndexOptions` or `None`
407 :returns: A tuple where the first item is the newly inserted
408 messages as a :class:`Message` instance, and the second
409 item is a boolean indicating if the message inserted was a
410 duplicate. This is the namedtuple ``AddedMessage(msg,
412 :rtype: Database.AddedMessage
414 If an exception is raised, no message was added.
416 :raises XapianError: A Xapian exception occurred.
417 :raises FileError: The file referred to by ``pathname`` could
419 :raises FileNotEmailError: The file referreed to by
420 ``pathname`` is not recognised as an email message.
421 :raises ReadOnlyDatabaseError: The database is opened in
423 :raises UpgradeRequiredError: The database must be upgraded
425 :raises ObjectDestroyedError: if used after destroyed.
427 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
428 filename = bytes(filename)
429 msg_pp = capi.ffi.new('notmuch_message_t **')
430 opts_p = indexopts._opts_p if indexopts else capi.ffi.NULL
431 ret = capi.lib.notmuch_database_index_file(
432 self._db_p, os.fsencode(filename), opts_p, msg_pp)
433 ok = [capi.lib.NOTMUCH_STATUS_SUCCESS,
434 capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
436 raise errors.NotmuchError(ret)
437 msg = message.Message(self, msg_pp[0], db=self)
439 msg.tags.from_maildir_flags()
440 return self.AddedMessage(
441 msg, ret == capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
443 def remove(self, filename):
444 """Remove a message from the notmuch database.
446 Removing a message which is not in the database is just a
447 silent nop-operation.
449 :param filename: The pathname of the file containing the
450 message to be removed.
451 :type filename: str, bytes, os.PathLike or pathlib.Path.
453 :returns: True if the message is still in the database. This
454 can happen when multiple files contain the same message ID.
455 The true/false distinction is fairly arbitrary, but think
456 of it as ``dup = db.remove_message(name); if dup: ...``.
459 :raises XapianError: A Xapian exception occurred.
460 :raises ReadOnlyDatabaseError: The database is opened in
462 :raises UpgradeRequiredError: The database must be upgraded
464 :raises ObjectDestroyedError: if used after destroyed.
466 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
467 filename = bytes(filename)
468 ret = capi.lib.notmuch_database_remove_message(self._db_p,
469 os.fsencode(filename))
470 ok = [capi.lib.NOTMUCH_STATUS_SUCCESS,
471 capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
473 raise errors.NotmuchError(ret)
474 if ret == capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
479 def find(self, msgid):
480 """Return the message matching the given message ID.
482 If a message with the given message ID is found a
483 :class:`Message` instance is returned. Otherwise a
484 :exc:`LookupError` is raised.
486 :param msgid: The message ID to look for.
489 :returns: The message instance.
492 :raises LookupError: If no message was found.
493 :raises OutOfMemoryError: When there is no memory to allocate
494 the message instance.
495 :raises XapianError: A Xapian exception occurred.
496 :raises ObjectDestroyedError: if used after destroyed.
498 msg_pp = capi.ffi.new('notmuch_message_t **')
499 ret = capi.lib.notmuch_database_find_message(self._db_p,
500 msgid.encode(), msg_pp)
501 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
502 raise errors.NotmuchError(ret)
504 if msg_p == capi.ffi.NULL:
506 msg = message.Message(self, msg_p, db=self)
509 def get(self, filename):
510 """Return the :class:`Message` given a pathname.
512 If a message with the given pathname exists in the database
513 return the :class:`Message` instance for the message.
514 Otherwise raise a :exc:`LookupError` exception.
516 :param filename: The pathname of the message.
517 :type filename: str, bytes, os.PathLike or pathlib.Path
519 :returns: The message instance.
522 :raises LookupError: If no message was found. This is also
523 a subclass of :exc:`KeyError`.
524 :raises OutOfMemoryError: When there is no memory to allocate
525 the message instance.
526 :raises XapianError: A Xapian exception occurred.
527 :raises ObjectDestroyedError: if used after destroyed.
529 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
530 filename = bytes(filename)
531 msg_pp = capi.ffi.new('notmuch_message_t **')
532 ret = capi.lib.notmuch_database_find_message_by_filename(
533 self._db_p, os.fsencode(filename), msg_pp)
534 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
535 raise errors.NotmuchError(ret)
537 if msg_p == capi.ffi.NULL:
539 msg = message.Message(self, msg_p, db=self)
544 """Return an immutable set with all tags used in this database.
546 This returns an immutable set-like object implementing the
547 collections.abc.Set Abstract Base Class. Due to the
548 underlying libnotmuch implementation some operations have
549 different performance characteristics then plain set objects.
550 Mainly any lookup operation is O(n) rather then O(1).
552 Normal usage treats tags as UTF-8 encoded unicode strings so
553 they are exposed to Python as normal unicode string objects.
554 If you need to handle tags stored in libnotmuch which are not
555 valid unicode do check the :class:`ImmutableTagSet` docs for
558 :rtype: ImmutableTagSet
560 :raises ObjectDestroyedError: if used after destroyed.
563 ref = self._cached_tagset
564 except AttributeError:
569 tagset = tags.ImmutableTagSet(
570 self, '_db_p', capi.lib.notmuch_database_get_all_tags)
571 self._cached_tagset = weakref.ref(tagset)
576 """Return a mutable mapping with the settings stored in this database.
578 This returns an mutable dict-like object implementing the
579 collections.abc.MutableMapping Abstract Base Class.
583 :raises ObjectDestroyedError: if used after destroyed.
586 ref = self._cached_config
587 except AttributeError:
588 config_mapping = None
590 config_mapping = ref()
591 if config_mapping is None:
592 config_mapping = config.ConfigMapping(self, '_db_p')
593 self._cached_config = weakref.ref(config_mapping)
594 return config_mapping
596 def _create_query(self, query, *,
597 omit_excluded=EXCLUDE.TRUE,
598 sort=SORT.UNSORTED, # Check this default
600 """Create an internal query object.
602 :raises OutOfMemoryError: if no memory is available to
605 if isinstance(query, str):
606 query = query.encode('utf-8')
607 query_p = capi.lib.notmuch_query_create(self._db_p, query)
608 if query_p == capi.ffi.NULL:
609 raise errors.OutOfMemoryError()
610 capi.lib.notmuch_query_set_omit_excluded(query_p, omit_excluded.value)
611 capi.lib.notmuch_query_set_sort(query_p, sort.value)
612 if exclude_tags is not None:
613 for tag in exclude_tags:
614 if isinstance(tag, str):
615 tag = tag.encode('utf-8')
616 capi.lib.notmuch_query_add_tag_exclude(query_p, tag)
617 return querymod.Query(self, query_p)
619 def messages(self, query, *,
620 omit_excluded=EXCLUDE.TRUE,
621 sort=SORT.UNSORTED, # Check this default
623 """Search the database for messages.
625 :returns: An iterator over the messages found.
628 :raises OutOfMemoryError: if no memory is available to
630 :raises ObjectDestroyedError: if used after destroyed.
632 query = self._create_query(query,
633 omit_excluded=omit_excluded,
635 exclude_tags=exclude_tags)
636 return query.messages()
638 def count_messages(self, query, *,
639 omit_excluded=EXCLUDE.TRUE,
640 sort=SORT.UNSORTED, # Check this default
642 """Search the database for messages.
644 :returns: An iterator over the messages found.
647 :raises ObjectDestroyedError: if used after destroyed.
649 query = self._create_query(query,
650 omit_excluded=omit_excluded,
652 exclude_tags=exclude_tags)
653 return query.count_messages()
655 def threads(self, query, *,
656 omit_excluded=EXCLUDE.TRUE,
657 sort=SORT.UNSORTED, # Check this default
659 query = self._create_query(query,
660 omit_excluded=omit_excluded,
662 exclude_tags=exclude_tags)
663 return query.threads()
665 def count_threads(self, query, *,
666 omit_excluded=EXCLUDE.TRUE,
667 sort=SORT.UNSORTED, # Check this default
669 query = self._create_query(query,
670 omit_excluded=omit_excluded,
672 exclude_tags=exclude_tags)
673 return query.count_threads()
675 def status_string(self):
676 raise NotImplementedError
679 return 'Database(path={self.path}, mode={self.mode})'.format(self=self)
683 """Context manager for atomic support.
685 This supports the notmuch_database_begin_atomic and
686 notmuch_database_end_atomic API calls. The object can not be
687 directly instantiated by the user, only via ``Database.atomic``.
688 It does keep a reference to the :class:`Database` instance to keep
691 :raises XapianError: When this is raised at enter time the atomic
692 section is not active. When it is raised at exit time the
693 atomic section is still active and you may need to try using
695 :raises ObjectDestroyedError: if used after destroyed.
698 def __init__(self, db, ptr_name):
700 self._ptr = lambda: getattr(db, ptr_name)
701 self._exit_fn = lambda: None
708 return self.parent.alive
714 ret = capi.lib.notmuch_database_begin_atomic(self._ptr())
715 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
716 raise errors.NotmuchError(ret)
717 self._exit_fn = self._end_atomic
720 def _end_atomic(self):
721 ret = capi.lib.notmuch_database_end_atomic(self._ptr())
722 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
723 raise errors.NotmuchError(ret)
725 def __exit__(self, exc_type, exc_value, traceback):
729 """Force ending the atomic section.
731 This can only be called once __exit__ has been called. It
732 will attempt to close the atomic section (again). This is
733 useful if the original exit raised an exception and the atomic
734 section is still open. But things are pretty ugly by now.
736 :raises XapianError: If exiting fails, the atomic section is
738 :raises UnbalancedAtomicError: If the database was currently
739 not in an atomic section.
740 :raises ObjectDestroyedError: if used after destroyed.
742 ret = capi.lib.notmuch_database_end_atomic(self._ptr())
743 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
744 raise errors.NotmuchError(ret)
747 """Abort the transaction.
749 Aborting a transaction will not commit any of the changes, but
750 will also implicitly close the database.
752 self._exit_fn = lambda: None
756 @functools.total_ordering
758 """A database revision.
760 The database revision number increases monotonically with each
761 commit to the database. Which means user-visible changes can be
762 ordered. This object is sortable with other revisions. It
763 carries the UUID of the database to ensure it is only ever
764 compared with revisions from the same database.
767 def __init__(self, rev, uuid):
773 """The revision number, a positive integer."""
778 """The UUID of the database, consider this opaque."""
781 def __eq__(self, other):
782 if isinstance(other, self.__class__):
783 if self.uuid != other.uuid:
785 return self.rev == other.rev
787 return NotImplemented
789 def __lt__(self, other):
790 if self.__class__ is other.__class__:
791 if self.uuid != other.uuid:
793 return self.rev < other.rev
795 return NotImplemented
798 return 'DbRevision(rev={self.rev}, uuid={self.uuid})'.format(self=self)
801 class IndexOptions(base.NotmuchObject):
804 This represents the indexing options which can be used to index a
805 message. See :meth:`Database.default_indexopts` to create an
806 instance of this. It can be used e.g. when indexing a new message
807 using :meth:`Database.add`.
809 _opts_p = base.MemoryPointer()
811 def __init__(self, parent, opts_p):
812 self._parent = parent
813 self._opts_p = opts_p
817 if not self._parent.alive:
821 except errors.ObjectDestroyedError:
828 capi.lib.notmuch_indexopts_destroy(self._opts_p)
832 def decrypt_policy(self):
833 """The decryption policy.
835 This is an enum from the :class:`DecryptionPolicy`. See the
836 `index.decrypt` section in :man:`notmuch-config` for details
837 on the options. **Do not set this to
838 :attr:`DecryptionPolicy.TRUE`** without considering the
839 security of your index.
841 You can change this policy by assigning a new
842 :class:`DecryptionPolicy` to this property.
844 :raises ObjectDestroyedError: if used after destroyed.
846 :returns: A :class:`DecryptionPolicy` enum instance.
848 raw = capi.lib.notmuch_indexopts_get_decrypt_policy(self._opts_p)
849 return DecryptionPolicy(raw)
851 @decrypt_policy.setter
852 def decrypt_policy(self, val):
853 ret = capi.lib.notmuch_indexopts_set_decrypt_policy(
854 self._opts_p, val.value)
855 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
856 raise errors.NotmuchError(ret, msg)