mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 22:06:30 +01:00
861 lines
No EOL
73 KiB
HTML
861 lines
No EOL
73 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>Parsing command arguments, theory and best practices — Evennia 3.x 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" />
|
||
<link rel="next" title="Understanding Color Tags" href="Tutorial-Understanding-Color-Tags.html" />
|
||
<link rel="prev" title="Automatically Tweet game stats" href="Web-Tweeting-Game-Stats.html" />
|
||
</head><body>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</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"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Tutorial-Understanding-Color-Tags.html" title="Understanding Color Tags"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Web-Tweeting-Game-Stats.html" title="Automatically Tweet game stats"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" accesskey="U">Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Parsing command arguments, theory and best practices</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
|
||
<div class="documentwrapper">
|
||
<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><a href="../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Parsing command arguments, theory and best practices</a><ul>
|
||
<li><a class="reference internal" href="#what-are-command-arguments">What are command arguments?</a></li>
|
||
<li><a class="reference internal" href="#working-with-strings">Working with strings</a><ul>
|
||
<li><a class="reference internal" href="#self-args">self.args</a></li>
|
||
<li><a class="reference internal" href="#stripping">Stripping</a></li>
|
||
<li><a class="reference internal" href="#convert-arguments-to-numbers">Convert arguments to numbers</a></li>
|
||
<li><a class="reference internal" href="#working-with-several-arguments">Working with several arguments</a></li>
|
||
<li><a class="reference internal" href="#optional-arguments">Optional arguments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#evennia-searches">Evennia searches</a><ul>
|
||
<li><a class="reference internal" href="#local-searches">Local searches</a></li>
|
||
<li><a class="reference internal" href="#quiet-searches">Quiet searches</a></li>
|
||
<li><a class="reference internal" href="#global-searches">Global searches</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#conclusion">Conclusion</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Web-Tweeting-Game-Stats.html"
|
||
title="previous chapter">Automatically Tweet game stats</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Tutorial-Understanding-Color-Tags.html"
|
||
title="next chapter">Understanding Color Tags</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Howtos/Tutorial-Parsing-Commands.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="parsing-command-arguments-theory-and-best-practices">
|
||
<h1>Parsing command arguments, theory and best practices<a class="headerlink" href="#parsing-command-arguments-theory-and-best-practices" title="Permalink to this headline">¶</a></h1>
|
||
<p>This tutorial will elaborate on the many ways one can parse command arguments. The first step after
|
||
<a class="reference internal" href="Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">adding a command</span></a> usually is to parse its arguments. There are lots of
|
||
ways to do it, but some are indeed better than others and this tutorial will try to present them.</p>
|
||
<p>If you’re a Python beginner, this tutorial might help you a lot. If you’re already familiar with
|
||
Python syntax, this tutorial might still contain useful information. There are still a lot of
|
||
things I find in the standard library that come as a surprise, though they were there all along.
|
||
This might be true for others.</p>
|
||
<p>In this tutorial we will:</p>
|
||
<ul class="simple">
|
||
<li><p>Parse arguments with numbers.</p></li>
|
||
<li><p>Parse arguments with delimiters.</p></li>
|
||
<li><p>Take a look at optional arguments.</p></li>
|
||
<li><p>Parse argument containing object names.</p></li>
|
||
</ul>
|
||
<section id="what-are-command-arguments">
|
||
<h2>What are command arguments?<a class="headerlink" href="#what-are-command-arguments" title="Permalink to this headline">¶</a></h2>
|
||
<p>I’m going to talk about command arguments and parsing a lot in this tutorial. So let’s be sure we
|
||
talk about the same thing before going any further:</p>
|
||
<blockquote>
|
||
<div><p>A command is an Evennia object that handles specific user input.</p>
|
||
</div></blockquote>
|
||
<p>For instance, the default <code class="docutils literal notranslate"><span class="pre">look</span></code> is a command. After having created your Evennia game, and
|
||
connected to it, you should be able to type <code class="docutils literal notranslate"><span class="pre">look</span></code> to see what’s around. In this context, <code class="docutils literal notranslate"><span class="pre">look</span></code> is
|
||
a command.</p>
|
||
<blockquote>
|
||
<div><p>Command arguments are additional text passed after the command.</p>
|
||
</div></blockquote>
|
||
<p>Following the same example, you can type <code class="docutils literal notranslate"><span class="pre">look</span> <span class="pre">self</span></code> to look at yourself. In this context, <code class="docutils literal notranslate"><span class="pre">self</span></code>
|
||
is the text specified after <code class="docutils literal notranslate"><span class="pre">look</span></code>. <code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">self"</span></code> is the argument to the <code class="docutils literal notranslate"><span class="pre">look</span></code> command.</p>
|
||
<p>Part of our task as a game developer is to connect user inputs (mostly commands) with actions in the
|
||
game. And most of the time, entering commands is not enough, we have to rely on arguments for
|
||
specifying actions with more accuracy.</p>
|
||
<p>Take the <code class="docutils literal notranslate"><span class="pre">say</span></code> command. If you couldn’t specify what to say as a command argument (<code class="docutils literal notranslate"><span class="pre">say</span> <span class="pre">hello!</span></code>),
|
||
you would have trouble communicating with others in the game. One would need to create a different
|
||
command for every kind of word or sentence, which is, of course, not practical.</p>
|
||
<p>Last thing: what is parsing?</p>
|
||
<blockquote>
|
||
<div><p>In our case, parsing is the process by which we convert command arguments into something we can work with.</p>
|
||
</div></blockquote>
|
||
<p>We don’t usually use the command argument as is (which is just text, of type <code class="docutils literal notranslate"><span class="pre">str</span></code> in Python). We
|
||
need to extract useful information. We might want to ask the user for a number, or the name of
|
||
another character present in the same room. We’re going to see how to do all that now.</p>
|
||
</section>
|
||
<section id="working-with-strings">
|
||
<h2>Working with strings<a class="headerlink" href="#working-with-strings" title="Permalink to this headline">¶</a></h2>
|
||
<p>In object terms, when you write a command in Evennia (when you write the Python class), the
|
||
arguments are stored in the <code class="docutils literal notranslate"><span class="pre">args</span></code> attribute. Which is to say, inside your <code class="docutils literal notranslate"><span class="pre">func</span></code> method, you can
|
||
access the command arguments in <code class="docutils literal notranslate"><span class="pre">self.args</span></code>.</p>
|
||
<section id="self-args">
|
||
<h3>self.args<a class="headerlink" href="#self-args" title="Permalink to this headline">¶</a></h3>
|
||
<p>To begin with, look at this example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CmdTest</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Test command.</span>
|
||
|
||
<span class="sd"> Syntax:</span>
|
||
<span class="sd"> test [argument]</span>
|
||
|
||
<span class="sd"> Enter any argument after test.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"test"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You have entered: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If you add this command and test it, you will receive exactly what you have entered without any
|
||
parsing:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">test</span> <span class="n">Whatever</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="n">Whatever</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p>The lines starting with <code class="docutils literal notranslate"><span class="pre">></span></code> indicate what you enter into your client. The other lines are what
|
||
you receive from the game server.</p>
|
||
</div></blockquote>
|
||
<p>Notice two things here:</p>
|
||
<ol class="simple">
|
||
<li><p>The left space between our command key (“test”, here) and our command argument is not removed.
|
||
That’s why there are two spaces in our output at line 2. Try entering something like “testok”.</p></li>
|
||
<li><p>Even if you don’t enter command arguments, the command will still be called with an empty string
|
||
in <code class="docutils literal notranslate"><span class="pre">self.args</span></code>.</p></li>
|
||
</ol>
|
||
<p>Perhaps a slight modification to our code would be appropriate to see what’s happening. We will
|
||
force Python to display the command arguments as a debug string using a little shortcut.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CmdTest</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Test command.</span>
|
||
|
||
<span class="sd"> Syntax:</span>
|
||
<span class="sd"> test [argument]</span>
|
||
|
||
<span class="sd"> Enter any argument after test.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"test"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You have entered: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">!r}</span><span class="s2">."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The only line we have changed is the last one, and we have added <code class="docutils literal notranslate"><span class="pre">!r</span></code> between our braces to tell
|
||
Python to print the debug version of the argument (the repr-ed version). Let’s see the result:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">test</span> <span class="n">Whatever</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s1">' Whatever'</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s1">''</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span> <span class="n">And</span> <span class="n">something</span> <span class="k">with</span> <span class="s1">'?</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s2">" And something with '?"</span><span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This displays the string in a way you could see in the Python interpreter. It might be easier to
|
||
read… to debug, anyway.</p>
|
||
<p>I insist so much on that point because it’s crucial: the command argument is just a string (of type
|
||
<code class="docutils literal notranslate"><span class="pre">str</span></code>) and we will use this to parse it. What you will see is mostly not Evennia-specific, it’s
|
||
Python-specific and could be used in any other project where you have the same need.</p>
|
||
</section>
|
||
<section id="stripping">
|
||
<h3>Stripping<a class="headerlink" href="#stripping" title="Permalink to this headline">¶</a></h3>
|
||
<p>As you’ve seen, our command arguments are stored with the space. And the space between the command
|
||
and the arguments is often of no importance.</p>
|
||
<blockquote>
|
||
<div><p>Why is it ever there?</p>
|
||
</div></blockquote>
|
||
<p>Evennia will try its best to find a matching command. If the user enters your command key with
|
||
arguments (but omits the space), Evennia will still be able to find and call the command. You might
|
||
have seen what happened if the user entered <code class="docutils literal notranslate"><span class="pre">testok</span></code>. In this case, <code class="docutils literal notranslate"><span class="pre">testok</span></code> could very well be a
|
||
command (Evennia checks for that) but seeing none, and because there’s a <code class="docutils literal notranslate"><span class="pre">test</span></code> command, Evennia
|
||
calls it with the arguments <code class="docutils literal notranslate"><span class="pre">"ok"</span></code>.</p>
|
||
<p>But most of the time, we don’t really care about this left space, so you will often see code to
|
||
remove it. There are different ways to do it in Python, but a command use case is the <code class="docutils literal notranslate"><span class="pre">strip</span></code>
|
||
method on <code class="docutils literal notranslate"><span class="pre">str</span></code> and its cousins, <code class="docutils literal notranslate"><span class="pre">lstrip</span></code> and <code class="docutils literal notranslate"><span class="pre">rstrip</span></code>.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">strip</span></code>: removes one or more characters (either spaces or other characters) from both ends of the
|
||
string.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">lstrip</span></code>: same thing but only removes from the left end (left strip) of the string.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">rstrip</span></code>: same thing but only removes from the right end (right strip) of the string.</p></li>
|
||
</ul>
|
||
<p>Some Python examples might help:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="s1">' this is '</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="c1"># remove spaces by default</span>
|
||
<span class="go">'this is'</span>
|
||
<span class="gp">>>> </span><span class="s2">" What if I'm right? "</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span> <span class="c1"># strip spaces from the left</span>
|
||
<span class="go">"What if I'm right? "</span>
|
||
<span class="gp">>>> </span><span class="s1">'Looks good to me...'</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span> <span class="c1"># removes '.'</span>
|
||
<span class="go">'Looks good to me'</span>
|
||
<span class="gp">>>> </span><span class="s1">'"Now, what is it?"'</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s1">'"?'</span><span class="p">)</span> <span class="c1"># removes '"' and '?' from both ends</span>
|
||
<span class="go">'Now, what is it'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Usually, since we don’t need the space separator, but still want our command to work if there’s no
|
||
separator, we call <code class="docutils literal notranslate"><span class="pre">lstrip</span></code> on the command arguments:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CmdTest</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Test command.</span>
|
||
|
||
<span class="sd"> Syntax:</span>
|
||
<span class="sd"> test [argument]</span>
|
||
|
||
<span class="sd"> Enter any argument after test.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"test"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Parse arguments, just strip them."""</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You have entered: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">!r}</span><span class="s2">."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p>We are now beginning to override the command’s <code class="docutils literal notranslate"><span class="pre">parse</span></code> method, which is typically useful just for
|
||
argument parsing. This method is executed before <code class="docutils literal notranslate"><span class="pre">func</span></code> and so <code class="docutils literal notranslate"><span class="pre">self.args</span></code> in <code class="docutils literal notranslate"><span class="pre">func()</span></code> will contain
|
||
our <code class="docutils literal notranslate"><span class="pre">self.args.lstrip()</span></code>.</p>
|
||
</div></blockquote>
|
||
<p>Let’s try it:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">test</span> <span class="n">Whatever</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s1">'Whatever'</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s1">''</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span> <span class="n">And</span> <span class="n">something</span> <span class="k">with</span> <span class="s1">'?</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s2">"And something with '?"</span><span class="o">.</span>
|
||
<span class="o">></span> <span class="n">test</span> <span class="n">And</span> <span class="n">something</span> <span class="k">with</span> <span class="n">lots</span> <span class="n">of</span> <span class="n">spaces</span>
|
||
<span class="n">You</span> <span class="n">have</span> <span class="n">entered</span><span class="p">:</span> <span class="s1">'And something with lots of spaces'</span><span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Spaces at the end of the string are kept, but all spaces at the beginning are removed:</p>
|
||
<blockquote>
|
||
<div><p><code class="docutils literal notranslate"><span class="pre">strip</span></code>, <code class="docutils literal notranslate"><span class="pre">lstrip</span></code> and <code class="docutils literal notranslate"><span class="pre">rstrip</span></code> without arguments will strip spaces, line breaks and other common
|
||
separators. You can specify one or more characters as a parameter. If you specify more than one
|
||
character, all of them will be stripped from your original string.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="convert-arguments-to-numbers">
|
||
<h3>Convert arguments to numbers<a class="headerlink" href="#convert-arguments-to-numbers" title="Permalink to this headline">¶</a></h3>
|
||
<p>As pointed out, <code class="docutils literal notranslate"><span class="pre">self.args</span></code> is a string (of type <code class="docutils literal notranslate"><span class="pre">str</span></code>). What if we want the user to enter a
|
||
number?</p>
|
||
<p>Let’s take a very simple example: creating a command, <code class="docutils literal notranslate"><span class="pre">roll</span></code>, that allows to roll a six-sided die.
|
||
The player has to guess the number, specifying the number as argument. To win, the player has to
|
||
match the number with the die. Let’s see an example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>> roll 3
|
||
You roll a die. It lands on the number 4.
|
||
You played 3, you have lost.
|
||
> dice 1
|
||
You roll a die. It lands on the number 2.
|
||
You played 1, you have lost.
|
||
> dice 1
|
||
You roll a die. It lands on the number 1.
|
||
You played 1, you have won!
|
||
</pre></div>
|
||
</div>
|
||
<p>If that’s your first command, it’s a good opportunity to try to write it. A command with a simple
|
||
and finite role always is a good starting choice. Here’s how we could (first) write it… but it
|
||
won’t work as is, I warn you:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CmdRoll</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Play random, enter a number and try your luck.</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> roll <number></span>
|
||
|
||
<span class="sd"> Enter a valid number as argument. A random die will be rolled and you</span>
|
||
<span class="sd"> will win if you have specified the correct number.</span>
|
||
|
||
<span class="sd"> Example:</span>
|
||
<span class="sd"> roll 3</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"roll"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Convert the argument to a number."""</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># Roll a random die</span>
|
||
<span class="n">figure</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="c1"># return a pseudo-random number between 1 and 6, including both</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You roll a die. It lands on the number </span><span class="si">{</span><span class="n">figure</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">==</span> <span class="n">figure</span><span class="p">:</span> <span class="c1"># THAT WILL BREAK!</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">, you have won!"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">, you have lost."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If you try this code, Python will complain that you try to compare a number with a string: <code class="docutils literal notranslate"><span class="pre">figure</span></code>
|
||
is a number and <code class="docutils literal notranslate"><span class="pre">self.args</span></code> is a string and can’t be compared as-is in Python. Python doesn’t do
|
||
“implicit converting” as some languages do. By the way, this might be annoying sometimes, and other
|
||
times you will be glad it tries to encourage you to be explicit rather than implicit about what to
|
||
do. This is an ongoing debate between programmers. Let’s move on!</p>
|
||
<p>So we need to convert the command argument from a <code class="docutils literal notranslate"><span class="pre">str</span></code> into an <code class="docutils literal notranslate"><span class="pre">int</span></code>. There are a few ways to do
|
||
it. But the proper way is to try to convert and deal with the <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> Python exception.</p>
|
||
<p>Converting a <code class="docutils literal notranslate"><span class="pre">str</span></code> into an <code class="docutils literal notranslate"><span class="pre">int</span></code> in Python is extremely simple: just use the <code class="docutils literal notranslate"><span class="pre">int</span></code> function, give it
|
||
the string and it returns an integer, if it could. If it can’t, it will raise <code class="docutils literal notranslate"><span class="pre">ValueError</span></code>. So
|
||
we’ll need to catch that. However, we also have to indicate to Evennia that, should the number be
|
||
invalid, no further parsing should be done. Here’s a new attempt at our command with this
|
||
converting:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CmdRoll</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Play random, enter a number and try your luck.</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> roll <number></span>
|
||
|
||
<span class="sd"> Enter a valid number as argument. A random die will be rolled and you</span>
|
||
<span class="sd"> will win if you have specified the correct number.</span>
|
||
|
||
<span class="sd"> Example:</span>
|
||
<span class="sd"> roll 3</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"roll"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Convert the argument to number if possible."""</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="c1"># Convert to int if possible</span>
|
||
<span class="c1"># If not, raise InterruptCommand. Evennia will catch this</span>
|
||
<span class="c1"># exception and not call the 'func' method.</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">entered</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2"> is not a valid number."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># Roll a random die</span>
|
||
<span class="n">figure</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="c1"># return a pseudo-random number between 1 and 6, including both</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You roll a die. It lands on the number </span><span class="si">{</span><span class="n">figure</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">entered</span> <span class="o">==</span> <span class="n">figure</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">entered</span><span class="si">}</span><span class="s2">, you have won!"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">entered</span><span class="si">}</span><span class="s2">, you have lost."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Before enjoying the result, let’s examine the <code class="docutils literal notranslate"><span class="pre">parse</span></code> method a little more: what it does is try to
|
||
convert the entered argument from a <code class="docutils literal notranslate"><span class="pre">str</span></code> to an <code class="docutils literal notranslate"><span class="pre">int</span></code>. This might fail (if a user enters <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">something</span></code>). In such a case, Python raises a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> exception. We catch it in our
|
||
<code class="docutils literal notranslate"><span class="pre">try/except</span></code> block, send a message to the user and raise the <code class="docutils literal notranslate"><span class="pre">InterruptCommand</span></code> exception in
|
||
response to tell Evennia to not run <code class="docutils literal notranslate"><span class="pre">func()</span></code>, since we have no valid number to give it.</p>
|
||
<p>In the <code class="docutils literal notranslate"><span class="pre">func</span></code> method, instead of using <code class="docutils literal notranslate"><span class="pre">self.args</span></code>, we use <code class="docutils literal notranslate"><span class="pre">self.entered</span></code> which we have defined in
|
||
our <code class="docutils literal notranslate"><span class="pre">parse</span></code> method. You can expect that, if <code class="docutils literal notranslate"><span class="pre">func()</span></code> is run, then <code class="docutils literal notranslate"><span class="pre">self.entered</span></code> contains a valid
|
||
number.</p>
|
||
<p>If you try this command, it will work as expected this time: the number is converted as it should
|
||
and compared to the die roll. You might spend some minutes playing this game. Time out!</p>
|
||
<p>Something else we could want to address: in our small example, we only want the user to enter a
|
||
positive number between 1 and 6. And the user can enter <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">0</span></code> or <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">-8</span></code> or <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">208</span></code> for
|
||
that matter, the game still works. It might be worth addressing. Again, you could write a
|
||
condition to do that, but since we’re catching an exception, we might end up with something cleaner
|
||
by grouping:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CmdRoll</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Play random, enter a number and try your luck.</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> roll <number></span>
|
||
|
||
<span class="sd"> Enter a valid number as argument. A random die will be rolled and you</span>
|
||
<span class="sd"> will win if you have specified the correct number.</span>
|
||
|
||
<span class="sd"> Example:</span>
|
||
<span class="sd"> roll 3</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"roll"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Convert the argument to number if possible."""</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="c1"># Convert to int if possible</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">entered</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="mi">1</span> <span class="o"><=</span> <span class="bp">self</span><span class="o">.</span><span class="n">entered</span> <span class="o"><=</span> <span class="mi">6</span><span class="p">:</span>
|
||
<span class="c1"># self.entered is not between 1 and 6 (including both)</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2"> is not a valid number."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># Roll a random die</span>
|
||
<span class="n">figure</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="c1"># return a pseudo-random number between 1 and 6, including both</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You roll a die. It lands on the number </span><span class="si">{</span><span class="n">figure</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">entered</span> <span class="o">==</span> <span class="n">figure</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">entered</span><span class="si">}</span><span class="s2">, you have won!"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">entered</span><span class="si">}</span><span class="s2">, you have lost."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Using grouped exceptions like that makes our code easier to read, but if you feel more comfortable
|
||
checking, afterward, that the number the user entered is in the right range, you can do so in a
|
||
latter condition.</p>
|
||
<blockquote>
|
||
<div><p>Notice that we have updated our <code class="docutils literal notranslate"><span class="pre">parse</span></code> method only in this last attempt, not our <code class="docutils literal notranslate"><span class="pre">func()</span></code> method
|
||
which remains the same. This is one goal of separating argument parsing from command processing,
|
||
these two actions are best kept isolated.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="working-with-several-arguments">
|
||
<h3>Working with several arguments<a class="headerlink" href="#working-with-several-arguments" title="Permalink to this headline">¶</a></h3>
|
||
<p>Often a command expects several arguments. So far, in our example with the “roll” command, we only
|
||
expect one argument: a number and just a number. What if we want the user to specify several
|
||
numbers? First the number of dice to roll, then the guess?</p>
|
||
<blockquote>
|
||
<div><p>You won’t win often if you roll 5 dice but that’s for the example.</p>
|
||
</div></blockquote>
|
||
<p>So we would like to interpret a command like this:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> roll 3 12
|
||
</pre></div>
|
||
</div>
|
||
<p>(To be understood: roll 3 dice, my guess is the total number will be 12.)</p>
|
||
<p>What we need is to cut our command argument, which is a <code class="docutils literal notranslate"><span class="pre">str</span></code>, break it at the space (we use the
|
||
space as a delimiter). Python provides the <code class="docutils literal notranslate"><span class="pre">str.split</span></code> method which we’ll use. Again, here are
|
||
some examples from the Python interpreter:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>>> args = "3 12"
|
||
>>> args.split(" ")
|
||
['3', '12']
|
||
>>> args = "a command with several arguments"
|
||
>>> args.split(" ")
|
||
['a', 'command', 'with', 'several', 'arguments']
|
||
>>>
|
||
</pre></div>
|
||
</div>
|
||
<p>As you can see, <code class="docutils literal notranslate"><span class="pre">str.split</span></code> will “convert” our strings into a list of strings. The specified
|
||
argument (<code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">"</span></code> in our case) is used as delimiter. So Python browses our original string. When it
|
||
sees a delimiter, it takes whatever is before this delimiter and append it to a list.</p>
|
||
<p>The point here is that <code class="docutils literal notranslate"><span class="pre">str.split</span></code> will be used to split our argument. But, as you can see from the
|
||
above output, we can never be sure of the length of the list at this point:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>>> args = "something"
|
||
>>> args.split(" ")
|
||
['something']
|
||
>>> args = ""
|
||
>>> args.split(" ")
|
||
['']
|
||
>>>
|
||
</pre></div>
|
||
</div>
|
||
<p>Again we could use a condition to check the number of split arguments, but Python offers a better
|
||
approach, making use of its exception mechanism. We’ll give a second argument to <code class="docutils literal notranslate"><span class="pre">str.split</span></code>, the
|
||
maximum number of splits to do. Let’s see an example, this feature might be confusing at first
|
||
glance:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>>> args = "that is something great"
|
||
>>> args.split(" ", 1) # one split, that is a list with two elements (before, after)
|
||
</pre></div>
|
||
</div>
|
||
<p>[‘that’, ‘is something great’]</p>
|
||
<blockquote>
|
||
<div><blockquote>
|
||
<div></div></blockquote>
|
||
</div></blockquote>
|
||
<p>Read this example as many times as needed to understand it. The second argument we give to
|
||
<code class="docutils literal notranslate"><span class="pre">str.split</span></code> is not the length of the list that should be returned, but the number of times we have
|
||
to split. Therefore, we specify 1 here, but we get a list of two elements (before the separator,
|
||
after the separator).</p>
|
||
<blockquote>
|
||
<div><p>What will happen if Python can’t split the number of times we ask?</p>
|
||
</div></blockquote>
|
||
<p>It won’t:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>>> args = "whatever"
|
||
>>> args.split(" ", 1) # there isn't even a space here...
|
||
['whatever']
|
||
>>>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is one moment I would have hoped for an exception and didn’t get one. But there’s another way
|
||
which will raise an exception if there is an error: variable unpacking.</p>
|
||
<p>We won’t talk about this feature in details here. It would be complicated. But the code is really
|
||
straightforward to use. Let’s take our example of the roll command but let’s add a first argument:
|
||
the number of dice to roll.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CmdRoll</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Play random, enter a number and try your luck.</span>
|
||
|
||
<span class="sd"> Specify two numbers separated by a space. The first number is the</span>
|
||
<span class="sd"> number of dice to roll (1, 2, 3) and the second is the expected sum</span>
|
||
<span class="sd"> of the roll.</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> roll <dice> <number></span>
|
||
|
||
<span class="sd"> For instance, to roll two 6-figure dice, enter 2 as first argument.</span>
|
||
<span class="sd"> If you think the sum of these two dice roll will be 10, you could enter:</span>
|
||
|
||
<span class="sd"> roll 2 10</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"roll"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Split the arguments and convert them."""</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="c1"># Split: we expect two arguments separated by a space</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">number</span><span class="p">,</span> <span class="n">guess</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Invalid usage. Enter two numbers separated by a space."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="c1"># Convert the entered number (first argument)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">number</span><span class="si">}</span><span class="s2"> is not a valid number of dice."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="c1"># Convert the entered guess (second argument)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">guess</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">guess</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="mi">1</span> <span class="o"><=</span> <span class="bp">self</span><span class="o">.</span><span class="n">guess</span> <span class="o"><=</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">*</span> <span class="mi">6</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">guess</span><span class="si">}</span><span class="s2"> is not a valid guess."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># Roll a random die X times (X being self.number)</span>
|
||
<span class="n">figure</span> <span class="o">=</span> <span class="mi">0</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="bp">self</span><span class="o">.</span><span class="n">number</span><span class="p">):</span>
|
||
<span class="n">figure</span> <span class="o">+=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You roll </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">number</span><span class="si">}</span><span class="s2"> dice and obtain the sum </span><span class="si">{</span><span class="n">figure</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">guess</span> <span class="o">==</span> <span class="n">figure</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">guess</span><span class="si">}</span><span class="s2">, you have won!"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You played </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">guess</span><span class="si">}</span><span class="s2">, you have lost."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The beginning of the <code class="docutils literal notranslate"><span class="pre">parse()</span></code> method is what interests us most:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="n">number</span><span class="p">,</span> <span class="n">guess</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Invalid usage. Enter two numbers separated by a space."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We split the argument using <code class="docutils literal notranslate"><span class="pre">str.split</span></code> but we capture the result in two variables. Python is smart
|
||
enough to know that we want what’s left of the space in the first variable, what’s right of the
|
||
space in the second variable. If there is not even a space in the string, Python will raise a
|
||
<code class="docutils literal notranslate"><span class="pre">ValueError</span></code> exception.</p>
|
||
<p>This code is much easier to read than browsing through the returned strings of <code class="docutils literal notranslate"><span class="pre">str.split</span></code>. We can
|
||
convert both variables the way we did previously. Actually there are not so many changes in this
|
||
version and the previous one, most of it is due to name changes for clarity.</p>
|
||
<blockquote>
|
||
<div><p>Splitting a string with a maximum of splits is a common occurrence while parsing command
|
||
arguments. You can also see the <code class="docutils literal notranslate"><span class="pre">str.rspli8t</span></code> method that does the same thing but from the right of
|
||
the string. Therefore, it will attempt to find delimiters at the end of the string and work toward
|
||
the beginning of it.</p>
|
||
</div></blockquote>
|
||
<p>We have used a space as a delimiter. This is absolutely not necessary. You might remember that
|
||
most default Evennia commands can take an <code class="docutils literal notranslate"><span class="pre">=</span></code> sign as a delimiter. Now you know how to parse them
|
||
as well:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>>> cmd_key = "tel"
|
||
>>> cmd_args = "book = chest"
|
||
>>> left, right = cmd_args.split("=") # mighht raise ValueError!
|
||
>>> left
|
||
'book '
|
||
>>> right
|
||
' chest'
|
||
>>>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="optional-arguments">
|
||
<h3>Optional arguments<a class="headerlink" href="#optional-arguments" title="Permalink to this headline">¶</a></h3>
|
||
<p>Sometimes, you’ll come across commands that have optional arguments. These arguments are not
|
||
necessary but they can be set if more information is needed. I will not provide the entire command
|
||
code here but just enough code to show the mechanism in Python:</p>
|
||
<p>Again, we’ll use <code class="docutils literal notranslate"><span class="pre">str.split</span></code>, knowing that we might not have any delimiter at all. For instance,
|
||
the player could enter the “tel” command like this:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> tel book
|
||
> tell book = chest
|
||
</pre></div>
|
||
</div>
|
||
<p>The equal sign is optional along with whatever is specified after it. A possible solution in our
|
||
<code class="docutils literal notranslate"><span class="pre">parse</span></code> method would be:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="c1"># = is optional</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">obj</span><span class="p">,</span> <span class="n">destination</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"="</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="n">obj</span> <span class="o">=</span> <span class="n">args</span>
|
||
<span class="n">destination</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This code would place everything the user entered in <code class="docutils literal notranslate"><span class="pre">obj</span></code> if she didn’t specify any equal sign.
|
||
Otherwise, what’s before the equal sign will go in <code class="docutils literal notranslate"><span class="pre">obj</span></code>, what’s after the equal sign will go in
|
||
<code class="docutils literal notranslate"><span class="pre">destination</span></code>. This makes for quick testing after that, more robust code with less conditions that
|
||
might too easily break your code if you’re not careful.</p>
|
||
<blockquote>
|
||
<div><p>Again, here we specified a maximum numbers of splits. If the users enters:</p>
|
||
</div></blockquote>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> tel book = chest = chair
|
||
</pre></div>
|
||
</div>
|
||
<p>Then <code class="docutils literal notranslate"><span class="pre">destination</span></code> will contain: <code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">chest</span> <span class="pre">=</span> <span class="pre">chair"</span></code>. This is often desired, but it’s up to you to
|
||
set parsing however you like.</p>
|
||
</section>
|
||
</section>
|
||
<section id="evennia-searches">
|
||
<h2>Evennia searches<a class="headerlink" href="#evennia-searches" title="Permalink to this headline">¶</a></h2>
|
||
<p>After this quick tour of some <code class="docutils literal notranslate"><span class="pre">str</span></code> methods, we’ll take a look at some Evennia-specific features
|
||
that you won’t find in standard Python.</p>
|
||
<p>One very common task is to convert a <code class="docutils literal notranslate"><span class="pre">str</span></code> into an Evennia object. Take the previous example:
|
||
having <code class="docutils literal notranslate"><span class="pre">"book"</span></code> in a variable is great, but we would prefer to know what the user is talking
|
||
about… what is this <code class="docutils literal notranslate"><span class="pre">"book"</span></code>?</p>
|
||
<p>To get an object from a string, we perform an Evennia search. Evennia provides a <code class="docutils literal notranslate"><span class="pre">search</span></code> method on
|
||
all typeclassed objects (you will most likely use the one on characters or accounts). This method
|
||
supports a very wide array of arguments and has <a class="reference internal" href="Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.html"><span class="doc std std-doc">its own tutorial</span></a>.
|
||
Some examples of useful cases follow:</p>
|
||
<section id="local-searches">
|
||
<h3>Local searches<a class="headerlink" href="#local-searches" title="Permalink to this headline">¶</a></h3>
|
||
<p>When an account or a character enters a command, the account or character is found in the <code class="docutils literal notranslate"><span class="pre">caller</span></code>
|
||
attribute. Therefore, <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> will contain an account or a character (or a session if that’s
|
||
a session command, though that’s not as frequent). The <code class="docutils literal notranslate"><span class="pre">search</span></code> method will be available on this
|
||
caller.</p>
|
||
<p>Let’s take the same example of our little “tel” command. The user can specify an object as
|
||
argument:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We then need to “convert” this string into an Evennia object. The Evennia object will be searched
|
||
in the caller’s location and its contents by default (that is to say, if the command has been
|
||
entered by a character, it will search the object in the character’s room and the character’s
|
||
inventory).</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We specify only one argument to the <code class="docutils literal notranslate"><span class="pre">search</span></code> method here: the string to search. If Evennia finds a
|
||
match, it will return it and we keep it in the <code class="docutils literal notranslate"><span class="pre">obj</span></code> attribute. If it can’t find anything, it will
|
||
return <code class="docutils literal notranslate"><span class="pre">None</span></code> so we need to check for that:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># A proper error message has already been sent to the caller</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>That’s it. After this condition, you know that whatever is in <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is a valid Evennia object
|
||
(another character, an object, an exit…).</p>
|
||
</section>
|
||
<section id="quiet-searches">
|
||
<h3>Quiet searches<a class="headerlink" href="#quiet-searches" title="Permalink to this headline">¶</a></h3>
|
||
<p>By default, Evennia will handle the case when more than one match is found in the search. The user
|
||
will be asked to narrow down and re-enter the command. You can, however, ask to be returned the
|
||
list of matches and handle this list yourself:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
|
||
<span class="n">objs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">quiet</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">objs</span><span class="p">:</span>
|
||
<span class="c1"># This is an empty list, so no match</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"No </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s2"> was found."</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">objs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># Take the first match even if there are several</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>All we have changed to obtain a list is a keyword argument in the <code class="docutils literal notranslate"><span class="pre">search</span></code> method: <code class="docutils literal notranslate"><span class="pre">quiet</span></code>. If set
|
||
to <code class="docutils literal notranslate"><span class="pre">True</span></code>, then errors are ignored and a list is always returned, so we need to handle it as such.
|
||
Notice in this example, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> will contain a valid object too, but if several matches are
|
||
found, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> will contain the first one, even if more matches are available.</p>
|
||
</section>
|
||
<section id="global-searches">
|
||
<h3>Global searches<a class="headerlink" href="#global-searches" title="Permalink to this headline">¶</a></h3>
|
||
<p>By default, Evennia will perform a local search, that is, a search limited by the location in which
|
||
the caller is. If you want to perform a global search (search in the entire database), just set the
|
||
<code class="docutils literal notranslate"><span class="pre">global_search</span></code> keyword argument to <code class="docutils literal notranslate"><span class="pre">True</span></code>:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">global_search</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="conclusion">
|
||
<h2>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">¶</a></h2>
|
||
<p>Parsing command arguments is vital for most game designers. If you design “intelligent” commands,
|
||
users should be able to guess how to use them without reading the help, or with a very quick peek at
|
||
said help. Good commands are intuitive to users. Better commands do what they’re told to do. For
|
||
game designers working on MUDs, commands are the main entry point for users into your game. This is
|
||
no trivial. If commands execute correctly (if their argument is parsed, if they don’t behave in
|
||
unexpected ways and report back the right errors), you will have happier players that might stay
|
||
longer on your game. I hope this tutorial gave you some pointers on ways to improve your command
|
||
parsing. There are, of course, other ways you will discover, or ways you are already using in your
|
||
code.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</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="right" >
|
||
<a href="Tutorial-Understanding-Color-Tags.html" title="Understanding Color Tags"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Web-Tweeting-Game-Stats.html" title="Automatically Tweet game stats"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Parsing command arguments, theory and best practices</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</div>
|
||
|
||
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2023, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |