From d23a8a94dbf66fddc56f0e20cfbad21041a49304 Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 29 May 2015 21:08:04 +0200 Subject: [PATCH] Updated scripts/ directory source to use Google style docstrings as per #709. --- evennia/scripts/manager.py | 104 ++++++++++++++++------- evennia/scripts/models.py | 44 +++------- evennia/scripts/scripthandler.py | 105 ++++++++++++++--------- evennia/scripts/tickerhandler.py | 140 ++++++++++++++++++++++++------- 4 files changed, 263 insertions(+), 130 deletions(-) diff --git a/evennia/scripts/manager.py b/evennia/scripts/manager.py index 60844dc5f4..507c74c1bd 100644 --- a/evennia/scripts/manager.py +++ b/evennia/scripts/manager.py @@ -38,9 +38,16 @@ class ScriptDBManager(TypedObjectManager): @returns_typeclass_list def get_all_scripts_on_obj(self, obj, key=None): """ - Returns as result all the Scripts related to a particular object. - key can be given as a dbref or name string. If given, only scripts - matching the key on the object will be returned. + Find all Scripts related to a particular object. + + Args: + obj (Object): Object whose Scripts we are looking for. + key (str, optional): Script identifier - can be given as a + dbref or name string. If given, only scripts matching the + key on the object will be returned. + Returns: + matches (list): Matching scripts. + """ if not obj: return [] @@ -64,8 +71,15 @@ class ScriptDBManager(TypedObjectManager): @returns_typeclass_list def get_all_scripts(self, key=None): """ - Return all scripts, alternative only - scripts with a certain key/dbref + Get all scripts in the database. + + Args: + key (str, optional): Restrict result to only those + with matching key or dbref. + + Returns: + scripts (list): All scripts found, or those matching `key`. + """ if key: script = [] @@ -79,10 +93,16 @@ class ScriptDBManager(TypedObjectManager): def delete_script(self, dbref): """ - 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. + This stops and deletes a specific script directly from the + script database. + + Args: + dbref (int): Database unique id. + + Notes: + This might be needed for global scripts not tied to a + specific game object + """ scripts = self.get_id(dbref) for script in make_iter(scripts): @@ -91,8 +111,12 @@ class ScriptDBManager(TypedObjectManager): def remove_non_persistent(self, obj=None): """ This cleans up the script database of all non-persistent - scripts, or only those on obj. It is called every time the server - restarts. + scripts. It is called every time the server restarts. + + Args: + obj (Object, optional): Only remove non-persistent scripts + assigned to this object. + """ if obj: to_stop = self.filter(db_obj=obj, db_persistent=False, db_is_active=True) @@ -114,26 +138,32 @@ class ScriptDBManager(TypedObjectManager): 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. - 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. + Args: + scripts (list, optional): A list of script objects to + validate. + obj (Object, optional): Validate only scripts defined on + this object. + key (str): Validate only scripts with this key. + dbref (int): Validate only the single script with this + particular id. + init_mode (str, optional): This is used during server + upstart and can have three values: + - `False` (no init mode). Called during run. + - `"reset"` - server reboot. Kill non-persistent scripts + - `"reload"` - server reload. Keep non-persistent scripts. + Returns: + nr_started, nr_stopped (tuple): Statistics on how many objects + where started and stopped. - init_mode - This is used during server upstart and can have - three values: - False (no init mode). Called during run. - "reset" - server reboot. Kill non-persistent scripts - "reload" - server reload. Keep non-persistent scripts. + Notes: + This method also makes sure start any scripts it validates + which should be harmless, since already-active scripts have + the property 'is_running' set and will be skipped. - 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. """ # we store a variable that tracks if we are calling a @@ -194,10 +224,13 @@ class ScriptDBManager(TypedObjectManager): """ 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. + Args: + ostring (str): Search criterion - a script dbef or key. + obj (Object, optional): Limit search to scripts defined on + this object + only_timed (bool): Limit search only to scripts that run + on a timer. + """ ostring = ostring.strip() @@ -218,7 +251,18 @@ class ScriptDBManager(TypedObjectManager): def copy_script(self, original_script, new_key=None, new_obj=None, new_locks=None): """ - Make an identical copy of the original_script + Make an identical copy of the original_script. + + Args: + original_script (Script): The Script to copy. + new_key (str, optional): Rename the copy. + new_obj (Object, optional): Place copy on different Object. + new_locks (str, optional): Give copy different locks from + the original. + + Returns: + script_copy (Script): A new Script instance, copied from + the original. """ typeclass = original_script.typeclass_path new_key = new_key if new_key is not None else original_script.key diff --git a/evennia/scripts/models.py b/evennia/scripts/models.py index e3cbcc9936..b9a79a7c80 100644 --- a/evennia/scripts/models.py +++ b/evennia/scripts/models.py @@ -3,8 +3,8 @@ Scripts are entities that perform some sort of action, either only once or repeatedly. They can be directly linked to a particular Evennia Object or be stand-alonw (in the latter case it is considered a 'global' script). Scripts can indicate both actions related to the -game world as well as pure behind-the-scenes events and -effects. Everything that has a time component in the game (i.e. is not +game world as well as pure behind-the-scenes events and effects. +Everything that has a time component in the game (i.e. is not hard-coded at startup or directly created/controlled by players) is handled by Scripts. @@ -14,14 +14,14 @@ Scripts can also implement at_start and at_end hooks for preparing and cleaning whatever effect they have had on the game object. Common examples of uses of Scripts: -- load the default cmdset to the player object's cmdhandler + +- Load the default cmdset to the player object's cmdhandler when logging in. -- switch to a different state, such as entering a text editor, +- Switch to a different state, such as entering a text editor, start combat or enter a dark room. -- Weather patterns in-game -- merge a new cmdset with the default one for changing which +- Merge a new cmdset with the default one for changing which commands are available at a particular time -- give the player/object a time-limited bonus/effect +- Give the player/object a time-limited bonus/effect """ from django.conf import settings @@ -118,9 +118,10 @@ class ScriptDB(TypedObject): # obj property def __get_obj(self): """ - property wrapper that homogenizes access to either - the db_player or db_obj field, using the same obj - property name + Property wrapper that homogenizes access to either the + db_player or db_obj field, using the same object property + name. + """ obj = _GA(self, "db_player") if not obj: @@ -131,6 +132,7 @@ class ScriptDB(TypedObject): """ Set player or obj to their right database field. If a dbref is given, assume ObjectDB. + """ try: value = _GA(value, "dbobj") @@ -157,25 +159,3 @@ class ScriptDB(TypedObject): _GA(self, "save")(update_fields=[fname]) obj = property(__get_obj, __set_obj) object = property(__get_obj, __set_obj) - - -# def at_typeclass_error(self): -# """ -# If this is called, it means the typeclass has a critical -# error and cannot even be loaded. We don't allow a script -# to be created under those circumstances. Already created, -# permanent scripts are set to already be active so they -# won't get activated now (next reboot the bug might be fixed) -# """ -# # By setting is_active=True, we trick the script not to run "again". -# self.is_active = True -# return super(ScriptDB, self).at_typeclass_error() -# -# delete_iter = 0 -# def delete(self): -# "Delete script" -# if self.delete_iter > 0: -# return -# self.delete_iter += 1 -# _GA(self, "attributes").clear() -# super(ScriptDB, self).delete() diff --git a/evennia/scripts/scripthandler.py b/evennia/scripts/scripthandler.py index 4e3eb78ee1..305eefebf3 100644 --- a/evennia/scripts/scripthandler.py +++ b/evennia/scripts/scripthandler.py @@ -1,8 +1,9 @@ """ -The script handler makes sure to check through all stored scripts -to make sure they are still relevant. -A scripthandler is automatically added to all game objects. You -access it through the property `scripts` on the game object. +The script handler makes sure to check through all stored scripts to +make sure they are still relevant. A scripthandler is automatically +added to all game objects. You access it through the property +`scripts` on the game object. + """ from evennia.scripts.models import ScriptDB @@ -14,20 +15,24 @@ from django.utils.translation import ugettext as _ class ScriptHandler(object): """ Implements the handler. This sits on each game object. + """ def __init__(self, obj): """ Set up internal state. - obj - a reference to the object this handler is attached to. - We retrieve all scripts attached to this object and check - if they are all persistent. If they are not, they are just - cruft left over from a server shutdown. + Args: + obj (Object): A reference to the object this handler is + attached to. + """ self.obj = obj def __str__(self): - "List the scripts tied to this object" + """ + List the scripts tied to this object. + + """ scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj) string = "" for script in scripts: @@ -51,12 +56,14 @@ class ScriptHandler(object): """ Add a script to this object. - scriptclass - either a class object - inheriting from Script, an instantiated script object - or a python path to such a class object. - key - optional identifier for the script (often set in script - definition) - autostart - start the script upon adding it + Args: + scriptclass (Scriptclass, Script or str): Either a class + object inheriting from DefaultScript, an instantiated + script object or a python path to such a class object. + key (str, optional): Identifier for the script (often set + in script definition and listings) + autostart (bool, optional): Start the script upon adding it. + """ if self.obj.__dbclass__.__name__ == "PlayerDB": # we add to a Player, not an Object @@ -71,56 +78,74 @@ class ScriptHandler(object): return False return True - def start(self, scriptid): + def start(self, key): """ - Find an already added script and force-start it + Find scripts and force-start them + + Args: + key (str): The script's key or dbref. + + Returns: + nr_started (int): The number of started scripts found. + """ - scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid) + scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key) num = 0 for script in scripts: num += script.start() return num - def get(self, scriptid): + def get(self, key): """ - Return one or all scripts on this object matching `scriptid`. Will return - a list. - """ - return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid) + Search scripts on this object. - def delete(self, scriptid=None): + Args: + key (str): Search criterion, the script's key or dbref. + + Returns: + scripts (list): The found scripts matching `key`. + + """ + return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key) + + def delete(self, key=None): """ Forcibly delete a script from this object. - scriptid can be a script key or the path to a script (in the - latter case all scripts with this path will be deleted!) - If no scriptid is set, delete all scripts on the object. + Args: + key (str, optional): A script key or the path to a script (in the + latter case all scripts with this path will be deleted!) + If no key is given, delete *all* scripts on the object! """ - delscripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid) + delscripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key) if not delscripts: - delscripts = [script for script in ScriptDB.objects.get_all_scripts_on_obj(self.obj) if script.path == scriptid] + delscripts = [script for script in ScriptDB.objects.get_all_scripts_on_obj(self.obj) if script.path == key] num = 0 for script in delscripts: num += script.stop() return num + # alias to delete + stop = delete - def stop(self, scriptid=None): + def all(self): """ - Alias for delete. scriptid can be a script key or a script path string. - """ - return self.delete(scriptid) + Get all scripts stored in this handler. - def all(self, scriptid=None): """ - Get all scripts stored in the handler, alternatively all matching a key. - """ - return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid) + return ScriptDB.objects.get_all_scripts_on_obj(self.obj) def validate(self, init_mode=False): """ - Runs a validation on this object's scripts only. - This should be called regularly to crank the wheels. + Runs a validation on this object's scripts only. This should + be called regularly to crank the wheels. + + Args: + init_mode (str, optional): - This is used during server + upstart and can have three values: + - `False` (no init mode). Called during run. + - `"reset"` - server reboot. Kill non-persistent scripts + - `"reload"` - server reload. Keep non-persistent scripts. + """ ScriptDB.objects.validate(obj=self.obj, init_mode=init_mode) - diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index fac6120f9f..deb4a895c9 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -88,8 +88,12 @@ class Ticker(object): If overloading, this callback is expected to handle all subscriptions when it is triggered. It should not return anything and should not traceback on poorly designed hooks. - The callback should ideally work under @inlineCallbacks so it can yield - appropriately. + The callback should ideally work under @inlineCallbacks so it + can yield appropriately. + + The _hook_key, which is passed down through the handler via + kwargs is used here to identify which hook method to call. + """ for store_key, (obj, args, kwargs) in self.subscriptions.items(): hook_key = yield kwargs.pop("_hook_key", "at_tick") @@ -111,6 +115,10 @@ class Ticker(object): def __init__(self, interval): """ Set up the ticker + + Args: + interval (int): The stepping interval. + """ self.interval = interval self.subscriptions = {} @@ -119,8 +127,12 @@ class Ticker(object): def validate(self, start_delay=None): """ - Start/stop the task depending on how many - subscribers we have using it. + Start/stop the task depending on how many subscribers we have + using it. + + Args: + start_delay (int): Time to way before starting. + """ subs = self.subscriptions if None in subs.values(): @@ -135,9 +147,19 @@ class Ticker(object): def add(self, store_key, obj, *args, **kwargs): """ - Sign up a subscriber to this ticker. If kwargs contains - a keyword _start_delay, this will be used to delay the start - of the trigger instead of interval. + Sign up a subscriber to this ticker. + Args: + store_key (str): Unique storage hash for this ticker subscription. + obj (Object): Object subscribing to this ticker. + args (any, optional): Arguments to call the hook method with. + + Kwargs: + _start_delay (int): If set, this will be + used to delay the start of the trigger instead of + `interval`. + _hooK_key (str): This carries the name of the hook method + to call. It is passed on as-is from this method. + """ start_delay = kwargs.pop("_start_delay", None) self.subscriptions[store_key] = (obj, args, kwargs) @@ -146,13 +168,18 @@ class Ticker(object): def remove(self, store_key): """ Unsubscribe object from this ticker + + Args: + store_key (str): Unique store key. + """ self.subscriptions.pop(store_key, False) self.validate() def stop(self): """ - Kill the Task, regardless of subscriptions + Kill the Task, regardless of subscriptions. + """ self.subscriptions = {} self.validate() @@ -160,18 +187,37 @@ class Ticker(object): class TickerPool(object): """ - This maintains a pool of Twisted LoopingCall tasks - for calling subscribed objects at given times. + This maintains a pool of + `evennia.scripts.scripts.ExtendedLoopingCall` tasks for calling + subscribed objects at given times. + """ ticker_class = Ticker def __init__(self): - "Initialize the pool" + """ + Initialize the pool. + + """ self.tickers = {} def add(self, store_key, obj, interval, *args, **kwargs): """ - Add new ticker subscriber + Add new ticker subscriber. + + Args: + store_key (str): Unique storage hash. + obj (Object): Object subscribing. + interval (int): How often to call the ticker. + args (any, optional): Arguments to send to the hook method. + + Kwargs: + _start_delay (int): If set, this will be + used to delay the start of the trigger instead of + `interval`. It is passed on as-is from this method. + _hooK_key (str): This carries the name of the hook method + to call. It is passed on as-is from this method. + """ if not interval: log_err(_ERROR_ADD_INTERVAL.format(store_key=store_key, obj=obj, @@ -184,7 +230,16 @@ class TickerPool(object): def remove(self, store_key, interval): """ - Remove subscription from pool + Remove subscription from pool. + + Args: + store_key (str): Unique storage hash. + interval (int): Ticker interval. + + Notes: + A given subscription is uniquely identified both + via its `store_key` and its `interval`. + """ if interval in self.tickers: self.tickers[interval].remove(store_key) @@ -193,7 +248,11 @@ class TickerPool(object): """ Stop all scripts in pool. This is done at server reload since restoring the pool will automatically re-populate the pool. - If interval is given, only stop tickers with that interval. + + Args: + interval (int, optional): Only stop tickers with this + interval. + """ if interval and interval in self.tickers: self.tickers[interval].stop() @@ -207,12 +266,17 @@ class TickerHandler(object): The Tickerhandler maintains a pool of tasks for subscribing objects to various tick rates. The pool maintains creation instructions and and re-applies them at a server restart. + """ ticker_pool_class = TickerPool def __init__(self, save_name="ticker_storage"): """ Initialize handler + + save_name (str, optional): The name of the ServerConfig + instance to store the handler state persistently. + """ self.ticker_storage = {} self.save_name = save_name @@ -220,10 +284,16 @@ class TickerHandler(object): def _store_key(self, obj, interval, idstring=""): """ - Tries to create a store_key for the object. - Returns a tuple (isdb, store_key) where isdb - is a boolean True if obj was a database object, - False otherwise. + Tries to create a store_key for the object. Returns a tuple + (isdb, store_key) where isdb is a boolean True if obj was a + database object, False otherwise. + + Args: + obj (Object): Subscribing object. + interval (int): Ticker interval + idstring (str, optional): Additional separator between + different subscription types. + """ if hasattr(obj, "db_key"): # create a store_key using the database representation @@ -243,9 +313,10 @@ class TickerHandler(object): def save(self): """ Save ticker_storage as a serialized string into a temporary - ServerConf field. Whereas saving is done on the fly, if called by - server when it shuts down, the current timer of each ticker will be - saved so it can start over from that point. + ServerConf field. Whereas saving is done on the fly, if called + by server when it shuts down, the current timer of each ticker + will be saved so it can start over from that point. + """ if self.ticker_storage: start_delays = dict((interval, ticker.task.next_call_time()) @@ -349,10 +420,15 @@ class TickerHandler(object): def clear(self, interval=None): """ - Stop/remove all tickers from handler, or the ones - with a given interval. This is the only supported - way to kill tickers for non-db objects. If interval - is given, only stop tickers with this interval. + Stop/remove all tickers from handler. + + Args: + interval (int): Only stop tickers with this interval. + + Notes: + This is the only supported way to kill tickers related to + non-db objects. + """ self.ticker_pool.stop(interval) if interval: @@ -365,9 +441,17 @@ class TickerHandler(object): def all(self, interval=None): """ - Get the subsciptions for a given interval. If interval - is not given, return a dictionary with lists for every - interval in the tickerhandler. + Get all subscriptions. + + Args: + interval (int): Limit match to tickers with this interval. + + Returns: + tickers (list): If `interval` was given, this is a list of + tickers using that interval. + tickerpool_layout (dict): If `interval` was *not* given, + this is a dict {interval1: [ticker1, ticker2, ...], ...} + """ if interval is None: # return dict of all, ordered by interval