Made typeclass loading a little more forgiving, adding a multitude of try-levels to fall back to in case of errors. Default is now to fallback to first settings-set default, then to the library default and only as a last resort fall back to the model. Every fallback step is logged. This should make the missing-hook error of #698 go away and report missing typeclasses in a better way.

This commit is contained in:
Griatch 2015-03-11 00:41:06 +01:00
parent c9ed8b5ec5
commit d752106906
5 changed files with 71 additions and 6 deletions

View file

@ -3,6 +3,7 @@ Default Typeclass for Comms.
See objects.objects for more information on Typeclassing. See objects.objects for more information on Typeclassing.
""" """
from django.conf import settings
from evennia.typeclasses.models import TypeclassBase from evennia.typeclasses.models import TypeclassBase
from evennia.comms.models import Msg, TempMsg, ChannelDB from evennia.comms.models import Msg, TempMsg, ChannelDB
from evennia.comms.managers import ChannelManager from evennia.comms.managers import ChannelManager
@ -15,7 +16,10 @@ class DefaultChannel(ChannelDB):
This is the base class for all Comms. Inherit from this to create different This is the base class for all Comms. Inherit from this to create different
types of communication channels. types of communication channels.
""" """
# typeclass setup
__metaclass__ = TypeclassBase __metaclass__ = TypeclassBase
__settingclasspath__ = settings.BASE_CHANNEL_TYPECLASS
__defaultclasspath__ = "evennia.comms.comms.DefaultChannel"
objects = ChannelManager() objects = ChannelManager()
def at_first_save(self): def at_first_save(self):

View file

@ -135,6 +135,8 @@ class DefaultObject(ObjectDB):
""" """
# typeclass setup # typeclass setup
__metaclass__ = TypeclassBase __metaclass__ = TypeclassBase
__settingsclasspath__ = settings.BASE_OBJECT_TYPECLASS
__defaultclasspath__ = "evennia.objects.objects.DefaultObject"
objects = ObjectManager() objects = ObjectManager()
# on-object properties # on-object properties

View file

@ -109,6 +109,9 @@ class DefaultPlayer(PlayerDB):
""" """
__metaclass__ = TypeclassBase __metaclass__ = TypeclassBase
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
__defaultclasspath__ = "evennia.players.players.DefaultPlayer"
objects = PlayerManager() objects = PlayerManager()
# properties # properties

View file

@ -8,6 +8,7 @@ ability to run timers.
from twisted.internet.defer import Deferred, maybeDeferred from twisted.internet.defer import Deferred, maybeDeferred
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings
from evennia.typeclasses.models import TypeclassBase from evennia.typeclasses.models import TypeclassBase
from evennia.scripts.models import ScriptDB from evennia.scripts.models import ScriptDB
from evennia.scripts.manager import ScriptManager from evennia.scripts.manager import ScriptManager
@ -140,6 +141,8 @@ class ScriptBase(ScriptDB):
""" """
__metaclass__ = TypeclassBase __metaclass__ = TypeclassBase
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
__defaultclasspath__ = "evennia.scripts.scripts.DefaultScript"
objects = ScriptManager() objects = ScriptManager()

View file

@ -89,8 +89,23 @@ class TypeclassBase(SharedMemoryModelBase):
""" """
# storage of stats # storage of stats
attrs["typename"] = name#cls.__name__ attrs["typename"] = name
attrs["path"] = "%s.%s" % (attrs["__module__"], name) attrs["path"] = "%s.%s" % (attrs["__module__"], name)
#defaultpath = attrs["__defaultclasspath__"]
#attrs["__defaultclass__"] = class_from_module(attrs["__defaultclasspath__"])
#try:
# defaultpath = attrs["__defaultclasspath__"]
# attrs["__defaultclass__"] = class_from_module(attrs["__defaultclasspath__"])
#except Exception:
# log_trace("Typeclass error for %s: Default typeclass '%s' could not load. "
# "Falling back to library base." % (name, defaultpath))
# try:
# # two levels down from TypedObject will always be the default base class.
# attrs["__defaultclass__"] = cls.__mro__[cls.__mro__.index(TypedObject)-2]
# except Exception:
# log_trace("Critical error for %s: Neither typeclass, "
# "default fallback nor base class could load." % name)
# attrs["__defaultclass__"] = cls
# typeclass proxy setup # typeclass proxy setup
if not "Meta" in attrs: if not "Meta" in attrs:
@ -172,9 +187,33 @@ class TypedObject(SharedMemoryModel):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
This is the main function of the typeclass system - The `__init__` method of typeclasses is the core operational
to dynamically re-apply a class based on the code of the typeclass system, where it dynamically re-applies
db_typeclass_path rather than use the one in the model. a class based on the db_typeclass_path database field rather
than use the one in the model.
Args:
Passed through to parent.
Kwargs:
Passed through to parent.
Notes:
The loading mechanism will attempt the following steps:
1. Attempt to load typeclass given on command line
1. Attempt to load typeclass stored in db_typeclass_path
1. Attempt to load `__settingsclasspath__`, which is by the
default classes defined to be the respective user-set
base typeclass settings, like `BASE_OBJECT_TYPECLASS`.
1. Attempt to load `__defaultclasspath__`, which is the
base classes in the library, like DefaultObject etc.
1. If everything else fails, use the database model.
Normal operation is to load successfully at either step 1
or 2 depending on how the class was called. Tracebacks
will be logged for every step the loader must take beyond
2.
""" """
typeclass_path = kwargs.pop("typeclass", None) typeclass_path = kwargs.pop("typeclass", None)
@ -182,15 +221,29 @@ class TypedObject(SharedMemoryModel):
if typeclass_path: if typeclass_path:
try: try:
self.__class__ = class_from_module(typeclass_path) self.__class__ = class_from_module(typeclass_path)
except ImportError: except Exception:
log_trace() log_trace()
try:
self.__class__ = class_from_module(self.__settingsclasspath__)
except Exception:
log_trace()
try:
self.__class__ = class_from_module(self.__defaultclasspath__)
except Exception:
log_trace()
self.__class__ = self._meta.proxy_for_model or self.__class__
finally: finally:
self.db_typclass_path = typeclass_path self.db_typclass_path = typeclass_path
elif self.db_typeclass_path: elif self.db_typeclass_path:
try: try:
self.__class__ = class_from_module(self.db_typeclass_path) self.__class__ = class_from_module(self.db_typeclass_path)
except ImportError: except Exception:
log_trace() log_trace()
try:
self.__class__ = class_from_module(self.__defaultclasspath__)
except Exception:
log_trace()
self.__dbclass__ = self._meta.proxy_for_model or self.__class__
else: else:
self.db_typeclass_path = "%s.%s" % (self.__module__, self.__class__.__name__) self.db_typeclass_path = "%s.%s" % (self.__module__, self.__class__.__name__)
# important to put this at the end since _meta is based on the set __class__ # important to put this at the end since _meta is based on the set __class__