evennia/docs/1.0-dev/_modules/evennia/help/utils.html
2021-05-29 13:55:45 +02:00

336 lines
No EOL
28 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.help.utils &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.help.utils</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.help.utils</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">Resources for indexing help entries and for splitting help entries into</span>
<span class="sd">sub-categories.</span>
<span class="sd">This is used primarily by the default `help` command.</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="c1"># these are words that Lunr normally ignores but which we want to find</span>
<span class="c1"># since we use them (e.g. as command names).</span>
<span class="c1"># Lunr&#39;s default word list is found here:</span>
<span class="c1"># https://github.com/yeraydiazdiaz/lunr.py/blob/master/lunr/stop_word_filter.py</span>
<span class="n">_LUNR_STOP_WORD_FILTER_EXCEPTIONS</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;about&quot;</span><span class="p">,</span> <span class="s2">&quot;might&quot;</span><span class="p">)</span>
<span class="n">_LUNR</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">_LUNR_EXCEPTION</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">_LUNR_GET_BUILDER</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">_LUNR_BUILDER_PIPELINE</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">_RE_HELP_SUBTOPICS_START</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
<span class="sa">r</span><span class="s2">&quot;^\s*?#\s*?subtopics\s*?$&quot;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">I</span> <span class="o">+</span> <span class="n">re</span><span class="o">.</span><span class="n">M</span><span class="p">)</span>
<span class="n">_RE_HELP_SUBTOPIC_SPLIT</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;^\s*?(\#{2,6}\s*?\w+?[a-z0-9 \-\?!,\.]*?)$&quot;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">M</span> <span class="o">+</span> <span class="n">re</span><span class="o">.</span><span class="n">I</span><span class="p">)</span>
<span class="n">_RE_HELP_SUBTOPIC_PARSE</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
<span class="sa">r</span><span class="s2">&quot;^(?P&lt;nesting&gt;\#{2,6})\s*?(?P&lt;name&gt;.*?)$&quot;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">I</span> <span class="o">+</span> <span class="n">re</span><span class="o">.</span><span class="n">M</span><span class="p">)</span>
<span class="n">MAX_SUBTOPIC_NESTING</span> <span class="o">=</span> <span class="mi">5</span>
<div class="viewcode-block" id="help_search_with_index"><a class="viewcode-back" href="../../../api/evennia.help.utils.html#evennia.help.utils.help_search_with_index">[docs]</a><span class="k">def</span> <span class="nf">help_search_with_index</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">candidate_entries</span><span class="p">,</span> <span class="n">suggestion_maxnum</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">fields</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Lunr-powered fast index search and suggestion wrapper. See https://lunrjs.com/.</span>
<span class="sd"> Args:</span>
<span class="sd"> query (str): The query to search for.</span>
<span class="sd"> candidate_entries (list): This is the body of possible entities to search. Each</span>
<span class="sd"> must have a property `.search_index_entry` that returns a dict with all</span>
<span class="sd"> keys in the `fields` arg.</span>
<span class="sd"> suggestion_maxnum (int): How many matches to allow at most in a multi-match.</span>
<span class="sd"> fields (list, optional): A list of Lunr field mappings</span>
<span class="sd"> ``{&quot;field_name&quot;: str, &quot;boost&quot;: int}``. See the Lunr documentation</span>
<span class="sd"> for more details. The field name must exist in the dicts returned</span>
<span class="sd"> by `.search_index_entry` of the candidates. If not given, a default setup</span>
<span class="sd"> is used, prefering keys &gt; aliases &gt; category &gt; tags.</span>
<span class="sd"> Returns:</span>
<span class="sd"> tuple: A tuple (matches, suggestions), each a list, where the `suggestion_maxnum` limits</span>
<span class="sd"> how many suggestions are included.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_LUNR</span><span class="p">,</span> <span class="n">_LUNR_EXCEPTION</span><span class="p">,</span> <span class="n">_LUNR_BUILDER_PIPELINE</span><span class="p">,</span> <span class="n">_LUNR_GET_BUILDER</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_LUNR</span><span class="p">:</span>
<span class="c1"># we have to delay-load lunr because it messes with logging if it&#39;s imported</span>
<span class="c1"># before twisted&#39;s logging has been set up</span>
<span class="kn">from</span> <span class="nn">lunr</span> <span class="kn">import</span> <span class="n">lunr</span> <span class="k">as</span> <span class="n">_LUNR</span>
<span class="kn">from</span> <span class="nn">lunr.exceptions</span> <span class="kn">import</span> <span class="n">QueryParseError</span> <span class="k">as</span> <span class="n">_LUNR_EXCEPTION</span>
<span class="kn">from</span> <span class="nn">lunr</span> <span class="kn">import</span> <span class="n">get_default_builder</span> <span class="k">as</span> <span class="n">_LUNR_GET_BUILDER</span>
<span class="kn">from</span> <span class="nn">lunr</span> <span class="kn">import</span> <span class="n">stop_word_filter</span>
<span class="kn">from</span> <span class="nn">lunr.stemmer</span> <span class="kn">import</span> <span class="n">stemmer</span>
<span class="kn">from</span> <span class="nn">lunr.trimmer</span> <span class="kn">import</span> <span class="n">trimmer</span>
<span class="c1"># pre-create a lunr index-builder pipeline where we&#39;ve removed some of</span>
<span class="c1"># the stop-words from the default in lunr.</span>
<span class="n">stop_words</span> <span class="o">=</span> <span class="n">stop_word_filter</span><span class="o">.</span><span class="n">WORDS</span>
<span class="k">for</span> <span class="n">ignore_word</span> <span class="ow">in</span> <span class="n">_LUNR_STOP_WORD_FILTER_EXCEPTIONS</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">stop_words</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">ignore_word</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">custom_stop_words_filter</span> <span class="o">=</span> <span class="n">stop_word_filter</span><span class="o">.</span><span class="n">generate_stop_word_filter</span><span class="p">(</span><span class="n">stop_words</span><span class="p">)</span>
<span class="n">_LUNR_BUILDER_PIPELINE</span> <span class="o">=</span> <span class="p">(</span><span class="n">trimmer</span><span class="p">,</span> <span class="n">custom_stop_words_filter</span><span class="p">,</span> <span class="n">stemmer</span><span class="p">)</span>
<span class="n">indx</span> <span class="o">=</span> <span class="p">[</span><span class="n">cnd</span><span class="o">.</span><span class="n">search_index_entry</span> <span class="k">for</span> <span class="n">cnd</span> <span class="ow">in</span> <span class="n">candidate_entries</span><span class="p">]</span>
<span class="n">mapping</span> <span class="o">=</span> <span class="p">{</span><span class="n">indx</span><span class="p">[</span><span class="n">ix</span><span class="p">][</span><span class="s2">&quot;key&quot;</span><span class="p">]:</span> <span class="n">cand</span> <span class="k">for</span> <span class="n">ix</span><span class="p">,</span> <span class="n">cand</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">candidate_entries</span><span class="p">)}</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">fields</span><span class="p">:</span>
<span class="n">fields</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span><span class="s2">&quot;field_name&quot;</span><span class="p">:</span> <span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="s2">&quot;boost&quot;</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span>
<span class="p">{</span><span class="s2">&quot;field_name&quot;</span><span class="p">:</span> <span class="s2">&quot;aliases&quot;</span><span class="p">,</span> <span class="s2">&quot;boost&quot;</span><span class="p">:</span> <span class="mi">9</span><span class="p">},</span>
<span class="p">{</span><span class="s2">&quot;field_name&quot;</span><span class="p">:</span> <span class="s2">&quot;category&quot;</span><span class="p">,</span> <span class="s2">&quot;boost&quot;</span><span class="p">:</span> <span class="mi">8</span><span class="p">},</span>
<span class="p">{</span><span class="s2">&quot;field_name&quot;</span><span class="p">:</span> <span class="s2">&quot;tags&quot;</span><span class="p">,</span> <span class="s2">&quot;boost&quot;</span><span class="p">:</span> <span class="mi">5</span><span class="p">},</span>
<span class="p">]</span>
<span class="c1"># build the search index</span>
<span class="n">builder</span> <span class="o">=</span> <span class="n">_LUNR_GET_BUILDER</span><span class="p">()</span>
<span class="n">builder</span><span class="o">.</span><span class="n">pipeline</span><span class="o">.</span><span class="n">reset</span><span class="p">()</span>
<span class="n">builder</span><span class="o">.</span><span class="n">pipeline</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="o">*</span><span class="n">_LUNR_BUILDER_PIPELINE</span><span class="p">)</span>
<span class="n">search_index</span> <span class="o">=</span> <span class="n">_LUNR</span><span class="p">(</span>
<span class="n">ref</span><span class="o">=</span><span class="s2">&quot;key&quot;</span><span class="p">,</span>
<span class="n">fields</span><span class="o">=</span><span class="n">fields</span><span class="p">,</span>
<span class="n">documents</span><span class="o">=</span><span class="n">indx</span><span class="p">,</span>
<span class="n">builder</span><span class="o">=</span><span class="n">builder</span>
<span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">matches</span> <span class="o">=</span> <span class="n">search_index</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">)[:</span><span class="n">suggestion_maxnum</span><span class="p">]</span>
<span class="k">except</span> <span class="n">_LUNR_EXCEPTION</span><span class="p">:</span>
<span class="c1"># this is a user-input problem</span>
<span class="n">matches</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># matches (objs), suggestions (strs)</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p">[</span><span class="n">mapping</span><span class="p">[</span><span class="n">match</span><span class="p">[</span><span class="s2">&quot;ref&quot;</span><span class="p">]]</span> <span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">matches</span><span class="p">],</span>
<span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">match</span><span class="p">[</span><span class="s2">&quot;ref&quot;</span><span class="p">])</span> <span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">matches</span><span class="p">],</span> <span class="c1"># + f&quot; (score {match[&#39;score&#39;]})&quot;) # good debug</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="parse_entry_for_subcategories"><a class="viewcode-back" href="../../../api/evennia.help.utils.html#evennia.help.utils.parse_entry_for_subcategories">[docs]</a><span class="k">def</span> <span class="nf">parse_entry_for_subcategories</span><span class="p">(</span><span class="n">entry</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Parse a command docstring for special sub-category blocks:</span>
<span class="sd"> Args:</span>
<span class="sd"> entry (str): A help entry to parse</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: The dict is a mapping that splits the entry into subcategories. This</span>
<span class="sd"> will always hold a key `None` for the main help entry and</span>
<span class="sd"> zero or more keys holding the subcategories. Each is itself</span>
<span class="sd"> a dict with a key `None` for the main text of that subcategory</span>
<span class="sd"> followed by any sub-sub-categories down to a max-depth of 5.</span>
<span class="sd"> Example:</span>
<span class="sd"> ::</span>
<span class="sd"> &#39;&#39;&#39;</span>
<span class="sd"> Main topic text</span>
<span class="sd"> # SUBTOPICS</span>
<span class="sd"> ## foo</span>
<span class="sd"> A subcategory of the main entry, accessible as `help topic foo`</span>
<span class="sd"> (or using /, like `help topic/foo`)</span>
<span class="sd"> ## bar</span>
<span class="sd"> Another subcategory, accessed as `help topic bar`</span>
<span class="sd"> (or `help topic/bar`)</span>
<span class="sd"> ### moo</span>
<span class="sd"> A subcategory of bar, accessed as `help bar moo`</span>
<span class="sd"> (or `help bar/moo`)</span>
<span class="sd"> #### dum</span>
<span class="sd"> A subcategory of moo, accessed `help bar moo dum`</span>
<span class="sd"> (or `help bar/moo/dum`)</span>
<span class="sd"> &#39;&#39;&#39;</span>
<span class="sd"> This will result in this returned entry structure:</span>
<span class="sd"> ::</span>
<span class="sd"> {</span>
<span class="sd"> None: &quot;Main topic text&quot;:</span>
<span class="sd"> &quot;foo&quot;: {</span>
<span class="sd"> None: &quot;main topic/foo text&quot;</span>
<span class="sd"> },</span>
<span class="sd"> &quot;bar&quot;: {</span>
<span class="sd"> None: &quot;Main topic/bar text&quot;,</span>
<span class="sd"> &quot;moo&quot;: {</span>
<span class="sd"> None: &quot;topic/bar/moo text&quot;</span>
<span class="sd"> &quot;dum&quot;: {</span>
<span class="sd"> None: &quot;topic/bar/moo/dum text&quot;</span>
<span class="sd"> }</span>
<span class="sd"> }</span>
<span class="sd"> }</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">topic</span><span class="p">,</span> <span class="o">*</span><span class="n">subtopics</span> <span class="o">=</span> <span class="n">_RE_HELP_SUBTOPICS_START</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">maxsplit</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">structure</span> <span class="o">=</span> <span class="p">{</span><span class="kc">None</span><span class="p">:</span> <span class="n">topic</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)}</span>
<span class="k">if</span> <span class="n">subtopics</span><span class="p">:</span>
<span class="n">subtopics</span> <span class="o">=</span> <span class="n">subtopics</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">structure</span>
<span class="n">keypath</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">current_nesting</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">subtopic</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># from evennia import set_trace;set_trace()</span>
<span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">_RE_HELP_SUBTOPIC_SPLIT</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">subtopics</span><span class="o">.</span><span class="n">strip</span><span class="p">()):</span>
<span class="n">subtopic_match</span> <span class="o">=</span> <span class="n">_RE_HELP_SUBTOPIC_PARSE</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">part</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="k">if</span> <span class="n">subtopic_match</span><span class="p">:</span>
<span class="c1"># a new sub(-sub..) category starts.</span>
<span class="n">mdict</span> <span class="o">=</span> <span class="n">subtopic_match</span><span class="o">.</span><span class="n">groupdict</span><span class="p">()</span>
<span class="n">subtopic</span> <span class="o">=</span> <span class="n">mdict</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="n">new_nesting</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">mdict</span><span class="p">[</span><span class="s1">&#39;nesting&#39;</span><span class="p">])</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">new_nesting</span> <span class="o">&gt;</span> <span class="n">MAX_SUBTOPIC_NESTING</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Can have max </span><span class="si">{</span><span class="n">MAX_SUBTOPIC_NESTING</span><span class="si">}</span><span class="s2"> levels of nested help subtopics.&quot;</span><span class="p">)</span>
<span class="n">nestdiff</span> <span class="o">=</span> <span class="n">new_nesting</span> <span class="o">-</span> <span class="n">current_nesting</span>
<span class="k">if</span> <span class="n">nestdiff</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="c1"># jumping back up in nesting</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">nestdiff</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">keypath</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">elif</span> <span class="n">nestdiff</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="c1"># don&#39;t add a deeper nesting but replace the current</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">keypath</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">keypath</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">subtopic</span><span class="p">)</span>
<span class="n">current_nesting</span> <span class="o">=</span> <span class="n">new_nesting</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># an entry belonging to a subtopic - find the nested location</span>
<span class="n">dct</span> <span class="o">=</span> <span class="n">structure</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">keypath</span> <span class="ow">and</span> <span class="n">subtopic</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">structure</span><span class="p">[</span><span class="n">subtopic</span><span class="p">]</span> <span class="o">=</span> <span class="n">part</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">keypath</span><span class="p">:</span>
<span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">dct</span><span class="p">:</span>
<span class="n">dct</span> <span class="o">=</span> <span class="n">dct</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">dct</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="kc">None</span><span class="p">:</span> <span class="n">part</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">structure</span></div>
</pre></div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../index.html">
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
<a href="https://discord.gg/NecFePw">Discord</a> -
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
</li>
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="utils.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.help.utils</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>