diff --git a/docs/source/_static/nature.css b/docs/source/_static/nature.css index 0f23db11c0..e422d24a4f 100644 --- a/docs/source/_static/nature.css +++ b/docs/source/_static/nature.css @@ -258,7 +258,17 @@ li > p:first-child { li > p { margin-top: 0px; margin-bottom: 0px; -} +} + + +/* The indents of kwarg - lists in api docs */ +dl.field-list.simple > dd.field-odd > ul.simple > li > ul { + margin-left: 24px; +} +dd.field-odd > ul.simple > li > ul > li > dl.simple { + margin-bottom: -8px; +} + .admonition.important { background-color: #fbf7c3; diff --git a/docs/source/conf.py b/docs/source/conf.py index 65325d8446..d20441116d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,6 +6,7 @@ import os import sys +import re import sphinx_theme from recommonmark.transform import AutoStructify from sphinx.util.osutil import cd @@ -117,13 +118,13 @@ _github_issue_choose = "https://github.com/evennia/evennia/issues/new/choose" def url_resolver(url): """ - Convert urls by catching special markers. + Convert urls by catching special markers. """ githubstart = "github:" apistart = "api:" choose_issue = "github:issue" sourcestart = "src:" - + if url.endswith(choose_issue): return _github_issue_choose elif githubstart in url: @@ -233,11 +234,39 @@ def autodoc_skip_member(app, what, name, obj, skip, options): return False -def autodoc_clean_docstring(app, what, name, obj, options, lines): - """Clean docstring of ansi. Must modify lines list in-place""" - if ansi_clean: - for il, line in enumerate(lines): - lines[il] = ansi_clean(line) +def autodoc_post_process_docstring(app, what, name, obj, options, lines): + """ + Post-process docstring in various ways. Must modify lines-list in-place. + """ + try: + + # clean out ANSI colors + + if ansi_clean: + for il, line in enumerate(lines): + lines[il] = ansi_clean(line) + + # post-parse docstrings to convert any remaining + # markdown -> reST since napoleon doesn't know Markdown + + def _sub_codeblock(match): + code = match.group(1) + return "::\n\n {}".format( + "\n ".join(lne for lne in code.split("\n"))) + + doc = "\n".join(lines) + doc = re.sub(r"```python\s*\n+(.*?)```", _sub_codeblock, doc, + flags=re.MULTILINE + re.DOTALL) + doc = re.sub(r"```", "", doc, flags=re.MULTILINE) + doc = re.sub(r"`{1}", "**", doc, flags=re.MULTILINE) + newlines = doc.split("\n") + # we must modify lines in-place + lines[:] = newlines[:] + + except Exception as err: + # if we don't print here we won't see what the error actually is + print(f"Post-process docstring exception: {err}") + raise # Napoleon Google-style docstring parser for autodocs @@ -247,7 +276,7 @@ napoleon_numpy_docstring = False napoleon_include_init_with_doc = False napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False -napoleon_use_admonition_for_examples = True +napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = False napoleon_use_admonition_for_references = False napoleon_use_ivar = False @@ -262,7 +291,7 @@ napoleon_use_rtype = False def setup(app): app.connect("autodoc-skip-member", autodoc_skip_member) - app.connect("autodoc-process-docstring", autodoc_clean_docstring) + app.connect("autodoc-process-docstring", autodoc_post_process_docstring) app.add_transform(AutoStructify) # build toctree file diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index 88ecddb883..58796fd0f0 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -76,6 +76,7 @@ class PortalSessionHandler(SessionHandler): Returns: sessid + """ self.latest_sessid += 1 if self.latest_sessid in self: @@ -247,7 +248,7 @@ class PortalSessionHandler(SessionHandler): for the protocol used, eg 'evennia.server.portal.irc.IRCClientFactory' config (dict): Dictionary of configuration options, fed as - **kwarg to protocol class' __init__ method. + `**kwargs` to protocol class' __init__ method. Raises: RuntimeError: If The correct factory class is not found. diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index ccf2840f91..d6aa4c86ed 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -415,9 +415,7 @@ class EvMenu(object): by default in all nodes of the menu. This will print out the current state of the menu. Deactivate for production use! When the debug flag is active, the `persistent` flag is deactivated. - - Kwargs: - any (any): All kwargs will become initialization variables on `caller.ndb._menutree`, + **kwargs: All kwargs will become initialization variables on `caller.ndb._menutree`, to be available at run. Raises: @@ -789,8 +787,7 @@ class EvMenu(object): raw_string (str): The raw default string entered on the previous node (only used if the node accepts it as an argument) - Kwargs: - any: Extra arguments to goto callables. + **kwargs: Extra arguments to goto callables. """ diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index dba466e454..cc4fa1465c 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -356,16 +356,16 @@ def list_to_string(inlist, endsep="and", addquote=False): Returns: liststr (str): The list represented as a string. - Examples: + Example: - ```python + ```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: @@ -839,7 +839,7 @@ def to_bytes(text, session=None): the text with "?" in place of problematic characters. If the specified encoding cannot be found, the protocol flag is reset to utf-8. In any case, returns bytes. - Note: + Notes: If `text` is already bytes, return it as is. """ @@ -879,7 +879,7 @@ def to_str(text, session=None): Returns: decoded_text (str): The decoded text. - Note: + Notes: If `text` is already str, return it as is. """ if isinstance(text, str): @@ -977,18 +977,17 @@ def inherits_from(obj, parent): 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. + obj (any): Object to analyze. This may be either an instance or + a class. + parent (any): Can be either an instance, a class or the python + path to the 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). + What differentiates this function from Python's `isinstance()` is the + flexibility in the types allowed for the object and parent being compared. """ @@ -1036,8 +1035,7 @@ def uses_database(name="sqlite3"): shortcut to having to use the full backend name. Args: - name (str): One of 'sqlite3', 'mysql', 'postgresql' - or 'oracle'. + name (str): One of 'sqlite3', 'mysql', 'postgresql' or 'oracle'. Returns: uses (bool): If the given database is used or not. @@ -1061,20 +1059,19 @@ def delay(timedelay, callback, *args, **kwargs): timedelay (int or float): The delay in seconds callback (callable): Will be called as `callback(*args, **kwargs)` after `timedelay` seconds. - args (any, optional): Will be used as arguments to callback - Kwargs: - persistent (bool, optional): should make the delay persistent - over a reboot or reload - any (any): Will be used as keyword arguments to callback. + *args: Will be used as arguments to callback + Keyword args: + persistent (bool): Make the delay persistent over a reboot or reload. + any: Any other keywords will be use as keyword arguments to callback. Returns: - deferred (deferred): Will fire with callback after - `timedelay` seconds. Note that if `timedelay()` 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: Will fire with callback after `timedelay` seconds. Note that + if `timedelay()` 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. - Note: + Notes: The task handler (`evennia.scripts.taskhandler.TASK_HANDLER`) will be called for persistent or non-persistent tasks. If persistent is set to True, the callback, its arguments @@ -1102,20 +1099,19 @@ def run_async(to_execute, *args, **kwargs): Args: to_execute (callable): If this is a callable, it will be - executed with *args and non-reserved *kwargs as arguments. + 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. - - Kwargs: + Keyword args: at_return (callable): Should point to a callable with one - argument. It will be called with the return value from - to_execute. + 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. + 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. + 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. + arguments to the at_err errback. Notes: All other `*args` and `**kwargs` will be passed on to @@ -1528,8 +1524,8 @@ def init_new_account(account): def string_similarity(string1, string2): """ This implements a "cosine-similarity" algorithm as described for example in - *Proceedings of the 22nd International Conference on Computation - Linguistics* (Coling 2008), pages 593-600, Manchester, August 2008. + *Proceedings of the 22nd International Conference on Computation + Linguistics* (Coling 2008), pages 593-600, Manchester, August 2008. The measure-vectors used is simply a "bag of words" type histogram (but for letters). @@ -1569,8 +1565,8 @@ def string_suggestions(string, vocabulary, cutoff=0.6, maxnum=3): Returns: suggestions (list): Suggestions from `vocabulary` with a - similarity-rating that higher than or equal to `cutoff`. - Could be empty if there are no matches. + similarity-rating that higher than or equal to `cutoff`. + Could be empty if there are no matches. """ return [ @@ -1638,11 +1634,9 @@ def string_partial_matching(alternatives, inp, ret_index=True): def format_table(table, extra_space=1): """ - Note: `evennia.utils.evtable` is more powerful than this, but this - function can be useful when the number of columns and rows are - unknown and must be calculated on the fly. + Format a 2D array of strings into a multi-column table. - Args. + Args: table (list): A list of lists to represent columns in the table: `[[val,val,val,...], [val,val,val,...], ...]`, where each val will be placed on a separate row in the @@ -1652,16 +1646,19 @@ def format_table(table, extra_space=1): padding (in characters) should be left between columns. Returns: - table (list): A list of lists representing the rows to print - out one by one. + list: A list of lists representing the rows to print out one by one. Notes: The function formats the columns to be as wide as the widest member of each column. - Examples: + `evennia.utils.evtable` is more powerful than this, but this + function can be useful when the number of columns and rows are + unknown and must be calculated on the fly. - ```python + Example: + + ```python ftable = format_table([[...], [...], ...]) for ir, row in enumarate(ftable): if ir == 0: @@ -1671,7 +1668,9 @@ def format_table(table, extra_space=1): string += "\n" + "".join(row) print string ``` + """ + if not table: return [[]] @@ -1703,12 +1702,10 @@ def percent(value, minval, maxval, formatting="{:3.1f}%"): current value as a percentage. If None, the raw float will be returned instead. Returns: - str or float: The formatted value or the raw percentage - as a float. + str or float: The formatted value or the raw percentage as a float. Notes: We try to handle a weird interval gracefully. - - If either maxval or minval is None (open interval), - we (aribtrarily) assume 100%. + - If either maxval or minval is None (open interval), we (aribtrarily) assume 100%. - If minval > maxval, we return 0%. - If minval == maxval == value we are looking at a single value match and return 100%. @@ -1759,7 +1756,9 @@ def percentile(iterable, percent, key=lambda x: x): percent (float): A value from 0.0 to 1.0. key (callable, optional). Function to compute value from each element of N. - @return - the percentile of the values + Returns: + float: The percentile of the values + """ if not iterable: return None @@ -1792,9 +1791,9 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None): decorations in the grid, such as horizontal bars. Returns: - gridstr (list): The grid as a list of ready-formatted rows. We return it - like this to make it easier to insert decorations between rows, such - as horizontal bars. + gridstr: The grid as a list of ready-formatted rows. We return it + like this to make it easier to insert decorations between rows, such + as horizontal bars. """ if not verbatim_elements: verbatim_elements = [] @@ -1883,13 +1882,13 @@ def get_evennia_pids(): Examples: This can be used to determine if we are in a subprocess by - something like: ```python self_pid = os.getpid() server_pid, portal_pid = get_evennia_pids() is_subprocess = self_pid not in (server_pid, portal_pid) ``` + """ server_pidfile = os.path.join(settings.GAME_DIR, "server.pid") portal_pidfile = os.path.join(settings.GAME_DIR, "portal.pid") @@ -2078,16 +2077,15 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs): query (str, optional): The search query used to produce `matches`. quiet (bool, optional): If `True`, no messages will be echoed to caller on errors. - - Kwargs: + Keyword args: nofound_string (str): Replacement string to echo on a notfound error. multimatch_string (str): Replacement string to echo on a multimatch error. Returns: processed_result (Object or None): This is always a single result - or `None`. If `None`, any error reporting/handling should - already have happened. The returned object is of the type we are - checking multimatches for (e.g. Objects or Commands) + or `None`. If `None`, any error reporting/handling should + already have happened. The returned object is of the type we are + checking multimatches for (e.g. Objects or Commands) """ @@ -2138,12 +2136,12 @@ class LimitedSizeOrderedDict(OrderedDict): """ Limited-size ordered dict. - Kwargs: + Keyword args: size_limit (int): Use this to limit the number of elements - alloweds to be in this list. By default the overshooting elements - will be removed in FIFO order. + alloweds to be in this list. By default the overshooting elements + will be removed in FIFO order. fifo (bool, optional): Defaults to `True`. Remove overshooting elements - in FIFO order. If `False`, remove in FILO order. + in FIFO order. If `False`, remove in FILO order. """ super().__init__() @@ -2210,10 +2208,10 @@ def get_all_typeclasses(parent=None): from this parent. Returns: - typeclasses (dict): On the form {"typeclass.path": typeclass, ...} + dict: On the form `{"typeclass.path": typeclass, ...}` Notes: - This will dynamicall retrieve all abstract django models inheriting at any distance + This will dynamically retrieve all abstract django models inheriting at any distance from the TypedObject base (aka a Typeclass) so it will work fine with any custom classes being added. @@ -2236,26 +2234,34 @@ def get_all_typeclasses(parent=None): def interactive(func): """ - Decorator to make a method pausable with yield(seconds) - and able to ask for user-input with response=yield(question). - For the question-asking to work, 'caller' must the name - of an argument or kwarg to the decorated function. + Decorator to make a method pausable with `yield(seconds)` + and able to ask for user-input with `response=yield(question)`. + For the question-asking to work, one of the args or kwargs to the + decorated function must be named 'caller'. - Note that this turns the method into a generator. + Raises: + ValueError: If asking an interactive question but the decorated + function has no arg or kwarg named 'caller'. + ValueError: If passing non int/float to yield using for pausing. - Example usage: + Example: - @interactive - def myfunc(caller): - caller.msg("This is a test") - # wait five seconds - yield(5) - # ask user (caller) a question - response = yield("Do you want to continue waiting?") - if response == "yes": + ```python + @interactive + def myfunc(caller): + caller.msg("This is a test") + # wait five seconds yield(5) - else: - # ... + # ask user (caller) a question + response = yield("Do you want to continue waiting?") + if response == "yes": + yield(5) + else: + # ... + ``` + + Notes: + This turns the decorated function or method into a generator. """ from evennia.utils.evmenu import get_input