From eb2bd8d44c2afec922570272441620de6682a642 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 27 Sep 2015 19:51:54 +0200 Subject: [PATCH] Converted a large part of utils/ folder to google code docstrings as per #709. --- evennia/commands/default/unloggedin.py | 4 +- evennia/contrib/email_login.py | 4 +- evennia/utils/ansi.py | 131 +++++- evennia/utils/batchprocessors.py | 58 ++- evennia/utils/create.py | 201 +++++---- evennia/utils/dbserialize.py | 71 ++- evennia/utils/eveditor.py | 28 +- evennia/utils/evform.py | 46 +- evennia/utils/gametime.py | 63 ++- evennia/utils/idmapper/manager.py | 3 + evennia/utils/idmapper/models.py | 62 ++- evennia/utils/inlinefunc.py | 73 +++- evennia/utils/logger.py | 34 +- evennia/utils/spawner.py | 40 +- evennia/utils/text2html.py | 113 ++++- evennia/utils/txws.py | 4 +- evennia/utils/utils.py | 578 +++++++++++++++++-------- 17 files changed, 1107 insertions(+), 406 deletions(-) diff --git a/evennia/commands/default/unloggedin.py b/evennia/commands/default/unloggedin.py index b628436851..f2063825ae 100644 --- a/evennia/commands/default/unloggedin.py +++ b/evennia/commands/default/unloggedin.py @@ -472,10 +472,10 @@ def _create_player(session, playername, password, permissions, typeclass=None): logger.log_trace() return False - # This needs to be called so the engine knows this player is + # This needs to be set so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) - utils.init_new_player(new_player) + new_player.db.FIRST_LOGIN = True # join the new player to the public channel pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"]) diff --git a/evennia/contrib/email_login.py b/evennia/contrib/email_login.py index 78eacbf388..0f73797fc8 100644 --- a/evennia/contrib/email_login.py +++ b/evennia/contrib/email_login.py @@ -213,10 +213,10 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m logger.log_trace() return - # This needs to be called so the engine knows this player is + # This needs to be set so the engine knows this player is # logging in for the first time. (so it knows to call the right # hooks during login later) - utils.init_new_player(new_player) + new_player.db.FIRST_LOGIN = True # join the new player to the public channel pchanneldef = settings.CHANNEL_PUBLIC diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index 0fa577da47..73d2684982 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -80,6 +80,13 @@ class ANSIParser(object): """ Replacer used by `re.sub` to replace ANSI markers with correct ANSI sequences + + Args: + ansimatch (re.matchobject): The match. + + Returns: + processed (str): The processed match string. + """ return self.ansi_map.get(ansimatch.group(), "") @@ -87,6 +94,13 @@ class ANSIParser(object): """ Replacer used by `re.sub` to replace ANSI bright background markers with Xterm256 replacement + + Args: + ansimatch (re.matchobject): The match. + + Returns: + processed (str): The processed match string. + """ return self.ansi_bright_bgs.get(ansimatch.group(), "") @@ -97,6 +111,14 @@ class ANSIParser(object): It checks `self.do_xterm256` to determine if conversion to standard ANSI should be done or not. + + Args: + rgbmatch (re.matchobject): The match. + convert (bool, optional): Convert 256-colors to 16. + + Returns: + processed (str): The processed match string. + """ if not rgbmatch: return "" @@ -177,21 +199,43 @@ class ANSIParser(object): def strip_raw_codes(self, string): """ Strips raw ANSI codes from a string. + + Args: + string (str): The string to strip. + + Returns: + string (str): The processed string. + """ return self.ansi_regex.sub("", string) def strip_mxp(self, string): """ Strips all MXP codes from a string. + + Args: + string (str): The string to strip. + + Returns: + string (str): The processed string. + """ return self.mxp_sub.sub(r'\2', string) def parse_ansi(self, string, strip_ansi=False, xterm256=False, mxp=False): """ - Parses a string, subbing color codes according to - the stored mapping. + Parses a string, subbing color codes according to the stored + mapping. - strip_ansi flag instead removes all ANSI markup. + Args: + string (str): The string to parse. + strip_ansi (boolean, optional): Strip all found ansi markup. + xterm256 (boolean, optional): If actually using xterm256 or if + these values should be converted to 16-color ANSI. + mxp (boolean, optional): Parse MXP commands in string. + + Returns: + string (str): The parsed string. """ if hasattr(string, '_raw_string'): @@ -351,27 +395,57 @@ def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER, xterm256=False, mxp """ Parses a string, subbing color codes as needed. + Args: + string (str): The string to parse. + strip_ansi (bool, optional): Strip all ANSI sequences. + parser (ansi.AnsiParser, optional): A parser instance to use. + xterm256 (bool, optional): Support xterm256 or not. + mxp (bool, optional): Support MXP markup or not. + + Returns: + string (str): The parsed string. + """ return parser.parse_ansi(string, strip_ansi=strip_ansi, xterm256=xterm256, mxp=mxp) def strip_ansi(string, parser=ANSI_PARSER): """ - Strip all ansi from the string. + Strip all ansi from the string. This handles the Evennia-specific + markup. + + Args: + parser (ansi.AnsiParser, optional): The parser to use. + + Returns: + string (str): The stripped string. """ return parser.parse_ansi(string, strip_ansi=True) def strip_raw_ansi(string, parser=ANSI_PARSER): """ - Remove raw ansi codes from string + Remove raw ansi codes from string. This assumes pure + ANSI-bytecodes in the string. + + Args: + string (str): The string to parse. + parser (bool, optional): The parser to use. + + Returns: + string (str): the stripped string. """ return parser.strip_raw_codes(string) def raw(string): """ - Escapes a string into a form which won't be colorized by the ansi parser. + Escapes a string into a form which won't be colorized by the ansi + parser. + + Returns: + string (str): The raw, escaped string. + """ return string.replace('{', '{{') @@ -385,8 +459,9 @@ def group(lst, n): def _spacing_preflight(func): """ - This wrapper function is used to do some preflight checks on functions used - for padding ANSIStrings. + This wrapper function is used to do some preflight checks on + functions used for padding ANSIStrings. + """ def wrapped(self, width, fillchar=None): if fillchar is None: @@ -404,8 +479,9 @@ def _spacing_preflight(func): def _query_super(func_name): """ - Have the string class handle this with the cleaned string instead of - ANSIString. + Have the string class handle this with the cleaned string instead + of ANSIString. + """ def wrapped(self, *args, **kwargs): return getattr(self.clean(), func_name)(*args, **kwargs) @@ -415,6 +491,7 @@ def _query_super(func_name): def _on_raw(func_name): """ Like query_super, but makes the operation run on the raw string. + """ def wrapped(self, *args, **kwargs): args = list(args) @@ -439,6 +516,7 @@ def _transform(func_name): return a string the same length as the original. This function allows us to do the same, replacing all the non-coded characters with the resulting string. + """ def wrapped(self, *args, **kwargs): replacement_string = _query_super(func_name)(self, *args, **kwargs) @@ -461,6 +539,7 @@ class ANSIMeta(type): """ Many functions on ANSIString are just light wrappers around the unicode base class. We apply them here, as part of the classes construction. + """ def __init__(cls, *args, **kwargs): for func_name in [ @@ -493,6 +572,7 @@ class ANSIString(unicode): Please refer to the Metaclass, ANSIMeta, which is used to apply wrappers for several of the methods that need not be defined directly here. + """ __metaclass__ = ANSIMeta @@ -506,6 +586,7 @@ class ANSIString(unicode): Internally, ANSIString can also passes itself precached code/character indexes and clean strings to avoid doing extra work when combining ANSIStrings. + """ string = args[0] if not isinstance(string, basestring): @@ -554,9 +635,11 @@ class ANSIString(unicode): def __unicode__(self): """ - Unfortunately, this is not called during print() statements due to a - bug in the Python interpreter. You can always do unicode() or str() - around the resulting ANSIString and print that. + Unfortunately, this is not called during print() statements + due to a bug in the Python interpreter. You can always do + unicode() or str() around the resulting ANSIString and print + that. + """ return self._raw_string @@ -564,6 +647,7 @@ class ANSIString(unicode): """ Let's make the repr the command that would actually be used to construct this object, for convenience and reference. + """ return "ANSIString(%s, decoded=True)" % repr(self._raw_string) @@ -592,6 +676,7 @@ class ANSIString(unicode): Finally, _code_indexes and _char_indexes are defined. These are lookup tables for which characters in the raw string are related to ANSI escapes, and which are for the readable text. + """ self.parser = kwargs.pop('parser', ANSI_PARSER) super(ANSIString, self).__init__() @@ -603,6 +688,7 @@ class ANSIString(unicode): """ Takes a list of integers, and produces a new one incrementing all by a number. + """ return [i + offset for i in iterable] @@ -610,6 +696,7 @@ class ANSIString(unicode): def _adder(cls, first, second): """ Joins two ANSIStrings, preserving calculated info. + """ raw_string = first._raw_string + second._raw_string @@ -629,6 +716,7 @@ class ANSIString(unicode): We have to be careful when adding two strings not to reprocess things that don't need to be reprocessed, lest we end up with escapes being interpreted literally. + """ if not isinstance(other, basestring): return NotImplemented @@ -639,6 +727,7 @@ class ANSIString(unicode): def __radd__(self, other): """ Likewise, if we're on the other end. + """ if not isinstance(other, basestring): return NotImplemented @@ -650,6 +739,7 @@ class ANSIString(unicode): """ This function is deprecated, so we just make it call the proper function. + """ return self.__getitem__(slice(i, j)) @@ -667,6 +757,7 @@ class ANSIString(unicode): indexes that need slicing in the raw string. We can check between those indexes to figure out what escape characters need to be replayed. + """ slice_indexes = self._char_indexes[slc] # If it's the end of the string, we need to append final color codes. @@ -700,6 +791,7 @@ class ANSIString(unicode): this is a regexable ANSIString, it will get the data from the raw string instead, bypassing ANSIString's intelligent escape skipping, for reasons explained in the __new__ method's docstring. + """ if isinstance(item, slice): # Slices must be handled specially. @@ -727,12 +819,14 @@ class ANSIString(unicode): def clean(self): """ Return a unicode object without the ANSI escapes. + """ return self._clean_string def raw(self): """ Return a unicode object with the ANSI escapes. + """ return self._raw_string @@ -746,6 +840,7 @@ class ANSIString(unicode): We use the same techniques we used in split() to make sure each are colored. + """ if hasattr(sep, '_clean_string'): sep = sep.clean() @@ -776,6 +871,7 @@ class ANSIString(unicode): It's possible that only one of these tables is actually needed, the other assumed to be what isn't in the first. + """ code_indexes = [] @@ -792,6 +888,7 @@ class ANSIString(unicode): """ Get the code characters from the given slice end to the next character. + """ try: index = self._char_indexes[index - 1] @@ -815,6 +912,7 @@ class ANSIString(unicode): PyPy is distributed under the MIT licence. http://opensource.org/licenses/MIT + """ bylen = len(by) if bylen == 0: @@ -837,6 +935,7 @@ class ANSIString(unicode): def __mul__(self, other): """ Multiplication method. Implemented for performance reasons. + """ if not isinstance(other, int): return NotImplemented @@ -863,6 +962,7 @@ class ANSIString(unicode): PyPy is distributed under the MIT licence. http://opensource.org/licenses/MIT + """ res = [] end = len(self) @@ -886,6 +986,7 @@ class ANSIString(unicode): def join(self, iterable): """ Joins together strings in an iterable. + """ result = ANSIString('') last_item = None @@ -902,6 +1003,7 @@ class ANSIString(unicode): """ Generate a line of characters in a more efficient way than just adding ANSIStrings. + """ if not isinstance(char, ANSIString): line = char * amount @@ -929,6 +1031,7 @@ class ANSIString(unicode): def center(self, width, fillchar, difference): """ Center some text with some spaces padding both sides. + """ remainder = difference % 2 difference /= 2 @@ -940,6 +1043,7 @@ class ANSIString(unicode): def ljust(self, width, fillchar, difference): """ Left justify some text. + """ return self + self._filler(fillchar, difference) @@ -947,5 +1051,6 @@ class ANSIString(unicode): def rjust(self, width, fillchar, difference): """ Right justify some text. + """ return self._filler(fillchar, difference) + self diff --git a/evennia/utils/batchprocessors.py b/evennia/utils/batchprocessors.py index d394581c9d..6c8bc1c27a 100644 --- a/evennia/utils/batchprocessors.py +++ b/evennia/utils/batchprocessors.py @@ -192,13 +192,23 @@ RE_CODE_SPLIT = re.compile(r"(^\#CODE.*?$|^\#HEADER)$", re.MULTILINE) def read_batchfile(pythonpath, file_ending='.py'): """ - This reads the contents of a batch-file. - Filename is considered to be a python path to a batch file - relative the directory specified in `settings.py`. + This reads the contents of a batch-file. Filename is considered + to be a python path to a batch file relative the directory + specified in `settings.py`. + + file_ending specify which batchfile ending should be assumed (.ev + or .py). The ending should not be included in the python path. + + Args: + pythonpath (str): A dot-python path to a file. + file_ending (str): The file ending of this file (.ev or .py) + + Returns: + text (str): The text content of the batch file. + + Raises: + IOError: If problems reading file. - file_ending specify which batchfile ending should be - assumed (.ev or .py). The ending should not be included - in the python path. """ # open the file @@ -293,6 +303,7 @@ def tb_filename(tb): def tb_iter(tb): + "Traceback iterator." while tb is not None: yield tb tb = tb.tb_next @@ -309,13 +320,22 @@ class BatchCodeProcessor(object): This parses the lines of a batchfile according to the following rules: - 1) Lines starting with #HEADER starts a header block (ends other blocks) - 2) Lines starting with #CODE begins a code block (ends other blocks) - 3) #CODE headers may be of the following form: - #CODE (info) objname, objname2, ... - 4) Lines starting with #INSERT are on form #INSERT filename. - 5) All lines outside blocks are stripped. - 6) All excess whitespace beginning/ending a block is stripped. + Args: + pythonpath (str): The dot-python path to the file. + debug (bool, optional): Insert delete-commands for + deleting created objects. + + Returns: + codeblocks (list): A list of all #CODE blocks. + + Notes: + 1. Lines starting with #HEADER starts a header block (ends other blocks) + 2. Lines starting with #CODE begins a code block (ends other blocks) + 3. #CODE headers may be of the following form: + #CODE (info) objname, objname2, ... + 4. Lines starting with #INSERT are on form #INSERT filename. + 5. All lines outside blocks are stripped. + 6. All excess whitespace beginning/ending a block is stripped. """ @@ -373,9 +393,17 @@ class BatchCodeProcessor(object): def code_exec(self, code, extra_environ=None, debug=False): """ - Execute a single code block, including imports and appending global vars + Execute a single code block, including imports and appending + global vars. + + Args: + code (str): Code to run. + extra_environ (dict): Environment variables to run with code. + debug (bool, optional): Insert delete statements for objects. + + Returns: + err (str or None): An error code or None (ok). - extra_environ - dict with environment variables """ # define the execution environment environdict = {"settings_module": settings} diff --git a/evennia/utils/create.py b/evennia/utils/create.py index a83f099ad9..fb2875d665 100644 --- a/evennia/utils/create.py +++ b/evennia/utils/create.py @@ -59,24 +59,35 @@ def create_object(typeclass=None, key=None, location=None, Create a new in-game object. - keywords: - typeclass - class or python path to a typeclass - key - name of the new object. If not set, a name of #dbref will be set. - home - obj or #dbref to use as the object's home location - permissions - a comma-separated string of permissions - locks - one or more lockstrings, separated by semicolons - aliases - a list of alternative keys - tags - a list of tag keys (using no category) - destination - obj or #dbref to use as an Exit's target + Kwargs: + typeclass (class or str): Class or python path to a typeclass. + key (str): Name of the new object. If not set, a name of + #dbref will be set. + home (Object or str): Obj or #dbref to use as the object's + home location. + permissions (str): A comma-separated string of permissions. + locks (str): one or more lockstrings, separated by semicolons. + aliases (list): A list of alternative keys. + tags (list): List of tag keys (using no category). + destination (Object or str): Obj or #dbref to use as an Exit's + target. + report_to (Object): The object to return error messages to. + nohome (bool): This allows the creation of objects without a + default home location; only used when creating the default + location itself or during unittests. + + Returns: + object (Object): A newly created object of the given typeclass. + + Raises: + ObjectDB.DoesNotExist: If trying to create an Object with + `location` or `home` that can't be found. - nohome - this allows the creation of objects without a default home location; - only used when creating the default location itself or during unittests """ global _ObjectDB if not _ObjectDB: from evennia.objects.models import ObjectDB as _ObjectDB - typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS if isinstance(typeclass, basestring): @@ -120,32 +131,42 @@ object = create_object # Script creation # -def create_script(typeclass, key=None, obj=None, player=None, locks=None, +def create_script(typeclass=None, key=None, obj=None, player=None, locks=None, interval=None, start_delay=None, repeats=None, persistent=None, autostart=True, report_to=None): """ - Create a new script. All scripts are a combination - of a database object that communicates with the - database, and an typeclass that 'decorates' the - database object into being different types of scripts. - It's behaviour is similar to the game objects except - scripts has a time component and are more limited in - scope. + Create a new script. All scripts are a combination of a database + object that communicates with the database, and an typeclass that + 'decorates' the database object into being different types of + scripts. It's behaviour is similar to the game objects except + scripts has a time component and are more limited in scope. + + Kwargs: + typeclass (class or str): Class or python path to a typeclass. + key (str): Name of the new object. If not set, a name of + #dbref will be set. + obj (Object): The entity on which this Script sits. If this + is `None`, we are creating a "global" script. + player (Player): The player on which this Script sits. It is + exclusiv to `obj`. + locks (str): one or more lockstrings, separated by semicolons. + interval (int): The triggering interval for this Script, in + seconds. If unset, the Script will not have a timing + component. + start_delay (bool): If `True`, will wait `interval` seconds + before triggering the first time. + repeats (int): The number of times to trigger before stopping. + If unset, will repeat indefinitely. + persistent (bool): If this Script survives a server shutdown + or not (all Scripts will survive a reload). + autostart (bool): If this Script will start immediately when + created or if the `start` method must be called explicitly. + report_to (Object): The object to return error messages to. - Argument 'typeclass' can be either an actual - typeclass object or a python path to such an object. - Only set key here if you want a unique name for this - particular script (set it in config to give - same key to all scripts of the same type). Set obj - to tie this script to a particular object. See evennia.scripts.manager for methods to manipulate existing scripts in the database. - report_to is an obtional object to receive error messages. - If report_to is not set, an Exception with the - error will be raised. If set, this method will - return None upon errors. """ global _ScriptDB if not _ScriptDB: @@ -194,10 +215,20 @@ script = create_script def create_help_entry(key, entrytext, category="General", locks=None): """ Create a static help entry in the help database. Note that Command - help entries are dynamic and directly taken from the __doc__ entries - of the command. The database-stored help entries are intended for more - general help on the game, more extensive info, in-game setting information - and so on. + help entries are dynamic and directly taken from the __doc__ + entries of the command. The database-stored help entries are + intended for more general help on the game, more extensive info, + in-game setting information and so on. + + Args: + key (str): The name of the help entry. + entrytext (str): The body of te help entry + category (str, optional): The help category of the entry. + locks (str, optional): A lockstring to restrict access. + + Returns: + help (HelpEntry): A newly created help entry. + """ global _HelpEntry if not _HelpEntry: @@ -230,24 +261,28 @@ help_entry = create_help_entry def create_message(senderobj, message, channels=None, receivers=None, locks=None, header=None): """ - Create a new communication message. Msgs are used for all - player-to-player communication, both between individual players - and over channels. - senderobj - the player sending the message. This must be the actual object. - message - text with the message. Eventual headers, titles etc - should all be included in this text string. Formatting - will be retained. - channels - a channel or a list of channels to send to. The channels - may be actual channel objects or their unique key strings. - receivers - a player to send to, or a list of them. May be Player objects - or playernames. - locks - lock definition string - header - mime-type or other optional information for the message + Create a new communication Msg. Msgs represent a unit of + database-persistent communication between entites. + + Args: + senderobj (Object or Player): The entity sending the Msg. + message (str): Text with the message. Eventual headers, titles + etc should all be included in this text string. Formatting + will be retained. + channels (Channel, key or list): A channel or a list of channels to + send to. The channels may be actual channel objects or their + unique key strings. + receivers (Object, Player, str or list): A Player/Object to send + to, or a list of them. May be Player objects or playernames. + locks (str): Lock definition string. + header (str): Mime-type or other optional information for the message + + Notes: + The Comm system is created very open-ended, so it's fully possible + to let a message both go to several channels and to several + receivers at the same time, it's up to the command definitions to + limit this as desired. - The Comm system is created very open-ended, so it's fully possible - to let a message both go to several channels and to several receivers - at the same time, it's up to the command definitions to limit this as - desired. """ global _Msg if not _Msg: @@ -275,16 +310,27 @@ def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None): """ - Create A communication Channel. A Channel serves as a central - hub for distributing Msgs to groups of people without - specifying the receivers explicitly. Instead players may - 'connect' to the channel and follow the flow of messages. By - default the channel allows access to all old messages, but - this can be turned off with the keep_log switch. + Create A communication Channel. A Channel serves as a central hub + for distributing Msgs to groups of people without specifying the + receivers explicitly. Instead players may 'connect' to the channel + and follow the flow of messages. By default the channel allows + access to all old messages, but this can be turned off with the + keep_log switch. + + Args: + key (str): This must be unique. + + Kwargs: + aliases (list): List of alternative (likely shorter) keynames. + desc (str): A description of the channel, for use in listings. + locks (str): Lockstring. + keep_log (bool): Log channel throughput. + typeclass (str or class): The typeclass of the Channel (not + often used). + + Returns: + channel (Channel): A newly created channel. - key - this must be unique. - aliases - list of alternative (likely shorter) keynames. - locks - lock string definitions """ typeclass = typeclass if typeclass else settings.BASE_CHANNEL_TYPECLASS @@ -322,23 +368,28 @@ def create_player(key, email, password, """ This creates a new player. - key - the player's name. This should be unique. - email - email on valid addr@addr.domain form. - password - password in cleartext - is_superuser - wether or not this player is to be a superuser - locks - lockstring - permission - list of permissions - report_to - an object with a msg() method to report errors to. If - not given, errors will be logged. + Args: + key (str): The player's name. This should be unique. + email (str): Email on valid addr@addr.domain form. This is + technically required but if set to `None`, an email of + `dummy@dummy.com` will be used as a placeholder. + password (str): Password in cleartext. - Will return the Player-typeclass or None/raise Exception if the - Typeclass given failed to load. + Kwargs: + is_superuser (bool): Wether or not this player is to be a superuser + locks (str): Lockstring. + permission (list): List of permission strings. + report_to (Object): An object with a msg() method to report + errors to. If not given, errors will be logged. - Concerning is_superuser: - Usually only the server admin should need to be superuser, all - other access levels can be handled with more fine-grained - permissions or groups. A superuser bypasses all lock checking - operations and is thus not suitable for play-testing the game. + Raises: + ValueError: If `key` already exists in database. + + Notes: + Usually only the server admin should need to be superuser, all + other access levels can be handled with more fine-grained + permissions or groups. A superuser bypasses all lock checking + operations and is thus not suitable for play-testing the game. """ global _PlayerDB diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index f5a9e31e8a..0e6f6caf71 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -25,7 +25,6 @@ try: from cPickle import dumps, loads except ImportError: from pickle import dumps, loads -from django.db import transaction from django.core.exceptions import ObjectDoesNotExist from django.contrib.contenttypes.models import ContentType from evennia.server.models import ServerConfig @@ -52,8 +51,14 @@ else: def _TO_DATESTRING(obj): """ - this will only be called with valid database objects. Returns datestring - on correct form. + Creates datestring hash. + + Args: + obj (Object): Database object. + + Returns: + datestring (str): A datestring hash. + """ try: return _GA(obj, "db_date_created").strftime(_DATESTRING) @@ -209,8 +214,14 @@ class _SaverSet(_SaverMutable, MutableSet): def pack_dbobj(item): """ Check and convert django database objects to an internal representation. - This either returns the original input item or a tuple - ("__packed_dbobj__", key, creation_time, id) + + Args: + item (any): A database entity to pack + + Returns: + packed (any or tuple): Either returns the original input item + or the packing tuple `("__packed_dbobj__", key, creation_time, id)`. + """ _init_globals() obj = item @@ -224,10 +235,18 @@ def pack_dbobj(item): def unpack_dbobj(item): """ - Check and convert internal representations back to Django database models. - The fact that item is a packed dbobj should be checked before this call. - This either returns the original input or converts the internal store back - to a database representation (its typeclass is returned if applicable). + Check and convert internal representations back to Django database + models. + + Args: + item (packed_dbobj): The fact that item is a packed dbobj + should be checked before this call. + + Returns: + unpacked (any): Either the original input or converts the + internal store back to a database representation (its + typeclass is returned if applicable). + """ _init_globals() try: @@ -244,11 +263,18 @@ def unpack_dbobj(item): def to_pickle(data): """ - This prepares data on arbitrary form to be pickled. It handles any nested - structure and returns data on a form that is safe to pickle (including - having converted any database models to their internal representation). - We also convert any Saver*-type objects back to their normal - representations, they are not pickle-safe. + This prepares data on arbitrary form to be pickled. It handles any + nested structure and returns data on a form that is safe to pickle + (including having converted any database models to their internal + representation). We also convert any Saver*-type objects back to + their normal representations, they are not pickle-safe. + + Args: + data (any): Data to pickle. + + Returns: + data (any): Pickled data. + """ def process_item(item): "Recursive processor and identification of data" @@ -281,13 +307,18 @@ def from_pickle(data, db_obj=None): object was removed (or changed in-place) in the database, None will be returned. - db_obj - this is the model instance (normally an Attribute) that - _Saver*-type iterables (_SaverList etc) will save to when they - update. It must have a 'value' property that saves assigned data - to the database. Skip if not serializing onto a given object. + Args_ + data (any): Pickled data to unpickle. + db_obj (Atribute, any): This is the model instance (normally + an Attribute) that _Saver*-type iterables (_SaverList etc) + will save to when they update. It must have a 'value' property + that saves assigned data to the database. Skip if not + serializing onto a given object. If db_obj is given, this + function will convert lists, dicts and sets to their + _SaverList, _SaverDict and _SaverSet counterparts. - If db_obj is given, this function will convert lists, dicts and sets - to their _SaverList, _SaverDict and _SaverSet counterparts. + Returns: + data (any): Unpickled data. """ def process_item(item): diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index 739eacf186..9d046770ff 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -631,7 +631,9 @@ class EvEditor(object): def get_buffer(self): """ - Return the current buffer + Return: + buffer (str): The current buffer. + """ return self._buffer @@ -639,6 +641,10 @@ class EvEditor(object): """ This should be called when the buffer has been changed somehow. It will handle unsaved flag and undo updating. + + Args: + buf (str): The text to update the buffer with. + """ if is_iter(buf): buf = "\n".join(buf) @@ -651,6 +657,7 @@ class EvEditor(object): def quit(self): """ Cleanly exit the editor. + """ try: self._quitfunc(self._caller) @@ -663,6 +670,7 @@ class EvEditor(object): """ Saves the content of the buffer. The 'quitting' argument is a bool indicating whether or not the editor intends to exit after saving. + """ if self._unsaved: try: @@ -680,6 +688,12 @@ class EvEditor(object): """ This updates the undo position. + Args: + step (int, optional): The amount of steps + to progress the undo position to. This + may be a negative value for undo and + a positive value for redo. + """ if step and step < 0: # undo @@ -706,8 +720,15 @@ class EvEditor(object): """ This displays the line editor buffer, or selected parts of it. - If `buf` is set and is not the full buffer, `offset` should define - the starting line number, to get the linenum display right. + Args: + buf (str, optional): The buffer or part of buffer to display. + offset (int, optional): If `buf` is set and is not the full buffer, + `offset` should define the actual starting line number, to + get the linenum display right. + linenums (bool, optional): Show line numbers in buffer. + raw (bool, optional): Tell protocol to not parse + formatting information. + """ if buf == None: buf = self._buffer @@ -733,6 +754,7 @@ class EvEditor(object): def display_help(self): """ Shows the help entry for the editor. + """ string = self._sep * _DEFAULT_WIDTH + _HELP_TEXT + self._sep * _DEFAULT_WIDTH self._caller.msg(string) diff --git a/evennia/utils/evform.py b/evennia/utils/evform.py index 9e4453efdd..e1b5955aef 100644 --- a/evennia/utils/evform.py +++ b/evennia/utils/evform.py @@ -168,17 +168,17 @@ class EvForm(object): """ Initiate the form - keywords: - filename - path to template file - form - dictionary of {"CELLCHAR":char, - "TABLECHAR":char, - "FORM":templatestring} + Kwargs: + filename (str): Path to template file. + cells (dict): A dictionary mapping of {id:text} + tables (dict): A dictionary mapping of {id:EvTable}. + form (dict): A dictionary of {"CELLCHAR":char, + "TABLECHAR":char, + "FORM":templatestring} if this is given, filename is not read. - cells - a dictionary mapping of {id:text} - tables - dictionary mapping of {id:EvTable} - - other kwargs are fed as options to the EvCells and EvTables - (see `evtable.EvCell` and `evtable.EvTable` for more info). + Notes: + Other kwargs are fed as options to the EvCells and EvTables + (see `evtable.EvCell` and `evtable.EvTable` for more info). """ self.filename = filename @@ -204,8 +204,9 @@ class EvForm(object): def _parse_rectangles(self, cellchar, tablechar, form, **kwargs): """ - Parse a form for rectangular formfields identified by - formchar enclosing an identifier. + Parse a form for rectangular formfields identified by formchar + enclosing an identifier. + """ # update options given at creation with new input - this @@ -337,6 +338,7 @@ class EvForm(object): def _populate_form(self, raw_form, mapping): """ Insert cell contents into form at given locations + """ form = copy.copy(raw_form) for key, (iy0, ix0, width, height, cell_or_table) in mapping.items(): @@ -352,11 +354,13 @@ class EvForm(object): """ Add mapping for form. - cells - a dictionary of {identifier:celltext} - tables - a dictionary of {identifier:table} + Args: + cells (dict): A dictionary of {identifier:celltext} + tables (dict): A dictionary of {identifier:table} - kwargs will be forwarded to tables/cells. See - evtable.EvCell and evtable.EvTable for info. + Notes: + kwargs will be forwarded to tables/cells. See + `evtable.EvCell` and `evtable.EvTable` for info. """ # clean kwargs (these cannot be overridden) @@ -373,7 +377,15 @@ class EvForm(object): def reload(self, filename=None, form=None, **kwargs): """ - Creates the form from a stored file name + Creates the form from a stored file name. + + Args: + filename (str): The file to read from. + form (dict): A mapping for the form. + + Notes: + Kwargs are passed through to Cel creation. + """ # clean kwargs (these cannot be overridden) kwargs.pop("enforce_size", None) diff --git a/evennia/utils/gametime.py b/evennia/utils/gametime.py index 8b4a7db370..56652ce376 100644 --- a/evennia/utils/gametime.py +++ b/evennia/utils/gametime.py @@ -65,21 +65,52 @@ def _format(seconds, *divisors) : # Access functions def runtime(format=False): - "Get the total runtime of the server since first start (minus downtimes)" + """ + Get the total runtime of the server since first start (minus + downtimes) + + Args: + format (bool, optional): Format into a time representation. + + Returns: + time (float or tuple): The runtime or the same time split up + into time units. + + """ runtime = SERVER_RUNTIME + (time() - SERVER_RUNTIME_LAST_UPDATED) if format: return _format(runtime, 31536000, 2628000, 604800, 86400, 3600, 60) return runtime def uptime(format=False): - "Get the current uptime of the server since last reload" + """ + Get the current uptime of the server since last reload + + Args: + format (bool, optional): Format into time representation. + + Returns: + time (float or tuple): The uptime or the same time split up + into time units. + + """ uptime = time() - SERVER_START_TIME if format: return _format(uptime, 31536000, 2628000, 604800, 86400, 3600, 60) return uptime def gametime(format=False): - "Get the total gametime of the server since first start (minus downtimes)" + """ + Get the total gametime of the server since first start (minus downtimes) + + Args: + format (bool, optional): Format into time representation. + + Returns: + time (float or tuple): The gametime or the same time split up + into time units. + + """ gametime = runtime() * TIMEFACTOR if format: return _format(gametime, YEAR, MONTH, WEEK, DAY, HOUR, MIN) @@ -94,9 +125,18 @@ def gametime_to_realtime(secs=0, mins=0, hrs=0, days=0, in-game, you will be able to find the number of real-world seconds this corresponds to (hint: Interval events deal with real life seconds). + Kwargs: + times (int): The various components of the time. + format (bool): Formatting the output. + + Returns: + time (float or tuple): The realtime difference or the same + time split up into time units. + Example: - gametime_to_realtime(days=2) -> number of seconds in real life from - now after which 2 in-game days will have passed. + gametime_to_realtime(days=2) -> number of seconds in real life from + now after which 2 in-game days will have passed. + """ realtime = (secs + mins * MIN + hrs * HOUR + days * DAY + weeks * WEEK + \ months * MONTH + yrs * YEAR) / TIMEFACTOR @@ -109,12 +149,21 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, weeks=0, months=0, yrs=0, format=False): """ This method calculates how much in-game time a real-world time - interval would correspond to. This is usually a lot less interesting - than the other way around. + interval would correspond to. This is usually a lot less + interesting than the other way around. + + Kwargs: + times (int): The various components of the time. + format (bool): Formatting the output. + + Returns: + time (float or tuple): The gametime difference or the same + time split up into time units. Example: realtime_to_gametime(days=2) -> number of game-world seconds corresponding to 2 real days. + """ gametime = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 + weeks * 604800 + months * 2628000 + yrs * 31536000) diff --git a/evennia/utils/idmapper/manager.py b/evennia/utils/idmapper/manager.py index dec3deb63e..105ceea08c 100644 --- a/evennia/utils/idmapper/manager.py +++ b/evennia/utils/idmapper/manager.py @@ -13,6 +13,9 @@ class SharedMemoryManager(Manager): # still use the singleton cache, but the active model isn't required # to be a SharedMemoryModel. def get(self, **kwargs): + """ + Data entity lookup. + """ items = kwargs.keys() inst = None if len(items) == 1: diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index 463895b861..d5da3c2e2e 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -17,7 +17,7 @@ from django.db.models.signals import post_save from django.db.models.base import Model, ModelBase from django.db.models.signals import pre_delete, post_syncdb from evennia.utils import logger -from evennia.utils.utils import dbref, get_evennia_pids, to_str,calledby +from evennia.utils.utils import dbref, get_evennia_pids, to_str from manager import SharedMemoryManager @@ -53,6 +53,7 @@ class SharedMemoryModelBase(ModelBase): or try to retrieve one from the class-wide cache by inferring the pk value from `args` and `kwargs`. If instance caching is enabled for this class, the cache is populated whenever possible (ie when it is possible to infer the pk value). + """ def new_instance(): return super(SharedMemoryModelBase, cls).__call__(*args, **kwargs) @@ -75,6 +76,7 @@ class SharedMemoryModelBase(ModelBase): """ Prepare the cache, making sure that proxies of the same db base share the same cache. + """ # the dbmodel is either the proxy base or ourselves dbmodel = cls._meta.proxy_for_model if cls._meta.proxy else cls @@ -89,15 +91,17 @@ class SharedMemoryModelBase(ModelBase): """ Field shortcut creation: - Takes field names `db_*` and creates property wrappers named without the - `db_` prefix. So db_key -> key + Takes field names `db_*` and creates property wrappers named + without the `db_` prefix. So db_key -> key - This wrapper happens on the class level, so there is no overhead when creating objects. - If a class already has a wrapper of the given name, the automatic creation is skipped. + This wrapper happens on the class level, so there is no + overhead when creating objects. If a class already has a + wrapper of the given name, the automatic creation is skipped. Notes: - Remember to document this auto-wrapping in the class header, this could seem very - much like magic to the user otherwise. + Remember to document this auto-wrapping in the class + header, this could seem very much like magic to the user + otherwise. """ attrs["typename"] = cls.__name__ @@ -218,8 +222,10 @@ class SharedMemoryModel(Model): @classmethod def _get_cache_key(cls, args, kwargs): """ - This method is used by the caching subsystem to infer the PK value from the constructor arguments. - It is used to decide if an instance has to be built or is already in the cache. + This method is used by the caching subsystem to infer the PK + value from the constructor arguments. It is used to decide if + an instance has to be built or is already in the cache. + """ result = None # Quick hack for my composites work for now. @@ -248,9 +254,11 @@ class SharedMemoryModel(Model): @classmethod def get_cached_instance(cls, id): """ - Method to retrieve a cached instance by pk value. Returns None when not found - (which will always be the case when caching is disabled for this class). Please - note that the lookup will be done even when instance caching is disabled. + Method to retrieve a cached instance by pk value. Returns None + when not found (which will always be the case when caching is + disabled for this class). Please note that the lookup will be + done even when instance caching is disabled. + """ return cls.__dbclass__.__instance_cache__.get(id) @@ -261,9 +269,9 @@ class SharedMemoryModel(Model): Args: instance (Class instance): the instance to cache. - new (bool, optional): this is the first time this - instance is cached (i.e. this is not an update - operation like after a db save). + new (bool, optional): this is the first time this instance is + cached (i.e. this is not an update operation like after a + db save). """ pk = instance._get_pk_val() @@ -281,6 +289,7 @@ class SharedMemoryModel(Model): def get_all_cached_instances(cls): """ Return the objects so far cached by idmapper for this class. + """ return cls.__dbclass__.__instance_cache__.values() @@ -288,6 +297,7 @@ class SharedMemoryModel(Model): def _flush_cached_by_key(cls, key, force=True): """ Remove the cached reference. + """ try: if force or not cls._idmapper_recache_protection: @@ -312,6 +322,7 @@ class SharedMemoryModel(Model): """ This will clean safe objects from the cache. Use `force` keyword to remove all objects, safe or not. + """ if force: cls.__dbclass__.__instance_cache__ = {} @@ -326,6 +337,7 @@ class SharedMemoryModel(Model): """ Flush this instance from the instance cache. Use `force` to override recache_protection for the object. + """ pk = self._get_pk_val() if pk and (force or not self._idmapper_recache_protection): @@ -334,12 +346,14 @@ class SharedMemoryModel(Model): def set_recache_protection(self, mode=True): """ Set if this instance should be allowed to be recached. + """ self._idmapper_recache_protection = bool(mode) def delete(self, *args, **kwargs): """ Delete the object, clearing cache. + """ self.flush_from_cache() self._is_deleted = True @@ -349,12 +363,11 @@ class SharedMemoryModel(Model): """ Central database save operation. - Arguments as per Django documentation - - Calls: - self.at__postsave(new) - # this is a wrapper set by oobhandler: - self._oob_at__postsave() + Notes: + Arguments as per Django documentation. + Calls `self.at__postsave(new)` + (this is a wrapper set by oobhandler: + self._oob_at__postsave()) """ @@ -400,6 +413,7 @@ class SharedMemoryModel(Model): class WeakSharedMemoryModelBase(SharedMemoryModelBase): """ Uses a WeakValue dictionary for caching instead of a regular one. + """ def _prepare(cls): super(WeakSharedMemoryModelBase, cls)._prepare() @@ -410,6 +424,7 @@ class WeakSharedMemoryModelBase(SharedMemoryModelBase): class WeakSharedMemoryModel(SharedMemoryModel): """ Uses a WeakValue dictionary for caching instead of a regular one + """ __metaclass__ = WeakSharedMemoryModelBase class Meta: @@ -424,6 +439,7 @@ def flush_cache(**kwargs): is `True`. Uses a signal so we make sure to catch cascades. + """ def class_hierarchy(clslist): """Recursively yield a class hierarchy""" @@ -448,6 +464,7 @@ post_syncdb.connect(flush_cache) def flush_cached_instance(sender, instance, **kwargs): """ Flush the idmapper cache only for a given instance. + """ # XXX: Is this the best way to make sure we can flush? if not hasattr(instance, 'flush_cached_instance'): @@ -459,6 +476,7 @@ pre_delete.connect(flush_cached_instance) def update_cached_instance(sender, instance, **kwargs): """ Re-cache the given instance in the idmapper cache. + """ if not hasattr(instance, 'cache_instance'): return @@ -481,6 +499,7 @@ def conditional_flush(max_rmem, force=False): cache is flushed. force (bool, optional): forces a flush, regardless of timeout. Defaults to `False`. + """ global LAST_FLUSH @@ -546,6 +565,7 @@ def cache_size(mb=True): Returns: total_num, {objclass:total_num, ...} + """ numtotal = [0] # use mutable to keep reference through recursion classdict = {} diff --git a/evennia/utils/inlinefunc.py b/evennia/utils/inlinefunc.py index 78abfbbed9..6ca8c392f3 100644 --- a/evennia/utils/inlinefunc.py +++ b/evennia/utils/inlinefunc.py @@ -1,8 +1,8 @@ """ Inlinefunc -This is a simple inline text language for use to custom-format text -in Evennia. It is applied BEFORE ANSI/MUX parsing is applied. +This is a simple inline text language for use to custom-format text in +Evennia. It is applied BEFORE ANSI/MUX parsing is applied. To activate Inlinefunc, settings.INLINEFUNC_ENABLED must be set. @@ -52,7 +52,10 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH # inline functions def pad(text, *args, **kwargs): - "Pad to width. pad(text, width=78, align='c', fillchar=' ')" + """ + Pad to width. pad(text, width=78, align='c', fillchar=' ') + + """ width = _DEFAULT_WIDTH align = 'c' fillchar = ' ' @@ -67,8 +70,12 @@ def pad(text, *args, **kwargs): break return utils.pad(text, width=width, align=align, fillchar=fillchar) + def crop(text, *args, **kwargs): - "Crop to width. crop(text, width=78, suffix='[...]')" + """ + Crop to width. crop(text, width=78, suffix='[...]') + + """ width = _DEFAULT_WIDTH suffix = "[...]" for iarg, arg in enumerate(args): @@ -80,8 +87,12 @@ def crop(text, *args, **kwargs): break return utils.crop(text, width=width, suffix=suffix) + def wrap(text, *args, **kwargs): - "Wrap/Fill text to width. fill(text, width=78, indent=0)" + """ + Wrap/Fill text to width. fill(text, width=78, indent=0) + + """ width = _DEFAULT_WIDTH indent = 0 for iarg, arg in enumerate(args): @@ -91,16 +102,24 @@ def wrap(text, *args, **kwargs): indent = int(arg) if arg.isdigit() else indent return utils.wrap(text, width=width, indent=indent) + def time(text, *args, **kwargs): - "Inserts current time" + """ + Inserts current time. + + """ import time strformat = "%h %d, %H:%M" if args and args[0]: strformat = str(args[0]) return time.strftime(strformat) + def you(text, *args, **kwargs): - "Inserts your name" + """ + Inserts your name. + + """ name = "You" sess = kwargs.get("session") if sess and sess.puppet: @@ -139,23 +158,40 @@ def _execute_inline_function(funcname, text, session): Get the enclosed text between {funcname(...) and {/funcname and execute the inline function to replace the whole block with the result. - Note that this lookup is "dumb" - we just grab the first end - tag we find. So to work correctly this function must be called - "inside out" on a nested function tree, so each call only works - on a "flat" tag. + + Args: + funcname (str): Inlinefunction identifier. + text (str): Text to process. + session (Session): Session object. + + Notes: + This lookup is "dumb" - we just grab the first end tag we find. So + to work correctly this function must be called "inside out" on a + nested function tree, so each call only works on a "flat" tag. + """ def subfunc(match): - "replace the entire block with the result of the function call" + """ + replace the entire block with the result of the function call + + """ args = [part.strip() for part in match.group(1).split(",")] intext = match.group(2) kwargs = {"session":session} return _INLINE_FUNCS[funcname][0](intext, *args, **kwargs) return _INLINE_FUNCS[funcname][1].sub(subfunc, text) + def _execute_inline_single_function(funcname, text, session): """ Get the arguments of a single function call (no matching end tag) and execute it with an empty text input. + + Args: + funcname (str): Function identifier. + text (str): String to process. + session (Session): Session id. + """ def subfunc(match): "replace the single call with the result of the function call" @@ -164,12 +200,20 @@ def _execute_inline_single_function(funcname, text, session): return _INLINE_FUNCS[funcname][0]("", *args, **kwargs) return _INLINE_FUNCS[funcname][2].sub(subfunc, text) + def parse_inlinefunc(text, strip=False, session=None): """ Parse inline function-replacement. - strip - remove all supported inlinefuncs from text - session - session calling for the parsing + Args: + text (str): Text to parse. + strip (bool, optional): Remove all supported inlinefuncs from text. + session (bool): Session calling for the parsing. + + Returns: + text (str): Parsed text with processed results of + inlinefuncs. + """ if strip: @@ -204,6 +248,7 @@ def parse_inlinefunc(text, strip=False, session=None): return "".join(outstack) + def _test(): # this should all be handled s = "This is a text with a{pad(78,c,-)text {pad(5)of{/pad {pad(30)nice{/pad size{/pad inside {pad(4,l)it{/pad." diff --git a/evennia/utils/logger.py b/evennia/utils/logger.py index 6d43cecc5a..e481c790bf 100644 --- a/evennia/utils/logger.py +++ b/evennia/utils/logger.py @@ -24,9 +24,13 @@ _TIMEZONE = None def log_trace(errmsg=None): """ - Log a traceback to the log. This should be called - from within an exception. errmsg is optional and - adds an extra line with added info. + Log a traceback to the log. This should be called from within an + exception. + + Args: + errmsg (str, optional): Adds an extra line with added info + at the end of the traceback in the log. + """ tracestring = format_exc() try: @@ -49,7 +53,9 @@ def log_err(errmsg): """ Prints/logs an error message to the server log. - errormsg: (string) The message to be logged. + Args: + errormsg (str): The message to be logged. + """ try: errmsg = str(errmsg) @@ -65,7 +71,9 @@ def log_warn(warnmsg): """ Prints/logs any warnings that aren't critical but should be noted. - warnmsg: (string) The message to be logged. + Args: + warnmsg (str): The message to be logged. + """ try: warnmsg = str(warnmsg) @@ -94,7 +102,10 @@ log_infomsg = log_info def log_dep(depmsg): """ - Prints a deprecation message + Prints a deprecation message. + + Args: + depmsg (str): The deprecation message to log. """ try: depmsg = str(depmsg) @@ -111,9 +122,13 @@ LOG_FILE_HANDLES = {} # holds open log handles def log_file(msg, filename="game.log"): """ - Arbitrary file logger using threads. Filename defaults to - 'game.log'. All logs will appear in the logs directory and log - entries will start on new lines following datetime info. + Arbitrary file logger using threads. + + Args: + filename (str, optional): Defaults to 'game.log'. All logs + will appear in the logs directory and log entries will start + on new lines following datetime info. + """ global LOG_FILE_HANDLES, _LOGDIR, _TIMEZONE @@ -131,6 +146,7 @@ def log_file(msg, filename="game.log"): # manually or log file won't be written to until the # write buffer is full. filehandle.flush() + def errback(failure): "Catching errors to normal log" log_trace() diff --git a/evennia/utils/spawner.py b/evennia/utils/spawner.py index 2921c7e367..f85e3c3cda 100644 --- a/evennia/utils/spawner.py +++ b/evennia/utils/spawner.py @@ -92,7 +92,10 @@ _handle_dbref = lambda inp: dbid_to_obj(inp, ObjectDB) def _validate_prototype(key, prototype, protparents, visited): - "Run validation on a prototype, checking for inifinite regress" + """ + Run validation on a prototype, checking for inifinite regress. + + """ assert isinstance(prototype, dict) if id(prototype) in visited: raise RuntimeError("%s has infinite nesting of prototypes." % key or prototype) @@ -110,9 +113,10 @@ def _validate_prototype(key, prototype, protparents, visited): def _get_prototype(dic, prot, protparents): """ - Recursively traverse a prototype dictionary, - including multiple inheritance. Use _validate_prototype - before this, we don't check for infinite recursion here. + Recursively traverse a prototype dictionary, including multiple + inheritance. Use _validate_prototype before this, we don't check + for infinite recursion here. + """ if "prototype" in dic: # move backwards through the inheritance @@ -130,10 +134,11 @@ def _batch_create_object(*objparams): optimized for speed. It does NOT check and convert various input so make sure the spawned Typeclass works before using this! - Input: - objsparams - each argument should be a tuple of arguments for the respective - creation/add handlers in the following order: - (create, permissions, locks, aliases, nattributes, attributes) + Args: + objsparams (any): Aach argument should be a tuple of arguments + for the respective creation/add handlers in the following + order: (create, permissions, locks, aliases, nattributes, + attributes) Returns: objects (list): A list of created objects @@ -168,16 +173,17 @@ def spawn(*prototypes, **kwargs): Spawn a number of prototyped objects. Each argument should be a prototype dictionary. - keyword args: - prototype_modules - a python-path to a - prototype module, or a list of such paths. These will be used - to build the global protparents dictionary accessible by the - input prototypes. If not given, it will instead look for modules + Kwargs: + prototype_modules (str or list): A python-path to a prototype + module, or a list of such paths. These will be used to build + the global protparents dictionary accessible by the input + prototypes. If not given, it will instead look for modules defined by settings.PROTOTYPE_MODULES. - prototype_parents - a dictionary holding a custom prototype-parent dictionary. Will - overload same-named prototypes from prototype_modules. - return_prototypes - only return a list of the prototype-parents - (no object creation happens) + prototype_parents (dict): A dictionary holding a custom + prototype-parent dictionary. Will overload same-named + prototypes from prototype_modules. + return_prototypes (bool): Only return a list of the + prototype-parents (no object creation happens) """ protparents = {} diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index 8427a3341c..8797e73ff0 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -82,8 +82,16 @@ class TextToHTMLparser(object): def re_color(self, text): """ - Replace ansi colors with html color class names. - Let the client choose how it will display colors, if it wishes to. """ + Replace ansi colors with html color class names. Let the + client choose how it will display colors, if it wishes to. + + Args: + text (str): the string with color to replace. + + Returns: + text (str): Re-colored text. + + """ for colorname, regex in self.re_fgs: text = regex.sub(r'''\1''' % colorname, text) for bgname, regex in self.re_bgs: @@ -91,19 +99,56 @@ class TextToHTMLparser(object): return self.re_normal.sub("", text) def re_bold(self, text): - "Clean out superfluous hilights rather than set to make it match the look of telnet." + """ + Clean out superfluous hilights rather than set to make + it match the look of telnet. + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ return self.re_hilite.sub(r'\1', text) def re_underline(self, text): - "Replace ansi underline with html underline class name." + """ + Replace ansi underline with html underline class name. + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ return self.re_uline.sub(r'\1', text) def remove_bells(self, text): - "Remove ansi specials" + """ + Remove ansi specials + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ return text.replace('\07', '') def remove_backspaces(self, text): - "Removes special escape sequences" + """ + Removes special escape sequences + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ backspace_or_eol = r'(.\010)|(\033\[K)' n = 1 while n > 0: @@ -111,11 +156,29 @@ class TextToHTMLparser(object): return text def convert_linebreaks(self, text): - "Extra method for cleaning linebreaks" + """ + Extra method for cleaning linebreaks + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ return text.replace(r'\n', r'
') def convert_urls(self, text): - "Replace urls (http://...) by valid HTML" + """ + Replace urls (http://...) by valid HTML. + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + + """ regexp = r"((ftp|www|http)(\W+\S+[^).,:;?\]\}(\) \r\n$\"\']+))" # -> added target to output prevent the web browser from attempting to # change pages (and losing our webclient session). @@ -123,14 +186,31 @@ class TextToHTMLparser(object): def convert_links(self, text): """ - Replaces links with HTML code + Replaces links with HTML code. + + Args: + text (str): Text to process. + + Returns: + text (str): Processed text. + """ html = "\\2" repl = self.re_link.sub(html, text) return repl - def do_sub(self, m): - "Helper method to be passed to re.sub." + def do_sub(self, match): + """ + Helper method to be passed to re.sub, + for handling all substitutions. + + Args: + match (re.Matchobject): Match for substitution. + + Returns: + text (str): Processed text. + + """ c = m.groupdict() if c['htmlchars']: return cgi.escape(c['htmlchars']) @@ -145,8 +225,15 @@ class TextToHTMLparser(object): def parse(self, text, strip_ansi=False): """ - Main access function, converts a text containing - ANSI codes into html statements. + Main access function, converts a text containing ANSI codes + into html statements. + + Args: + text (str): Text to process. + strip_ansi (bool, optional): + + Returns: + text (str): Parsed text. """ # parse everything to ansi first text = parse_ansi(text, strip_ansi=strip_ansi, xterm256=False, mxp=True) diff --git a/evennia/utils/txws.py b/evennia/utils/txws.py index 03aed43e64..4a565eac4f 100644 --- a/evennia/utils/txws.py +++ b/evennia/utils/txws.py @@ -228,7 +228,7 @@ def make_hybi07_frame_dwim(buf): """ Make a HyBi-07 frame with binary or text data according to the type of buf. """ - + # TODO: eliminate magic numbers. if isinstance(buf, str): return make_hybi07_frame(buf, opcode=0x2) @@ -349,7 +349,7 @@ class WebSocketProtocol(ProtocolWrapper): def setBinaryMode(self, mode): """ If True, send str as binary and unicode as text. - + Defaults to false for backwards compatibility. """ self.do_binary_frames = bool(mode) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 3009936013..2b93f498c0 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -40,22 +40,34 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH def is_iter(iterable): """ - Checks if an object behaves iterably. However, - strings are not accepted as iterable (although - they are actually iterable), since string iterations - are usually not what we want to do with a string. - """ - # use a try..except here to avoid a property - # lookup when using this from a typeclassed entity - try: - _GA(iterable, '__iter__') - return True - except AttributeError: - return False + Checks if an object behaves iterably. + Args: + iterable (any): Entity to check for iterability. + + Returns: + is_iterable (bool): If `iterable` is iterable or not. + + Notes: + Strings are *not* accepted as iterable (although they are + actually iterable), since string iterations are usually not + what we want to do with a string. + + """ + return hasattr(iterable, '__iter__') def make_iter(obj): - "Makes sure that the object is always iterable." + """ + Makes sure that the object is always iterable. + + Args: + obj (any): Object to make iterable. + + Returns: + iterable (list or iterable): The same object + passed-through or made iterable. + + """ return not hasattr(obj, '__iter__') and [obj] or obj @@ -63,10 +75,15 @@ def wrap(text, width=_DEFAULT_WIDTH, indent=0): """ Safely wrap text to a certain number of characters. - text: (str) The text to wrap. - width: (int) The number of characters to wrap to. - indent: (int) How much to indent new lines (the first line - will not be indented) + Args: + text (str): The text to wrap. + width (int, optional): The number of characters to wrap to. + indent (int): How much to indent new lines (the first line + will not be indented) + + Returns: + text (str): Properly wrapped text. + """ if not text: return "" @@ -76,10 +93,21 @@ def wrap(text, width=_DEFAULT_WIDTH, indent=0): # alias - fill fill = wrap + def pad(text, width=_DEFAULT_WIDTH, align="c", fillchar=" "): """ - Pads to a given width, align is one of c,l,r - and fillchar defaults to the space character. + Pads to a given width. + + Args: + text (str): Text to pad. + width (int, optional): The width to pad to, in characters. + align (str, optional): This is one of 'c', 'l' or 'r' (center, + left or right). + fillchar (str, optional): The character to fill with. + + Returns: + text (str): The padded text. + """ align = align if align in ('c', 'l', 'r') else 'c' fillchar = fillchar[0] if fillchar else " " @@ -90,12 +118,24 @@ def pad(text, width=_DEFAULT_WIDTH, align="c", fillchar=" "): else: return text.center(width, fillchar) + def crop(text, width=_DEFAULT_WIDTH, suffix="[...]"): """ - Crop text to a certain width, adding `suffix` to show that the line - continues. Cropping will be done so that the suffix will also fit - within the given width. If width is too small to fit both crop - and suffix, crop without the suffix. + Crop text to a certain width, throwing away text from too-long + lines. + + Args: + text (str): Text to crop. + width (int, optional): Width of line to crop, in characters. + suffix (str, optional): This is appended to the end of cropped + lines to show that the line actually continues. Cropping + will be done so that the suffix will also fit within the + given width. If width is too small to fit both crop and + suffix, the suffix will be dropped. + + Returns: + text (str): The cropped text. + """ utext = to_unicode(text) @@ -110,11 +150,19 @@ def crop(text, width=_DEFAULT_WIDTH, suffix="[...]"): def dedent(text): """ - Safely clean all whitespace at the left - of a paragraph. This is useful for preserving - triple-quoted string indentation while still - shifting it all to be next to the left edge of - the display. + Safely clean all whitespace at the left of a paragraph. + + Args: + text (str): The text to dedent. + + Returns: + text (str): Dedented string. + + Notes: + This is useful for preserving triple-quoted string indentation + while still shifting it all to be next to the left edge of the + display. + """ if not text: return "" @@ -123,19 +171,31 @@ def dedent(text): def list_to_string(inlist, endsep="and", addquote=False): """ - This pretty-formats a list as string output, adding - an optional alternative separator to the second to last entry. - If `addquote` is `True`, the outgoing strings will be surrounded by quotes. + This pretty-formats a list as string output, adding an optional + alternative separator to the second to last entry. If `addquote` + is `True`, the outgoing strings will be surrounded by quotes. + + Args: + inlist (list): The list to print. + endsep (str, optional): If set, the last item separator will + be replaced with this value. + addquote (bool, optional): This will surround all outgoing + values with double quotes. + + Returns: + liststr (str): The list represented as a string. Examples: - ``` - no endsep: - [1,2,3] -> '1, 2, 3' - with endsep=='and': - [1,2,3] -> '1, 2 and 3' - with addquote and endsep - [1,2,3] -> '"1", "2" and "3"' - ``` + + ```python + # no endsep: + [1,2,3] -> '1, 2, 3' + # with endsep=='and': + [1,2,3] -> '1, 2 and 3' + # with addquote and endsep + [1,2,3] -> '"1", "2" and "3"' + ``` + """ if not endsep: endsep = "," @@ -155,10 +215,17 @@ def list_to_string(inlist, endsep="and", addquote=False): def wildcard_to_regexp(instring): """ - Converts a player-supplied string that may have wildcards in it to regular - expressions. This is useful for name matching. + Converts a player-supplied string that may have wildcards in it to + regular expressions. This is useful for name matching. + + Args: + instring (string): A string that may potentially contain + wildcards (`*` or `?`). + + Returns: + regex (str): A string where wildcards were replaced with + regular expressions. - instring: (string) A string that may potentially contain wildcards (`*` or `?`). """ regexp_string = "" @@ -182,10 +249,13 @@ def time_format(seconds, style=0): """ Function to return a 'prettified' version of a value in seconds. - Style 0: 1d 08:30 - Style 1: 1d - Style 2: 1 day, 8 hours, 30 minutes - Style 3: 1 day, 8 hours, 30 minutes, 10 seconds + Args: + seconds (int): Number if seconds to format. + style (int): One of the following styles: + 0. "1d 08:30" + 1. "1d" + 2. "1 day, 8 hours, 30 minutes" + 3. "1 day, 8 hours, 30 minutes, 10 seconds" """ if seconds < 0: seconds = 0 @@ -208,8 +278,8 @@ def time_format(seconds, style=0): retval = '%id %02i:%02i' % (days, hours, minutes,) else: retval = '%02i:%02i' % (hours, minutes,) - return retval + elif style is 1: """ Simple, abbreviated form that only shows the highest time amount. @@ -275,8 +345,15 @@ def time_format(seconds, style=0): def datetime_format(dtobj): """ - Takes a datetime object instance (e.g. from Django's `DateTimeField`) - and returns a string describing how long ago that date was. + Pretty-prints the time since a given time. + + Args: + dtobj (datetime): An datetime object, e.g. from Django's + `DateTimeField`. + + Returns: + deltatime (str): A string describing how long ago `dtobj` + took place. """ @@ -302,16 +379,26 @@ def datetime_format(dtobj): def host_os_is(osname): """ Check to see if the host OS matches the query. - Common osnames are - posix - nt + + Args: + osname (str): Common names are "posix" (linux/unix/mac) and + "nt" (windows). + + Args: + is_os (bool): If the os matches or not. + """ - if os.name == osname: - return True - return False + return os.name == osname def get_evennia_version(): + """ + Helper method for getting the current evennia version. + + Returns: + version (str): The version string. + + """ import evennia return evennia.__version__ @@ -345,11 +432,17 @@ def pypath_to_realpath(python_path, file_ending='.py'): def dbref(dbref, reqhash=True): """ - Converts/checks if input is a valid dbref. If `reqhash` is set, - only input strings on the form '#N', where N is an integer is - accepted. Otherwise strings '#N', 'N' and integers N are all - accepted. - Output is the integer part. + Converts/checks if input is a valid dbref. + + Args: + dbref (int or str): A datbase ref on the form N or #N. + reqhash (bool, optional): Require the #N form to accept + input as a valid dbref. + + Returns: + dbref (int or None): The integer part of the dbref or `None` + if input was not a valid dbref. + """ if reqhash: num = (int(dbref.lstrip('#')) if (isinstance(dbref, basestring) and @@ -366,10 +459,22 @@ def dbref(dbref, reqhash=True): def dbid_to_obj(inp, objclass, raise_errors=True): """ - Convert a #dbid to a valid object of `objclass`. `objclass` - should be a valid object class to filter against (`objclass.filter` ...) - If not `raise_errors` is set, this will swallow errors of non-existing - objects. + Convert a #dbid to a valid object. + + Args: + inp (str or int): A valid dbref. + objclass (class): A valid django model to filter against. + raise_errors (bool, optional): Whether to raise errors + or return `None` on errors. + + Returns: + obj (Object or None): An entity loaded from the dbref. + + Raises: + Exception: If `raise_errors` is `True` and + `objclass.objects.get(id=dbref)` did not return a valid + object. + """ dbid = dbref(inp) if not dbid: @@ -389,13 +494,28 @@ def dbid_to_obj(inp, objclass, raise_errors=True): raise return inp + def to_unicode(obj, encoding='utf-8', force_string=False): """ - This decodes a suitable object to the unicode format. Note that - one needs to encode it back to utf-8 before writing to disk or - printing. Note that non-string objects are let through without - conversion - this is important for e.g. Attributes. Use - `force_string` to enforce conversion of objects to string. + This decodes a suitable object to the unicode format. + + Args: + obj (any): Object to decode to unicode. + encoding (str, optional): The encoding type to use for the + dedoding. + force_string (bool, optional): Always convert to string, no + matter what type `obj` is initially. + + Returns: + result (unicode or any): Will return a unicode object if input + was a string. If input was not a string, the original will be + returned unchanged unless `force_string` is also set. + + Notes: + One needs to encode the obj back to utf-8 before writing to disk + or printing. That non-string objects are let through without + conversion is important for e.g. Attributes. + """ if force_string and not isinstance(obj, basestring): @@ -427,10 +547,20 @@ def to_unicode(obj, encoding='utf-8', force_string=False): def to_str(obj, encoding='utf-8', force_string=False): """ This encodes a unicode string back to byte-representation, - for printing, writing to disk etc. Note that non-string - objects are let through without modification - this is - required e.g. for Attributes. Use `force_string` to force - conversion of objects to strings. + for printing, writing to disk etc. + + Args: + obj (any): Object to encode to bytecode. + encoding (str, optional): The encoding type to use for the + encoding. + force_string (bool, optional): Always convert to string, no + matter what type `obj` is initially. + + Notes: + Non-string objects are let through without modification - this + is required e.g. for Attributes. Use `force_string` to force + conversion of objects to strings. + """ if force_string and not isinstance(obj, basestring): @@ -460,8 +590,16 @@ def validate_email_address(emailaddress): """ Checks if an email address is syntactically correct. - (This snippet was adapted from - http://commandline.org.uk/python/email-syntax-check.) + Args: + emailaddress (str): Email address to validate. + + Returns: + is_valid (bool): If this is a valid email or not. + + Notes. + (This snippet was adapted from + http://commandline.org.uk/python/email-syntax-check.) + """ emailaddress = r"%s" % emailaddress @@ -498,11 +636,23 @@ def validate_email_address(emailaddress): def inherits_from(obj, parent): """ - Takes an object and tries to determine if it inherits at any distance - from parent. What differs this function from e.g. `isinstance()` - is that `obj` may be both an instance and a class, and parent - may be an instance, a class, or the python path to a class (counting - from the evennia root directory). + Takes an object and tries to determine if it inherits at *any* + distance from parent. + + Args: + obj (any): Object to analyze. This may be either an instance + or a class. + parent (any): Can be either instance, class or python path to class. + + Returns: + inherits_from (bool): If `parent` is a parent to `obj` or not. + + Notes: + What differs this function from e.g. `isinstance()` is that `obj` + may be both an instance and a class, and parent may be an + instance, a class, or the python path to a class (counting from + the evennia root directory). + """ if callable(obj): @@ -524,9 +674,13 @@ def inherits_from(obj, parent): def server_services(): """ - Lists all services active on the Server. Observe that - since services are launched in memory, this function will - only return any results if called from inside the game. + Lists all services active on the Server. Observe that since + services are launched in memory, this function will only return + any results if called from inside the game. + + Returns: + services (dict): A dict of available services. + """ from evennia.server.sessionhandler import SESSIONS if hasattr(SESSIONS, "server") and hasattr(SESSIONS.server, "services"): @@ -543,7 +697,13 @@ def uses_database(name="sqlite3"): Checks if the game is currently using a given database. This is a shortcut to having to use the full backend name. - name - one of 'sqlite3', 'mysql', 'postgresql_psycopg2' or 'oracle' + Args: + name (str): One of 'sqlite3', 'mysql', 'postgresql_psycopg2' + or 'oracle'. + + Returns: + uses (bool): If the given database is used or not. + """ try: engine = settings.DATABASES["default"]["ENGINE"] @@ -555,17 +715,21 @@ def uses_database(name="sqlite3"): def delay(delay=2, callback=None, retval=None): """ Delay the return of a value. - Inputs: - delay (int) - the delay in seconds - callback (func() or func(retval)) - if given, will be called without - arguments or with `retval` after delay seconds. - retval (any) - this will be returned by this function after a delay, - or as input to callback. + + Args: + delay (int): The delay in seconds + callback (callable, optional): Will be called without arguments + or with `retval` after delay seconds. + retval (any, optional): Whis will be returned by this function + after a delay, or as input to callback. + Returns: - deferred that will fire with callback after `delay` seconds. Note that - if `delay()` is used in the commandhandler callback chain, the callback - chain can be defined directly in the command body and don't need to be - specified here. + deferred (deferred): Will fire fire with callback after + `delay` seconds. Note that if `delay()` is used in the + commandhandler callback chain, the callback chain can be + defined directly in the command body and don't need to be + specified here. + """ callb = callback or defer.Deferred().callback if retval is not None: @@ -579,14 +743,18 @@ _OBJECTMODELS = None def clean_object_caches(obj): """ Clean all object caches on the given object. + + Args: + obj (Object instace): An object whose caches to clean. + + Notes: + This is only the contents cache these days. + """ global _TYPECLASSMODELS, _OBJECTMODELS if not _TYPECLASSMODELS: from evennia.typeclasses import models as _TYPECLASSMODELS - #if not _OBJECTMODELS: - # from evennia.objects import models as _OBJECTMODELS - #print "recaching:", obj if not obj: return # contents cache @@ -612,45 +780,39 @@ def run_async(to_execute, *args, **kwargs): """ Runs a function or executes a code snippet asynchronously. - Inputs: - to_execute (callable) - if this is a callable, it will - be executed with *args and non-reserved *kwargs as - arguments. - The callable will be executed using ProcPool, or in - a thread if ProcPool is not available. + Args: + to_execute (callable): If this is a callable, it will be + executed with *args and non-reserved *kwargs as arguments. + The callable will be executed using ProcPool, or in a thread + if ProcPool is not available. - reserved kwargs: - 'at_return' -should point to a callable with one argument. - It will be called with the return value from - to_execute. - 'at_return_kwargs' - this dictionary which be used as keyword - arguments to the at_return callback. - 'at_err' - this will be called with a Failure instance if - there is an error in to_execute. - 'at_err_kwargs' - this dictionary will be used as keyword - arguments to the at_err errback. + Kwargs: + at_return (callable): Should point to a callable with one + argument. It will be called with the return value from + to_execute. + at_return_kwargs (dict): This dictionary will be used as + keyword arguments to the at_return callback. + at_err (callable): This will be called with a Failure instance + if there is an error in to_execute. + at_err_kwargs (dict): This dictionary will be used as keyword + arguments to the at_err errback. - *args - these args will be used - as arguments for that function. If to_execute is a string - *args are not used. - *kwargs - these kwargs will be used - as keyword arguments in that function. If a string, they - instead are used to define the executable environment - that should be available to execute the code in to_execute. + Notes: + All other `*args` and `**kwargs` will be passed on to + `to_execute`. Run_async will relay executed code to a thread + or procpool. - run_async will relay executed code to a thread or procpool. + Use this function with restrain and only for features/commands + that you know has no influence on the cause-and-effect order of your + game (commands given after the async function might be executed before + it has finished). Accessing the same property from different threads + can lead to unpredicted behaviour if you are not careful (this is called a + "race condition"). - Use this function with restrain and only for features/commands - that you know has no influence on the cause-and-effect order of your - game (commands given after the async function might be executed before - it has finished). Accessing the same property from different threads - can lead to unpredicted behaviour if you are not careful (this is called a - "race condition"). - - Also note that some databases, notably sqlite3, don't support access from - multiple threads simultaneously, so if you do heavy database access from - your `to_execute` under sqlite3 you will probably run very slow or even get - tracebacks. + Also note that some databases, notably sqlite3, don't support access from + multiple threads simultaneously, so if you do heavy database access from + your `to_execute` under sqlite3 you will probably run very slow or even get + tracebacks. """ @@ -676,9 +838,12 @@ def run_async(to_execute, *args, **kwargs): def check_evennia_dependencies(): """ Checks the versions of Evennia's dependencies including making - some checks for runtime libraries + some checks for runtime libraries. + + Returns: + result (bool): `False` if a show-stopping version mismatch is + found. - Returns False if a show-stopping version mismatch is found. """ # check main dependencies @@ -711,6 +876,14 @@ def check_evennia_dependencies(): def has_parent(basepath, obj): """ Checks if `basepath` is somewhere in `obj`s parent tree. + + Args: + basepath (str): Python dotpath to compare against obj path. + obj (any): Object whose path is to be checked. + + Returns: + has_parent (bool): If the check was successful or not. + """ try: return any(cls for cls in obj.__class__.mro() @@ -726,15 +899,15 @@ def mod_import(module): A generic Python module loader. Args: - module - this can be either a Python path (dot-notation like - `evennia.objects.models`), an absolute path - (e.g. `/home/eve/evennia/evennia/objects.models.py`) - or an already imported module object (e.g. `models`) + module (str, module): This can be either a Python path + (dot-notation like `evennia.objects.models`), an absolute path + (e.g. `/home/eve/evennia/evennia/objects.models.py`) or an + already imported module object (e.g. `models`) Returns: - an imported module. If the input argument was already a model, - this is returned as-is, otherwise the path is parsed and imported. - Error: - returns `None`. The error is also logged. + module (module or None): An imported module. If the input argument was + already a module, this is returned as-is, otherwise the path is + parsed and imported. Returns `None` and logs error if import failed. + """ def log_trace(errmsg=None): @@ -800,8 +973,21 @@ def mod_import(module): def all_from_module(module): """ - Return all global-level variables from a module as a dict. - Ignores modules and variable names starting with an underscore. + Return all global-level variables from a module. + + Args: + module (str, module): This can be either a Python path + (dot-notation like `evennia.objects.models`), an absolute path + (e.g. `/home/eve/evennia/evennia/objects.models.py`) or an + already imported module object (e.g. `models`) + + Returns: + variables (dict): A dict of {variablename: variable} for all + variables in the given module. + + Notes: + Ignores modules and variable names starting with an underscore. + """ mod = mod_import(module) if not mod: @@ -812,23 +998,24 @@ def all_from_module(module): def variable_from_module(module, variable=None, default=None): """ - Retrieve a variable or list of variables from a module. The variable(s) - must be defined globally in the module. If no variable is given (or a - list entry is `None`), all global variables are extracted from the module. - - If `module` cannot be imported or a given `variable` not found, `default` - is returned. + Retrieve a variable or list of variables from a module. The + variable(s) must be defined globally in the module. If no variable + is given (or a list entry is `None`), all global variables are + extracted from the module. Args: - module (string or module)- python path, absolute path or a module. - variable (string or iterable) - single variable name or iterable of - variable names to extract. - default (string) - default value to use if a variable fails - to be extracted. Ignored if `variable` is not given. + module (string or module): Python path, absolute path or a module. + variable (string or iterable, optional): Single variable name or iterable + of variable names to extract. If not given, all variables in + the module will be returned. + default (string, optional): Default value to use if a variable fails to + be extracted. Ignored if `variable` is not given. + Returns: - a single value or a list of values depending on the type of - `variable` argument. Errors in lists are replaced by the - `default` argument. + variables (value or list): A single value or a list of values + depending on if `variable` is given or not. Errors in lists + are replaced by the `default` argument. + """ if not module: @@ -856,8 +1043,20 @@ def string_from_module(module, variable=None, default=None): """ This is a wrapper for `variable_from_module` that requires return value to be a string to pass. It's primarily used by login screen. - if `variable` is not set, returns a list of all string variables in - `module`. + + Args: + module (string or module): Python path, absolute path or a module. + variable (string or iterable, optional): Single variable name or iterable + of variable names to extract. If not given, all variables in + the module will be returned. + default (string, optional): Default value to use if a variable fails to + be extracted. Ignored if `variable` is not given. + + Returns: + variables (value or list): A single (string) value or a list of values + depending on if `variable` is given or not. Errors in lists (such + as the value not being a string) are replaced by the `default` argument. + """ val = variable_from_module(module, variable=variable, default=default) if val: @@ -868,23 +1067,38 @@ def string_from_module(module, variable=None, default=None): return result if result else default return default + def random_string_from_module(module): """ Returns a random global string from a module. + + Args: + module (string or module): Python path, absolute path or a module. + + Returns: + random (string): A random stribg variable from `module`. """ return random.choice(string_from_module(module)) + def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None): """ Import a variable based on a fuzzy path. First the literal `path` will be tried, then all given `defaultpaths` will be prepended to see a match is found. - path - full or partial python path. - variable - name of variable to import from module. - defaultpaths - an iterable of python paths to attempt - in order if importing directly from - `path` doesn't work. + Args: + path (str): Full or partial python path. + variable (str): Name of variable to import from module. + default (string, optional): Default value to use if a variable fails to + be extracted. Ignored if `variable` is not given. + defaultpaths (iterable, options): Python paths to attempt in order if + importing directly from `path` doesn't work. + + Returns: + value (any): The variable imported from the module, or `default`, if + not found. + """ paths = [path] + make_iter(defaultpaths) for modpath in paths: @@ -898,13 +1112,23 @@ def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None): return getattr(mod, variable, default) return default + def class_from_module(path, defaultpaths=None): """ Return a class from a module, given the module's path. This is primarily used to convert db_typeclass_path:s to classes. - if a list of `defaultpaths` is given, try subsequent runs by - prepending those paths to the given `path`. + Args: + path (str): Full Python dot-path to module. + defaultpaths (iterable, optional): If a direc import from `path` fails, + try subsequent imports by prepending those paths to `path`. + + Returns: + class (Class): An uninstatiated class recovered from path. + + Raises: + ImportError: If all loading failed. + """ cls = None if defaultpaths: @@ -949,14 +1173,10 @@ object_from_module = class_from_module def init_new_player(player): """ - Helper method to call all hooks, set flags etc on a newly created - player (and potentially their character, if it exists already). + Deprecated. """ - # the FIRST_LOGIN flags are necessary for the system to call - # the relevant first-login hooks. - #if player.character: - # player.character.db.FIRST_LOGIN = True - player.db.FIRST_LOGIN = True + from evennia.utils import logger + logger.log_depmsg("evennia.utils.utils.init_new_player is DEPRECATED and should not be used.") def string_similarity(string1, string2): @@ -967,8 +1187,14 @@ def string_similarity(string1, string2): The measure-vectors used is simply a "bag of words" type histogram (but for letters). - The function returns a value 0...1 rating how similar the two strings - are. The strings can contain multiple words. + Args: + string1 (str): String to compare (may contain any number of words). + string2 (str): Second string to compare (any number of words). + + Returns: + similarity (float): A value 0...1 rating how similar the two + strings are. + """ vocabulary = set(list(string1 + string2)) vec1 = [string1.count(v) for v in vocabulary]