<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>Modul „argparse“</title>
  <date>2012-12-18</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->

<!--
argparse
A new module for command line parsing, argparse, was introduced to overcome the limitations of optparse which did not provide support for positional arguments (not just options), subcommands, required options and other common patterns of specifying and validating options.
-->  


<slide title="Úvod">

    <p>
        Modul <strong>argparse</strong> je poslední inkarnací nadstavbových modulů pro <a href="overview.xml">zpracování argumentů příkazové řádky skriptu</a>.
    </p>
    <p>
        Názory na jeho uspořádání a použití se různí, ale za sebe mohu říct, že pro člověka, který potřebuje příležitostně zpracovat pár argumentů na příkazové řádce, je na použití zdaleka nejprůhlednější a nejpříjemnější. Proto jsem mu věnoval samostatnou kapitolu, ve které se pokusím to důležité z oficiální dokumentace převyprávět na menší ploše.
    </p>

</slide>
<slide title="Příklad">

  <p>
    Ukažme si nejdříve chování kódu vybaveného modulem <em>argparse</em> při různých voláních skriptu z příkazové řádky. Daný skript pouze (přehledně) vypisuje, jaké argumenty na příkazové řádce obdržel:
  </p>
  
  <p class="enumerate">
    Nevíme, jak program vůbec zavolat, tak to zkusíme bez parametrů:
  </p>
  <example layout="vertical">
    <cmd>python3 argparse.mod.py</cmd>
    <out src="_files/argparse/argparse.mod.out1" lang="text"/>
  </example>

  <p class="enumerate">
    Aha, takže přepínače nejsou povinné, ale jméno souboru ano. Tak ho zkusíme přidat (<em>test.html</em>):
  </p>
  <example layout="vertical">
    <cmd>python3 argparse.mod.py test.html</cmd>
    <out src="_files/argparse/argparse.mod.out2" lang="text"/>
  </example>

  <p class="enumerate">
    Vida, přepínače mají nějaké výchozí hodnoty. Nebyla by k nim nějaká nápověda?
  </p>
  <example layout="vertical">
    <cmd>python3 argparse.mod.py --help</cmd>
    <out src="_files/argparse/argparse.mod.out3" lang="text"/>
  </example>
  <note>
    A jak je dobrým zvykem, téhož výsledku dosáhneme zavoláním: <code>python3 argparse.mod.py -h</code>
  </note>

  <p class="enumerate">
    Tak to zkusme všechno najednou:
  </p>
  <example layout="vertical">
    <cmd>python3 argparse.mod.py -x -c stylopis.css test.html</cmd>
    <out src="_files/argparse/argparse.mod.out4" lang="text"/>
  </example>
  <notes>
    <note>
        Přepínačem <code>-c</code> jsme změnili stylopisový soubor (klíč <em>cssfile</em>) z výchozího <em>style.css</em> na <em>stylopis.css</em> a přepínačem <code>-x</code> jsme změnili výchozí hodnotu klíče <em>xhtml_flag</em> z <code>False</code> na <code>True</code>. <em>test.html</em> je jméno souboru (určeného ke zpracování).
    </note>
    <note>
        V „dlouhém“ provedení by volání vypadalo: <code>python3 argparse.mod.py --xhtml --cssfile=stylopis.css test.html</code> (znak <code>=</code> tam sice není povinný, ale lépe se to s ním čte)
    </note>
  </notes>

  <p class="enumerate">
    Aby se neřeklo, tak ještě obligátní dotaz na verzi programu :)
  </p>
  <example layout="vertical">
    <cmd>python3 argparse.mod.py --version</cmd>
    <out src="_files/argparse/argparse.mod.out5" lang="text"/>
  </example>

</slide>
<slide title="Příklad – zdrojový kód">

  <!--p>
    Okomentovaný zdrojový kód k předchozímu příkladu:
  </p-->
  <example src="_files/argparse/argparse.mod.py" lang="python" />

</slide>
<slide title="Několik poznámek">

  <ul>
    <li>
        Mnoho atributů nastavení je nepovinných, potřebné hodnoty se v běžných případech doplní celkem logicky.
    </li>
    <li>
        Mezi přepínači a pozičními argumenty není z hlediska zavedení prakticky žádný rozdíl.
        <note>
            Díky čemuž je uvedení pozičních hlídáno automaticky a nemusíte je pracně ručně ověřovat a vyhazovat výjimku, když se nedopočítáte potřebného počtu.
        </note>
    </li>
    <li>
        Návratový objekt je pouze jeden, jednotlivé argumenty (a to i poziční) jsou v něm přístupné pod svými jmény.
        <note>
            Také z něj jde udělat slovník pomocí funkce <em>vars()</em>. Jinak sám o sobě je to v podstatě pouze objekt s pojmenovanými atributy.
        </note>
    </li>
  </ul>

