mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Move dynamic method generation to class instead of object level
This commit is contained in:
parent
9c64054826
commit
867f46db2b
@ -117,4 +117,5 @@ Various things that require other things before they can be migrated:
|
|||||||
5. In the new API refresh() does not re-read from disk. That might break a
|
5. In the new API refresh() does not re-read from disk. That might break a
|
||||||
few things, for example content server reloading on db change as well as
|
few things, for example content server reloading on db change as well as
|
||||||
dump/restore of db?
|
dump/restore of db?
|
||||||
|
6. grep the sources for TODO
|
||||||
'''
|
'''
|
||||||
|
@ -7,7 +7,6 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, traceback, types
|
import os, traceback, types
|
||||||
from functools import partial
|
|
||||||
from future_builtins import zip
|
from future_builtins import zip
|
||||||
|
|
||||||
from calibre import force_unicode
|
from calibre import force_unicode
|
||||||
@ -52,110 +51,8 @@ class LibraryDatabase(object):
|
|||||||
|
|
||||||
self.get_property = self.data.get_property
|
self.get_property = self.data.get_property
|
||||||
|
|
||||||
MT = lambda func: types.MethodType(func, self, LibraryDatabase)
|
|
||||||
|
|
||||||
for prop in (
|
|
||||||
'author_sort', 'authors', 'comment', 'comments', 'publisher',
|
|
||||||
'rating', 'series', 'series_index', 'tags', 'title', 'title_sort',
|
|
||||||
'timestamp', 'uuid', 'pubdate', 'ondevice',
|
|
||||||
'metadata_last_modified', 'languages',
|
|
||||||
):
|
|
||||||
fm = {'comment':'comments', 'metadata_last_modified':
|
|
||||||
'last_modified', 'title_sort':'sort'}.get(prop, prop)
|
|
||||||
setattr(self, prop, partial(self.get_property,
|
|
||||||
loc=self.FIELD_MAP[fm]))
|
|
||||||
|
|
||||||
self.has_cover = MT(lambda self, book_id:self.new_api.field_for('cover', book_id))
|
|
||||||
self.get_identifiers = MT(
|
|
||||||
lambda self, index, index_is_id=False: self.new_api.field_for('identifiers', index if index_is_id else self.data.index_to_id(index)))
|
|
||||||
|
|
||||||
for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'):
|
|
||||||
setattr(self, meth, getattr(self.new_api, meth))
|
|
||||||
|
|
||||||
# Legacy API to get information about many-(one, many) fields
|
|
||||||
for field in ('authors', 'tags', 'publisher', 'series'):
|
|
||||||
def getter(field):
|
|
||||||
def func(self):
|
|
||||||
return self.new_api.all_field_names(field)
|
|
||||||
return func
|
|
||||||
name = field[:-1] if field in {'authors', 'tags'} else field
|
|
||||||
setattr(self, 'all_%s_names' % name, MT(getter(field)))
|
|
||||||
self.all_formats = MT(lambda self:self.new_api.all_field_names('formats'))
|
|
||||||
|
|
||||||
for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems():
|
|
||||||
setattr(self, func, partial(self.field_id_map, field))
|
|
||||||
self.all_tags = MT(lambda self: list(self.all_tag_names()))
|
|
||||||
self.get_authors_with_ids = MT(
|
|
||||||
lambda self: [[aid, adata['name'], adata['sort'], adata['link']] for aid, adata in self.new_api.author_data().iteritems()])
|
|
||||||
for field in ('tags', 'series', 'publishers', 'ratings', 'languages'):
|
|
||||||
def getter(field):
|
|
||||||
fname = field[:-1] if field in {'publishers', 'ratings'} else field
|
|
||||||
def func(self):
|
|
||||||
return [[tid, tag] for tid, tag in self.new_api.get_id_map(fname).iteritems()]
|
|
||||||
return func
|
|
||||||
setattr(self, 'get_%s_with_ids' % field,
|
|
||||||
MT(getter(field)))
|
|
||||||
for field in ('author', 'tag', 'series'):
|
|
||||||
def getter(field):
|
|
||||||
field = field if field == 'series' else (field+'s')
|
|
||||||
def func(self, item_id):
|
|
||||||
return self.new_api.get_item_name(field, item_id)
|
|
||||||
return func
|
|
||||||
setattr(self, '%s_name' % field, MT(getter(field)))
|
|
||||||
for field in ('publisher', 'series', 'tag'):
|
|
||||||
def getter(field):
|
|
||||||
fname = 'tags' if field == 'tag' else field
|
|
||||||
def func(self, item_id):
|
|
||||||
self.new_api.remove_items(fname, (item_id,))
|
|
||||||
return func
|
|
||||||
setattr(self, 'delete_%s_using_id' % field, MT(getter(field)))
|
|
||||||
|
|
||||||
# Legacy field API
|
|
||||||
for func in (
|
|
||||||
'standard_field_keys', 'custom_field_keys', 'all_field_keys',
|
|
||||||
'searchable_fields', 'sortable_field_keys',
|
|
||||||
'search_term_to_field_key', 'custom_field_metadata',
|
|
||||||
'all_metadata'):
|
|
||||||
setattr(self, func, getattr(self.field_metadata, func))
|
|
||||||
self.metadata_for_field = self.field_metadata.get
|
|
||||||
|
|
||||||
# Legacy setter API
|
|
||||||
for field in (
|
|
||||||
'!authors', 'author_sort', 'comment', 'has_cover', 'identifiers', 'languages',
|
|
||||||
'pubdate', '!publisher', 'rating', '!series', 'series_index', 'timestamp', 'uuid',
|
|
||||||
):
|
|
||||||
def setter(field):
|
|
||||||
has_case_change = field.startswith('!')
|
|
||||||
field = {'comment':'comments',}.get(field, field)
|
|
||||||
if has_case_change:
|
|
||||||
field = field[1:]
|
|
||||||
acc = field == 'series'
|
|
||||||
def func(self, book_id, val, notify=True, commit=True, allow_case_change=acc):
|
|
||||||
ret = self.new_api.set_field(field, {book_id:val}, allow_case_change=allow_case_change)
|
|
||||||
if notify:
|
|
||||||
self.notify([book_id])
|
|
||||||
return ret
|
|
||||||
elif field == 'has_cover':
|
|
||||||
def func(self, book_id, val):
|
|
||||||
self.new_api.set_field('cover', {book_id:bool(val)})
|
|
||||||
else:
|
|
||||||
def func(self, book_id, val, notify=True, commit=True):
|
|
||||||
if not val and field == 'uuid':
|
|
||||||
return
|
|
||||||
ret = self.new_api.set_field(field, {book_id:val})
|
|
||||||
if notify:
|
|
||||||
self.notify([book_id])
|
|
||||||
return ret if field == 'languages' else None
|
|
||||||
return func
|
|
||||||
setattr(self, 'set_%s' % field.replace('!', ''), MT(setter(field)))
|
|
||||||
|
|
||||||
self.last_update_check = self.last_modified()
|
self.last_update_check = self.last_modified()
|
||||||
self.book_on_device_func = None
|
self.book_on_device_func = None
|
||||||
# Cleaning is not required anymore
|
|
||||||
self.clean = self.clean_custom = MT(lambda self:None)
|
|
||||||
self.clean_standard_field = MT(lambda self, field, commit=False:None)
|
|
||||||
# apsw operates in autocommit mode
|
|
||||||
self.commit = MT(lambda self:None)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.backend.close()
|
self.backend.close()
|
||||||
@ -456,3 +353,139 @@ class LibraryDatabase(object):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
MT = lambda func: types.MethodType(func, None, LibraryDatabase)
|
||||||
|
|
||||||
|
# Legacy getter API {{{
|
||||||
|
for prop in ('author_sort', 'authors', 'comment', 'comments', 'publisher',
|
||||||
|
'rating', 'series', 'series_index', 'tags', 'title', 'title_sort',
|
||||||
|
'timestamp', 'uuid', 'pubdate', 'ondevice', 'metadata_last_modified', 'languages',):
|
||||||
|
def getter(prop):
|
||||||
|
fm = {'comment':'comments', 'metadata_last_modified':
|
||||||
|
'last_modified', 'title_sort':'sort'}.get(prop, prop)
|
||||||
|
def func(self, index, index_is_id=False):
|
||||||
|
return self.get_property(index, index_is_id=index_is_id, loc=self.FIELD_MAP[fm])
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, prop, MT(getter(prop)))
|
||||||
|
|
||||||
|
LibraryDatabase.has_cover = MT(lambda self, book_id:self.new_api.field_for('cover', book_id))
|
||||||
|
LibraryDatabase.get_identifiers = MT(
|
||||||
|
lambda self, index, index_is_id=False: self.new_api.field_for('identifiers', index if index_is_id else self.data.index_to_id(index)))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Legacy setter API {{{
|
||||||
|
for field in (
|
||||||
|
'!authors', 'author_sort', 'comment', 'has_cover', 'identifiers', 'languages',
|
||||||
|
'pubdate', '!publisher', 'rating', '!series', 'series_index', 'timestamp', 'uuid',
|
||||||
|
):
|
||||||
|
def setter(field):
|
||||||
|
has_case_change = field.startswith('!')
|
||||||
|
field = {'comment':'comments',}.get(field, field)
|
||||||
|
if has_case_change:
|
||||||
|
field = field[1:]
|
||||||
|
acc = field == 'series'
|
||||||
|
def func(self, book_id, val, notify=True, commit=True, allow_case_change=acc):
|
||||||
|
ret = self.new_api.set_field(field, {book_id:val}, allow_case_change=allow_case_change)
|
||||||
|
if notify:
|
||||||
|
self.notify([book_id])
|
||||||
|
return ret
|
||||||
|
elif field == 'has_cover':
|
||||||
|
def func(self, book_id, val):
|
||||||
|
self.new_api.set_field('cover', {book_id:bool(val)})
|
||||||
|
else:
|
||||||
|
def func(self, book_id, val, notify=True, commit=True):
|
||||||
|
if not val and field == 'uuid':
|
||||||
|
return
|
||||||
|
ret = self.new_api.set_field(field, {book_id:val})
|
||||||
|
if notify:
|
||||||
|
self.notify([book_id])
|
||||||
|
return ret if field == 'languages' else None
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, 'set_%s' % field.replace('!', ''), MT(setter(field)))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Legacy API to get information about many-(one, many) fields {{{
|
||||||
|
for field in ('authors', 'tags', 'publisher', 'series'):
|
||||||
|
def getter(field):
|
||||||
|
def func(self):
|
||||||
|
return self.new_api.all_field_names(field)
|
||||||
|
return func
|
||||||
|
name = field[:-1] if field in {'authors', 'tags'} else field
|
||||||
|
setattr(LibraryDatabase, 'all_%s_names' % name, MT(getter(field)))
|
||||||
|
LibraryDatabase.all_formats = MT(lambda self:self.new_api.all_field_names('formats'))
|
||||||
|
|
||||||
|
for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems():
|
||||||
|
def getter(field):
|
||||||
|
def func(self):
|
||||||
|
return self.field_id_map(field)
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, func, MT(getter(field)))
|
||||||
|
|
||||||
|
LibraryDatabase.all_tags = MT(lambda self: list(self.all_tag_names()))
|
||||||
|
LibraryDatabase.get_authors_with_ids = MT(
|
||||||
|
lambda self: [[aid, adata['name'], adata['sort'], adata['link']] for aid, adata in self.new_api.author_data().iteritems()])
|
||||||
|
|
||||||
|
for field in ('tags', 'series', 'publishers', 'ratings', 'languages'):
|
||||||
|
def getter(field):
|
||||||
|
fname = field[:-1] if field in {'publishers', 'ratings'} else field
|
||||||
|
def func(self):
|
||||||
|
return [[tid, tag] for tid, tag in self.new_api.get_id_map(fname).iteritems()]
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, 'get_%s_with_ids' % field, MT(getter(field)))
|
||||||
|
|
||||||
|
for field in ('author', 'tag', 'series'):
|
||||||
|
def getter(field):
|
||||||
|
field = field if field == 'series' else (field+'s')
|
||||||
|
def func(self, item_id):
|
||||||
|
return self.new_api.get_item_name(field, item_id)
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, '%s_name' % field, MT(getter(field)))
|
||||||
|
|
||||||
|
for field in ('publisher', 'series', 'tag'):
|
||||||
|
def getter(field):
|
||||||
|
fname = 'tags' if field == 'tag' else field
|
||||||
|
def func(self, item_id):
|
||||||
|
self.new_api.remove_items(fname, (item_id,))
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, 'delete_%s_using_id' % field, MT(getter(field)))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Legacy field API {{{
|
||||||
|
for func in (
|
||||||
|
'standard_field_keys', '!custom_field_keys', 'all_field_keys',
|
||||||
|
'searchable_fields', 'sortable_field_keys',
|
||||||
|
'search_term_to_field_key', '!custom_field_metadata',
|
||||||
|
'all_metadata'):
|
||||||
|
def getter(func):
|
||||||
|
if func.startswith('!'):
|
||||||
|
func = func[1:]
|
||||||
|
def meth(self, include_composites=True):
|
||||||
|
return getattr(self.field_metadata, func)(include_composites=include_composites)
|
||||||
|
elif func == 'search_term_to_field_key':
|
||||||
|
def meth(self, term):
|
||||||
|
return self.field_metadata.search_term_to_field_key(term)
|
||||||
|
else:
|
||||||
|
def meth(self):
|
||||||
|
return getattr(self.field_metadata, func)()
|
||||||
|
return meth
|
||||||
|
setattr(LibraryDatabase, func.replace('!', ''), MT(getter(func)))
|
||||||
|
LibraryDatabase.metadata_for_field = MT(lambda self, field:self.field_metadata.get(field))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Miscellaneous API {{{
|
||||||
|
for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'):
|
||||||
|
def getter(meth):
|
||||||
|
def func(self, x):
|
||||||
|
return getattr(self.new_api, meth)(x)
|
||||||
|
return func
|
||||||
|
setattr(LibraryDatabase, meth, MT(getter(meth)))
|
||||||
|
|
||||||
|
# Cleaning is not required anymore
|
||||||
|
LibraryDatabase.clean = LibraryDatabase.clean_custom = MT(lambda self:None)
|
||||||
|
LibraryDatabase.clean_standard_field = MT(lambda self, field, commit=False:None)
|
||||||
|
# apsw operates in autocommit mode
|
||||||
|
LibraryDatabase.commit = MT(lambda self:None)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
del MT
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ class LegacyTest(BaseTest):
|
|||||||
'windows_check_if_files_in_use',
|
'windows_check_if_files_in_use',
|
||||||
}
|
}
|
||||||
SKIP_ARGSPEC = {
|
SKIP_ARGSPEC = {
|
||||||
'__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors',
|
'__init__',
|
||||||
}
|
}
|
||||||
|
|
||||||
missing = []
|
missing = []
|
||||||
|
Loading…
x
Reference in New Issue
Block a user