diff --git a/src/comms/models.py b/src/comms/models.py index bc9aeb25a9..0dee5373f0 100644 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -4,7 +4,7 @@ Models for the comsystem. The comsystem's main component is the Message, which carries the actual information between two parties. Msgs are stored in the database and usually not -deleted. +deleted. A Msg always have one sender (a user), but can have any number targets, both users and channels. @@ -16,7 +16,7 @@ be able to delete connections on the fly). from django.db import models from src.utils.idmapper.models import SharedMemoryModel -from src.comms import managers +from src.comms import managers from src.locks.lockhandler import LockHandler from src.utils import logger from src.utils.utils import is_iter, to_str @@ -35,18 +35,18 @@ def obj_to_id(inp): """ dbref = is_dbref(inp) if dbref: - return str(dbref) + return str(dbref) if hasattr(inp, 'id'): return str(inp.id) if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'): return str(inp.dbobj.id) - return str(inp) + return str(inp) def id_to_obj(dbref, db_model='PlayerDB'): """ loads from dbref to object. Uses the db_model to search - for the id. - """ + for the id. + """ if db_model == 'PlayerDB': from src.players.models import PlayerDB as db_model else: @@ -55,7 +55,7 @@ def id_to_obj(dbref, db_model='PlayerDB'): dbref = int(dbref.strip()) return db_model.objects.get(id=dbref) except Exception: - return None + return None #------------------------------------------------------------ # @@ -67,18 +67,18 @@ class Msg(SharedMemoryModel): """ A single message. This model describes all ooc messages sent in-game, both to channels and between players. - + The Msg class defines the following properties: sender - sender of message receivers - list of target objects for message channels - list of channels message was sent to message - the text being sent date_sent - time message was sent - hide_from_sender - bool if message should be hidden from sender + hide_from_sender - bool if message should be hidden from sender hide_from_receivers - list of receiver objects to hide message from hide_from_channels - list of channels objects to hide message from permissions - perm strings - + """ # # Msg database model setup @@ -90,24 +90,24 @@ class Msg(SharedMemoryModel): # There must always be one sender of the message. db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set', null=True, verbose_name='sender') # in the case of external senders, no Player object might be available - db_sender_external = models.CharField('external sender', max_length=255, null=True, blank=True, + db_sender_external = models.CharField('external sender', max_length=255, null=True, blank=True, help_text="identifier for external sender, for example a sender over an IRC connection (i.e. someone who doesn't have an exixtence in-game).") # The destination objects of this message. Stored as a # comma-separated string of object dbrefs. Can be defined along # with channels below. - db_receivers = models.CharField('receivers', max_length=255, null=True, blank=True, - help_text='comma-separated list of object dbrefs this message is aimed at.') + db_receivers = models.CharField('receivers', max_length=255, null=True, blank=True, + help_text='comma-separated list of object dbrefs this message is aimed at.') # The channels this message was sent to. Stored as a # comma-separated string of channel dbrefs. A message can both # have channel targets and destination objects. - db_channels = models.CharField('channels', max_length=255, null=True, blank=True, + db_channels = models.CharField('channels', max_length=255, null=True, blank=True, help_text='comma-separated list of channel dbrefs this message is aimed at.') # The actual message and a timestamp. The message field - # should itself handle eventual headers etc. + # should itself handle eventual headers etc. db_message = models.TextField('messsage') db_date_sent = models.DateTimeField('date sent', editable=False, auto_now_add=True) # lock storage - db_lock_storage = models.CharField('locks', max_length=512, blank=True, + db_lock_storage = models.CharField('locks', max_length=512, blank=True, help_text='access locks on this message.') # These are settable by senders/receivers/channels respectively. # Stored as a comma-separated string of dbrefs. Can be used by the @@ -117,9 +117,9 @@ class Msg(SharedMemoryModel): db_hide_from_receivers = models.CharField(max_length=255, null=True, blank=True) db_hide_from_channels = models.CharField(max_length=255, null=True, blank=True) # Storage of lock strings - #db_lock_storage = models.TextField(null=True) - - # Database manager + #db_lock_storage = models.TextField(null=True) + + # Database manager objects = managers.MsgManager() def __init__(self, *args, **kwargs): @@ -134,8 +134,8 @@ class Msg(SharedMemoryModel): # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self + # defined that allows the user to do self.attr = value, + # value = self.attr and del self.attr respectively (where self # is the object in question). # sender property (wraps db_sender) @@ -183,7 +183,7 @@ class Msg(SharedMemoryModel): if is_iter(value): value = ",".join([obj_to_id(val) for val in value]) self.db_receivers = obj_to_id(value) - self.save() + self.save() #@receivers.deleter def receivers_del(self): "Deleter. Allows for del self.receivers" @@ -204,7 +204,7 @@ class Msg(SharedMemoryModel): if is_iter(value): value = ",".join([obj_to_id(val) for val in value]) self.db_channels = obj_to_id(value) - self.save() + self.save() #@channels.deleter def channels_del(self): "Deleter. Allows for del self.channels" @@ -253,7 +253,7 @@ class Msg(SharedMemoryModel): def hide_from_sender_set(self, value): "Setter. Allows for self.hide_from_senders = value." self.db_hide_from_sender = value - self.save() + self.save() #@hide_from_sender.deleter def hide_from_sender_del(self): "Deleter. Allows for del self.hide_from_senders" @@ -274,7 +274,7 @@ class Msg(SharedMemoryModel): if is_iter(value): value = ",".join([obj_to_id(val) for val in value]) self.db_hide_from_receivers = obj_to_id(value) - self.save() + self.save() #@hide_from_receivers.deleter def hide_from_receivers_del(self): "Deleter. Allows for del self.hide_from_receivers" @@ -286,7 +286,7 @@ class Msg(SharedMemoryModel): #@property def hide_from_channels_get(self): "Getter. Allows for value = self.hide_from_channels. Returns a list of hide_from_channels." - if self.db_hide_from_channels: + if self.db_hide_from_channels: return [id_to_obj(dbref) for dbref in self.db_hide_from_channels.split(',')] return [] #@hide_from_channels.setter @@ -295,7 +295,7 @@ class Msg(SharedMemoryModel): if is_iter(value): value = ",".join([obj_to_id(val) for val in value]) self.db_hide_from_channels = obj_to_id(value) - self.save() + self.save() #@hide_from_channels.deleter def hide_from_channels_del(self): "Deleter. Allows for del self.hide_from_channels" @@ -304,7 +304,7 @@ class Msg(SharedMemoryModel): hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del) # lock_storage property (wraps db_lock_storage) - #@property + #@property def lock_storage_get(self): "Getter. Allows for value = self.lock_storage" return self.db_lock_storage @@ -321,15 +321,15 @@ class Msg(SharedMemoryModel): db_model_name = "msg" # used by attributes to safely store objects - # + # # Msg class methods - # + # def __str__(self): "Print text" - if self.channels: - return "%s -> %s: %s" % (self.sender.key, - ", ".join([chan.key for chan in self.channels]), + if self.channels: + return "%s -> %s: %s" % (self.sender.key, + ", ".join([chan.key for chan in self.channels]), self.message) else: return "%s -> %s: %s" % (self.sender.key, @@ -341,7 +341,7 @@ class Msg(SharedMemoryModel): accessing_obj - object trying to access this one access_type - type of access sought default - what to return if no lock of access_type was found - """ + """ return self.locks.check(accessing_obj, access_type=access_type, default=default) @@ -353,61 +353,61 @@ class Msg(SharedMemoryModel): class TempMsg(object): """ - This is a non-persistent object for sending - temporary messages that will not be stored. - It mimics the "real" Msg object, but don't require - sender to be given. - + This is a non-persistent object for sending + temporary messages that will not be stored. + It mimics the "real" Msg object, but don't require + sender to be given. + """ def __init__(self, sender=None, receivers=[], channels=[], message="", permissions=[]): self.sender = sender - self.receivers = receivers + self.receivers = receivers self.message = message self.permissions = permissions self.hide_from_sender = False self.hide_from_sender = receivers = False - self.hide_from_channels = False + self.hide_from_channels = False #------------------------------------------------------------ # # Channel # #------------------------------------------------------------ - + class Channel(SharedMemoryModel): """ This is the basis of a comm channel, only implementing - the very basics of distributing messages. - + the very basics of distributing messages. + The Channel class defines the following properties: key - main name for channel desc - optional description of channel aliases - alternative names for the channel keep_log - bool if the channel should remember messages permissions - perm strings - + """ - + # # Channel database model setup # # # These databse fields are all set using their corresponding properties, # named same as the field, but withtout the db_* prefix. - + # unique identifier for this channel db_key = models.CharField('key', max_length=255, unique=True, db_index=True) - # optional description of channel - db_desc = models.CharField('description', max_length=80, blank=True, null=True) + # optional description of channel + db_desc = models.CharField('description', max_length=80, blank=True, null=True) # aliases for the channel. These are searched by cmdhandler # as well to determine if a command is the name of a channel. - # Several aliases are separated by commas. - db_aliases = models.CharField('aliases', max_length=255) + # Several aliases are separated by commas. + db_aliases = models.CharField('aliases', max_length=255) # Whether this channel should remember its past messages db_keep_log = models.BooleanField(default=True) # Storage of lock definitions db_lock_storage = models.CharField('locks', max_length=512, blank=True) - + # Database manager objects = managers.ChannelManager() @@ -416,15 +416,15 @@ class Channel(SharedMemoryModel): verbose_name = "Channel" def __init__(self, *args, **kwargs): - SharedMemoryModel.__init__(self, *args, **kwargs) + SharedMemoryModel.__init__(self, *args, **kwargs) self.locks = LockHandler(self) - + # Wrapper properties to easily set database fields. These are # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self + # defined that allows the user to do self.attr = value, + # value = self.attr and del self.attr respectively (where self # is the object in question). # key property (wraps db_key) @@ -473,14 +473,14 @@ class Channel(SharedMemoryModel): if is_iter(value): value = ",".join([str(val).strip().lower() for val in value]) self.db_aliases = value - self.save() + self.save() #@aliases_del.deleter def aliases_del(self): "Deleter. Allows for del self.aliases" self.db_aliases = "" self.save() aliases = property(aliases_get, aliases_set, aliases_del) - + # keep_log property (wraps db_keep_log) #@property def keep_log_get(self): @@ -499,7 +499,7 @@ class Channel(SharedMemoryModel): keep_log = property(keep_log_get, keep_log_set, keep_log_del) # lock_storage property (wraps db_lock_storage) - #@property + #@property def lock_storage_get(self): "Getter. Allows for value = self.lock_storage" return self.db_lock_storage @@ -527,11 +527,11 @@ class Channel(SharedMemoryModel): def __str__(self): return "Channel '%s' (%s)" % (self.key, self.desc) - + def has_connection(self, player): """ Checks so this player is actually listening - to this channel. + to this channel. """ return PlayerChannelConnection.objects.has_connection(player, self) @@ -539,54 +539,54 @@ class Channel(SharedMemoryModel): """ Send the given message to all players connected to channel. Note that no permission-checking is done here; it is assumed to have been - done before calling this method. + done before calling this method. - msgobj - a Msg instance or a message string. In the latter case a Msg will be created. + msgobj - a Msg instance or a message string. In the latter case a Msg will be created. from_obj - if msgobj is not an Msg-instance, this is used to create a message on the fly. If from_obj is None, no Msg object will - be created and the message will be sent without being logged. - """ + be created and the message will be sent without being logged. + """ if isinstance(msgobj, basestring): # given msgobj is a string if from_obj: if isinstance(from_obj, basestring): msgobj = Msg(db_sender_external=from_obj, db_message=msgobj) else: - msgobj = Msg(db_sender=from_obj, db_message=msgobj) - # try to use + msgobj = Msg(db_sender=from_obj, db_message=msgobj) + # try to use msgobj.save() - msgobj.channels = [self] - msg = msgobj.message + msgobj.channels = [self] + msg = msgobj.message else: - # this just sends a message, without any sender + # this just sends a message, without any sender # (and without storing it in a persistent Msg object) msg = to_str(msgobj) else: msg = msgobj.message # get all players connected to this channel and send to them - for conn in Channel.objects.get_all_connections(self): + for conn in Channel.objects.get_all_connections(self): try: conn.player.msg(msg, from_obj) except AttributeError: - try: + try: conn.to_external(msg, from_obj, from_channel=self) except Exception: logger.log_trace("Cannot send msg to connection '%s'" % conn) - return True + return True def tempmsg(self, message): """ A wrapper for sending non-persistent messages. Nothing - will be stored in the database. + will be stored in the database. - message - a Msg object or a text string. + message - a Msg object or a text string. """ - if type(msgobj) == Msg: - # extract only the string - message = message.message + if type(msgobj) == Msg: + # extract only the string + message = message.message return self.msg(message) - + def connect_to(self, player): "Connect the user to this channel" if not self.access(player, 'listen'): @@ -594,7 +594,7 @@ class Channel(SharedMemoryModel): conn = PlayerChannelConnection.objects.create_connection(player, self) if conn: return True - return False + return False def disconnect_from(self, player): "Disconnect user from this channel." @@ -611,14 +611,14 @@ class Channel(SharedMemoryModel): accessing_obj - object trying to access this one access_type - type of access sought default - what to return if no lock of access_type was found - """ + """ return self.locks.check(accessing_obj, access_type=access_type, default=default) class PlayerChannelConnection(SharedMemoryModel): """ This connects a player object to a particular comm channel. The advantage of making it like this is that one can easily - break the connection just by deleting this object. + break the connection just by deleting this object. """ # Player connected to a channel @@ -672,19 +672,19 @@ class PlayerChannelConnection(SharedMemoryModel): class ExternalChannelConnection(SharedMemoryModel): """ - This defines an external protocol connecting to + This defines an external protocol connecting to a channel, while storing some critical info about - that connection. + that connection. """ # evennia channel connecting to - db_channel = models.ForeignKey(Channel, verbose_name='channel', + db_channel = models.ForeignKey(Channel, verbose_name='channel', help_text='which channel this connection is tied to.') # external connection identifier - db_external_key = models.CharField('external key', max_length=128, + db_external_key = models.CharField('external key', max_length=128, help_text='external connection identifier, used by calling protocol.') # eval-code to use when the channel tries to send a message - # to the external protocol. - db_external_send_code = models.TextField('executable code', blank=True, + # to the external protocol. + db_external_send_code = models.TextField('executable code', blank=True, help_text='this is a custom snippet of Python code to connect the external protocol to the in-game channel.') # custom config for the connection db_external_config = models.TextField('external config', blank=True, @@ -693,10 +693,10 @@ class ExternalChannelConnection(SharedMemoryModel): db_is_enabled = models.BooleanField('is enabled', default=True, help_text='turn on/off the connection.') objects = managers.ExternalChannelConnectionManager() - + class Meta: verbose_name = "External Channel Connection" - + def __str__(self): return "%s <-> external %s" % (self.channel.key, self.db_external_key) @@ -778,7 +778,7 @@ class ExternalChannelConnection(SharedMemoryModel): self.save() #@is_enabled.deleter def is_enabled_del(self): - "Deleter. Allows for del self.is_enabled. Deletes connection." + "Deleter. Allows for del self.is_enabled. Deletes connection." self.delete() is_enabled = property(is_enabled_get, is_enabled_set, is_enabled_del) @@ -789,23 +789,22 @@ class ExternalChannelConnection(SharedMemoryModel): def to_channel(self, message, from_obj=None): "Send external -> channel" if not from_obj: - from_obj = self.external_key + from_obj = self.external_key self.channel.msg(message, from_obj=from_obj) def to_external(self, message, from_obj=None, from_channel=None): "Send channel -> external" - # make sure we are not echoing back our own message to ourselves - # (this would result in a nasty infinite loop) + # make sure we are not echoing back our own message to ourselves + # (this would result in a nasty infinite loop) if from_obj == self.external_key: - return - + return + try: - # we execute the code snippet that should make it possible for the + # we execute the code snippet that should make it possible for the # connection to contact the protocol correctly (as set by the protocol). # Note that the code block has access to the variables here, such - # as message, from_obj and from_channel. + # as message, from_obj and from_channel. exec(to_str(self.external_send_code)) except Exception: logger.log_trace("Channel %s could not send to External %s" % (self.channel, self.external_key)) - diff --git a/src/help/manager.py b/src/help/manager.py index 4e32f2043c..81268d74ae 100644 --- a/src/help/manager.py +++ b/src/help/manager.py @@ -3,6 +3,7 @@ Custom manager for HelpEntry objects. """ from django.db import models from src.utils import logger, utils +__all__ = ("HelpEntryManager",) class HelpEntryManager(models.Manager): """ diff --git a/src/help/models.py b/src/help/models.py index 524dafee8e..8ae7f19f7a 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -15,6 +15,7 @@ from src.help.manager import HelpEntryManager from src.utils import ansi from src.locks.lockhandler import LockHandler from src.utils.utils import is_iter +__all__ = ("HelpEntry",) #------------------------------------------------------------ # @@ -32,6 +33,9 @@ class HelpEntry(SharedMemoryModel): entrytext - the actual help text permissions - perm strings + Method: + access + """ # @@ -78,88 +82,88 @@ class HelpEntry(SharedMemoryModel): # key property (wraps db_key) #@property - def key_get(self): + def __key_get(self): "Getter. Allows for value = self.key" return self.db_key #@key.setter - def key_set(self, value): + def __key_set(self, value): "Setter. Allows for self.key = value" self.db_key = value self.save() #@key.deleter - def key_del(self): + def __key_del(self): "Deleter. Allows for del self.key. Deletes entry." self.delete() - key = property(key_get, key_set, key_del) + key = property(__key_get, __key_set, __key_del) # help_category property (wraps db_help_category) #@property - def help_category_get(self): + def __help_category_get(self): "Getter. Allows for value = self.help_category" return self.db_help_category #@help_category.setter - def help_category_set(self, value): + def __help_category_set(self, value): "Setter. Allows for self.help_category = value" self.db_help_category = value self.save() #@help_category.deleter - def help_category_del(self): + def __help_category_del(self): "Deleter. Allows for del self.help_category" self.db_help_category = "General" self.save() - help_category = property(help_category_get, help_category_set, help_category_del) + help_category = property(__help_category_get, __help_category_set, __help_category_del) # entrytext property (wraps db_entrytext) #@property - def entrytext_get(self): + def __entrytext_get(self): "Getter. Allows for value = self.entrytext" return self.db_entrytext #@entrytext.setter - def entrytext_set(self, value): + def __entrytext_set(self, value): "Setter. Allows for self.entrytext = value" self.db_entrytext = value self.save() #@entrytext.deleter - def entrytext_del(self): + def __entrytext_del(self): "Deleter. Allows for del self.entrytext" self.db_entrytext = "" self.save() - entrytext = property(entrytext_get, entrytext_set, entrytext_del) + entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) # permissions property #@property - def permissions_get(self): + def __permissions_get(self): "Getter. Allows for value = self.permissions. Returns a list of permissions." return [perm.strip() for perm in self.db_permissions.split(',')] #@permissions.setter - def permissions_set(self, value): + def __permissions_set(self, value): "Setter. Allows for self.permissions = value. Stores as a comma-separated string." if is_iter(value): value = ",".join([str(val).strip().lower() for val in value]) self.db_permissions = value self.save() #@permissions.deleter - def permissions_del(self): + def __permissions_del(self): "Deleter. Allows for del self.permissions" self.db_permissions = "" self.save() - permissions = property(permissions_get, permissions_set, permissions_del) + permissions = property(__permissions_get, __permissions_set, __permissions_del) # lock_storage property (wraps db_lock_storage) #@property - def lock_storage_get(self): + def __lock_storage_get(self): "Getter. Allows for value = self.lock_storage" return self.db_lock_storage #@nick.setter - def lock_storage_set(self, value): + def __lock_storage_set(self, value): """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" self.db_lock_storage = value self.save() #@nick.deleter - def lock_storage_del(self): + def __lock_storage_del(self): "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del) + lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) # diff --git a/src/locks/lockfuncs.py b/src/locks/lockfuncs.py index cd1e3f4017..3abb449361 100644 --- a/src/locks/lockfuncs.py +++ b/src/locks/lockfuncs.py @@ -3,13 +3,13 @@ This module provides a set of permission lock functions for use with Evennia's permissions system. To call these locks, make sure this module is included in the -settings tuple PERMISSION_FUNC_MODULES then define a lock on the form -':func(args)' and add it to the object's lockhandler. -Run the access() method of the handler to execute the lock check. +settings tuple PERMISSION_FUNC_MODULES then define a lock on the form +':func(args)' and add it to the object's lockhandler. +Run the access() method of the handler to execute the lock check. Note that accessing_obj and accessed_obj can be any object type with a lock variable/field, so be careful to not expect -a certain object type. +a certain object type. Appendix: MUX locks @@ -22,7 +22,7 @@ available like the MUX ones. So many of these are not available in basic Evennia, but could all be implemented easily if needed for the individual game. -MUX Name: Affects: Effect: +MUX Name: Affects: Effect: ------------------------------------------------------------------------------- DefaultLock: Exits: controls who may traverse the exit to its destination. @@ -34,43 +34,43 @@ DefaultLock: Exits: controls who may traverse the exit to Players/Things: controls who may GET the object. Evennia: "get:" ParentLock: All: controls who may make @parent links to the object. Evennia: Typeclasses and "puppet:" ReceiveLock: Players/Things: controls who may give things to the object. - Evennia: + Evennia: SpeechLock: All but Exits: controls who may speak in that location - Evennia: + Evennia: TeloutLock: All but Exits: controls who may teleport out of the location. - Evennia: + Evennia: TportLock: Rooms/Things: controls who may teleport there - Evennia: + Evennia: UseLock: All but Exits: controls who may USE the object, GIVE the object money and have the PAY attributes run, have their messages heard and possibly acted on by LISTEN and AxHEAR, and invoke $-commands stored on the object. - Evennia: Commands and Cmdsets. + Evennia: Commands and Cmdsets. DropLock: All but rooms: controls who may drop that object. Evennia: VisibleLock: All: Controls object visibility when the object @@ -85,7 +85,7 @@ from django.conf import settings from src.utils import search from src.utils import utils -PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY] +_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY] def _to_player(accessing_obj): "Helper function. Makes sure an accessing object is a player object" @@ -95,85 +95,85 @@ def _to_player(accessing_obj): return accessing_obj -# lock functions +# lock functions def true(*args, **kwargs): "Always returns True." return True def all(*args, **kwargs): - return True + return True def false(*args, **kwargs): "Always returns False" - return False + return False def none(*args, **kwargs): - return False + return False def perm(accessing_obj, accessed_obj, *args, **kwargs): """ - The basic permission-checker. Ignores case. + The basic permission-checker. Ignores case. - Usage: + Usage: perm() - + where is the permission accessing_obj must - have in order to pass the lock. If the given permission - is part of PERMISSION_HIERARCHY, permission is also granted - to all ranks higher up in the hierarchy. + have in order to pass the lock. If the given permission + is part of _PERMISSION_HIERARCHY, permission is also granted + to all ranks higher up in the hierarchy. """ try: perm = args[0].lower() permissions = [p.lower() for p in accessing_obj.permissions] except AttributeError, IndexError: - return False - + return False + if perm in permissions: # simplest case - we have a direct match - return True - if perm in PERMISSION_HIERARCHY: + return True + if perm in _PERMISSION_HIERARCHY: # check if we have a higher hierarchy position - ppos = PERMISSION_HIERARCHY.index(perm) - return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY) + ppos = _PERMISSION_HIERARCHY.index(perm) + return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in permissions and hpos > ppos) - return False + return False def perm_above(accessing_obj, accessed_obj, *args, **kwargs): """ Only allow objects with a permission *higher* in the permission - hierarchy than the one given. If there is no such higher rank, + hierarchy than the one given. If there is no such higher rank, it's assumed we refer to superuser. If no hierarchy is defined, - this function has no meaning and returns False. + this function has no meaning and returns False. """ try: perm = args[0].lower() except IndexError: - return False + return False - if perm in PERMISSION_HIERARCHY: - ppos = PERMISSION_HIERARCHY.index(perm) - return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY) + if perm in _PERMISSION_HIERARCHY: + ppos = _PERMISSION_HIERARCHY.index(perm) + return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos) - return False + return False def pperm(accessing_obj, accessed_obj, *args, **kwargs): """ - The basic permission-checker for Player objects. Ignores case. + The basic permission-checker for Player objects. Ignores case. - Usage: + Usage: pperm() - + where is the permission accessing_obj must - have in order to pass the lock. If the given permission - is part of PERMISSION_HIERARCHY, permission is also granted - to all ranks higher up in the hierarchy. + have in order to pass the lock. If the given permission + is part of _PERMISSION_HIERARCHY, permission is also granted + to all ranks higher up in the hierarchy. """ return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs) def pperm_above(accessing_obj, accessed_obj, *args, **kwargs): """ Only allow Player objects with a permission *higher* in the permission - hierarchy than the one given. If there is no such higher rank, + hierarchy than the one given. If there is no such higher rank, it's assumed we refer to superuser. If no hierarchy is defined, - this function has no meaning and returns False. + this function has no meaning and returns False. """ return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs) @@ -181,7 +181,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: dbref(3) - + This lock type checks if the checking object has a particular dbref. Note that this only works for checking objects that are stored @@ -195,7 +195,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs): return False if hasattr(accessing_obj, 'id'): return dbref == accessing_obj.id - return False + return False def pdbref(accessing_obj, accessed_obj, *args, **kwargs): """ @@ -212,7 +212,7 @@ def pid(accessing_obj, accessed_obj, *args, **kwargs): return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs) -# this is more efficient than multiple if ... elif statments +# this is more efficient than multiple if ... elif statments CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or int(val1) == int(val2), 'gt': lambda val1, val2: int(val1) > int(val2), 'lt': lambda val1, val2: int(val1) < int(val2), @@ -232,7 +232,7 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): how the value should be compared with one on accessing_obj (so compare=gt means the accessing_obj must have a value greater than the one given). - + Searches attributes *and* properties stored on the checking object. The first form works like a flag - if the attribute/property exists on the object, the value is checked for @@ -240,17 +240,17 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): attribute/property matches. Note that all retrieved values will be converted to strings before doing the comparison. """ - # deal with arguments + # deal with arguments if not args: return False attrname = args[0].strip() - value = None + value = None if len(args) > 1: value = args[1].strip() compare = 'eq' if kwargs: compare = kwargs.get('compare', 'eq') - + def valcompare(val1, val2, typ='eq'): "compare based on type" try: @@ -258,20 +258,20 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): except Exception, e: #print e # this might happen if we try to compare two things that cannot be compared - return False + return False - # first, look for normal properties on the object trying to gain access + # first, look for normal properties on the object trying to gain access if hasattr(accessing_obj, attrname): if value: - return valcompare(str(getattr(accessing_obj, attrname)), value, compare) + return valcompare(str(getattr(accessing_obj, attrname)), value, compare) return bool(getattr(accessing_obj, attrname)) # will return Fail on False value etc - # check attributes, if they exist + # check attributes, if they exist if (hasattr(accessing_obj, 'has_attribute') and accessing_obj.has_attribute(attrname)): if value: - return (hasattr(accessing_obj, 'get_attribute') + return (hasattr(accessing_obj, 'get_attribute') and valcompare(accessing_obj.get_attribute(attrname), value, compare)) return bool(accessing_obj.get_attribute(attrname)) # fails on False/None values - return False + return False def objattr(accessing_obj, accessed_obj, *args, **kwargs): """ @@ -280,7 +280,7 @@ def objattr(accessing_obj, accessed_obj, *args, **kwargs): objattr(attrname, value) objattr(attrname, value, compare=type) - Works like attr, except it looks for an attribute on + Works like attr, except it looks for an attribute on accessing_obj.obj, if such an entity exists. Suitable for commands. @@ -295,8 +295,8 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs): locattr(attrname, value) locattr(attrname, value, compare=type) - Works like attr, except it looks for an attribute on - accessing_obj.location, if such an entity exists. + Works like attr, except it looks for an attribute on + accessing_obj.location, if such an entity exists. """ if hasattr(accessing_obj, "location"): @@ -305,14 +305,14 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs): def attr_eq(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) """ return attr(accessing_obj, accessed_obj, *args, **kwargs) def attr_gt(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) Only true if access_obj's attribute > the value given. @@ -320,7 +320,7 @@ def attr_gt(accessing_obj, accessed_obj, *args, **kwargs): return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'}) def attr_ge(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) Only true if access_obj's attribute >= the value given. @@ -328,7 +328,7 @@ def attr_ge(accessing_obj, accessed_obj, *args, **kwargs): return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'}) def attr_lt(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) Only true if access_obj's attribute < the value given. @@ -336,7 +336,7 @@ def attr_lt(accessing_obj, accessed_obj, *args, **kwargs): return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'}) def attr_le(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) Only true if access_obj's attribute <= the value given. @@ -344,7 +344,7 @@ def attr_le(accessing_obj, accessed_obj, *args, **kwargs): return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'}) def attr_ne(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: attr_gt(attrname, 54) Only true if access_obj's attribute != the value given. @@ -353,7 +353,7 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs): def holds(accessing_obj, accessed_obj, *args, **kwargs): """ - Usage: + Usage: holds() # checks if accessed_obj or accessed_obj.obj is held by accessing_obj holds(key/dbref) # checks if accessing_obj holds an object with given key/dbref @@ -364,50 +364,50 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs): try: # commands and scripts don't have contents, so we are usually looking # for the contents of their .obj property instead (i.e. the object the - # command/script is attached to). + # command/script is attached to). contents = accessing_obj.contents except AttributeError: - try: + try: contents = accessing_obj.obj.contents except AttributeError: - return False + return False def check_holds(objid): # helper function. Compares both dbrefs and keys/aliases. objid = str(objid) - dbref = utils.dbref(objid) + dbref = utils.dbref(objid) if dbref and any((True for obj in contents if obj.id == dbref)): - return True + return True objid = objid.lower() - return any((True for obj in contents + return any((True for obj in contents if obj.key.lower() == objid or objid in [al.lower() for al in obj.aliases])) if args and args[0]: return check_holds(args[0]) - else: + else: try: if check_holds(accessed_obj.id): #print "holds: accessed_obj.id - True" return True except Exception: - pass + pass #print "holds: accessed_obj.obj.id -", hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id) return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id) - + def superuser(*args, **kwargs): """ Only accepts an accesing_obj that is superuser (e.g. user #1) - + Since a superuser would not ever reach this check (superusers bypass the lock entirely), any user who gets this far cannot be a superuser, hence we just return False. :) """ - return False - + return False + def serversetting(accessing_obj, accessed_obj, *args, **kwargs): """ Only returns true if the Evennia settings exists, alternatively has a certain value. - + Usage: serversetting(IRC_ENABLED) serversetting(BASE_SCRIPT_PATH, [game.gamesrc.scripts]) @@ -418,16 +418,16 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs): return False if len(args) < 2: setting = args[0] - val = "True" + val = "True" else: setting, val = args[0], args[1] # convert if val == 'True': val = True elif val == 'False': - val = False + val = False elif val.isdigit(): val = int(val) if setting in settings._wrapped.__dict__: return settings._wrapped.__dict__[setting] == val - return False + return False diff --git a/src/locks/lockhandler.py b/src/locks/lockhandler.py index 9d758bce8f..409a91381c 100644 --- a/src/locks/lockhandler.py +++ b/src/locks/lockhandler.py @@ -5,15 +5,15 @@ A lock defines access to a particular subsystem or property of Evennia. For example, the "owner" property can be impmemented as a lock. Or the disability to lift an object or to ban users. -A lock consists of three parts: +A lock consists of three parts: - access_type - this defines what kind of access this lock regulates. This - just a string. + just a string. - function call - this is one or many calls to functions that will determine if the lock is passed or not. - - lock function(s). These are regular python functions with a special + - lock function(s). These are regular python functions with a special set of allowed arguments. They should always return a boolean depending - on if they allow access or not. + on if they allow access or not. # Lock function @@ -26,26 +26,26 @@ take four arguments looking like this: The accessing object is the object wanting to gain access. The accessed object is the object this lock resides on -args and kwargs will hold optional arguments and/or keyword arguments +args and kwargs will hold optional arguments and/or keyword arguments to the function as a list and a dictionary respectively. Example: - + perm(accessing_obj, accessed_obj, *args, **kwargs): "Checking if the object has a particular, desired permission" if args: - desired_perm = args[0] + desired_perm = args[0] return desired_perm in accessing_obj.permissions - return False + return False Lock functions should most often be pretty general and ideally possible to -re-use and combine in various ways to build clever locks. +re-use and combine in various ways to build clever locks. # Lock definition ("Lock string") -A lock definition is a string with a special syntax. It is added to -each object's lockhandler, making that lock available from then on. +A lock definition is a string with a special syntax. It is added to +each object's lockhandler, making that lock available from then on. The lock definition looks like this: @@ -54,15 +54,15 @@ The lock definition looks like this: That is, the access_type, a colon followed by calls to lock functions combined with AND or OR. NOT negates the result of the following call. -Example: - - We want to limit who may edit a particular object (let's call this access_type +Example: + + We want to limit who may edit a particular object (let's call this access_type for 'edit', it depends on what the command is looking for). We want this to only work for those with the Permission 'Builders'. So we use our lock function above and define it like this: 'edit:perm(Builders)' - + Here, the lock-function perm() will be called with the string 'Builders' (accessing_obj and accessed_obj are added automatically, you only need to add the args/kwargs, if any). @@ -73,17 +73,17 @@ could use AND: 'edit:perm(Builders) AND perm(GoodGuy)' To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example, -the lock function can do anything and compare any properties of the calling object to +the lock function can do anything and compare any properties of the calling object to decide if the lock is passed or not. 'lift:attrib(very_strong) AND NOT attrib(bad_back)' To make these work, add the string to the lockhandler of the object you want -to apply the lock to: +to apply the lock to: obj.lockhandler.add('edit:perm(Builders)') -From then on, a command that wants to check for 'edit' access on this +From then on, a command that wants to check for 'edit' access on this object would do something like this: if not target_obj.lockhandler.has_perm(caller, 'edit'): @@ -93,8 +93,8 @@ All objects also has a shortcut called 'access' that is recommended to use inste if not target_obj.access(caller, 'edit'): caller.msg("Sorry, you cannot edit that.") - -# Permissions + +# Permissions Permissions are just text strings stored in a comma-separated list on typeclassed objects. The default perm() lock function uses them, @@ -106,8 +106,8 @@ to any other identifier you can use. import re, inspect from django.conf import settings -from src.utils import logger, utils - +from src.utils import logger, utils +__all__ = ("LockHandler", ) # # Exception class # @@ -120,17 +120,17 @@ class LockException(Exception): # Cached lock functions # -LOCKFUNCS = {} -def cache_lockfuncs(): +_LOCKFUNCS = {} +def _cache_lockfuncs(): "Updates the cache." - global LOCKFUNCS - LOCKFUNCS = {} + global _LOCKFUNCS + _LOCKFUNCS = {} for modulepath in settings.LOCK_FUNC_MODULES: modulepath = utils.pypath_to_realpath(modulepath) mod = utils.mod_import(modulepath) - if mod: + if mod: for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])): - LOCKFUNCS[tup[0]] = tup[1] + _LOCKFUNCS[tup[0]] = tup[1] else: logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath) @@ -138,21 +138,21 @@ def cache_lockfuncs(): # pre-compiled regular expressions # -RE_FUNCS = re.compile(r"\w+\([^)]*\)") -RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)") -RE_OK = re.compile(r"%s|and|or|not") +_RE_FUNCS = re.compile(r"\w+\([^)]*\)") +_RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)") +_RE_OK = re.compile(r"%s|and|or|not") # # -# Lock handler +# Lock handler # # class LockHandler(object): """ - This handler should be attached to all objects implementing - permission checks, under the property 'lockhandler'. + This handler should be attached to all objects implementing + permission checks, under the property 'lockhandler'. """ def __init__(self, obj): @@ -160,13 +160,13 @@ class LockHandler(object): Loads and pre-caches all relevant locks and their functions. """ - if not LOCKFUNCS: - cache_lockfuncs() + if not _LOCKFUNCS: + _cache_lockfuncs() self.obj = obj - self.locks = {} - self.log_obj = None + self.locks = {} + self.log_obj = None self.no_errors = True - self.reset_flag = False + self.reset_flag = False self._cache_locks(self.obj.lock_storage) @@ -177,7 +177,7 @@ class LockHandler(object): def _log_error(self, message): "Try to log errors back to object" if self.log_obj and hasattr(self.log_obj, 'msg'): - self.log_obj.msg(message) + self.log_obj.msg(message) elif hasattr(self.obj, 'msg'): self.obj.msg(message) else: @@ -192,12 +192,12 @@ class LockHandler(object): """ locks = {} if not storage_lockstring: - return locks + return locks nlocks = storage_lockstring.count(';') + 1 duplicates = 0 - elist = [] # errors + elist = [] # errors wlist = [] # warnings - for raw_lockstring in storage_lockstring.split(';'): + for raw_lockstring in storage_lockstring.split(';'): lock_funcs = [] try: access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1)) @@ -206,42 +206,42 @@ class LockHandler(object): return locks # parse the lock functions and separators - funclist = RE_FUNCS.findall(rhs) + funclist = _RE_FUNCS.findall(rhs) evalstring = rhs.replace('AND','and').replace('OR','or').replace('NOT','not') nfuncs = len(funclist) - for funcstring in funclist: + for funcstring in funclist: funcname, rest = (part.strip().strip(')') for part in funcstring.split('(', 1)) - func = LOCKFUNCS.get(funcname, None) + func = _LOCKFUNCS.get(funcname, None) if not callable(func): elist.append("Lock: function '%s' is not available." % funcstring) - continue + continue args = list(arg.strip() for arg in rest.split(',') if not '=' in arg) - kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg]) + kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg]) lock_funcs.append((func, args, kwargs)) - evalstring = evalstring.replace(funcstring, '%s') - if len(lock_funcs) < nfuncs: + evalstring = evalstring.replace(funcstring, '%s') + if len(lock_funcs) < nfuncs: continue try: # purge the eval string of any superfluos items, then test it - evalstring = " ".join(RE_OK.findall(evalstring)) + evalstring = " ".join(_RE_OK.findall(evalstring)) eval(evalstring % tuple(True for func in funclist)) except Exception: elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring) continue - if access_type in locks: + if access_type in locks: duplicates += 1 wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \ (access_type, locks[access_type][2], raw_lockstring)) - locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring) + locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring) if wlist and self.log_obj: # a warning text was set, it's not an error, so only report if log_obj is available. self._log_error("\n".join(wlist)) if elist: - # an error text was set, raise exception. - raise LockException("\n".join(elist)) + # an error text was set, raise exception. + raise LockException("\n".join(elist)) self.no_errors = False - # return the gathered locks in an easily executable form - return locks + # return the gathered locks in an easily executable form + return locks def _cache_locks(self, storage_lockstring): """Store data""" @@ -253,29 +253,29 @@ class LockHandler(object): def add(self, lockstring, log_obj=None): """ - Add a new lockstring on the form ':'. Multiple - access types should be separated by semicolon (;). + Add a new lockstring on the form ':'. Multiple + access types should be separated by semicolon (;). If log_obj is given, it will be fed error information. """ if log_obj: self.log_obj = log_obj - self.no_errors = True - # sanity checks - for lockdef in lockstring.split(';'): + self.no_errors = True + # sanity checks + for lockdef in lockstring.split(';'): if not ':' in lockstring: self._log_error("Lock: '%s' contains no colon (:)." % lockdef) - return False + return False access_type, rhs = [part.strip() for part in lockdef.split(':', 1)] if not access_type: self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef) - return False + return False if rhs.count('(') != rhs.count(')'): self._log_error("Lock: '%s' has mismatched parentheses." % lockdef) - return False - if not RE_FUNCS.findall(rhs): + return False + if not _RE_FUNCS.findall(rhs): self._log_error("Lock: '%s' has no valid lock functions." % lockdef) - return False + return False # get the lock string storage_lockstring = self.obj.lock_storage if storage_lockstring: @@ -285,8 +285,8 @@ class LockHandler(object): # cache the locks will get rid of eventual doublets self._cache_locks(storage_lockstring) self._save_locks() - self.log_obj = None - return self.no_errors + self.log_obj = None + return self.no_errors def get(self, access_type): "get the lockstring of a particular type" @@ -297,8 +297,8 @@ class LockHandler(object): if access_type in self.locks: del self.locks[access_type] self._save_locks() - return True - return False + return True + return False def clear(self): "Remove all locks" @@ -307,10 +307,10 @@ class LockHandler(object): def reset(self): """ Set the reset flag, so the the lock will be re-cached at next checking. - This is usually set by @reload. + This is usually set by @reload. """ self.reset_flag = True - + def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False): """ Checks a lock of the correct type by passing execution @@ -322,8 +322,8 @@ class LockHandler(object): no_superuser_bypass - don't use this unless you really, really need to, it makes supersusers susceptible to the lock check. - A lock is executed in the follwoing way: - + A lock is executed in the follwoing way: + Parsing the lockstring, we (during cache) extract the valid lock functions and store their function objects in the right order along with their args/kwargs. These are now executed in @@ -340,11 +340,11 @@ class LockHandler(object): """ if self.reset_flag: - # rebuild cache + # rebuild cache self._cache_locks(self.obj.lock_storage) - self.reset_flag = False + self.reset_flag = False - if (not no_superuser_bypass + if (not no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser) or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)))): @@ -353,11 +353,11 @@ class LockHandler(object): return True if access_type in self.locks: - # we have a lock, test it. - evalstring, func_tup, raw_string = self.locks[access_type] + # we have a lock, test it. + evalstring, func_tup, raw_string = self.locks[access_type] # execute all lock funcs in the correct order, producing a tuple of True/False results. true_false = tuple(bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup) - # the True/False tuple goes into evalstring, which combines them + # the True/False tuple goes into evalstring, which combines them # with AND/OR/NOT in order to get the final result. return eval(evalstring % true_false) else: @@ -367,22 +367,22 @@ class LockHandler(object): """ Do a direct check against a lockstring ('atype:func()..'), without any intermediary storage on the accessed object (this can be left - to None if the lock functions called don't access it). atype can also be + to None if the lock functions called don't access it). atype can also be put to a dummy value since no lock selection is made. """ if ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser) or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser))): - return True - + return True + locks = self. _parse_lockstring(lockstring) for access_type in locks: evalstring, func_tup, raw_string = locks[access_type] true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup) return eval(evalstring % true_false) - -def test(): - # testing + +def _test(): + # testing class TestObj(object): pass @@ -390,9 +390,9 @@ def test(): import pdb obj1 = TestObj() obj2 = TestObj() - + #obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Wizards);examine:perm(Builders);delete:perm(Wizards);get:all()" - #obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()" + #obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()" obj1.lock_storage = "listen:perm(Immortals)" pdb.set_trace() diff --git a/src/locks/tests.py b/src/locks/tests.py index d267520cca..d6abd2bb74 100644 --- a/src/locks/tests.py +++ b/src/locks/tests.py @@ -2,7 +2,7 @@ """ This is part of Evennia's unittest framework, for testing -the stability and integrrity of the codebase during updates. +the stability and integrrity of the codebase during updates. This module tests the lock functionality of Evennia. @@ -49,7 +49,7 @@ class TestLockfuncs(LockTest): def testrun(self): self.obj2.permissions = ['Wizards'] self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1)) - self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1)) + self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1)) self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Wizards')) self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders')) dbref = self.obj2.dbref diff --git a/src/objects/manager.py b/src/objects/manager.py index a847b7f72a..4dbb179a74 100644 --- a/src/objects/manager.py +++ b/src/objects/manager.py @@ -10,9 +10,11 @@ from src.utils import utils from src.utils.utils import to_unicode from src.utils import logger +__all__ = ("ObjectManager",) + # Try to use a custom way to parse id-tagged multimatches. -AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1)) +_AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1)) class ObjectManager(TypedObjectManager): """ @@ -288,7 +290,7 @@ class ObjectManager(TypedObjectManager): matches = local_and_global_search(ostring, exact=True) if not matches: # if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it. - match_number, ostring = AT_MULTIMATCH_INPUT(ostring) + match_number, ostring = _AT_MULTIMATCH_INPUT(ostring) if match_number != None and ostring: # Run search again, without match number: matches = local_and_global_search(ostring, exact=True) diff --git a/src/objects/models.py b/src/objects/models.py index 93ea509cfc..932bd7a3f7 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -32,7 +32,8 @@ from src.scripts.scripthandler import ScriptHandler from src.utils import logger from src.utils.utils import make_iter, to_unicode, to_str, mod_import -#PlayerDB = ContentType.objects.get(app_label="players", model="playerdb").model_class() +#__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB") + _AT_SEARCH_RESULT = mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) @@ -211,7 +212,7 @@ class ObjectDB(TypedObject): # aliases property (wraps (db_aliases) #@property - def aliases_get(self): + def __aliases_get(self): "Getter. Allows for value = self.aliases" try: return _GA(self, "_cached_aliases") @@ -220,23 +221,23 @@ class ObjectDB(TypedObject): _SA(self, "_cached_aliases", aliases) return aliases #@aliases.setter - def aliases_set(self, aliases): + def __aliases_set(self, aliases): "Setter. Allows for self.aliases = value" for alias in make_iter(aliases): new_alias = Alias(db_key=alias, db_obj=self) new_alias.save() _SA(self, "_cached_aliases", aliases) #@aliases.deleter - def aliases_del(self): + def __aliases_del(self): "Deleter. Allows for del self.aliases" for alias in Alias.objects.filter(db_obj=self): alias.delete() _DA(self, "_cached_aliases") - aliases = property(aliases_get, aliases_set, aliases_del) + aliases = property(__aliases_get, __aliases_set, __aliases_del) # player property (wraps db_player) #@property - def player_get(self): + def __player_get(self): """ Getter. Allows for value = self.player. We have to be careful here since Player is also @@ -244,29 +245,29 @@ class ObjectDB(TypedObject): """ return _get_cache(self, "player") #@player.setter - def player_set(self, player): + def __player_set(self, player): "Setter. Allows for self.player = value" if isinstance(player, TypeClass): player = player.dbobj _set_cache(self, "player", player) #@player.deleter - def player_del(self): + def __player_del(self): "Deleter. Allows for del self.player" self.db_player = None self.save() _del_cache(self, "player") - player = property(player_get, player_set, player_del) + player = property(__player_get, __player_set, __player_del) # location property (wraps db_location) #@property - def location_get(self): + def __location_get(self): "Getter. Allows for value = self.location." loc = _get_cache(self, "location") if loc: return loc.typeclass return None #@location.setter - def location_set(self, location): + def __location_set(self, location): "Setter. Allows for self.location = location" try: if location == None or type(location) == ObjectDB: @@ -289,23 +290,23 @@ class ObjectDB(TypedObject): logger.log_trace(string) raise #@location.deleter - def location_del(self): + def __location_del(self): "Deleter. Allows for del self.location" self.db_location = None self.save() _del_cache(self, "location") - location = property(location_get, location_set, location_del) + location = property(__location_get, __location_set, __location_del) # home property (wraps db_home) #@property - def home_get(self): + def __home_get(self): "Getter. Allows for value = self.home" home = _get_cache(self, "home") if home: return home.typeclass return None #@home.setter - def home_set(self, home): + def __home_set(self, home): "Setter. Allows for self.home = value" try: if home == None or type(home) == ObjectDB: @@ -326,23 +327,23 @@ class ObjectDB(TypedObject): logger.log_trace(string) #raise #@home.deleter - def home_del(self): + def __home_del(self): "Deleter. Allows for del self.home." self.db_home = None self.save() _del_cache(self, "home") - home = property(home_get, home_set, home_del) + home = property(__home_get, __home_set, __home_del) # destination property (wraps db_destination) #@property - def destination_get(self): + def __destination_get(self): "Getter. Allows for value = self.destination." dest = _get_cache(self, "destination") if dest: return dest.typeclass return None #@destination.setter - def destination_set(self, destination): + def __destination_set(self, destination): "Setter. Allows for self.destination = destination" try: if destination == None or type(destination) == ObjectDB: @@ -365,33 +366,33 @@ class ObjectDB(TypedObject): logger.log_trace(string) raise #@destination.deleter - def destination_del(self): + def __destination_del(self): "Deleter. Allows for del self.destination" self.db_destination = None self.save() _del_cache(self, "destination") - destination = property(destination_get, destination_set, destination_del) + destination = property(__destination_get, __destination_set, __destination_del) # cmdset_storage property. # This seems very sensitive to caching, so leaving it be for now. /Griatch #@property - def cmdset_storage_get(self): + def __cmdset_storage_get(self): "Getter. Allows for value = self.name. Returns a list of cmdset_storage." if self.db_cmdset_storage: return [path.strip() for path in self.db_cmdset_storage.split(',')] return [] #@cmdset_storage.setter - def cmdset_storage_set(self, value): + def __cmdset_storage_set(self, value): "Setter. Allows for self.name = value. Stores as a comma-separated string." value = ",".join(str(val).strip() for val in make_iter(value)) self.db_cmdset_storage = value self.save() #@cmdset_storage.deleter - def cmdset_storage_del(self): + def __cmdset_storage_del(self): "Deleter. Allows for del self.name" self.db_cmdset_storage = "" self.save() - cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del) + cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del) class Meta: "Define Django meta options" @@ -409,7 +410,7 @@ class ObjectDB(TypedObject): _default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object" #@property - def sessions_get(self): + def __sessions_get(self): """ Retrieve sessions connected to this object. """ @@ -417,42 +418,43 @@ class ObjectDB(TypedObject): if self.player: return self.player.sessions return [] - sessions = property(sessions_get) + sessions = property(__sessions_get) #@property - def has_player_get(self): + def __has_player_get(self): """ Convenience function for checking if an active player is currently connected to this object """ return any(self.sessions) - has_player = property(has_player_get) - is_player = property(has_player_get) + has_player = property(__has_player_get) + is_player = property(__has_player_get) #@property - def is_superuser_get(self): + def __is_superuser_get(self): "Check if user has a player, and if so, if it is a superuser." return any(self.sessions) and self.player.is_superuser - is_superuser = property(is_superuser_get) + is_superuser = property(__is_superuser_get) #@property def contents_get(self, exclude=None): """ Returns the contents of this object, i.e. all objects that has this object set as its location. + This should be publically available. """ return ObjectDB.objects.get_contents(self, excludeobj=exclude) contents = property(contents_get) #@property - def exits_get(self): + def __exits_get(self): """ Returns all exits from this object, i.e. all objects at this location having the property destination != None. """ return [exi for exi in self.contents if exi.destination] - exits = property(exits_get) + exits = property(__exits_get) # diff --git a/src/objects/objects.py b/src/objects/objects.py index 1df23f37ce..1e5880bea4 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -17,6 +17,7 @@ they control by simply linking to a new object's user property. from src.typeclasses.typeclass import TypeClass from src.commands import cmdset, command +__all__ = ("Object", "Character", "Room", "Exit") # # Base class to inherit from. @@ -24,11 +25,10 @@ from src.commands import cmdset, command class Object(TypeClass): """ - This is the base class for all in-game objects. - Inherit from this to create different types of - objects in the game. + This is the base class for all in-game objects. Inherit from this + to create different types of objects in the game. """ - + # __init__ is only defined here in order to present docstring to API. def __init__(self, dbobj): """ This is the root typeclass object, implementing an in-game Evennia diff --git a/src/players/manager.py b/src/players/manager.py index 8713a950bd..d2be0dabd4 100644 --- a/src/players/manager.py +++ b/src/players/manager.py @@ -2,12 +2,13 @@ The managers for the custom Player object and permissions. """ -import datetime +import datetime from functools import update_wrapper from django.db import models from django.contrib.auth.models import User from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager from src.utils import logger +__all__ = ("PlayerManager",) # # Player Manager @@ -35,16 +36,16 @@ def returns_player_list(method): players.append(user.get_profile()) except Exception: # there is something wrong with get_profile. But - # there is a 1-1 relation between Users-Players, so we + # there is a 1-1 relation between Users-Players, so we # try to go the other way instead. from src.players.models import PlayerDB match = PlayerDB.objects.filter(user=user) if match: players.append(match[0]) else: - logger.log_trace("No connection User<->Player, maybe database was partially reset?") + logger.log_trace("No connection User<->Player, maybe database was partially reset?") return players - return update_wrapper(func, method) + return update_wrapper(func, method) def returns_player(method): """ @@ -57,18 +58,18 @@ def returns_player(method): if match: return match[0] else: - return None + return None return update_wrapper(func, method) class PlayerManager(TypedObjectManager): """ - This PlayerManager implements methods for searching + This PlayerManager implements methods for searching and manipulating Players directly from the database. Evennia-specific search methods (will return Characters if possible or a Typeclass/list of Typeclassed objects, whereas Django-general methods will return Querysets or database objects): - + dbref (converter) dbref_search get_dbref_range @@ -83,14 +84,14 @@ class PlayerManager(TypedObjectManager): get_player_from_name player_search (equivalent to ev.search_player) swap_character - + """ def num_total_players(self): """ Returns the total number of registered users/players. """ return self.count() - + @returns_typeclass_list def get_connected_players(self): """ @@ -99,7 +100,7 @@ class PlayerManager(TypedObjectManager): return [player for player in self.all() if player.sessions] @returns_typeclass_list - @returns_player_list + @returns_player_list def get_recently_created_players(self, days=7): """ Returns a QuerySet containing the player User accounts that have been @@ -145,8 +146,8 @@ class PlayerManager(TypedObjectManager): players = self.filter(user__username=uname) if players: return players[0] - return None - + return None + # @returns_typeclass_list # def get_players_with_perm(self, permstring): # """ @@ -159,7 +160,7 @@ class PlayerManager(TypedObjectManager): # @returns_typeclass_list # def get_players_with_group(self, groupstring): # """ - # Returns all players belonging to the given group. + # Returns all players belonging to the given group. # """ # return [player.user for player in self.all() # if player.has_group(groupstring)] @@ -167,9 +168,9 @@ class PlayerManager(TypedObjectManager): @returns_typeclass_list def player_search(self, ostring): """ - Searches for a particular player by name or + Searches for a particular player by name or database id. - + ostring = a string or database id. """ ostring = ostring.lstrip("*") @@ -178,7 +179,7 @@ class PlayerManager(TypedObjectManager): matches = self.filter(id=dbref) if matches: return matches - return self.filter(user__username__iexact=ostring) + return self.filter(user__username__iexact=ostring) def swap_character(self, player, new_character, delete_old_character=False): """ @@ -192,7 +193,7 @@ class PlayerManager(TypedObjectManager): return False # do the swap - old_character = player.character + old_character = player.character if old_character: old_character.player = None try: @@ -202,8 +203,7 @@ class PlayerManager(TypedObjectManager): # recover old setup old_character.player = player player.character = old_character - return False + return False if delete_old_character: old_character.delete() - return True - + return True diff --git a/src/players/models.py b/src/players/models.py index 8c2faaa22c..44bef9577a 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -53,6 +53,8 @@ from src.utils import logger, utils from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler +__all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB") + _AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) #------------------------------------------------------------ diff --git a/src/players/player.py b/src/players/player.py index dfae3cc1d9..0a6cc72644 100644 --- a/src/players/player.py +++ b/src/players/player.py @@ -1,10 +1,10 @@ """ Typeclass for Player objects -Note that this object is primarily intended to -store OOC information, not game info! This +Note that this object is primarily intended to +store OOC information, not game info! This object represents the actual user (not their -character) and has NO actual precence in the +character) and has NO actual precence in the game world (this is handled by the associated character object, so you should customize that instead for most things). @@ -12,12 +12,12 @@ instead for most things). """ from django.conf import settings from src.typeclasses.typeclass import TypeClass - +__all__ = ("Player",) CMDSET_OOC = settings.CMDSET_OOC class Player(TypeClass): """ - Base typeclass for all Players. + Base typeclass for all Players. """ def __init__(self, dbobj): """ @@ -27,7 +27,7 @@ class Player(TypeClass): can connect to a Character Object in order to "enter" the game. - Player Typeclass API: + Player Typeclass API: * Available properties (only available on initiated typeclass objects) @@ -38,22 +38,22 @@ class Player(TypeClass): dbobj (Player, read-only) - link to database model. dbobj.typeclass points back to this class typeclass (Player, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. date_created (string) - time stamp of object creation - permissions (list of strings) - list of permission strings + permissions (list of strings) - list of permission strings user (User, read-only) - django User authorization object obj (Object) - game object controlled by player. 'character' can also be used. sessions (list of Sessions) - sessions connected to this player is_superuser (bool, read-only) - if the connected user is a superuser - * Handlers + * Handlers locks - lock-handler: use locks.add() to add new lock strings db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr - ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data scripts - script-handler. Add new scripts to object with scripts.add() cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object nicks - nick-handler. New nicks with nicks.add(). - + * Helper methods msg(outgoing_string, from_obj=None, data=None) @@ -68,12 +68,12 @@ class Player(TypeClass): * Hook methods basetype_setup() - at_player_creation() + at_player_creation() - note that the following hooks are also found on Objects and are usually handled on the character level: - at_init() + at_init() at_cmdset_get() at_first_login() at_post_login() @@ -86,11 +86,11 @@ class Player(TypeClass): """ super(Player, self).__init__(dbobj) - ## methods inherited from database model + ## methods inherited from database model def msg(self, outgoing_string, from_obj=None, data=None): - """ - Evennia -> User + """ + Evennia -> User This is the main route for sending data back to the user from the server. outgoing_string (string) - text data to send @@ -106,7 +106,7 @@ class Player(TypeClass): new_character (Object) - character/object to swap to delete_old_character (bool) - delete the old character when swapping - + Returns: True/False depending on if swap suceeded or not. """ return self.dbobj.swap_character(new_character, delete_old_character=delete_old_character) @@ -117,44 +117,44 @@ class Player(TypeClass): lets its typeclass execute the command. Evennia also calls this method whenever the player sends a command on the command line. - Argument: + Argument: raw_string (string) - raw command input Returns Deferred - this is an asynchronous Twisted object that will not fire until the command has actually finished executing. To overload - this one needs to attach callback functions to it, with addCallback(function). + this one needs to attach callback functions to it, with addCallback(function). This function will be called with an eventual return value from the command - execution. + execution. This return is not used at all by Evennia by default, but might be useful - for coders intending to implement some sort of nested command structure. + for coders intending to implement some sort of nested command structure. """ self.dbobj.execute_cmd(raw_string) - def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, + def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False): """ This method mimicks object.search if self.character is set. Otherwise only - other Players can be searched with this method. + other Players can be searched with this method. """ - self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks, + self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks, location=location, ignore_errors=ignore_errors, player=player) - + def is_typeclass(self, typeclass, exact=False): """ Returns true if this object has this type OR has a typeclass which is an subclass of the given typeclass. - + typeclass - can be a class object or the - python path to such an object to match against. - + python path to such an object to match against. + exact - returns true only if the object's type is exactly this typeclass, ignoring parents. - Returns: Boolean - """ + Returns: Boolean + """ return self.dbobj.is_typeclass(typeclass, exact=exact) def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True): @@ -162,18 +162,18 @@ class Player(TypeClass): This performs an in-situ swap of the typeclass. This means that in-game, this object will suddenly be something else. Player will not be affected. To 'move' a player to a different - object entirely (while retaining this object's type), use + object entirely (while retaining this object's type), use self.player.swap_object(). - Note that this might be an error prone operation if the + Note that this might be an error prone operation if the old/new typeclass was heavily customized - your code - might expect one and not the other, so be careful to + might expect one and not the other, so be careful to bug test your code if using this feature! Often its easiest to create a new object and just swap the player over to - that one instead. + that one instead. - Arguments: - new_typeclass (path/classobj) - type to switch to + Arguments: + new_typeclass (path/classobj) - type to switch to clean_attributes (bool/list) - will delete all attributes stored on this object (but not any of the database fields such as name or @@ -185,10 +185,10 @@ class Player(TypeClass): no_default - if this is active, the swapper will not allow for swapping to a default typeclass in case the given one fails for some reason. Instead the old one - will be preserved. - Returns: + will be preserved. + Returns: boolean True/False depending on if the swap worked or not. - + """ self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default) @@ -200,52 +200,52 @@ class Player(TypeClass): accessing_obj (Object)- object trying to access this one access_type (string) - type of access sought default (bool) - what to return if no lock of access_type was found - """ + """ return self.dbobj.access(accessing_obj, access_type=access_type, default=default) def check_permstring(self, permstring): """ This explicitly checks the given string against this object's 'permissions' property without involving any locks. - + permstring (string) - permission string that need to match a permission on the object. (example: 'Builders') """ return self.dbobj.check_permstring(permstring) ## player hooks - + def basetype_setup(self): """ - This sets up the basic properties for a player. + This sets up the basic properties for a player. Overload this with at_player_creation rather than - changing this method. - + changing this method. + """ # the text encoding to use. self.db.encoding = "utf-8" - + # A basic security setup self.locks.add("examine:perm(Wizards)") self.locks.add("edit:perm(Wizards)") self.locks.add("delete:perm(Wizards)") - self.locks.add("boot:perm(Wizards)") + self.locks.add("boot:perm(Wizards)") self.locks.add("msg:all()") - # The ooc player cmdset + # The ooc player cmdset self.cmdset.add_default(CMDSET_OOC, permanent=True) - self.cmdset.outside_access = False - + self.cmdset.outside_access = False + def at_player_creation(self): """ This is called once, the very first time the player is created (i.e. first time they register with the game). It's a good place to store attributes all players should have, - like configuration values etc. - """ + like configuration values etc. + """ pass - + def at_init(self): """ @@ -257,7 +257,7 @@ class Player(TypeClass): happens the moment the player logs in or reconnects after a reload. """ - pass + pass # Note that the hooks below also exist in the character object's # typeclass. You can often ignore these and rely on the character @@ -292,7 +292,7 @@ class Player(TypeClass): """ Called at the end of the login process, just before letting - them loose. + them loose. """ pass @@ -307,29 +307,29 @@ class Player(TypeClass): """ Called when any text is emitted to this object. If it returns False, no text - will be sent automatically. + will be sent automatically. """ - return True + return True def at_message_send(self, message, to_object): """ Called whenever this object tries to send text to another object. Only called if the object supplied - itself as a sender in the msg() call. + itself as a sender in the msg() call. """ - pass + pass def at_server_reload(self): """ - This hook is called whenever the server is shutting down for restart/reboot. + This hook is called whenever the server is shutting down for restart/reboot. If you want to, for example, save non-persistent properties across a restart, - this is the place to do it. + this is the place to do it. """ pass def at_server_shutdown(self): """ - This hook is called whenever the server is shutting down fully (i.e. not for - a restart). + This hook is called whenever the server is shutting down fully (i.e. not for + a restart). """ pass diff --git a/src/scripts/manager.py b/src/scripts/manager.py index 5ffff0b132..4e3fbc1929 100644 --- a/src/scripts/manager.py +++ b/src/scripts/manager.py @@ -4,18 +4,19 @@ The custom manager for Scripts. from src.typeclasses.managers import TypedObjectManager from src.typeclasses.managers import returns_typeclass_list +__all__ = ("ScriptManager",) VALIDATE_ITERATION = 0 class ScriptManager(TypedObjectManager): """ - This Scriptmanager implements methods for searching + This Scriptmanager implements methods for searching and manipulating Scripts directly from the database. Evennia-specific search methods (will return Typeclasses or lists of Typeclasses, whereas Django-general methods will return - Querysets or database objects). - + Querysets or database objects). + dbref (converter) dbref_search get_dbref_range @@ -38,15 +39,15 @@ class ScriptManager(TypedObjectManager): if not obj: return [] scripts = self.filter(db_obj=obj) - if key: + if key: return scripts.filter(db_key=key) - return scripts + return scripts @returns_typeclass_list def get_all_scripts(self, key=None): """ Return all scripts, alternative only - scripts with a certain key/dbref or path. + scripts with a certain key/dbref or path. """ if key: dbref = self.dbref(key) @@ -66,7 +67,7 @@ class ScriptManager(TypedObjectManager): This stops and deletes a specific script directly from the script database. This might be needed for global scripts not tied to - a specific game object. + a specific game object. """ scripts = self.get_id(dbref) for script in scripts: @@ -76,7 +77,7 @@ class ScriptManager(TypedObjectManager): """ This cleans up the script database of all non-persistent scripts, or only those on obj. It is called every time the server restarts - and + and """ if obj: to_stop = self.filter(db_persistent=False, db_obj=obj) @@ -86,70 +87,70 @@ class ScriptManager(TypedObjectManager): for script in to_stop.filter(db_is_active=True): script.stop() for script in to_stop.filter(db_is_active=False): - script.delete() + script.delete() return nr_deleted - def validate(self, scripts=None, obj=None, key=None, dbref=None, + def validate(self, scripts=None, obj=None, key=None, dbref=None, init_mode=False): """ This will step through the script database and make sure all objects run scripts that are still valid in the context they are in. This is called by the game engine at regular - intervals but can also be initiated by player scripts. + intervals but can also be initiated by player scripts. If key and/or obj is given, only update the related script/object. Only one of the arguments are supposed to be supplied at a time, since they are exclusive to each other. - + scripts = a list of scripts objects obtained somewhere. obj = validate only scripts defined on a special object. key = validate only scripts with a particular key - dbref = validate only the single script with this particular id. + dbref = validate only the single script with this particular id. init_mode - This is used during server upstart and can have - three values: + three values: False (no init mode). Called during run. "reset" - server reboot. Kill non-persistent scripts "reload" - server reload. Keep non-persistent scripts. - + This method also makes sure start any scripts it validates, this should be harmless, since already-active scripts - have the property 'is_running' set and will be skipped. + have the property 'is_running' set and will be skipped. """ - # we store a variable that tracks if we are calling a - # validation from within another validation (avoids - # loops). + # we store a variable that tracks if we are calling a + # validation from within another validation (avoids + # loops). - global VALIDATE_ITERATION + global VALIDATE_ITERATION if VALIDATE_ITERATION > 0: # we are in a nested validation. Exit. VALIDATE_ITERATION -= 1 - return None, None + return None, None VALIDATE_ITERATION += 1 # not in a validation - loop. Validate as normal. - + nr_started = 0 - nr_stopped = 0 + nr_stopped = 0 if init_mode: if init_mode == 'reset': - # special mode when server starts or object logs in. - # This deletes all non-persistent scripts from database + # special mode when server starts or object logs in. + # This deletes all non-persistent scripts from database nr_stopped += self.remove_non_persistent(obj=obj) # turn off the activity flag for all remaining scripts scripts = self.get_all_scripts() for script in scripts: - script.dbobj.is_active = False - + script.dbobj.is_active = False + elif not scripts: - # normal operation + # normal operation if dbref and self.dbref(dbref): scripts = self.get_id(dbref) elif obj: - scripts = self.get_all_scripts_on_obj(obj, key=key) + scripts = self.get_all_scripts_on_obj(obj, key=key) else: scripts = self.get_all_scripts(key=key) #self.model.get_all_cached_instances() @@ -158,46 +159,46 @@ class ScriptManager(TypedObjectManager): VALIDATE_ITERATION -= 1 return None, None - #print "scripts to validate: [%s]" % (", ".join(script.key for script in scripts)) + #print "scripts to validate: [%s]" % (", ".join(script.key for script in scripts)) for script in scripts: - #print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode) - if script.is_valid(): - nr_started += script.start(force_restart=init_mode) + #print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode) + if script.is_valid(): + nr_started += script.start(force_restart=init_mode) #print "back from start. nr_started=", nr_started else: script.stop() nr_stopped += 1 VALIDATE_ITERATION -= 1 return nr_started, nr_stopped - + @returns_typeclass_list def script_search(self, ostring, obj=None, only_timed=False): """ Search for a particular script. - + ostring - search criterion - a script ID or key obj - limit search to scripts defined on this object only_timed - limit search only to scripts that run - on a timer. + on a timer. """ ostring = ostring.strip() - + dbref = self.dbref(ostring) if dbref: # this is a dbref, try to find the script directly dbref_match = self.dbref_search(dbref) if dbref_match: - ok = True + ok = True if obj and obj != dbref_match.obj: ok = False if only_timed and dbref_match.interval: - ok = False + ok = False if ok: return [dbref_match] # not a dbref; normal search scripts = self.filter(db_key__iexact=ostring) - + if obj: scripts = scripts.exclude(db_obj=None).filter(db_obj__db_key__iexact=ostring) if only_timed: diff --git a/src/scripts/models.py b/src/scripts/models.py index fc0e9c2101..538866bca9 100644 --- a/src/scripts/models.py +++ b/src/scripts/models.py @@ -30,6 +30,8 @@ from src.typeclasses.models import Attribute, TypedObject from django.contrib.contenttypes.models import ContentType from src.scripts.manager import ScriptManager +__all__ = ("ScriptAttribute", "ScriptDB") + #------------------------------------------------------------ # # ScriptAttribute @@ -121,122 +123,122 @@ class ScriptDB(TypedObject): # desc property (wraps db_desc) #@property - def desc_get(self): + def __desc_get(self): "Getter. Allows for value = self.desc" return self.db_desc #@desc.setter - def desc_set(self, value): + def __desc_set(self, value): "Setter. Allows for self.desc = value" self.db_desc = value self.save() #@desc.deleter - def desc_del(self): + def __desc_del(self): "Deleter. Allows for del self.desc" self.db_desc = "" self.save() - desc = property(desc_get, desc_set, desc_del) + desc = property(__desc_get, __desc_set, __desc_del) # obj property (wraps db_obj) #@property - def obj_get(self): + def __obj_get(self): "Getter. Allows for value = self.obj" return self.db_obj #@obj.setter - def obj_set(self, value): + def __obj_set(self, value): "Setter. Allows for self.obj = value" self.db_obj = value self.save() #@obj.deleter - def obj_del(self): + def __obj_del(self): "Deleter. Allows for del self.obj" self.db_obj = None self.save() - obj = property(obj_get, obj_set, obj_del) + obj = property(__obj_get, __obj_set, __obj_del) # interval property (wraps db_interval) #@property - def interval_get(self): + def __interval_get(self): "Getter. Allows for value = self.interval" return self.db_interval #@interval.setter - def interval_set(self, value): + def __interval_set(self, value): "Setter. Allows for self.interval = value" self.db_interval = int(value) self.save() #@interval.deleter - def interval_del(self): + def __interval_del(self): "Deleter. Allows for del self.interval" self.db_interval = 0 self.save() - interval = property(interval_get, interval_set, interval_del) + interval = property(__interval_get, __interval_set, __interval_del) # start_delay property (wraps db_start_delay) #@property - def start_delay_get(self): + def __start_delay_get(self): "Getter. Allows for value = self.start_delay" return self.db_start_delay #@start_delay.setter - def start_delay_set(self, value): + def __start_delay_set(self, value): "Setter. Allows for self.start_delay = value" self.db_start_delay = value self.save() #@start_delay.deleter - def start_delay_del(self): + def __start_delay_del(self): "Deleter. Allows for del self.start_delay" self.db_start_delay = False self.save() - start_delay = property(start_delay_get, start_delay_set, start_delay_del) + start_delay = property(__start_delay_get, __start_delay_set, __start_delay_del) # repeats property (wraps db_repeats) #@property - def repeats_get(self): + def __repeats_get(self): "Getter. Allows for value = self.repeats" return self.db_repeats #@repeats.setter - def repeats_set(self, value): + def __repeats_set(self, value): "Setter. Allows for self.repeats = value" self.db_repeats = int(value) self.save() #@repeats.deleter - def repeats_del(self): + def __repeats_del(self): "Deleter. Allows for del self.repeats" self.db_repeats = 0 self.save() - repeats = property(repeats_get, repeats_set, repeats_del) + repeats = property(__repeats_get, __repeats_set, __repeats_del) # persistent property (wraps db_persistent) #@property - def persistent_get(self): + def __persistent_get(self): "Getter. Allows for value = self.persistent" return self.db_persistent #@persistent.setter - def persistent_set(self, value): + def __persistent_set(self, value): "Setter. Allows for self.persistent = value" self.db_persistent = value self.save() #@persistent.deleter - def persistent_del(self): + def __persistent_del(self): "Deleter. Allows for del self.persistent" self.db_persistent = False self.save() - persistent = property(persistent_get, persistent_set, persistent_del) + persistent = property(__persistent_get, __persistent_set, __persistent_del) # is_active property (wraps db_is_active) #@property - def is_active_get(self): + def __is_active_get(self): "Getter. Allows for value = self.is_active" return self.db_is_active #@is_active.setter - def is_active_set(self, value): + def __is_active_set(self, value): "Setter. Allows for self.is_active = value" self.db_is_active = value self.save() #@is_active.deleter - def is_active_del(self): + def __is_active_del(self): "Deleter. Allows for del self.is_active" self.db_is_active = False self.save() - is_active = property(is_active_get, is_active_set, is_active_del) + is_active = property(__is_active_get, __is_active_set, __is_active_del) # # diff --git a/src/scripts/scripts.py b/src/scripts/scripts.py index bcb137c588..0934f59b61 100644 --- a/src/scripts/scripts.py +++ b/src/scripts/scripts.py @@ -2,27 +2,29 @@ This module contains the base Script class that all scripts are inheriting from. -It also defines a few common scripts. +It also defines a few common scripts. """ -from time import time +from time import time from twisted.internet.defer import maybeDeferred from twisted.internet.task import LoopingCall -from twisted.internet import task +from twisted.internet import task from src.server.sessionhandler import SESSIONS from src.typeclasses.typeclass import TypeClass from src.scripts.models import ScriptDB -from src.comms import channelhandler +from src.comms import channelhandler from src.utils import logger +__all__ = ("Script", "DoNothing", "CheckSessions", "ValidateScripts", "ValidateChannelHandler", "AddCmdSet") + # -# Base script, inherit from Script below instead. +# Base script, inherit from Script below instead. # class ScriptClass(TypeClass): """ Base class for scripts. Don't inherit from this, inherit from Script instead. """ - # private methods + # private methods def __eq__(self, other): """ @@ -32,7 +34,7 @@ class ScriptClass(TypeClass): try: return other.id == self.id except Exception: - return False + return False def _start_task(self, start_now=True): "start task runner" @@ -43,17 +45,17 @@ class ScriptClass(TypeClass): #print " start with paused time:", self.key, self.ndb._paused_time self.ndb.twisted_task.start(self.ndb._paused_time, now=False) else: - # starting script anew. + # starting script anew. #print "_start_task: self.interval:", self.key, self.dbobj.interval self.ndb.twisted_task.start(self.dbobj.interval, now=start_now and not self.start_delay) - self.ndb.time_last_called = int(time()) - + self.ndb.time_last_called = int(time()) + def _stop_task(self): "stop task runner" try: #print "stopping twisted task:", id(self.ndb.twisted_task), self.obj if self.ndb.twisted_task and self.ndb.twisted_task.running: - self.ndb.twisted_task.stop() + self.ndb.twisted_task.stop() except Exception: logger.log_trace() def _step_err_callback(self, e): @@ -69,14 +71,14 @@ class ScriptClass(TypeClass): "step task runner. No try..except needed due to defer wrap." if not self.is_valid(): self.stop() - return + return self.at_repeat() repeats = self.dbobj.db_repeats if repeats <= 0: pass # infinite repeat elif repeats == 1: self.stop() - return + return else: self.dbobj.db_repeats -= 1 self.ndb.time_last_called = int(time()) @@ -84,7 +86,7 @@ class ScriptClass(TypeClass): if self.ndb._paused_time: # this means we were running an unpaused script, for the time remaining - # after the pause. Now we start a normal-running timer again. + # after the pause. Now we start a normal-running timer again. #print "switching to normal run:", self.key del self.ndb._paused_time self._stop_task() @@ -93,23 +95,23 @@ class ScriptClass(TypeClass): def _step_task(self): "step task" - try: + try: d = maybeDeferred(self._step_succ_callback) - d.addErrback(self._step_err_callback) + d.addErrback(self._step_err_callback) return d except Exception: - logger.log_trace() + logger.log_trace() - - # Public methods + + # Public methods def time_until_next_repeat(self): """ Returns the time in seconds until the script will be - run again. If this is not a stepping script, returns None. + run again. If this is not a stepping script, returns None. This is not used in any way by the script's stepping system; it's only here for the user to be able to - check in on their scripts and when they will next be run. + check in on their scripts and when they will next be run. """ try: if self.ndb._paused_time: @@ -117,7 +119,7 @@ class ScriptClass(TypeClass): else: return max(0, (self.ndb.time_last_called + self.dbobj.db_interval) - int(time())) except Exception: - return None + return None def start(self, force_restart=False): """ @@ -125,33 +127,33 @@ class ScriptClass(TypeClass): persistent scripts, this is usually once every server start) force_restart - if True, will always restart the script, regardless - of if it has started before. - - returns 0 or 1 to indicated the script has been started or not. Used in counting. + of if it has started before. + + returns 0 or 1 to indicated the script has been started or not. Used in counting. """ - #print "Script %s (%s) start (active:%s, force:%s) ..." % (self.key, id(self.dbobj), - # self.is_active, force_restart) + #print "Script %s (%s) start (active:%s, force:%s) ..." % (self.key, id(self.dbobj), + # self.is_active, force_restart) if self.dbobj.is_active and not force_restart: # script already runs and should not be restarted. - return 0 + return 0 obj = self.obj - if obj: - # check so the scripted object is valid and initalized + if obj: + # check so the scripted object is valid and initalized try: - dummy = object.__getattribute__(obj, 'cmdset') + dummy = object.__getattribute__(obj, 'cmdset') except AttributeError: # this means the object is not initialized. self.dbobj.is_active = False - return 0 + return 0 - # try to restart a paused script + # try to restart a paused script if self.unpause(): return 1 - # try to start the script from scratch - try: + # try to start the script from scratch + try: self.dbobj.is_active = True self.at_start() if self.dbobj.db_interval > 0: @@ -159,15 +161,15 @@ class ScriptClass(TypeClass): return 1 except Exception: logger.log_trace() - self.dbobj.is_active = False + self.dbobj.is_active = False return 0 def stop(self, kill=False): """ Called to stop the script from running. - This also deletes the script. + This also deletes the script. - kill - don't call finishing hooks. + kill - don't call finishing hooks. """ #print "stopping script %s" % self.key #import pdb @@ -197,29 +199,29 @@ class ScriptClass(TypeClass): #print "pausing", self.key, self.time_until_next_repeat() dt = self.time_until_next_repeat() if dt == None: - return - self.db._paused_time = dt + return + self.db._paused_time = dt self._stop_task() - + def unpause(self): """ - Restart a paused script. This WILL call at_start(). + Restart a paused script. This WILL call at_start(). """ #print "unpausing", self.key, self.db._paused_time - dt = self.db._paused_time + dt = self.db._paused_time if dt == None: return False try: self.dbobj.is_active = True self.at_start() - self.ndb._paused_time = dt + self.ndb._paused_time = dt self._start_task(start_now=False) del self.db._paused_time except Exception, e: logger.log_trace() self.dbobj.is_active = False return False - return True + return True # hooks def at_script_creation(self): @@ -230,7 +232,7 @@ class ScriptClass(TypeClass): pass def at_start(self): "placeholder." - pass + pass def at_stop(self): "placeholder" pass @@ -240,7 +242,7 @@ class ScriptClass(TypeClass): def at_init(self): "called when typeclass re-caches. Usually not used for scripts." pass - + # # Base Script - inherit from this @@ -255,36 +257,36 @@ class Script(ScriptClass): def __init__(self, dbobj): """ This is the base TypeClass for all Scripts. Scripts describe events, timers and states in game, - they can have a time component or describe a state that changes under certain conditions. + they can have a time component or describe a state that changes under certain conditions. Script API: * Available properties (only available on initiated Typeclass objects) - key (string) - name of object + key (string) - name of object name (string)- same as key aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. dbref (int, read-only) - unique #id-number. Also "id" can be used. dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. date_created (string) - time stamp of object creation - permissions (list of strings) - list of permission strings + permissions (list of strings) - list of permission strings desc (string) - optional description of script, shown in listings - obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add()) - interval (int) - how often script should run, in seconds. <0 turns off ticker + obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add()) + interval (int) - how often script should run, in seconds. <=0 turns off ticker start_delay (bool) - if the script should start repeating right away or wait self.interval seconds - repeats (int) - how many times the script should repeat before stopping. 0 means infinite repeats + repeats (int) - how many times the script should repeat before stopping. <=0 means infinite repeats persistent (bool) - if script should survive a server shutdown or not - is_active (bool) - if script is currently running + is_active (bool) - if script is currently running * Handlers locks - lock-handler: use locks.add() to add new lock strings db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr - ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data - * Helper methods + * Helper methods start() - start script (this usually happens automatically at creation and obj.script.add() etc) stop() - stop script, and delete it @@ -292,48 +294,48 @@ class Script(ScriptClass): unpause() - restart a previously paused script. The script will continue as if it was never paused. time_until_next_repeat() - if a timed script (interval>0), returns time until next tick - * Hook methods + * Hook methods at_script_creation() - called only once, when an object of this class is first created. is_valid() - is called to check if the script is valid to be running at the current time. If is_valid() returns False, the running script is stopped and removed from the game. You can use this - to check state changes (i.e. an script tracking some combat - stats at regular intervals is only valid to run while there is - actual combat going on). + to check state changes (i.e. an script tracking some combat + stats at regular intervals is only valid to run while there is + actual combat going on). at_start() - Called every time the script is started, which for persistent scripts is at least once every server start. Note that this is unaffected by self.delay_start, which only delays the first call - to at_repeat(). + to at_repeat(). at_repeat() - Called every self.interval seconds. It will be called immediately upon launch unless self.delay_start is True, which will delay - the first call of this method by self.interval seconds. If - self.interval==0, this method will never be called. + the first call of this method by self.interval seconds. If + self.interval<=0, this method will never be called. at_stop() - Called as the script object is stopped and is about to be removed from the game, e.g. because is_valid() returned False. - at_server_reload() - Called when server reloads. Can be used to save temporary + at_server_reload() - Called when server reloads. Can be used to save temporary variables you want should survive a reload. - at_server_shutdown() - called at a full server shutdown. + at_server_shutdown() - called at a full server shutdown. - - """ + + """ super(Script, self).__init__(dbobj) def at_script_creation(self): """ Only called once, by the create function. """ - self.key = "" + self.key = "" self.desc = "" self.interval = 0 # infinite self.start_delay = False self.repeats = 0 # infinite - self.persistent = False - + self.persistent = False + def is_valid(self): """ - Is called to check if the script is valid to run at this time. + Is called to check if the script is valid to run at this time. Should return a boolean. The method is assumed to collect all needed information from its related self.obj. """ @@ -342,7 +344,7 @@ class Script(ScriptClass): def at_start(self): """ Called whenever the script is started, which for persistent - scripts is at least once every server start. It will also be called + scripts is at least once every server start. It will also be called when starting again after a pause (such as after a server reload) """ pass @@ -350,10 +352,10 @@ class Script(ScriptClass): def at_repeat(self): """ Called repeatedly if this Script is set to repeat - regularly. + regularly. """ pass - + def at_stop(self): """ Called whenever when it's time for this script to stop @@ -363,38 +365,38 @@ class Script(ScriptClass): def at_server_reload(self): """ - This hook is called whenever the server is shutting down for restart/reboot. + This hook is called whenever the server is shutting down for restart/reboot. If you want to, for example, save non-persistent properties across a restart, - this is the place to do it. + this is the place to do it. """ pass def at_server_shutdown(self): """ - This hook is called whenever the server is shutting down fully (i.e. not for - a restart). + This hook is called whenever the server is shutting down fully (i.e. not for + a restart). """ pass -# Some useful default Script types used by Evennia. +# Some useful default Script types used by Evennia. class DoNothing(Script): - "An script that does nothing. Used as default fallback." - def at_script_creation(self): + "An script that does nothing. Used as default fallback." + def at_script_creation(self): "Setup the script" self.key = "sys_do_nothing" - self.desc = "This is a placeholder script." - + self.desc = "This is a placeholder script." + class CheckSessions(Script): "Check sessions regularly." def at_script_creation(self): "Setup the script" self.key = "sys_session_check" - self.desc = "Checks sessions so they are live." - self.interval = 60 # repeat every 60 seconds - self.persistent = True + self.desc = "Checks sessions so they are live." + self.interval = 60 # repeat every 60 seconds + self.persistent = True def at_repeat(self): "called every 60 seconds" @@ -403,7 +405,7 @@ class CheckSessions(Script): SESSIONS.validate_sessions() class ValidateScripts(Script): - "Check script validation regularly" + "Check script validation regularly" def at_script_creation(self): "Setup the script" self.key = "sys_scripts_validate" @@ -412,31 +414,31 @@ class ValidateScripts(Script): self.persistent = True def at_repeat(self): - "called every hour" + "called every hour" #print "ValidateScripts run." ScriptDB.objects.validate() class ValidateChannelHandler(Script): - "Update the channelhandler to make sure it's in sync." + "Update the channelhandler to make sure it's in sync." def at_script_creation(self): "Setup the script" self.key = "sys_channels_validate" - self.desc = "Updates the channel handler" + self.desc = "Updates the channel handler" self.interval = 3700 # validate a little later than ValidateScripts self.persistent = True - + def at_repeat(self): "called every hour+" #print "ValidateChannelHandler run." channelhandler.CHANNELHANDLER.update() - + class AddCmdSet(Script): """ This script permanently assigns a command set to an object whenever it is started. This is not used by the core system anymore, it's here mostly - as an example. + as an example. """ def at_script_creation(self): "Setup the script" @@ -444,12 +446,12 @@ class AddCmdSet(Script): self.key = "add_cmdset" if not self.desc: self.desc = "Adds a cmdset to an object." - self.persistent = True + self.persistent = True # this needs to be assigned to upon creation. # It should be a string pointing to the right - # cmdset module and cmdset class name, e.g. - # 'examples.cmdset_redbutton.RedButtonCmdSet' + # cmdset module and cmdset class name, e.g. + # 'examples.cmdset_redbutton.RedButtonCmdSet' # self.db.cmdset = # self.db.add_default = @@ -461,12 +463,12 @@ class AddCmdSet(Script): self.obj.cmdset.add_default(cmdset) else: self.obj.cmdset.add(cmdset) - + def at_stop(self): """ This removes the cmdset when the script stops """ - cmdset = self.db.cmdset + cmdset = self.db.cmdset if cmdset: if self.db.add_default: self.obj.cmdset.delete_default() diff --git a/src/typeclasses/managers.py b/src/typeclasses/managers.py index 932336ad26..87505e942a 100644 --- a/src/typeclasses/managers.py +++ b/src/typeclasses/managers.py @@ -7,7 +7,7 @@ from functools import update_wrapper from django.db import models from src.utils import idmapper from src.utils.utils import make_iter -#from src.typeclasses import idmap +__all__ = ("AttributeManager", "TypedObjectManager") # Managers diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 4cc70c9061..ba7c100487 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -43,6 +43,8 @@ from src.locks.lockhandler import LockHandler from src.utils import logger, utils from src.utils.utils import make_iter, is_iter, has_parent, to_unicode, to_str +__all__ = ("Attribute", "TypeNick", "TypedObject") + _PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY] _CTYPEGET = ContentType.objects.get diff --git a/src/typeclasses/typeclass.py b/src/typeclasses/typeclass.py index c0843e3003..17643f05dd 100644 --- a/src/typeclasses/typeclass.py +++ b/src/typeclasses/typeclass.py @@ -13,6 +13,8 @@ used by the typesystem or django itself. from src.utils.logger import log_trace, log_errmsg from django.conf import settings +__all__ = ("TypeClass",) + # these are called so many times it's worth to avoid lookup calls _GA = object.__getattribute__ _SA = object.__setattr__