More API documentation

This commit is contained in:
Kovid Goyal 2014-07-04 22:56:34 +05:30
parent 041f92959c
commit 116af5fde3

View File

@ -87,6 +87,16 @@ def _add_newbook_tag(mi):
class Cache(object):
'''
An in-memory cache of the metadata.db file from a calibre library.
This class also serves as a threadsafe API for accessing the database.
The in-memory cache is maintained in normal form for maximum performance.
SQLITE is simply used as a way to read and write from metadata.db robustly.
All table reading/sorting/searching/caching logic is re-implemented. This
was necessary for maximum performance and flexibility.
'''
def __init__(self, backend):
self.backend = backend
self.fields = {}
@ -513,6 +523,18 @@ class Cache(object):
@api
def format_metadata(self, book_id, fmt, allow_cache=True, update_db=False):
'''
Return the path, size and mtime for the specified format for the specified book.
You should not use path unless you absolutely have to,
since accessing it directly breaks the threadsafe guarantees of this API. Instead use
the :meth:`copy_format_to` method.
:param allow_cache: If ``True`` cached values are used, otherwise a
slow filesystem access is done. The cache values could be out of date
if access was performed to the filesystem outside of this API.
:param update_db: If ``True`` The max_size field of the database is updates for this book.
'''
if not fmt:
return {}
fmt = fmt.upper()
@ -546,10 +568,12 @@ class Cache(object):
@read_api
def pref(self, name, default=None):
' Return the value for the specified preference or the value specified as ``default`` if the preference is not set. '
return self.backend.prefs.get(name, default)
@write_api
def set_pref(self, name, val):
' Set the specified preference to the specified value. See also :meth:`pref`. '
self.backend.prefs.set(name, val)
if name == 'grouped_search_terms':
self._clear_search_caches()
@ -579,6 +603,10 @@ class Cache(object):
@read_api
def get_proxy_metadata(self, book_id):
''' Like :meth:`get_metadata` except that it returns a ProxyMetadata
object that only reads values from the database on demand. This is much
faster than get_metadata when only a small number of fields need to be
accessed from the reutrned metadata object. '''
return ProxyMetadata(self, book_id)
@api
@ -675,14 +703,16 @@ class Cache(object):
@read_api
def format_abspath(self, book_id, fmt):
'''
Return absolute path to the ebook file of format `format`
Return absolute path to the ebook file of format `format`. You should
almost never use this, as it breaks the threadsafe promise of this API.
Instead use, :meth:`copy_format_to`.
Currently used only in calibredb list, the viewer, edit book,
compare_format to original format and the catalogs (via
get_data_as_dict()).
Apart from the viewer, I don't believe any of the others do any file
I/O with the results of this call.
Apart from the viewer and edit book, I don't believe any of the others
do any file write I/O with the results of this call.
'''
fmt = (fmt or '').upper()
try:
@ -706,6 +736,7 @@ class Cache(object):
@api
def save_original_format(self, book_id, fmt):
' Save a copy of the specified format as ORIGINAL_FORMAT, overwriting any existing ORIGINAL_FORMAT. '
fmt = fmt.upper()
if 'ORIGINAL' in fmt:
raise ValueError('Cannot save original of an original fmt')
@ -718,6 +749,9 @@ class Cache(object):
@api
def restore_original_format(self, book_id, original_fmt):
''' Restore the specified format from the previously saved
ORIGINAL_FORMAT, if any. Return True on success. The ORIGINAL_FORMAT is
deleted after a successful restore. '''
original_fmt = original_fmt.upper()
fmtfile = self.format(book_id, original_fmt, as_file=True)
if fmtfile is not None:
@ -893,10 +927,22 @@ class Cache(object):
@read_api
def search(self, query, restriction='', virtual_fields=None, book_ids=None):
'''
Search the database for the specified query, returning a set of matched book ids.
:param restriction: A restriction that is ANDed to the specified query. Note that
restrictions are cached, therefore the search for a AND b will be slower than a with restriction b.
:param virtual_fields: Used internally (virtual fields such as on_device to search over).
:param book_ids: If not None, a set of book ids for which books will
be searched instead of searching all books.
'''
return self._search_api(self, query, restriction, virtual_fields=virtual_fields, book_ids=book_ids)
@api
def get_categories(self, sort='name', book_ids=None, icon_map=None, already_fixed=None):
' Used internally to implement the Tag Browser '
try:
with self.safe_read_lock:
return get_categories(self, sort=sort, book_ids=book_ids, icon_map=icon_map)
@ -943,6 +989,16 @@ class Cache(object):
@write_api
def set_field(self, name, book_id_to_val_map, allow_case_change=True, do_path_update=True):
'''
Set the values of the field specified by ``name``. Returns the set of all book ids that were affected by the change.
:param book_id_to_val_map: Mapping of book_ids to values that should be applied.
:param allow_case_change: If True, the case of many-one or many-many fields will be changed.
For example, if a book has the tag ``tag1`` and you set the tag for another book to ``Tag1``
then the both books will have the tag ``Tag1`` if allow_case_change is True, otherwise they will
both have the tag ``tag1``.
:param do_path_update: Used internally, you should never change it.
'''
f = self.fields[name]
is_series = f.metadata['datatype'] == 'series'
update_path = name in {'title', 'authors'}
@ -1021,11 +1077,9 @@ class Cache(object):
@write_api
def clear_dirtied(self, book_id, sequence):
'''
Clear the dirtied indicator for the books. This is used when fetching
metadata, creating an OPF, and writing a file are separated into steps.
The last step is clearing the indicator
'''
# Clear the dirtied indicator for the books. This is used when fetching
# metadata, creating an OPF, and writing a file are separated into steps.
# The last step is clearing the indicator
dc_sequence = self.dirtied_cache.get(book_id, None)
if dc_sequence is None or sequence is None or dc_sequence == sequence:
self.backend.execute('DELETE FROM metadata_dirtied WHERE book=?',
@ -1062,12 +1116,10 @@ class Cache(object):
@write_api
def dump_metadata(self, book_ids=None, remove_from_dirtied=True,
callback=None):
'''
Write metadata for each record to an individual OPF file. If callback
is not None, it is called once at the start with the number of book_ids
being processed. And once for every book_id, with arguments (book_id,
mi, ok).
'''
# Write metadata for each record to an individual OPF file. If callback
# is not None, it is called once at the start with the number of book_ids
# being processed. And once for every book_id, with arguments (book_id,
# mi, ok).
if book_ids is None:
book_ids = set(self.dirtied_cache)
@ -1240,6 +1292,13 @@ class Cache(object):
@api
def add_format(self, book_id, fmt, stream_or_path, replace=True, run_hooks=True, dbapi=None):
'''
Add a format to the specified book. Return True of the format was added successfully.
:param replace: If True replace existing format, otherwise if the format already exists, return False.
:param run_hooks: If True, file type plugins are run on the format before and after being added.
:param dbapi: Internal use only.
'''
if run_hooks:
# Run import plugins, the write lock is not held to cater for
# broken plugins that might spin the event loop by popping up a
@ -1289,6 +1348,12 @@ class Cache(object):
@write_api
def remove_formats(self, formats_map, db_only=False):
'''
Remove the specified formats from the specified books.
:param formats_map: A mapping of book_id to a list of formats to be removed from the book.
:param db_only: If True, only remove the record fo the format from the db, dont delete the actual format file from the filesystem.
'''
table = self.fields['formats'].table
formats_map = {book_id:frozenset((f or '').upper() for f in fmts) for book_id, fmts in formats_map.iteritems()}
@ -1319,6 +1384,13 @@ class Cache(object):
@read_api
def get_next_series_num_for(self, series, field='series', current_indices=False):
'''
Return the next series index for the specified series, taking into account the various preferences that
control next series number generation.
:param field: The series-like field (defaults to the builtin series column)
:param current_indices: If True, returns a mapping of book_id to current series_index value instead.
'''
books = ()
sf = self.fields[field]
if series:
@ -1364,6 +1436,7 @@ class Cache(object):
@read_api
def has_id(self, book_id):
' Return True iff the specified book_id exists in the db '''
return book_id in self.fields['title'].table.book_col_map
@write_api
@ -1435,6 +1508,9 @@ class Cache(object):
@write_api
def remove_books(self, book_ids, permanent=False):
''' Remove the books specified by the book_ids from the database and delete
their format files. If ``permanent`` is False, then the format files
are not deleted. '''
path_map = {}
for book_id in book_ids:
try:
@ -1470,6 +1546,11 @@ class Cache(object):
@write_api
def rename_items(self, field, item_id_to_new_name_map, change_index=True):
'''
Rename items from a many-one or many-many field such as tags or series.
:param change_index: When renaming in a series-like field also change the series_index values.
'''
f = self.fields[field]
try:
func = f.table.rename_item