Updated scripts/ directory source to use Google style docstrings as per #709.

This commit is contained in:
Griatch 2015-05-29 21:08:04 +02:00
parent 13042e6d1a
commit d23a8a94db
4 changed files with 263 additions and 130 deletions

View file

@ -38,9 +38,16 @@ class ScriptDBManager(TypedObjectManager):
@returns_typeclass_list @returns_typeclass_list
def get_all_scripts_on_obj(self, obj, key=None): def get_all_scripts_on_obj(self, obj, key=None):
""" """
Returns as result all the Scripts related to a particular object. Find all 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. 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: if not obj:
return [] return []
@ -64,8 +71,15 @@ class ScriptDBManager(TypedObjectManager):
@returns_typeclass_list @returns_typeclass_list
def get_all_scripts(self, key=None): def get_all_scripts(self, key=None):
""" """
Return all scripts, alternative only Get all scripts in the database.
scripts with a certain key/dbref
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: if key:
script = [] script = []
@ -79,10 +93,16 @@ class ScriptDBManager(TypedObjectManager):
def delete_script(self, dbref): def delete_script(self, dbref):
""" """
This stops and deletes a specific script directly This stops and deletes a specific script directly from the
from the script database. This might be script database.
needed for global scripts not tied to
a specific game object. 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) scripts = self.get_id(dbref)
for script in make_iter(scripts): for script in make_iter(scripts):
@ -91,8 +111,12 @@ class ScriptDBManager(TypedObjectManager):
def remove_non_persistent(self, obj=None): def remove_non_persistent(self, obj=None):
""" """
This cleans up the script database of all non-persistent This cleans up the script database of all non-persistent
scripts, or only those on obj. It is called every time the server scripts. It is called every time the server restarts.
restarts.
Args:
obj (Object, optional): Only remove non-persistent scripts
assigned to this object.
""" """
if obj: if obj:
to_stop = self.filter(db_obj=obj, db_persistent=False, db_is_active=True) 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 all objects run scripts that are still valid in the context
they are in. This is called by the game engine at regular they are in. This is called by the game engine at regular
intervals but can also be initiated by player scripts. intervals but can also be initiated by player scripts.
If key and/or obj is given, only update the related
script/object.
Only one of the arguments are supposed to be supplied Only one of the arguments are supposed to be supplied
at a time, since they are exclusive to each other. at a time, since they are exclusive to each other.
scripts = a list of scripts objects obtained somewhere. Args:
obj = validate only scripts defined on a special object. scripts (list, optional): A list of script objects to
key = validate only scripts with a particular key validate.
dbref = validate only the single script with this particular id. 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 Notes:
three values: This method also makes sure start any scripts it validates
False (no init mode). Called during run. which should be harmless, since already-active scripts have
"reset" - server reboot. Kill non-persistent scripts the property 'is_running' set and will be skipped.
"reload" - server reload. Keep non-persistent scripts.
This method also makes sure start any scripts it validates,
this should be harmless, since already-active scripts
have the property 'is_running' set and will be skipped.
""" """
# we store a variable that tracks if we are calling a # we store a variable that tracks if we are calling a
@ -194,10 +224,13 @@ class ScriptDBManager(TypedObjectManager):
""" """
Search for a particular script. Search for a particular script.
ostring - search criterion - a script ID or key Args:
obj - limit search to scripts defined on this object ostring (str): Search criterion - a script dbef or key.
only_timed - limit search only to scripts that run obj (Object, optional): Limit search to scripts defined on
this object
only_timed (bool): Limit search only to scripts that run
on a timer. on a timer.
""" """
ostring = ostring.strip() 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): 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 typeclass = original_script.typeclass_path
new_key = new_key if new_key is not None else original_script.key new_key = new_key if new_key is not None else original_script.key

View file

@ -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 once or repeatedly. They can be directly linked to a particular
Evennia Object or be stand-alonw (in the latter case it is considered Evennia Object or be stand-alonw (in the latter case it is considered
a 'global' script). Scripts can indicate both actions related to the a 'global' script). Scripts can indicate both actions related to the
game world as well as pure behind-the-scenes events and game world as well as pure behind-the-scenes events and effects.
effects. Everything that has a time component in the game (i.e. is not Everything that has a time component in the game (i.e. is not
hard-coded at startup or directly created/controlled by players) is hard-coded at startup or directly created/controlled by players) is
handled by Scripts. 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. cleaning whatever effect they have had on the game object.
Common examples of uses of Scripts: 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. 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. 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 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 from django.conf import settings
@ -118,9 +118,10 @@ class ScriptDB(TypedObject):
# obj property # obj property
def __get_obj(self): def __get_obj(self):
""" """
property wrapper that homogenizes access to either Property wrapper that homogenizes access to either the
the db_player or db_obj field, using the same obj db_player or db_obj field, using the same object property
property name name.
""" """
obj = _GA(self, "db_player") obj = _GA(self, "db_player")
if not obj: if not obj:
@ -131,6 +132,7 @@ class ScriptDB(TypedObject):
""" """
Set player or obj to their right database field. If Set player or obj to their right database field. If
a dbref is given, assume ObjectDB. a dbref is given, assume ObjectDB.
""" """
try: try:
value = _GA(value, "dbobj") value = _GA(value, "dbobj")
@ -157,25 +159,3 @@ class ScriptDB(TypedObject):
_GA(self, "save")(update_fields=[fname]) _GA(self, "save")(update_fields=[fname])
obj = property(__get_obj, __set_obj) obj = property(__get_obj, __set_obj)
object = 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()

