diff --git a/neo/lib/interfaces.py b/neo/lib/interfaces.py index 35b05ceab90b2ff4336c4629efd67f291d3dcdcc..761c983b3d2c56838d21eba59d198457d3a74635 100644 --- a/neo/lib/interfaces.py +++ b/neo/lib/interfaces.py @@ -74,7 +74,7 @@ def implements(obj, ignore=()): assert not wrong_signature, wrong_signature return obj -def _set_code(func): +def _stub(func): args, varargs, varkw, _ = inspect.getargspec(func) if varargs: args.append("*" + varargs) @@ -82,16 +82,25 @@ def _set_code(func): args.append("**" + varkw) exec "def %s(%s): raise NotImplementedError\nf = %s" % ( func.__name__, ",".join(args), func.__name__) - func.func_code = f.func_code + return f def abstract(func): - _set_code(func) - func.__abstract__ = 1 - return func + f = _stub(func) + f.__abstract__ = 1 + f.__defaults__ = func.__defaults__ + f.__doc__ = func.__doc__ + return f def requires(*args): for func in args: - _set_code(func) + # Tolerate useless abstract decoration on required method (e.g. it + # simplifies the implementation of a fallback decorator), but remove + # marker since it does not need to be implemented if it's required + # by a method that is overridden. + try: + del func.__abstract__ + except AttributeError: + func.__code__ = _stub(func).__code__ def decorator(func): func.__requires__ = args return func diff --git a/neo/storage/database/importer.py b/neo/storage/database/importer.py index 091dc454c8e40f6070beb353ddc1f647a8ab9111..e329f045ab6ef0d54009367095f5aa4844db5450 100644 --- a/neo/storage/database/importer.py +++ b/neo/storage/database/importer.py @@ -33,7 +33,7 @@ from ZODB.FileStorage import FileStorage from ..app import option_defaults from . import buildDatabaseManager, DatabaseFailure -from .manager import DatabaseManager +from .manager import DatabaseManager, Fallback from neo.lib import compress, logging, patch, util from neo.lib.interfaces import implements from neo.lib.protocol import BackendNotImplemented, MAX_TID @@ -692,6 +692,9 @@ class ImporterDatabaseManager(DatabaseManager): def _fetchObject(*_): raise AssertionError + getLastObjectTID = Fallback.getLastObjectTID.__func__ + _getDataTID = Fallback._getDataTID.__func__ + def getObjectHistory(self, *args, **kw): raise BackendNotImplemented(self.getObjectHistory) diff --git a/neo/storage/database/manager.py b/neo/storage/database/manager.py index 4ac10e0a735712c73a434c37bb5368d3e6f50052..8d439cc4f5ea6a31cc22d068655dfa28425a5c78 100644 --- a/neo/storage/database/manager.py +++ b/neo/storage/database/manager.py @@ -26,22 +26,9 @@ from . import DatabaseFailure READABLE = CellStates.UP_TO_DATE, CellStates.FEEDING -def lazymethod(func): - def getter(self): - cls = self.__class__ - name = func.__name__ - assert name not in cls.__dict__ - setattr(cls, name, func(self)) - return getattr(self, name) - return property(getter, doc=func.__doc__) - def fallback(func): - def warn(self): - logging.info("Fallback to generic/slow implementation of %s." - " It should be overridden by backend storage (%s).", - func.__name__, self.__class__.__name__) - return func - return lazymethod(wraps(func)(warn)) + setattr(Fallback, func.__name__, func) + return abstract(func) def splitOIDField(tid, oids): if len(oids) % 8: @@ -52,6 +39,9 @@ def splitOIDField(tid, oids): class CreationUndone(Exception): pass +class Fallback(object): + pass + class DatabaseManager(object): """This class only describes an interface for database managers.""" @@ -493,6 +483,7 @@ class DatabaseManager(object): None if data_serial is None else util.p64(data_serial)) @fallback + @requires(_getObject) def _fetchObject(self, oid, tid): """Specialized version of _getObject, for replication""" r = self._getObject(oid, tid) @@ -738,6 +729,7 @@ class DatabaseManager(object): return self._pruneData(data_id_list) @fallback + @requires(_getObject) def _getDataTID(self, oid, tid=None, before_tid=None): """ Return a 2-tuple: