From 0dae03156c2ae53d13e4e464c93f88fb4db5ff3f Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 18 Sep 2012 01:03:35 +0200 Subject: [PATCH] Some optimizations, cleanup and a few bugfixes. Just changing a spurious property retrieval in the typeclass removed an extra, pointless database query. --- src/objects/models.py | 9 +++------ src/players/models.py | 36 ++++++++++++++++++++---------------- src/typeclasses/models.py | 18 +++++++++++++++--- src/typeclasses/typeclass.py | 24 ++++++++++++------------ src/utils/utils.py | 8 +++++++- 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/objects/models.py b/src/objects/models.py index 9b0058856a..5010938566 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -632,16 +632,13 @@ class ObjectDB(TypedObject): data (object): an optional data object that may or may not be used by the protocol. """ - # This is an important function that must always work. - # we use a different __getattribute__ to avoid recursive loops. - - if object.__getattribute__(self, 'player'): - object.__getattribute__(self, 'player').msg(message, from_obj=from_obj, data=data) + if _GA(self, 'player'): + _GA(_GA(self, 'player'), "msg")(message, from_obj=from_obj, data=data) def emit_to(self, message, from_obj=None, data=None): "Deprecated. Alias for msg" logger.log_depmsg("emit_to() is deprecated. Use msg() instead.") - self.msg(message, from_obj, data) + _GA(self, "msg")(message, from_obj, data) def msg_contents(self, message, exclude=None, from_obj=None, data=None): """ diff --git a/src/players/models.py b/src/players/models.py index ed33e21ba2..4021b5a6f1 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -44,7 +44,6 @@ from django.conf import settings from django.db import models from django.contrib.auth.models import User from django.utils.encoding import smart_str -from django.contrib.contenttypes.models import ContentType from src.typeclasses.models import _get_cache, _set_cache, _del_cache from src.server.sessionhandler import SESSIONS @@ -64,6 +63,8 @@ _GA = object.__getattribute__ _SA = object.__setattr__ _DA = object.__delattr__ +_TYPECLASS = None + #------------------------------------------------------------ # # PlayerAttribute @@ -199,8 +200,11 @@ class PlayerDB(TypedObject): #@obj.setter def obj_set(self, value): "Setter. Allows for self.obj = value" - from src.typeclasses.typeclass import TypeClass - if isinstance(value, TypeClass): + global _TYPECLASS + if not _TYPECLASS: + from src.typeclasses.typeclass import TypeClass as _TYPECLASS + + if isinstance(value, _TYPECLASS): value = value.dbobj try: _set_cache(self, "obj", value) @@ -260,7 +264,6 @@ class PlayerDB(TypedObject): #@is_connected.setter def is_connected_set(self, value): "Setter. Allows for self.is_connected = value" - print "set_is_connected:", self, value _set_cache(self, "is_connected", value) #@is_connected.deleter def is_connected_del(self): @@ -356,21 +359,19 @@ class PlayerDB(TypedObject): Evennia -> User This is the main route for sending data back to the user from the server. """ - if from_obj: try: - from_obj.at_msg_send(outgoing_string, to_obj=self, data=data) + _GA(from_obj, "at_msg_send")(outgoing_string, to_obj=self, data=data) except Exception: pass - - if (object.__getattribute__(self, "character") - and not self.character.at_msg_receive(outgoing_string, from_obj=from_obj, data=data)): + if (_GA(self, "character") and not + _GA(self, "character").at_msg_receive(outgoing_string, from_obj=from_obj, data=data)): # the at_msg_receive() hook may block receiving of certain messages return outgoing_string = utils.to_str(outgoing_string, force_string=True) - for session in object.__getattribute__(self, 'sessions'): + for session in _GA(self, 'sessions'): session.msg(outgoing_string, data) @@ -383,8 +384,8 @@ class PlayerDB(TypedObject): def delete(self, *args, **kwargs): "Make sure to delete user also when deleting player - the two may never exist separately." try: - if self.user: - self.user.delete() + if _GA(self, "user"): + _GA(_GA(self, "user"), "delete")() except AssertionError: pass try: @@ -398,7 +399,7 @@ class PlayerDB(TypedObject): def execute_cmd(self, raw_string): """ - Do something as this playe. This command transparently + Do something as this player. This command transparently lets its typeclass execute the command. raw_string - raw command input coming from the command line. """ @@ -423,8 +424,11 @@ class PlayerDB(TypedObject): the Player object itself. If no Character exists (since Player is OOC), None will be returned. """ - matches = self.__class__.objects.player_search(ostring) + matches = _GA(_GA(_GA(self, "_class__"), "objects"), "player_search")(ostring) matches = _AT_SEARCH_RESULT(self, ostring, matches, global_search=True) - if matches and return_character and hasattr(matches, "character"): - return matches.character + if matches and return_character: + try: + return _GA(matches, "character") + except: + pass return matches diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 54d8bea501..9dd7e2d874 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -56,6 +56,7 @@ _DA = object.__delattr__ _PLOADS = pickle.loads _PDUMPS = pickle.dumps + # Property Cache mechanism. def _get_cache(obj, name): @@ -889,6 +890,17 @@ class TypedObject(SharedMemoryModel): # try to look back to this very database object.) return _GA(_GA(self, 'typeclass'), propname) + def _hasattr(self, obj, attrname): + """ + Loop-safe version of hasattr, to avoid running a lookup that + will be rerouted up the typeclass. Returns True/False. + """ + try: + _GA(obj, attrname) + return True + except AttributeError: + return False + #@property _dbid_cache = None def __dbid_get(self): @@ -1504,7 +1516,7 @@ class TypedObject(SharedMemoryModel): def nattr(self, attribute_name=None, value=None, delete=False): """ This is the equivalence of self.attr but for non-persistent - stores. + stores. Will not raise error but return None. """ if attribute_name == None: # act as a list method @@ -1515,11 +1527,11 @@ class TypedObject(SharedMemoryModel): if not val.startswith['_']] elif delete == True: if hasattr(self.ndb, attribute_name): - _DA(self.db, attribute_name) + _DA(_GA(self, "db"), attribute_name) elif value == None: # act as a getter. if hasattr(self.ndb, attribute_name): - _GA(self.ndb, attribute_name) + _GA(_GA(self, "ndb"), attribute_name) else: return None else: diff --git a/src/typeclasses/typeclass.py b/src/typeclasses/typeclass.py index 3f4c23ea8b..f9af464dfa 100644 --- a/src/typeclasses/typeclass.py +++ b/src/typeclasses/typeclass.py @@ -11,7 +11,6 @@ used by the typesystem or django itself. """ from src.utils.logger import log_trace, log_errmsg -from django.conf import settings __all__ = ("TypeClass",) @@ -115,10 +114,11 @@ class TypeClass(object): return _GA(dbobj, propname) except AttributeError: try: + #XXX deprecated return _GA(dbobj,"get_attribute_raise")(propname) except AttributeError: string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s." - raise AttributeError(string % (propname, dbobj, dbobj.dbid, dbobj.typeclass_path)) + raise AttributeError(string % (propname, dbobj, _GA(dbobj, "dbid"), _GA(dbobj, "typeclass_path"))) def __setattr__(self, propname, value): """ @@ -134,7 +134,6 @@ class TypeClass(object): string += " (protected: [%s])" % (", ".join(PROTECTED)) log_errmsg(string % (self.name, propname)) return - try: dbobj = _GA(self, 'dbobj') except AttributeError: @@ -148,19 +147,20 @@ class TypeClass(object): _GA(dbobj, propname) _SA(dbobj, propname, value) except AttributeError: + #XXX deprecated dbobj.set_attribute(propname, value) else: _SA(self, propname, value) - def __eq__(self, other): - """ - dbobj-recognized comparison - """ - try: - return other == self or other == _GA(self, dbobj) or other == _GA(self, dbobj).user - except AttributeError: - # if self.dbobj.user fails it means the two previous comparisons failed already - return False + def __eq__(self, other): + """ + dbobj-recognized comparison + """ + try: + return other == self or other == _GA(self, dbobj) or other == _GA(self, dbobj).user + except AttributeError: + # if self.dbobj.user fails it means the two previous comparisons failed already + return False def __delattr__(self, propname): diff --git a/src/utils/utils.py b/src/utils/utils.py index 7e4b10f06c..6bd1d13c9e 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -34,7 +34,13 @@ def is_iter(iterable): they are actually iterable), since string iterations are usually not what we want to do with a string. """ - return hasattr(iterable, '__iter__') + # use a try..except here to avoid a property + # lookup when using this from a typeclassed entity + try: + _GA(iterable, '__iter__') + return True + except AttributeError: + return False def make_iter(obj): "Makes sure that the object is always iterable."