</slide>
<slide title="Vytvoření parseru">

    <p>
        Nový objekt pro zpracování argumentů příkazové řádky založíme pomocí třídy <strong>ArgumentParser</strong>. Ta má hafo nepovinných parametrů, z nichž nejužitečnější jsou asi následující dva:
    </p>
    <example lang="python">
        parser = ArgumentParser(description="", epilog="")
    </example>
    <ul>
        <li>
            <code>description</code> – text v nápovědě mezi vzorem příkazové řádky a popisem jednotlivých argumentů
        </li>
        <li>
            <code>epilog</code> – text úplně na konci nápovědy
        </li>
    </ul>

</slide>
<slide title="Přidávání argumentů I">

    <p>
        O přidávání argumentů do parseru se stará metoda <strong>add_argument()</strong>. Ta má jediný povinný parametr (nebo skupinu parametrů) – řetězec (řetězce) zadávající jména argumentů. Přitom nepovinné parametry (tzv. <em>přepínače</em>) se od povinných (<em>pozičních</em>) liší přítomností znaménka <code>„-“</code> (v základním nastavení) před svým jménem:
    </p>
    <example lang="python">
        parser.add_argument('-x', '--xhtml')
        parser.add_argument('file')
    </example>
    <br/>
    <note>
        Znak(y) použité pro identifikaci přepínačů je možno předepsat při zavádění parseru:
        <example lang="python">
            parser = argparse.ArgumentParser(prefix_chars='-+/')
        </example>
    </note>

</slide>
<slide title="Přidávání argumentů II">

    <p>
        Zadané argumenty z příkazové řádky skončí ve výstupním objektu <code>Namespace</code> přímo pod svými „dlouhými“ jmény:
    </p>
    <ul>
        <li>
            Nezadaný přepínač získá výchozí hodnotu <em>None</em>:
            <example layout="vertical">
                <cmd>test.py baf</cmd>
                <out lang="python">
                    Namespace(file='baf', xhtml=None)
                </out>
            </example>
        </li>
        <li>
            Přepínače se mohou vyskytovat na libovolném místě zadání a se svojí hodnotou mohou – ale také nemusí (v případě „krátké“ varianty) – být spojeny pomocí znaku <code>=</code> nebo mezery:
            <example layout="vertical">
                <cmd>test.py baf -x haf</cmd>
                <out lang="python">
                    Namespace(file='baf', xhtml='haf')
                </out>
            </example>
            <example layout="vertical">
                <cmd>test.py baf -xhaf</cmd>
                <out lang="python">
                    Namespace(file='baf', xhtml='haf')
                </out>
            </example>
            <example layout="vertical">
                <cmd>test.py -x=haf baf</cmd>
                <out lang="python">
                    Namespace(file='baf', xhtml='haf')
                </out>
            </example>
            <notes>
                <note>
                    Pro „dlouhou“ variantu jsou tedy ale možné pouze varianty <code>--xhtml haf</code> a <code>--xhtml=haf</code>.
                </note>
                <note>
                    Naopak více <em>přepínačů</em> (z nichž nejvýše ten poslední očekává nějaký vstup) může být spojeno dohromady, tj. může být pravda například: <code>test.py -x -y -z ⇔ test.py -xyz</code>
                </note>
            </notes>
        </li>
    </ul>

</slide>
<slide title="Získání argumentů I">

    <p>
        Za „vytažení“ argumentů z příkazové řádky je zodpovědná metoda parseru <code>parse_args()</code>.
    </p>
    <note>
        Ve výchozím nastavení bere svůj vstup ze <code>sys.argv</code>, ale podle potřeby jí stačí přeposlat jako vstup seznam řetězců (tedy v principu stejný seznam jako vrátí <code>sys.argv[1:]</code>).
    </note>
    <p>
        Ta se pokusí podle předpisů z metod <em>add_argument()</em> získat odpovídající hodnoty, kterými naplní návratový objekt <code>Namespace</code>, kde jsou přístupné pod klasickou tečkovou notací:
    </p>
    <example layout="vertical">
        <cmd>test.py -x soubor</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('file')
            args = parser.parse_args()
            print(args)
            print('xhtml:', args.xhtml)
            print('file:', args.file)
        </program>
        <out lang="python">
            Namespace(file='soubor', xhtml=True)
            xhtml: True
            file: soubor
        </out>
    </example>
    <note>
        Potřebujete-li místo toho přidat naparsované argumenty do jiného již existujícího objektu, stačí použít parametr <em>namespace</em>:
        <example layout="vertical">
            <cmd>test.py -x soubor</cmd>
            <program lang="python">
                class MojeArgumenty:
                    pass
                ma = MojeArgumenty()
                
                parser.parse_args(namespace=ma)
                print(ma)
                print('xhtml:', ma.xhtml)
                print('file:', ma.file)
            </program>
            <out lang="python">
                &lt;__main__.MojeArgumenty object at 0x00C2B110>
                xhtml: True
                file: soubor
            </out>
        </example>
    </note>

</slide>
<slide title="Získání argumentů II">

    <p>
        Zároveň se metoda <em>parse_args()</em> pokusí pohlídat většinu chyb, ke kterým může dojít, a v případě výskytu jakékoli z nich běh programu zastaví:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('file')
            args = parser.parse_args()
        </program>
        <out lang="python">
            usage: test.py [-h] [-x] file
            test.py: error: too few arguments
        </out>
    </example>

</slide>
<slide title="Získání argumentů III">

    <p>
        Poziční argument, který začíná na označovač přepínače (ve výchozím nastavení tedy znak <code>-</code>), může pochopitelně v některých případech při načítání způsobit pěkné trable. Chcete-li zdůraznit, že od jistého místa v zadání dále se vyskytují již pouze poziční argumenty, použijte <em>pseudorozdělovač</em> <code>--</code>:
    </p>
    <example layout="vertical">
        <cmd>test.py -x -- soubor</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('file')
            args = parser.parse_args()
            print(args)
            print('xhtml:', args.xhtml)
            print('file:', args.file)
        </program>
        <out lang="python">
            Namespace(file='soubor', xhtml=True)
            xhtml: True
            file: soubor
        </out>
    </example>

</slide>
<slide title="Vlastnosti argumentů">

    <p>
        Konkrétní chování argumentů předepisuje armáda nepovinných parametrů, které jsou však všechny užitečné a proto si je postupně probereme:
    </p>
    <ul>
        <li>
            <a href="?slajd=12"><code>default</code></a> – výchozí hodnota pro nezadaný argument
        </li>
        <li>
            <a href="?slajd=13"><code>required</code></a> – je daný argument vyžadován?
        </li>
        <li>
            <a href="?slajd=14"><code>dest</code></a> – alternativní název pro uložení argumentu
        </li>
        <li>
            <a href="?slajd=15"><code>type</code></a> – typ argumetu
        </li>
        <li>
            <a href="?slajd=17"><code>choices</code></a> – množina možných hodnot argumentu
        </li>
        <li>
            <a href="?slajd=18"><code>action</code></a> – operace k vykonání při zadání argumentu
        </li>
        <li>
            <a href="?slajd=25"><code>nargs</code></a> – násobnost argumentu
        </li>
        <li>
            <a href="?slajd=30"><code>const</code></a> – konstanta (vyžadována některými kombinacemi <em>action</em> a <em>nargs</em>)
        </li>
        <li>
            <a href="?slajd=31"><code>help</code></a> – nápověda pro argument
        </li>
        <li>
            <a href="?slajd=32"><code>metavar</code></a> – alternativní označení argumentu do nápovědy
        </li>
    </ul>

</slide>
<slide title="Parametr „default“">

    <p>
        Výchozí hodnotou pro nepovinné argumenty je <code>None</code>. Chceme-li jinou výchozí hodnotu, použijeme parametr <em>default</em>:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', default="x.py")
            parser.add_argument('-y')
        </program>
        <out lang="python">
            Namespace(x='x.py', y=None)
        </out>
    </example>
    <p>
        Poznámka 1: Nepovinné argumenty jsou především všechny přepínače (není-li pomocí <em>required</em> řečeno jinak), ale patří mezi ně i poziční s možnou násobností 0 (tedy při <em>nargs="?"</em> a <em>nargs="*"</em>).
    </p>
    <p>
        Poznámka 2: Nebude-li nepovinný argument zadán, můžeme místo „probublání“ výchozí hodnoty též úplně zamezit jeho objevení ve výsledném objektu pomocí volby <code>default=argparse.SUPPRESS</code>.
    </p>

</slide>
<slide title="Parametr „required“">

    <p>
        Potřebujeme-li si přítomnost nějakého <strong>přepínače</strong> vynutit, použijeme atribut <em>required</em>:
    </p>
    <example lang="python">
        parser.add_argument('-x', required=True)
        parser.add_argument('-y')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            usage: test.py [-h] -x X [-y Y]
            test.py: error: argument -x is required
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x=haf</cmd>
        <out lang="python">
            Namespace(x='haf', y=None)
        </out>
    </example>

</slide>
<slide title="Parametr „dest“">

    <p>
        Pokud z nejrůznějších důvodů potřebujeme uložit zadaný <strong>přepínač</strong> pod jiným názvem než je jeho „dlouhá“ hodnota z metody <em>add_argument()</em>, použijeme parametr <em>dest</em>:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', dest="metajazyk")
        </program>
        <out lang="python">
            Namespace(metajazyk=None)
        </out>
    </example>

</slide>
<slide title="Parametr „type“">

    <p>
        Modul <em>argparse</em> umí pohlídat typ zadávaných parametrů a provést příslušnou automatickou konverzi <em>z výchozího řetězce</em>:
    </p>
    <example layout="vertical">
        <cmd>test.py -x=2 -y=2</cmd>
        <program lang="python">
            parser.add_argument('-x')
            parser.add_argument('-y', type=int)
        </program>
        <out lang="python">
            Namespace(x='2', y=2)
            x: &lt;class 'str'&gt;
            y: &lt;class 'int'&gt;
        </out>
    </example>
    <p>
        Jelikož typem může být odkaz na libovolný <em>callable</em>-objekt, tedy např. funkci nebo metodu, jsou možnosti zadání vskutku veliké – projde kupříkladu i zadání typu <code>type=open</code>.
    </p>

