diff --git a/contrib/tutorial_world/objects.py b/contrib/tutorial_world/objects.py index 978de148f3..1e7561ff99 100644 --- a/contrib/tutorial_world/objects.py +++ b/contrib/tutorial_world/objects.py @@ -141,7 +141,6 @@ class CmdClimb(Command): obj = self.caller.search(self.args.strip()) if not obj: return - print "obj", "self.obj", obj, self if obj != self.obj: self.caller.msg("Try as you might, you cannot climb that.") return diff --git a/src/commands/command.py b/src/commands/command.py index 8cd332e5c1..c37eb0bd5d 100644 --- a/src/commands/command.py +++ b/src/commands/command.py @@ -7,7 +7,7 @@ All commands in Evennia inherit from the 'Command' class in this module. import re from src.locks.lockhandler import LockHandler -from src.utils.utils import is_iter, fill, LazyLoadHandler +from src.utils.utils import is_iter, fill, lazy_property def _init_command(mcs, **kwargs): @@ -155,7 +155,10 @@ class Command(object): overloading evential same-named class properties.""" if kwargs: _init_command(self, **kwargs) - self.lockhandler = LazyLoadHandler(self, "lockhandler", LockHandler) + + @lazy_property + def lockhandler(self): + return LockHandler(self) def __str__(self): "Print the command" diff --git a/src/comms/models.py b/src/comms/models.py index 688898f7b5..f37d7c5057 100644 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -27,7 +27,7 @@ from src.utils.idmapper.models import SharedMemoryModel from src.comms import managers from src.comms.managers import identify_object from src.locks.lockhandler import LockHandler -from src.utils.utils import crop, make_iter, LazyLoadHandler +from src.utils.utils import crop, make_iter, lazy_property __all__ = ("Msg", "TempMsg", "ChannelDB") @@ -103,7 +103,6 @@ class Msg(SharedMemoryModel): def __init__(self, *args, **kwargs): SharedMemoryModel.__init__(self, *args, **kwargs) - #_SA(self, "locks", LazyLoadHandler(self, "locks", LockHandler)) self.extra_senders = [] class Meta: @@ -299,10 +298,13 @@ class TempMsg(object): self.header = header self.message = message self.lock_storage = lockstring - self.locks = LazyLoadHandler(self, "locks", LockHandler) self.hide_from = hide_from and make_iter(hide_from) or [] self.date_sent = datetime.now() + @lazy_property + def locks(self): + return LockHandler(self) + def __str__(self): "This handles what is shown when e.g. printing the message" senders = ",".join(obj.key for obj in self.senders) @@ -359,12 +361,6 @@ class ChannelDB(TypedObject): _typeclass_paths = settings.CHANNEL_TYPECLASS_PATHS _default_typeclass_path = settings.BASE_CHANNEL_TYPECLASS or "src.comms.comms.Channel" - def __init__(self, *args, **kwargs): - TypedObject.__init__(self, *args, **kwargs) - _SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler)) - _SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler)) - _SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler)) - class Meta: "Define Django meta options" verbose_name = "Channel" diff --git a/src/help/models.py b/src/help/models.py index 8f97fa4069..d81f7a284f 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -14,7 +14,7 @@ from src.utils.idmapper.models import SharedMemoryModel from src.help.manager import HelpEntryManager from src.typeclasses.models import Tag, TagHandler from src.locks.lockhandler import LockHandler -from src.utils.utils import LazyLoadHandler +from src.utils.utils import lazy_property __all__ = ("HelpEntry",) @@ -66,10 +66,16 @@ class HelpEntry(SharedMemoryModel): objects = HelpEntryManager() _is_deleted = False - def __init__(self, *args, **kwargs): - SharedMemoryModel.__init__(self, *args, **kwargs) - self.locks = LazyLoadHandler(self, "locks", LockHandler) - self.tags = LazyLoadHandler(self, "tags", TagHandler) + # lazy-loaded handlers + + @lazy_property + def locks(self): + return LockHandler(self) + + @lazy_property + def tags(self): + return TagHandler(self) + class Meta: "Define Django meta options" diff --git a/src/objects/models.py b/src/objects/models.py index 7ad13c661b..9aeb8ea658 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -19,16 +19,15 @@ from django.db import models from django.conf import settings from django.core.exceptions import ObjectDoesNotExist -from src.typeclasses.models import (TypedObject, TagHandler, NickHandler, - AliasHandler, AttributeHandler) +from src.typeclasses.models import TypedObject, NickHandler from src.objects.manager import ObjectManager from src.players.models import PlayerDB from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler from src.scripts.scripthandler import ScriptHandler from src.utils import logger -from src.utils.utils import (make_iter, to_str, to_unicode, - variable_from_module, dbref, LazyLoadHandler) +from src.utils.utils import (make_iter, to_str, to_unicode, lazy_property, + variable_from_module, dbref) from django.utils.translation import ugettext as _ @@ -130,19 +129,18 @@ class ObjectDB(TypedObject): _typeclass_paths = settings.OBJECT_TYPECLASS_PATHS _default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object" - # Add the object-specific handlers - def __init__(self, *args, **kwargs): - "Parent must be initialized first." - TypedObject.__init__(self, *args, **kwargs) - # handlers - _SA(self, "cmdset", LazyLoadHandler(self, "cmdset", CmdSetHandler, True)) - _SA(self, "scripts", LazyLoadHandler(self, "scripts", ScriptHandler)) - _SA(self, "nicks", LazyLoadHandler(self, "nicks", NickHandler)) - #_SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler)) - #_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler)) - #_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler)) - # make sure to sync the contents cache when initializing - #_GA(self, "contents_update")() + # lazy-load handlers + @lazy_property + def cmdset(self): + return CmdSetHandler(self, True) + + @lazy_property + def scripts(self): + return ScriptHandler(self) + + @lazy_property + def nicks(self): + return NickHandler(self) def _at_db_player_postsave(self): """ diff --git a/src/players/models.py b/src/players/models.py index cd2b633300..23d2b5363e 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -23,13 +23,12 @@ from django.utils.encoding import smart_str from src.players import manager from src.scripts.models import ScriptDB -from src.typeclasses.models import (TypedObject, TagHandler, NickHandler, - AliasHandler, AttributeHandler) +from src.typeclasses.models import (TypedObject, NickHandler) from src.scripts.scripthandler import ScriptHandler from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler from src.utils import utils, logger -from src.utils.utils import to_str, make_iter, LazyLoadHandler +from src.utils.utils import to_str, make_iter, lazy_property from django.utils.translation import ugettext as _ @@ -111,15 +110,19 @@ class PlayerDB(TypedObject, AbstractUser): app_label = 'players' verbose_name = 'Player' - def __init__(self, *args, **kwargs): - "Parent must be initiated first" - TypedObject.__init__(self, *args, **kwargs) - # handlers - _SA(self, "cmdset", LazyLoadHandler(self, "cmdset", CmdSetHandler, True)) - _SA(self, "scripts", LazyLoadHandler(self, "scripts", ScriptHandler)) - _SA(self, "nicks", LazyLoadHandler(self, "nicks", NickHandler)) - #_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler)) - #_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler)) + # lazy-loading of handlers + @lazy_property + def cmdset(self): + return CmdSetHandler(self, True) + + @lazy_property + def scripts(self): + return ScriptHandler(self) + + @lazy_property + def nicks(self): + return NickHandler(self) + # alias to the objs property def __characters_get(self): diff --git a/src/scripts/models.py b/src/scripts/models.py index 2942d2d43c..45347cff9a 100644 --- a/src/scripts/models.py +++ b/src/scripts/models.py @@ -27,9 +27,9 @@ Common examples of uses of Scripts: from django.conf import settings from django.db import models from django.core.exceptions import ObjectDoesNotExist -from src.typeclasses.models import TypedObject, TagHandler, AttributeHandler +from src.typeclasses.models import TypedObject from src.scripts.manager import ScriptManager -from src.utils.utils import dbref, to_str, LazyLoadHandler +from src.utils.utils import dbref, to_str __all__ = ("ScriptDB",) _GA = object.__getattribute__ @@ -108,13 +108,6 @@ class ScriptDB(TypedObject): "Define Django meta options" verbose_name = "Script" - def __init__(self, *args, **kwargs): - super(ScriptDB, self).__init__(*args, **kwargs) - _SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler)) - _SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler)) - #_SA(self, "aliases", AliasHandler(self)) - - # # # ScriptDB class properties diff --git a/src/server/serversession.py b/src/server/serversession.py index 00551f4b42..4cdd2375a5 100644 --- a/src/server/serversession.py +++ b/src/server/serversession.py @@ -13,8 +13,9 @@ from django.conf import settings #from src.scripts.models import ScriptDB from src.comms.models import ChannelDB from src.utils import logger, utils -from src.utils.utils import make_iter, to_unicode, LazyLoadHandler -from src.commands import cmdhandler, cmdsethandler +from src.utils.utils import make_iter, to_unicode +from src.commands.cmdhandler import cmdhandler +from src.commands.cmdsethandler import CmdSetHandler from src.server.session import Session IDLE_COMMAND = settings.IDLE_COMMAND @@ -49,7 +50,7 @@ class ServerSession(Session): self.puppet = None self.player = None self.cmdset_storage_string = "" - self.cmdset = LazyLoadHandler(self, "cmdset", cmdsethandler.CmdSetHandler, True) + self.cmdset = CmdSetHandler(self, True) def __cmdset_storage_get(self): return [path.strip() for path in self.cmdset_storage_string.split(',')] @@ -103,7 +104,7 @@ class ServerSession(Session): self.player.save() # add the session-level cmdset - self.cmdset = LazyLoadHandler(self, "cmdset", cmdsethandler.CmdSetHandler, True) + self.cmdset = CmdSetHandler(self, True) def at_disconnect(self): """ @@ -198,7 +199,7 @@ class ServerSession(Session): else: text = self.player.nicks.nickreplace(text, categories=("inputline", "channels"), include_player=False) - cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid) + cmdhandler(self, text, callertype="session", sessid=self.sessid) self.update_session_counters() if "oob" in kwargs: # handle oob instructions diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 9c0eded5e5..3c77fbfe3e 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -34,7 +34,6 @@ import weakref from django.db import models from django.core.exceptions import ObjectDoesNotExist from django.conf import settings -from django.db.models import Q from django.utils.encoding import smart_str from django.contrib.contenttypes.models import ContentType @@ -48,7 +47,7 @@ from src.typeclasses import managers from src.locks.lockhandler import LockHandler from src.utils import logger from src.utils.utils import ( - make_iter, is_iter, to_str, inherits_from, LazyLoadHandler) + make_iter, is_iter, to_str, inherits_from, lazy_property) from src.utils.dbserialize import to_pickle, from_pickle from src.utils.picklefield import PickledObjectField @@ -132,12 +131,9 @@ class Attribute(SharedMemoryModel): # Database manager objects = managers.AttributeManager() - # Lock handler self.locks - def __init__(self, *args, **kwargs): - "Initializes the parent first -important!" - #SharedMemoryModel.__init__(self, *args, **kwargs) - super(Attribute, self).__init__(*args, **kwargs) - self.locks = LazyLoadHandler(self, "locks", LockHandler) + @lazy_property + def locks(self): + return LockHandler(self) class Meta: "Define Django meta options" @@ -801,15 +797,33 @@ class TypedObject(SharedMemoryModel): def __init__(self, *args, **kwargs): "We must initialize the parent first - important!" super(TypedObject, self).__init__(*args, **kwargs) - #SharedMemoryModel.__init__(self, *args, **kwargs) _SA(self, "dbobj", self) # this allows for self-reference - _SA(self, "locks", LazyLoadHandler(self, "locks", LockHandler)) - _SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler)) - _SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler)) - _SA(self, "permissions", LazyLoadHandler(self, "permissions", PermissionHandler)) - _SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler)) - _SA(self, "nattributes", NAttributeHandler(self)) - #_SA(self, "nattributes", LazyLoadHandler(self, "nattributes", NAttributeHandler)) + + # initialize all handlers in a lazy fashion + @lazy_property + def attributes(self): + return AttributeHandler(self) + + @lazy_property + def locks(self): + return LockHandler(self) + + @lazy_property + def tags(self): + return TagHandler(self) + + @lazy_property + def aliases(self): + return AliasHandler(self) + + @lazy_property + def permissions(self): + return PermissionHandler(self) + + @lazy_property + def nattributes(self): + return NAttributeHandler(self) + class Meta: """ @@ -1276,13 +1290,10 @@ class TypedObject(SharedMemoryModel): if not TICKER_HANDLER: from src.scripts.tickerhandler import TICKER_HANDLER TICKER_HANDLER.remove(self) # removes objects' all ticker subscriptions - if not isinstance(_GA(self, "permissions"), LazyLoadHandler): - _GA(self, "permissions").clear() - if not isinstance(_GA(self, "attributes"), LazyLoadHandler): - _GA(self, "attributes").clear() - if not isinstance(_GA(self, "aliases"), LazyLoadHandler): - _GA(self, "aliases").clear() - if hasattr(self, "nicks") and not isinstance(_GA(self, "nicks"), LazyLoadHandler): + _GA(self, "permissions").clear() + _GA(self, "attributes").clear() + _GA(self, "aliases").clear() + if hasattr(self, "nicks"): _GA(self, "nicks").clear() _SA(self, "_cached_typeclass", None) _GA(self, "flush_from_cache")() diff --git a/src/utils/utils.py b/src/utils/utils.py index 24474b1676..c72274d32e 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -1060,67 +1060,38 @@ def deepsize(obj, max_depth=4): size = getsizeof(obj) + sum([p[1] for p in sizedict.values()]) return size -# lazy load handlers - -import weakref -class LazyLoadHandler(object): +# lazy load handler +_missing = object() +class lazy_property(object): """ - Load handlers only when they are actually accessed + Delays loading of property until first access. Credit goes to + the Implementation in the werkzeug suite: + http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.cached_property + + This should be used as a decorator in a class and is in Evennia + mainly used to lazy-load handlers: + + @lazy_property + def attributes(self): + return AttributeHandler(self) + + Once initialized, the AttributeHandler will be available + as a property "attributes" on the object. + """ - def __init__(self, obj, name, cls, *args): - """ - Set up a delayed load of a class. The 'name' must be named the - same as the variable to which the LazyLoadHandler is assigned. - """ - _SA(self, "obj", weakref.ref(obj)) - _SA(self, "name", name) - _SA(self, "cls", cls) - _SA(self, "args", args) + def __init__(self, func, name=None, doc=None): + "Store all properties for now" + self.__name__ = name or func.__name__ + self.__module__ = func.__module__ + self.__doc__ = doc or func.__doc__ + self.func = func - def _instantiate(self): - """ - Initialize handler as cls(obj, *args) - """ - obj = _GA(self, "obj")() - instance = _GA(self, "cls")(weakref.proxy(obj), *_GA(self, "args")) - _SA(obj, _GA(self, "name"), instance) - return instance - - def __getattribute__(self, name): - """ - Access means loading the handler - """ - return getattr(_GA(self, "_instantiate")(), name) - - def __setattr__(self, name, value): - """ - Setting means loading the handler - """ - setattr(_GA(self, "_instantiate")(), name, value) - - def __delattr__(self, name): - """ - Deleting also triggers loading of handler - """ - delattr(_GA(self, "_instantiate")(), name) - - def __repr__(self): - return repr(_GA(self, "_instantiate")()) - def __str__(self): - return str(_GA(self, "_instantiate")()) - def __unicode__(self): - return str(_GA(self, "_instantiate")()) - -class NonWeakLazyLoadHandler(LazyLoadHandler): - """ - Variation of LazyLoadHandler that does not - create a weak reference when initiating. - """ - def _instantiate(self): - """ - Initialize handler as cls(obj, *args) - """ - obj = _GA(self, "obj")() - instance = _GA(self, "cls")(obj, *_GA(self, "args")) - _SA(obj, _GA(self, "name"), instance) - return instance + def __get__(self, obj, type=None): + "Triggers initialization" + if obj is None: + return self + value = obj.__dict__.get(self.__name__, _missing) + if value is _missing: + value = self.func(obj) + obj.__dict__[self.__name__] = value + return value