View file

@ -1,8 +1,9 @@
""" """
The script handler makes sure to check through all stored scripts The script handler makes sure to check through all stored scripts to
to make sure they are still relevant. make sure they are still relevant. A scripthandler is automatically
A scripthandler is automatically added to all game objects. You added to all game objects. You access it through the property
access it through the property `scripts` on the game object. `scripts` on the game object.
""" """
from evennia.scripts.models import ScriptDB from evennia.scripts.models import ScriptDB
@ -14,20 +15,24 @@ from django.utils.translation import ugettext as _
class ScriptHandler(object): class ScriptHandler(object):
""" """
Implements the handler. This sits on each game object. Implements the handler. This sits on each game object.
""" """
def __init__(self, obj): def __init__(self, obj):
""" """
Set up internal state. 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 Args:
if they are all persistent. If they are not, they are just obj (Object): A reference to the object this handler is
cruft left over from a server shutdown. attached to.
""" """
self.obj = obj self.obj = obj
def __str__(self): 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) scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj)
string = "" string = ""
for script in scripts: for script in scripts:
@ -51,12 +56,14 @@ class ScriptHandler(object):
""" """
Add a script to this object. Add a script to this object.
scriptclass - either a class object Args:
inheriting from Script, an instantiated script object scriptclass (Scriptclass, Script or str): Either a class
or a python path to such a class object. object inheriting from DefaultScript, an instantiated
key - optional identifier for the script (often set in script script object or a python path to such a class object.
definition) key (str, optional): Identifier for the script (often set
autostart - start the script upon adding it in script definition and listings)
autostart (bool, optional): Start the script upon adding it.
""" """
if self.obj.__dbclass__.__name__ == "PlayerDB": if self.obj.__dbclass__.__name__ == "PlayerDB":
# we add to a Player, not an Object # we add to a Player, not an Object
@ -71,56 +78,74 @@ class ScriptHandler(object):
return False return False
return True 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 num = 0
for script in scripts: for script in scripts:
num += script.start() num += script.start()
return num return num
def get(self, scriptid): def get(self, key):
""" """
Return one or all scripts on this object matching `scriptid`. Will return Search scripts on this object.
a list.
"""
return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid)
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. Forcibly delete a script from this object.
scriptid can be a script key or the path to a script (in the 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!) latter case all scripts with this path will be deleted!)
If no scriptid is set, delete all scripts on the object. 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: 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 num = 0
for script in delscripts: for script in delscripts:
num += script.stop() num += script.stop()
return num 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. Get all scripts stored in this handler.
"""
return self.delete(scriptid)
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)
"""
return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid)
def validate(self, init_mode=False): def validate(self, init_mode=False):
""" """
Runs a validation on this object's scripts only. Runs a validation on this object's scripts only. This should
This should be called regularly to crank the wheels. 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) ScriptDB.objects.validate(obj=self.obj, init_mode=init_mode)

View file

@ -88,8 +88,12 @@ class Ticker(object):
If overloading, this callback is expected to handle all If overloading, this callback is expected to handle all
subscriptions when it is triggered. It should not return subscriptions when it is triggered. It should not return
anything and should not traceback on poorly designed hooks. anything and should not traceback on poorly designed hooks.
The callback should ideally work under @inlineCallbacks so it can yield The callback should ideally work under @inlineCallbacks so it
appropriately. 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(): for store_key, (obj, args, kwargs) in self.subscriptions.items():
hook_key = yield kwargs.pop("_hook_key", "at_tick") hook_key = yield kwargs.pop("_hook_key", "at_tick")
@ -111,6 +115,10 @@ class Ticker(object):
def __init__(self, interval): def __init__(self, interval):
""" """
Set up the ticker Set up the ticker
Args:
interval (int): The stepping interval.
""" """
self.interval = interval self.interval = interval
self.subscriptions = {} self.subscriptions = {}
@ -119,8 +127,12 @@ class Ticker(object):
def validate(self, start_delay=None): def validate(self, start_delay=None):
""" """
Start/stop the task depending on how many Start/stop the task depending on how many subscribers we have
subscribers we have using it. using it.
Args:
start_delay (int): Time to way before starting.
""" """
subs = self.subscriptions subs = self.subscriptions
if None in subs.values(): if None in subs.values():
@ -135,9 +147,19 @@ class Ticker(object):
def add(self, store_key, obj, *args, **kwargs): def add(self, store_key, obj, *args, **kwargs):
""" """
Sign up a subscriber to this ticker. If kwargs contains Sign up a subscriber to this ticker.
a keyword _start_delay, this will be used to delay the start Args:
of the trigger instead of interval. 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) start_delay = kwargs.pop("_start_delay", None)
self.subscriptions[store_key] = (obj, args, kwargs) self.subscriptions[store_key] = (obj, args, kwargs)
@ -146,13 +168,18 @@ class Ticker(object):
def remove(self, store_key): def remove(self, store_key):
""" """
Unsubscribe object from this ticker Unsubscribe object from this ticker
Args:
store_key (str): Unique store key.
""" """
self.subscriptions.pop(store_key, False) self.subscriptions.pop(store_key, False)
self.validate() self.validate()
def stop(self): def stop(self):
""" """
Kill the Task, regardless of subscriptions Kill the Task, regardless of subscriptions.
""" """
self.subscriptions = {} self.subscriptions = {}
self.validate() self.validate()
@ -160,18 +187,37 @@ class Ticker(object):
class TickerPool(object): class TickerPool(object):
""" """
This maintains a pool of Twisted LoopingCall tasks This maintains a pool of
for calling subscribed objects at given times. `evennia.scripts.scripts.ExtendedLoopingCall` tasks for calling
subscribed objects at given times.
""" """
ticker_class = Ticker ticker_class = Ticker
def __init__(self): def __init__(self):
"Initialize the pool" """
Initialize the pool.
"""
self.tickers = {} self.tickers = {}
def add(self, store_key, obj, interval, *args, **kwargs): 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: if not interval:
log_err(_ERROR_ADD_INTERVAL.format(store_key=store_key, obj=obj, 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): 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: if interval in self.tickers:
self.tickers[interval].remove(store_key) 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 Stop all scripts in pool. This is done at server reload since
restoring the pool will automatically re-populate the pool. 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: if interval and interval in self.tickers:
self.tickers[interval].stop() self.tickers[interval].stop()
@ -207,12 +266,17 @@ class TickerHandler(object):
The Tickerhandler maintains a pool of tasks for subscribing The Tickerhandler maintains a pool of tasks for subscribing
objects to various tick rates. The pool maintains creation objects to various tick rates. The pool maintains creation
instructions and and re-applies them at a server restart. instructions and and re-applies them at a server restart.
""" """
ticker_pool_class = TickerPool ticker_pool_class = TickerPool
def __init__(self, save_name="ticker_storage"): def __init__(self, save_name="ticker_storage"):
""" """
Initialize handler Initialize handler
save_name (str, optional): The name of the ServerConfig
instance to store the handler state persistently.
""" """
self.ticker_storage = {} self.ticker_storage = {}
self.save_name = save_name self.save_name = save_name
@ -220,10 +284,16 @@ class TickerHandler(object):
def _store_key(self, obj, interval, idstring=""): def _store_key(self, obj, interval, idstring=""):
""" """
Tries to create a store_key for the object. Tries to create a store_key for the object. Returns a tuple
Returns a tuple (isdb, store_key) where isdb (isdb, store_key) where isdb is a boolean True if obj was a
is a boolean True if obj was a database object, database object, False otherwise.
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"): if hasattr(obj, "db_key"):
# create a store_key using the database representation # create a store_key using the database representation
@ -243,9 +313,10 @@ class TickerHandler(object):
def save(self): def save(self):
""" """
Save ticker_storage as a serialized string into a temporary Save ticker_storage as a serialized string into a temporary
ServerConf field. Whereas saving is done on the fly, if called by ServerConf field. Whereas saving is done on the fly, if called
server when it shuts down, the current timer of each ticker will be by server when it shuts down, the current timer of each ticker
saved so it can start over from that point. will be saved so it can start over from that point.
""" """
if self.ticker_storage: if self.ticker_storage:
start_delays = dict((interval, ticker.task.next_call_time()) start_delays = dict((interval, ticker.task.next_call_time())
@ -349,10 +420,15 @@ class TickerHandler(object):
def clear(self, interval=None): def clear(self, interval=None):
""" """
Stop/remove all tickers from handler, or the ones Stop/remove all tickers from handler.
with a given interval. This is the only supported
way to kill tickers for non-db objects. If interval Args:
is given, only stop tickers with this interval. 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) self.ticker_pool.stop(interval)
if interval: if interval:
@ -365,9 +441,17 @@ class TickerHandler(object):
def all(self, interval=None): def all(self, interval=None):
""" """
Get the subsciptions for a given interval. If interval Get all subscriptions.
is not given, return a dictionary with lists for every
interval in the tickerhandler. 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: if interval is None:
# return dict of all, ordered by interval # return dict of all, ordered by interval