</slide>
<slide title="Vyrobení vlastního typu">

    <p>
        Vyrobení alternativního typu je směšně jednoduché – stačí např. předat odkaz na funkci, jejímž jediným vstupem bude příslušný řetězec z příkazové řádky. A s ním si pak už můžete dělat, co chcete.
    </p>
    <note>
        Funkce přitom musí vracet svůj vstupní argument (podrobený případné transformaci), nebo vyhodit výjimku <code>argparse.ArgumentTypeError</code> .
    </note>
    <p>
        Kupříkladu zadání hexadecimálního čísla v intervalu <code>0x00</code> až <code>0xff</code> by mohla hlídat například takováto funkce:
    </p>
    <example lang="python">
        from argparse import ArgumentTypeError
        
        def rozsah(val):
            try:
                n = int(val, base=16)
                if 0 &lt;= n &lt;= 255:
                    return n
                else:
                    msg = 'Uvedené číslo nespadá do povoleného rozsahu!'
                    raise ArgumentTypeError(msg)
            except ValueError:
                msg = 'Zadaný vstup se nepodařilo převést na číslo!'
                raise ArgumentTypeError(msg)
         
         # zadání příslušného argumentu
         parser.add_argument('-r', '--rozsah', type=rozsah)
    </example>
    <example layout="vertical">
        <cmd>test.py -r ff</cmd>
        <out lang="python">
            Namespace(rozsah=255)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -r 100</cmd>
        <out lang="python">
            usage: test.py [-h] [-r ROZSAH]
            test.py: error: argument -r/--rozsah: Uvedené číslo nespadá do povoleného rozsahu!
        </out>
    </example>

</slide>
<slide title="Parametr „choices“">

    <p class="enumerate">
        Pokud má mít nějaký přepínač pouze jistou množinu povolených hodnot, přidejte parametr <em>choices=VÝBĚR</em>, kde <em>VÝBĚR</em>em může být jakýkoliv typ, který podporuje operátor <code>in</code>, zde například seznam:
    </p>
    <example lang="python">
        parser.add_argument('-e', '--env',
                            dest='environment',
                            choices=['production', 'staging', 'testing',],
                            default='production',
                            help='Environment to run on')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(environment='production')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -e testing</cmd>
        <out lang="python">
            Namespace(environment='testing')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -e run</cmd>
        <out lang="python">
            usage: test.py [-h] [-e {production,staging,testing}]
            test.py: error: argument -e/--env: invalid choice: 'run' (choose from 'production', 'staging', 'testing')
        </out>
    </example>
    
    <p class="enumerate">
        Stejně tak dobře můžete ale napsat klidně i toto:
    </p>
    <example lang="python">
        parser.add_argument('-n', '--number',
                            type=int,
                            choices=range(1, 10),
                            help="Celočíselný multiplikátor v rozsahu 1-9")
    </example>
    <!--example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(number=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -n 3</cmd>
        <out lang="python">
            Namespace(number=3)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -e testing</cmd>
        <out lang="python">
            usage: test.py [-h] [-n {1,2,3,4,5,6,7,8,9}]
            test.py: error: argument -n/- -number: invalid choice: 11 (choose from 1, 2, 3, 4, 5, 6, 7, 8, 9)
        </out>
    </example-->

</slide>
<slide title="Parametr „action“">

    <p>
        Parametr <em>action</em> určuje, co bude provedeno s hodnotou příslušného argumentu na příkazové řádce. Má k dispozici následující hlavní standardní operace..
    </p>
    <ul>
        <li>
            <code>store</code> – uloží zadanou hodnotu (ve výchozím nastavení jako řetězec) pod daným jménem; toto je výchozí chování metody <em>add_argument()</em>
        </li>
        <li>
            <code>store_const</code> – je-li odpovídající přepínač přítomen, uloží hodnotu parametru <em>const=</em> na tomto argumentu
        </li>
        <li>
            <code>store_true</code> &amp; <code>store_false</code> – uloží boolovskou hodnotu <code>True</code> nebo <code>False</code>
        </li>
        <li>
            <code>append</code> – přidá zadanou hodnotu do seznamu (využití u opakujících se argumentů)
        </li>
        <li>
            <code>append_const</code> – je-li odpovídající přepínač přítomen, přidá do seznamu hodnotu parametru <em>const=</em> na tomto argumentu
        </li>
    </ul>
    <p>
        ..plus jednu specializovanou:
    </p>
    <ul>
        <li>
            <code>version</code> – vypíše hodnotu parametru <code>version=</code> na tomto argumentu
        </li>
    </ul>
    <p>
        Navíc si můžete připsat další vlastní operace rozšířením objektu <code>argparse.Action</code>, budete-li potřebovat.
    </p>

</slide>
<slide title="action='store'">

    <p>
        Uložení zadané hodnoty pod příslušným jménem je výchozí chování parseru argumentů:
    </p>
    <example layout="vertical">
        <cmd>test.py -x ahoj</cmd>
        <program lang="python">
            parser.add_argument('-x')
        </program>
        <out lang="python">
            Namespace(x='ahoj')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x ahoj</cmd>
        <program lang="python">
            parser.add_argument('-x', action='store')
        </program>
        <out lang="python">
            Namespace(x='ahoj')
        </out>
    </example>
    <note>
        Nezapomeňte, že pokud neurčíte jiný typ, je hodnota uložena jako řetězec přímo převzaný z příkazové řádky.
    </note>

</slide>
<slide title="action='store_const'">

    <p>
        Pro přepínače je možné použít specifický způsob zadání hodnoty – je-li nastaveno <code>action='store_const'</code>, signalizuje přítomnost přepínače na příkazové řádce, že pod dané jméno se má uložit hodnota odpovídajícího parametru <code>const=</code>:
    </p>
    <example lang="python">
        parser.add_argument('-x', action='store_const', const='text')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(x=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x</cmd>
        <out lang="python">
            Namespace(x='text')
        </out>
    </example>
    <note>
        Ukládané hodnoty nejsou pochopitelně typem omezeny na řetězce, klidně to mohou být třeba odkazy na funkce.
    </note>

</slide>
<slide title="action='store_true' &amp; action='store_false'">

    <p>
        Tyto dvě akce představují speciální variantu <code>store_const</code>, pouze pod příslušné jméno je uložena odpovídající pravdivostní hodnota:
    </p>
    <example lang="python">
        parser.add_argument('-x', action='store_true')
        parser.add_argument('-y', action='store_false')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(x=False, y=True)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x -y</cmd>
        <out lang="python">
            Namespace(x=True, y=False)
        </out>
    </example>

</slide>
<slide title="action='append'">

    <p>
        Parametr <code>action='append'</code> je velmi výhodný například v případě, kdy se příkazová řádka skládá z více nezávislých kusů a některé z přepínačů se navzájem nepřebíjí (metodou „poslední vítězí“), ale naopak skládají dohromady:
    </p>
    <example lang="python">
        parser.add_argument('-x', action='append')
    </example>
    <example layout="vertical">
        <cmd>test.py -x 1</cmd>
        <out lang="python">
            Namespace(x=['1'])
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x 1 -x=2</cmd>
        <out lang="python">
            Namespace(x=['1', '2'])
        </out>
    </example>

</slide>
<slide title="action='append_const'">

    <p>
        Parametr <code>action='append_const'</code> se chová velmi podobně jako parametr <em>append</em>, jen místo zadané hodnoty se ukládá hodnota z parametru <code>const=</code>. Obzvláště šikovné, pokud má více argumentů stejný cíl:
    </p>
    <example lang="python">
        parser.add_argument('--str', dest='types', action='append_const', const=str)
        parser.add_argument('--int', dest='types', action='append_const', const=int)
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(types=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py --str</cmd>
        <out lang="python">
            Namespace(types=[&lt;class 'str'>])
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py --str --int</cmd>
        <out lang="python">
            Namespace(types=[&lt;class 'str'>, &lt;class 'int'>])
        </out>
    </example>
    <note>
        Naprosto neinvenčně (téměř) opsáno přímo z dokumentace.
    </note>

</slide>
<slide title="action='version'">

    <p>
        Při zadání příslušného přepínače vypíše hodnotu parametru <code>version=</code> na tomto argumentu. V nejjednodušším případě jako prostý řetězec..
    </p>
    <example layout="vertical">
        <cmd>test.py --version</cmd>
        <program lang="python">
            parser.add_argument('--version', action='version', version='2.1')
        </program>
        <out lang="python">
            2.1
        </out>
    </example>
    <p>
        ..ale můžete použít i hodnoty nadefinované jinde:
    </p>
    <example layout="vertical">
        <cmd>test.py --version</cmd>
        <program lang="python">
            parser.add_argument('--version', action='version', version='%(prog)s 2.1')
        </program>
        <out lang="python">
            test.py 2.1
        </out>
    </example>
    <note>
        A kdybyste potřebovali, jméno programu můžete zadat „natvrdo“ při zavádění parseru pomocí <code>parser = ArgumentParser(prog='TestovaciProgram')</code> .
    </note>

</slide>
<slide title="Parametr „nargs“">

    <p>
        Obecně určuje počet argumentů příslušejících pod stejné jméno příslušná akce (tj. parametr <em>action</em>) a většinou to znamená právě jednu hodnotu (a ve výchozím nastavení jako řetězec). Potřebujete-li ale jiné chování, parametr <em>nargs</em> umožňuje určit násobnost (počet výskytů) příslušného argumentu (převážně – ale ne vždy – přepínače). Pro rozlišení používá standardní označení <code>N?*+</code>, konkrétní chování však záleží na typu argumentu:
    </p>
    <ul>
        <li>
            <code>N</code> – uvedený počet (<em>N</em>) argumentů z příkazové řádky bude složen dohromady do seznamu
        </li>
        <li>
            <code>?</code> – je-li přítomna, je z příkazová řádky načtena jedna hodnota
        </li>
        <li>
            <code>*</code> – z příkazové řádky řádky jsou všechny odpovídající argumenty zahrnuty do jednoho seznamu
        </li>
        <li>
            <code>+</code> – stejně jako <code>*</code>, ale narozdíl od ní je alespoň jeden argument vyžadován, jinak dojde k chybě
        </li>
    </ul>

</slide>
<slide title="nargs=N">

    <p class="enumerate">
        Složí uvedený počet (<em>N</em>) argumentů z příkazové řádky do seznamu:
    </p>
    <example lang="python">
        parser.add_argument('-y', nargs=3)
    </example>
    <example layout="vertical">
        <cmd>test.py -y 1</cmd>
        <out lang="python">
            usage: test.py [-h] [-y Y Y Y]
            test.py: error: argument -y: expected 3 arguments
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -y 1 2 3</cmd>
        <out lang="python">
            Namespace(y=['1', '2', '3'])
        </out>
    </example>
    
    <p class="enumerate">
        Pozor – vytvoření seznamu se týká i jednoho argumentu!
    </p>
    <example layout="vertical">
        <cmd>test.py -x a</cmd>
        <program lang="python">
            parser.add_argument('-x', nargs=1)
        </program>
        <out lang="python">
            Namespace(x=['a'])
        </out>
    </example>

</slide>
<slide title="nargs='?'">

    <p class="enumerate">
        Je-li na příkazové řádce přítomna, načte právě jednu hodnotu. Není-li, pak se chování liší podle typu parametru:
    </p>
    <ul>
        <li>
            pro <em>poziční</em> argument doplní hodnotu z <code>default</code>
        </li>
        <li>
            pro <em>přepínač</em> doplní hodnotu z <code>const</code>
        </li>
    </ul>
    <example lang="python">
        parser.add_argument('-x', nargs='?', const='ahoj')
        parser.add_argument('file', nargs='?', default='text')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(file='text', x=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x</cmd>
        <out lang="python">
            Namespace(file='text', x='ahoj')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x baf</cmd>
        <out lang="python">
            Namespace(file='text', x='baf')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x baf soubor</cmd>
        <out lang="python">
            Namespace(file='soubor', x='baf')
        </out>
    </example>
    
    <p class="enumerate">
        Typické použití u pozičních argumentů je například pro volitelné vstupní/výstupní soubory:
    </p>
    <example layout="vertical">
        <program lang="python">
            parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
                                default=sys.stdin)
        </program>
        <out lang="python">
            Namespace(infile=&lt;_io.textIOWrapper name='&lt;stdin&gt;' mode='r' encoding='UTF-8'&gt;)
        </out>
    </example>
    <note>
        Na základě experimentování typ <em>FileType</em> zjevně otevře uvedené soubory vždy v kódování UTF-8 nezávisle na platformě.
    </note>

</slide>
<slide title="nargs='*'">

    <p>
        Načte z příkazové řádky všechny odpovídající argumenty a zahrne je do jednoho seznamu. Ale když nakombinujete dohromady poziční argumenty a přepínače, musíte si dát pozor na jejich počet i pořadí:
    </p>
    <example lang="python">
        parser.add_argument('-x', nargs='*')
        parser.add_argument('-y', nargs='*')
        parser.add_argument('file', nargs='*', default='text')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(file1='text', x=None, y=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x 1 2 3</cmd>
        <out lang="python">
            Namespace(file='text', x=['1', '2', '3'], y=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x 1 2 3 -y a</cmd>
        <out lang="python">
            Namespace(file='text', x=['1', '2', '3'], y=['a'])
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py vrr haf -x 1 2 3 -y a</cmd>
        <out lang="python">
            Namespace(file=['vrr', 'haf'], x=['1', '2', '3'], y=['a'])
        </out>
    </example>

</slide>
<slide title="nargs='+'">

    <p>
        Parametr <em>nargs='+'</em> se chová z větší části stejně jako <em>nargs='*'</em>, ale narozdíl od něj je alespoň jeden argument na vstupu vyžadován, jinak dojde k chybě:
    </p>
    <example lang="python">
        parser.add_argument('-x', nargs='+')
        parser.add_argument('file', nargs='+', default='text')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            usage: test.py [-h] [-x X [X ...]] file [file ...]
            test.py: error: too few arguments
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py vrr haf</cmd>
        <out lang="python">
            Namespace(file=['vrr', 'haf'], x=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py vrr haf -x</cmd>
        <out lang="python">
            usage: test.py [-h] [-x X [X ...]] file [file ...]
            test.py: error: argument -x: expected at least one argument
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py vrr haf -x 1 2</cmd>
        <out lang="python">
            Namespace(file=['vrr', 'haf'], x=['1', '2'])
        </out>
    </example>

</slide>
<slide title="Parametr „const“">

    <p class="enumerate">
        Jak jsme viděli na příkladu akcí <a href="?slajd=20"><code>store_const</code></a> a <a href="?slajd=23"><code>append_const</code></a>, určuje parametr <code>const</code> hodnotu, která bude uložena/přidána pod příslušné jméno:
    </p>
    <example lang="python">
        parser.add_argument('-x', action='store_const', const='vrr')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(x=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -x</cmd>
        <out lang="python">
            Namespace(x='vrr')
        </out>
    </example>
    
    <p class="enumerate">
        Kromě toho pro přepínače s násobností <code>?</code> (tedy nulakrát nebo právě jednou) dodává výchozí hodnotu, nejsou-li uvedeny:
    </p>
    <example lang="python">
        parser.add_argument('-y', const='haf', nargs='?')
    </example>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <out lang="python">
            Namespace(y=None)
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -y</cmd>
        <out lang="python">
            Namespace(y='haf')
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -y vrr</cmd>
        <out lang="python">
            Namespace(y='vrr')
        </out>
    </example>

</slide>
<slide title="Parametr „help“">

    <p class="enumerate">
        Do parametru help se uvádí text, který se vypíše uživateli při vyžádání nápovědy (typicky tedy jako odpověď na <em>-h</em> nebo <em>--help</em>):
    </p>
    <example layout="vertical">
        <cmd>test.py -h</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml',
                                help='Proveď parsování podle standardu XHTML.')
        </program>
        <out lang="python">
            usage: test.py [-h] [-x XHTML]
            
            optional arguments:
              -h, --help            show this help message and exit
              -x XHTML, --xhtml XHTML
                                    Proveď parsování podle standardu XHTML.
        </out>
    </example>
    
    <p class="enumerate">
        Při zadávání (nejen) nápovědních textů můžete s výhodou využít některé předdefinované (rozuměno zavedením parseru) formátovací konstanty jako například:
    </p>
    <ul>
        <li>
            <code>%(prog)s</code> – jméno skriptu (nebo hodnota parametru <em>prog=</em> na konstruktoru <code>argparse.ArgumentParser()</code>)
        </li>
        <li>
            hodnoty prakticky všech možných parametrů na metodě <code>add_argument()</code>, tj. <code>%(prog)s</code>, <code>%(dest)s</code>, <code>%(nargs)s</code>, <code>%(const)s</code>, <code>%(default)s</code>, <code>%(type)s</code>, <code>%(choices)s</code> a <code>%(metavar)s</code> (a dokonce i <code>%(help)s</code> :-)
        </li>
        <!--li>
            a možná i něco dalšího…
        </li-->
    </ul>

</slide>
<slide title="Parametr „metavar“">

    <p class="enumerate">
        Při výpisu nápovědy (jak v popisu <em>usage:</em> tak v celé nápovědě <em>--help</em>) jsou použita jistým způsobem upravená jména cílových proměnných pro jednotlivé argumenty:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('-n', '--numbers', nargs=3)
            parser.add_argument('file')
        </program>
        <out lang="python">
            usage: test.py [-h] [-x] [-n NUMBERS NUMBERS NUMBERS] file
            test.py: error: too few arguments
        </out>
    </example>
    <p>
        Tyto <strong>zobrazené</strong> názvy (a pouze je, nic jiného!) můžete změnit pomocí parametru <code>metavar=</code>:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('-n', '--numbers', nargs=3, metavar='N')
            parser.add_argument('file')
        </program>
        <out lang="python">
            usage: test.py [-h] [-x] [-n N N N] file
            test.py: error: too few arguments
        </out>
    </example>
    
    <p class="enumerate">
        Co je šikovné, tak v součinnosti s hodnotou parametru <em>nargs</em> můžete zadat <em>metavars</em> jako n-tici:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            parser.add_argument('-x', '--xhtml', action='store_true')
            parser.add_argument('-n', '--numbers', nargs=3, metavar=('N1', 'N2', 'N3'))
            parser.add_argument('file')
        </program>
        <out lang="python">
            usage: test.py [-h] [-x] [-n N1 N2 N3] file
            test.py: error: too few arguments
        </out>
    </example>

</slide>
<slide title="Skupiny argumentů">

    <p>
        Ve výchozím nastavení rozděluje nápověda při výpisu argumenty na dvě skupiny – přepínače a poziční. Chcete-li, můžete si ale provést vlastní rozdělení do skupin za pomoci metody <strong>add_argument_group()</strong> příslušného parseru. Argumenty se pak přidávají místo parseru do těchto nově zavedených skupin:
    </p>
    <example layout="vertical">
        <cmd>python3 argparse_groups.py --help</cmd>
        <program src="_files/argparse/argparse_groups.py" lang="python"/>
        <out src="_files/argparse/argparse_groups.out" lang="text"/>
    </example>
    <note>
        Argumenty nepřiřazené explicitně do těchto nových skupin skončí na původních místech (tedy ve skupinách <em>positional arguments</em> a/nebo <em>optional arguments</em>).
    </note>

</slide>
<slide title="Navzájem se vylučující argumenty">

    <p class="enumerate">
        Někdy můžete potřebovat, aby z nějaké skupiny či skupin šel vybrat pouze a jenom právě jeden argument. <em>argparse</em> má pro tento případ k dispozici metodu <strong>add_mutually_exclusive_group()</strong>. Navzdory podobnosti s metodou <em>add_argument_group()</em> nevytváří (alespoň zatím) žádné v nápovědě viditelné skupiny – uvedené argumenty jsou pouze svázány poutem exkluzivity:
    </p>
    <example lang="python">
        group1 = parser.add_mutually_exclusive_group()
        group1.add_argument("-x", action="store_true")
        group1.add_argument("-y", action="store_true")
        group1.add_argument("-z", action="store_true")
    </example>
    <example layout="vertical">
        <cmd>test.py -xyz</cmd>
        <out lang="python">
            usage: test.py [-h] [-x | -y | -z]
            test.py: error: argument -y: not allowed with argument -x
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -yz</cmd>
        <out lang="python">
            usage: test.py [-h] [-x | -y | -z]
            test.py: error: argument -z: not allowed with argument -y
        </out>
    </example>
    <example layout="vertical">
        <cmd>test.py -z</cmd>
        <out lang="python">
            Namespace(x=False, y=False, z=True)
        </out>
    </example>
    
    <p class="enumerate">
        Zadání argumentu z exkluzivní skupiny lze vynutit pomocí parametru <code>required=True</code>:
    </p>
    <example layout="vertical">
        <cmd>test.py</cmd>
        <program lang="python">
            group1 = parser.add_mutually_exclusive_group(required=True)
            group1.add_argument("-x", action="store_true")
            group1.add_argument("-y", action="store_true")
        </program>
        <out lang="python">
            usage: test.py [-h] (-x | -y)
            test2.py: error: one of the arguments -x -y is required
        </out>
    </example>

</slide>
<slide title="Výchozí hodnoty argumentů">

    <p>
        Argumenty mohou mít nějaku hodnotu, aniž bychom je vždy museli zadávat na příkazové řádce (pokud samozřejmě nepatří mezi argumenty vyžadované). Mohou ji získat:
    </p>
    <ul>
        <li>
            <a href="?slajd=12">při zavedení z parametru <code>default=</code></a> ;
        </li>
        <li>
            z globálního nastavení pomocí metody parseru <strong>set_defaults()</strong>.
        </li>
    </ul>
    <p>
        Metoda <em>set_defaults()</em> zvítězí i nad parametrem <em>default=</em>, jak si snadno můžeme ověřit pomocí doplňkové metody <strong>get_default()</strong>:
    </p>
    <example layout="vertical">
        <cmd>test.py -x 123</cmd>
        <program lang="python">
            parser.add_argument("-x", default="Monty")
            parser.add_argument("-y", default="Python")
            parser.set_defaults(y='not snake', z=42)
            
            args = parser.parse_args()
            print( args )
            
            print( 'x default:', parser.get_default('x') )
            print( 'y default:', parser.get_default('y') )
            print( 'z default:', parser.get_default('z') )
        </program>
        <out lang="python">
            Namespace(x='123', y='not snake', z=42)
            x default: Monty
            y default: not snake
            z default: 42
        </out>
    </example>
    <note>
        Vlastnost výstupního objektu <code>args.z</code> v tomto kódu přitom není zadatelná z příkazové řádky! Má tedy právě tu hodnotu, na jakou ji <em>set_defaults()</em> nastaví.
    </note>

</slide>
<slide title="Částečné zpracování">

    <p>
        Díky metodě <strong>parse_known_args()</strong> umožňuje modul <em>argparse</em> zpracovat i příkazovou řádku, která obsahuje neznámé argumenty (na kterých by se metoda <em>parse_args()</em> s hláškou „<em>unrecognized arguments</em>“ zastavila). Vrací přitom dvojici, jejímž:
    </p>
    <ol>
        <li>
            prvním prvkem je standardní návratový objekt rozpoznaných argumentů;
        </li>
        <li>
            druhým prvkem je seznam nerozpoznaných argumentů.
        </li>
    </ol>
    <example layout="vertical">
        <cmd>test.py -y -t --baf=vrr</cmd>
        <program lang="python">
            parser.add_argument("-x", action="store_true")
            parser.add_argument("-y", action="store_true")
            
            args = parser.parse_known_args()
            print( args )
        </program>
        <out lang="python">
            (Namespace(x=False, y=True), ['-t', '--baf=vrr'])
        </out>
    </example>

</slide>
<slide title="„Argumenty argumentů“">

    <p>
    </p>
    <example lang="python">
    </example>

</slide>
<!--
<slide title="">

    <p>
    </p>
    <example lang="python">
    </example>

</slide>
-->

</lecture>
