From 87fd8889a5fe698fabbb2194fb0a7d640902e978 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 May 2010 22:36:27 -0600 Subject: [PATCH] Framework for replacement of MetaInformation --- src/calibre/ebooks/metadata/book/__init__.py | 5 +- src/calibre/ebooks/metadata/book/base.py | 129 ++++++++++++++++++ .../dialogs/config/create_custom_column.py | 4 +- 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 src/calibre/ebooks/metadata/book/base.py diff --git a/src/calibre/ebooks/metadata/book/__init__.py b/src/calibre/ebooks/metadata/book/__init__.py index 76fe736f9c..9a44a36489 100644 --- a/src/calibre/ebooks/metadata/book/__init__.py +++ b/src/calibre/ebooks/metadata/book/__init__.py @@ -6,7 +6,8 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' ''' -All fields must have a NULL value represented as None +All fields must have a NULL value represented as None for simple types, +an empty list/dictionary for complex types and (None, None) for cover_data ''' SOCIAL_METADATA_FIELDS = frozenset([ @@ -61,7 +62,7 @@ PUBLICATION_METADATA_FIELDS = frozenset([ ]) BOOK_STRUCTURE_FIELDS = frozenset([ - # These are used by code + # These are used by code, Null values are None. 'toc', 'spine', 'guide', 'manifest', ]) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py new file mode 100644 index 0000000000..bf653b38bb --- /dev/null +++ b/src/calibre/ebooks/metadata/book/base.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import copy + +from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS + +NULL_VALUES = { + 'user_metadata': {}, + 'cover_data' : (None, None), + 'tags' : [], + 'classifiers' : {}, + 'languages' : [], + 'device_collections': [], + 'authors' : [_('Unknown')], + 'title' : _('Unknown'), +} + +class Metadata(object): + + ''' + This class must expose a superset of the API of MetaInformation in terms + of attribute access and methods. Only the __init__ method is different. + MetaInformation will simply become a function that creates and fills in + the attributes of this class. + + Please keep the method based API of this class to a minimum. Every method + becomes a reserved field name. + ''' + + def __init__(self): + object.__setattr__(self, '_data', copy.deepcopy(NULL_VALUES)) + + def __getattribute__(self, field): + _data = object.__getattribute__(self, '_data') + if field in RESERVED_METADATA_FIELDS: + return _data.get(field, None) + try: + return object.__getattribute__(self, field) + except AttributeError: + pass + if field in _data['user_metadata'].iterkeys(): + # TODO: getting user metadata values + pass + raise AttributeError( + 'Metadata object has no attribute named: '+ repr(field)) + + + def __setattr__(self, field, val): + _data = object.__getattribute__(self, '_data') + if field in RESERVED_METADATA_FIELDS: + if field != 'user_metadata': + if not val: + val = NULL_VALUES[field] + _data[field] = val + else: + raise AttributeError('You cannot set user_metadata directly.') + elif field in _data['user_metadata'].iterkeys(): + # TODO: Setting custom column values + pass + else: + # You are allowed to stick arbitrary attributes onto this object as + # long as they dont conflict with global or user metadata names + # Don't abuse this privilege + self.__dict__[field] = val + + @property + def reserved_names(self): + 'The set of names you cannot use for your own purposes on this object' + _data = object.__getattribute__(self, '_data') + return frozenset(RESERVED_FIELD_NAMES).union(frozenset( + _data['user_metadata'].iterkeys())) + + @property + def user_metadata_names(self): + 'The set of user metadata names this object knows about' + _data = object.__getattribute__(self, '_data') + return frozenset(_data['user_metadata'].iterkeys()) + + # Old MetaInformation API {{{ + def copy(self): + pass + + def print_all_attributes(self): + pass + + def smart_update(self, other): + pass + + def format_series_index(self): + pass + + def authors_from_string(self, raw): + pass + + def format_authors(self): + pass + + def format_tags(self): + pass + + def format_rating(self): + return unicode(self.rating) + + def __unicode__(self): + pass + + def to_html(self): + pass + + def __str__(self): + return self.__unicode__().encode('utf-8') + + def __nonzero__(self): + return True + + # }}} + +_m = Metadata() +RESERVED_FIELD_NAMES = \ + frozenset(_m.__dict__.iterkeys()).union( # _data + RESERVED_METADATA_FIELDS).union( + frozenset(Metadata.__dict__.iterkeys())) # methods defined in Metadata +del _m + diff --git a/src/calibre/gui2/dialogs/config/create_custom_column.py b/src/calibre/gui2/dialogs/config/create_custom_column.py index b25968c8e5..357e1e2ad8 100644 --- a/src/calibre/gui2/dialogs/config/create_custom_column.py +++ b/src/calibre/gui2/dialogs/config/create_custom_column.py @@ -10,7 +10,7 @@ from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn from calibre.gui2 import error_dialog -from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS +from calibre.ebooks.metadata.book.base import RESERVED_FIELD_NAMES class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): @@ -103,7 +103,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): return self.simple_error('', _('No lookup name was provided')) if not col_heading: return self.simple_error('', _('No column heading was provided')) - if col in RESERVED_METADATA_FIELDS: + if col in RESERVED_FIELD_NAMES: return self.simple_error('', _('The lookup name %s is reserved and cannot be used')%col) bad_col = False if col in self.parent.custcols: