# -*- org-confirm-babel-evaluate: nil -*- 
#+author:      Thomas P. Harte
#+title:       ~parsearg~: turns ~argparse~ on its head, the declarative way
#+email:       tharte@cantab.net

#+latex_class:   review
#+latex_header:  \usepackage{minted}
#+latex_header:  \usemintedstyle{colorful}
#+latex_header:  \usepackage[ruled]{algorithm2e}
#+latex_header:  \usepackage{enumitem}
#+options:       tex:imagemagick
#+include:       /home/tharte/dot/include/newcommands.tex src latex
#+bind:          org-latex-image-default-width 0.5\linewidth $
#+bind:          org-latex-prefer-user-labels t
#+latex_header:  \newtheorem{lemma}{Lemma}[section]
#+latex_header:  \newtheorem{defn}{Definition}[section]
#+latex_header:  \newtheorem{remark}{Remark}[section]

#+latex_header_extra: \usepackage{mdframed}
#+latex_header_extra: \BeforeBeginEnvironment{verbatim}{\begin{mdframed}}
#+latex_header_extra: \AfterEndEnvironment{verbatim}{\end{mdframed}}

#+begin_export latex
\newtcbox{\mybox}[1][]{%
    nobeforeafter, math upper, tcbox raise base,
    enhanced, colframe=blue!30!black,
    colback=lightgreen!30, boxrule=0.5pt,
    #1
}
#+end_export

#+BEGIN_SRC bash :session *bash* :exports none :results silent :eval yes 
export PYTHONPATH=/home/tharte/dot/py/python/parsearg
#+END_SRC

* Quickstart
** Overview
~parsearg~ is a Python package for writing command-line interfaces (``CLI'')
that augments (rather than replaces) the standard Python module for writing
CLIs, ~argparse~. There is nothing wrong with ~argparse~: It's fine in terms of
the /functionality/ that it provides, but it can be clunky to use, especially
when a program's structure has subcommands, or nested subcommands (/i.e./
subcommands that have subcommands).  Moreover, because of the imperative
nature of ~argparse~, it makes it hard to understand how a program's interface
is structured (/viz./ the program's ``view'').

~parsearg~ puts a layer on top of ~argparse~ that makes writing a CLI easy. you
declare your CLI, /i.e./ your program's view, with a ~dict~ so that the view
is a data structure (/i.e./ pure configuration). The view declares the /intent/ of
the CLI and you are relieved of the trouble of having to instruct ~argparse~
on how to put the CLI together: ~parsearg~ does that for you.

** Usage
Suppose we wish to create a program called ~todos.py~ to manage the TO-DOs
of a set of different users. We want to have subprograms of ~todos.py~; for
example, we may want to create a user (~python todos.py create user~, say),
or we may want to create a TO-DO for a particular user 
(~python todos.py create todo~, say).  
We might also want to add optional parameters to each subprogram such as
the user's email and phone number, or the TO-DO's due date. An invocation
of the program's CLI might look like the following:
#+BEGIN_SRC bash :session *bash* :exports code :results silent :eval no
python todos.py create user Bob --email=bob@email.com --phone=+1-212-555-1234
python todos.py create todo Bob 'taxes' --due-date=2021-05-17
#+END_SRC
With ~argparse~, the subprogram ~create~ would necessitate fiddling
with subparsers.  With ~parsearg~, the CLI for the above is declared
with a ~dict~ and ~parsearg.parser.ParseArg~ supplants the normal use of
~argparse.ArgumentParser~. Moreover, the callback associated with
each subcommand is explicitly linked to its declaration.
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval no :tangle ./todos.py
import sys
from parsearg.parser import ParseArg

def create_user(args):
    print(f'created user: {args.name!r} (email: {args.email}, phone: {args.phone})')
    
def create_todo(args):
    print(f'created TO-DO for user {args.user!r}: {args.title} (due: {args.due_date})')
    
view = {
    'create|user': {
        'callback':   create_user,
        'name':       {'help': 'create user name', 'action': 'store'},
        '-e|--email': {'help': "create user's email address", 'action': 'store', 'default': ''},
        '-p|--phone': {'help': "create user's phone number", 'action': 'store', 'default': ''},
    },
    'create|todo': {
        'callback':   create_todo,
        'user':       {'help': 'user name', 'action': 'store'},
        'title':      {'help': 'title of to-do', 'action': 'store'},
        '-d|--due-date': {'help': 'due date for the to-do', 'action': 'store', 'default': None},
    },
}

def main(args):
    # ParseArg takes the place of argparse.ArgumentParser
    parser = ParseArg(d=view)

    # parser.parse_args returns an argparse.Namespace
    ns     = parser.parse_args(args)

    # ns.callback contains the function in the 'callback' key of 'view'
    result = ns.callback(ns)

if __name__ == "__main__":
    args = sys.argv[1:] if len(sys.argv) > 1 else []
    
    main(' '.join(args))
#+END_SRC
A fully worked version of the ~todos.py~ example is presented in the docs. The output
of the above is:
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py create user Bob --email=bob@email.com --phone=212-555-1234
#+END_SRC

#+RESULTS:
: created user: 'Bob' (email: bob@email.com, phone: 212-555-1234)

#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py create todo Bob 'taxes' --due-date=2021-05-17
#+END_SRC

#+RESULTS:
: created TO-DO for user 'Bob': taxes (due: 2021-05-17)

Because ~parsearg~ is built on top of ~argparse~, all the usual features
are available, such as the extensive help features (essentially
making the CLI self-documenting):
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py --help
#+END_SRC

#+RESULTS:
: usage: todos.py [-h] {create} ...
: 
: positional arguments:
:   {create}
: 
: optional arguments:
:   -h, --help  show this help message and exit


#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py create --help
#+END_SRC

#+RESULTS:
: usage: todos.py create [-h] {user,todo} ...
: 
: positional arguments:
:   {user,todo}
: 
: optional arguments:
:   -h, --help   show this help message and exit

#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py create user --help
#+END_SRC

#+RESULTS:
#+begin_example
usage: todos.py create user [-h] [-e EMAIL] [-p PHONE] name

positional arguments:
  name                  create user name

optional arguments:
  -h, --help            show this help message and exit
  -e EMAIL, --email EMAIL
                        create user's email address
  -p PHONE, --phone PHONE
                        create user's phone number
#+end_example

#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
python todos.py create todo --help
#+END_SRC

#+RESULTS:
#+begin_example
usage: todos.py create todo [-h] [-d DUE_DATE] user title

positional arguments:
  user                  user name
  title                 title of to-do

optional arguments:
  -h, --help            show this help message and exit
  -d DUE_DATE, --due-date DUE_DATE
                        due date for the to-do
#+end_example

* Overview
The ``standard'' Python module for writing command-line interfaces (``CLI'') 
is ~argparse~. It is standard in so far as it is one of the batteries 
that comes included with the Python distribution, so no special 
installation is required. 
Probably because ~argparse~ is a bit clunky to use,
many other (non-standard) packages have been developed for creating CLIs.
Why ``clunky''? Putting together a CLI with ~argparse~
alone is nothing if not an exercise in imperative programming, and this
has three very negative consequences:

1. It obfuscates the intention of the CLI design;
2. It is prone to errors;
3. It discourages CLI design in the first instance; it
   makes debugging a CLI design very difficult; and it makes refactoring or
   re-configuring the CLI design overly burdensome.

In spite of this clunkiness, ~argparse~ has everything we need in 
terms of functionality. ~parsearg~, then, is nothing more than a layer
over ~argparse~ that exposes the ~argparse~ functionality via a ~dict~.
The ~dict~ is the View component of the 
[[https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller][Model-View-Controller (``MVC'')]]
design pattern. The ~dict~ embeds callbacks from the Controller component,
thereby achieving a clean separation of duties, which is what the MVC pattern
calls for. By separating the View component into a ~dict~, the CLI design
can be expressed in a declarative way: ~parsearg~ manifests the /intention/
of the CLI design without having to specify how that design is implemented
in terms of ~argparse~'s parsers and subparsers (~parsearg~ does that
for you).

Other packages---such as [[https://click.palletsprojects.com][~click~]] and [[https://micheles.github.io/plac/][~plac~]]---effectively
decorate functions that are part of the Controller with functionality
from the View. Unfortunately, while this may 
expose the functionality of ~argparse~ in a more friendly way
via the packages' decorators, it dissipates the elements of the View 
across the Controller and in so doing it makes the CLI design difficult to grasp.

The ~parsearg~ philosophy is that ~argparse~ is already good enough in terms
of the functionality that it provides, but that it just needs a little nudge
in terms of how it's used.  Arguments to be added to a CLI with ~argparse~
can be clearly specified as data, as can the callbacks that
consume these arguments. ~parsearg~ takes advantage of this by specifiying
everything (in the View component of MVC) as a ~dict~, from which ~parsearg~
then generates a parser (or set of nested parsers) using ~argparse~. 
The Controller is then free to use the generated parser.

The ~parsearg~ approach is declarative because it manifests the CLI design
in a data structure: a ~dict~, which is one of Python's built-in data structures.
The keys of this ~dict~ form a flattened tree of the CLI's subcommands. 
Keys like ~A|B|C~ are easy to specify and neatly summarize the nested
hierarchy of subcommands: ~A -> B -> C~. They are also easy to change. 
The magic, such as it is, of ~parsearg~ is that it unflattens 
this flattened tree into a tree of ~argparse~ parsers. ~parsearg~ requires nothing 
special: It works with Python out of the box, and therefore uses
what's already available without introducing dependencies.

Simple? The following examples should help.

* ~parsearg.examples.todos~
Yet another To-Do app? Yes, because:

1. It illustrates a  sufficiently realistic, but not overly complex, problem;
2. It also illustrates the MVC pattern in the wild;
3. It shows how ~parsearg~ neatly segments the View component into a ~dict~.
  
Let's start with the outer layer of the onion. How do we interact with ~todos.py~?
#+BEGIN_SRC bash :session *bash* :exports none :results silent :eval yes 
export PYTHONPATH=/home/tharte/dot/py/python/parsearg
rm -rf /home/tharte/dot/py/python/parsearg/examples/todo.db
#+END_SRC
First, create some users in the ~User~ table with the ~create user~ subcommand of ~todos.py~. 
Note that we do not (yet) have a phone number for user ~bar~, nor do we have an email address
for user ~qux~:
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes :tangle ./parsearg/examples/todos.sh
python todos.py create user foo -e foo@foo.com -p 212-555-1234
python todos.py create user bar -e bar@bar.com
python todos.py create user qux -p 212-123-5555
#+END_SRC

#+RESULTS:
: 'create user foo -e foo@foo.com -p 212-555-1234':
: -------------------------------------------------
: 	None
: 'create user bar -e bar@bar.com':
: ---------------------------------
: 	None
: 'create user qux -p 212-123-5555':
: ----------------------------------
: 	None

Second, create some to-dos in the ~Todo~ table with the ~create todo~ subcommand of ~todos.py~. 
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes :tangle ./parsearg/examples/todos.sh
python todos.py create todo foo title1 -c description1 -d 2020-11-30
python todos.py create todo foo title2 -c description2 --due-date=2020-12-31
python todos.py create todo qux todo-1 --description=Christmas-party -d 2020-11-30
python todos.py create todo qux todo-2 --description=New-Year-party
#+END_SRC

#+RESULTS:
#+begin_example
'create todo foo title1 -c description1 -d 2020-11-30':
-------------------------------------------------------
	None
'create todo foo title2 -c description2 --due-date=2020-12-31':
---------------------------------------------------------------
	None
'create todo qux todo-1 --description=Christmas-party -d 2020-11-30':
---------------------------------------------------------------------
	None
'create todo qux todo-2 --description=New-Year-party':
------------------------------------------------------
	None
#+end_example

Let's make some changes to the records entered so far. We can add an email address
for user ~qux~ and a phone number for user ~bar~ using the ~update user email~ 
and ~update user phone~ subcommands, respectively:
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes :tangle ./parsearg/examples/todos.sh
python todos.py update user email qux qux@quxbar.com
python todos.py update user phone bar 203-555-1212
#+END_SRC

#+RESULTS:
: 'update user email qux qux@quxbar.com':
: ---------------------------------------
: 	None
: 'update user phone bar 203-555-1212':
: -------------------------------------
: 	None

Now update two of the to-dos, changing the ~title~ and the ~description~ in the fourth
to-do using the ~update todo title~ and ~update todo description~ subcommands, respectively:
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes :tangle ./parsearg/examples/todos.sh
python todos.py update todo title 4 most-important
python todos.py update todo description 4 2021-party
#+END_SRC

#+RESULTS:
: 'update todo title 4 most-important':
: -------------------------------------
: 	None
: 'update todo description 4 2021-party':
: ---------------------------------------
: 	None

#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes :tangle ./parsearg/examples/todos.sh
python todos.py show users
python todos.py show todos
#+END_SRC

#+RESULTS:
: 'show users':
: -------------
: 	None
: 'show todos':
: -------------
: 	None

The result of these commands is that the two tables (~User~ and ~Todo~)
are populated in a [[https://www.sqlite.org/index.html][SQLite]] database:
#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
sqlite3 todo.db 'select * from User;'
#+END_SRC

#+RESULTS:
: foo|foo@foo.com|212-555-1234|2020-11-23 22:02:58
: bar|bar@bar.com|203-555-1212|2020-11-23 22:02:58
: qux|qux@quxbar.com|212-123-5555|2020-11-23 22:02:59

#+BEGIN_SRC bash :session *bash* :exports both :results output :eval yes 
sqlite3 todo.db 'select * from Todo;'
#+END_SRC

#+RESULTS:
: 1|title1|description1|2020-11-30|2020-11-23 22:03:02|foo
: 2|title2|description2|2020-12-31|2020-11-23 22:03:02|foo
: 3|todo-1|Christmas-party|2020-11-30|2020-11-23 22:03:02|qux
: 4|most-important|2021-party|None|2020-11-23 22:03:02|qux

Let's look at the Python code for [[https://github.com/tharte/parsearg/blob/master/parsearg/examples/todos.py][~todos.py~]].

The View component is entirely contained within a single ~dict~, /viz./ 
~view~, which has been formatted here for clarity using ~parsearg.utils.show~:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.examples.todos import view
from parsearg.utils import show

show(view)
#+END_SRC

#+RESULTS:
#+begin_example
'purge|users':
    'callback':
    <function purge_users at 0x7f535ca1dca0>
'purge|todos':
    'callback':
    <function purge_todos at 0x7f535ca298b0>
'show|users':
    'callback':
    <function show_users at 0x7f535ca29940>
'show|todos':
    'callback':
    <function show_todos at 0x7f535ca299d0>
'create|user':
    'callback':
    <function create_user at 0x7f535ca29a60>
    'name':
    {'help': 'create user name', 'action': 'store'}
    '-e|--email':
    {'help': "create user's email address", 'action': 'store', 'default': ''}
    '-p|--phone':
    {'help': "create user's phone number", 'action': 'store', 'default': ''}
'create|todo':
    'callback':
    <function create_todo at 0x7f535ca29af0>
    'user':
    {'help': 'user name', 'action': 'store'}
    'title':
    {'help': 'title of to-do', 'action': 'store'}
    '-c|--description':
    {'help': 'description of to-do', 'action': 'store', 'default': ''}
    '-d|--due-date':
    {'help': 'due date for the to-do', 'action': 'store', 'default': None}
'update|user|email':
    'callback':
    <function update_user_email at 0x7f535ca29b80>
    'name':
    {'help': 'user name', 'action': 'store'}
    'email':
    {'help': 'user email', 'action': 'store'}
'update|user|phone':
    'callback':
    <function update_user_phone at 0x7f535ca29c10>
    'name':
    {'help': 'user name', 'action': 'store'}
    'phone':
    {'help': 'user phone', 'action': 'store'}
'update|todo|title':
    'callback':
    <function update_todo_title at 0x7f535ca29ca0>
    'id':
    {'help': 'ID of to-do', 'action': 'store'}
    'title':
    {'help': 'title of to-do', 'action': 'store'}
'update|todo|description':
    'callback':
    <function update_todo_description at 0x7f535ca29d30>
    'id':
    {'help': 'ID of to-do', 'action': 'store'}
    'description':
    {'help': 'description of to-do', 'action': 'store'}
#+end_example

We can generate a tree view of the CLI design specified by the above ~dict~,
namely ~view~, as follows:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.parser import ParseArg

print(
    ParseArg(d=view, root_name='TODO').tree.show(quiet=True)
)
#+END_SRC

#+RESULTS:
#+begin_example
TODO
    show
        users
        todos
    update
        todo
            description
            title
        user
            phone
            email
    purge
        users
        todos
    create
        todo
        user
#+end_example

* How ~parsearg~ works 
It is easier to explain how ~parsearg~ works with a simpler abstraction 
than the above example ~parsearg.examples.todos.py~. Here, we will 
consider nested parsers as trees. We introduce two trees for this purpose:
1. The ``A tree'', and
2. The ``A-AA tree''.

** The A-tree 
The ``A tree'' has three levels. As each node of the tree must
necessarily occupy a positional argument of the command line,
~[-f]~ and ~[-g]~ are correspondingly /optional arguments/ that attach to the nodes 
(~A~ and ~BB~, respectively, in the below diagram). 
#+headers:  :width 800 :height 500 
#+begin_src dot :exports results :results output graphics :file ./img/tree-abc-a.png
digraph G {
    subgraph {
    edge [penwidth=0.5; arrowsize=0.65, arrowhead=normal];
        A   [label="A"shape=none];
        Aopt[label="", shape=none];
        B   [shape=none];
        BB  [shape=none];
        BBB [shape=none];
        BBopt[label="", shape=none];
        C   [shape=none];
        CC  [shape=none];
        CCC [shape=none];

	A -> Aopt [minlen=0.1, label="[-f]", dir=back, fontsize=10.0, arrowType=vee];
	A -> B;
	A -> BB;
	A -> BBB;
	BB -> BBopt [minlen=0.1, label="[-g]", dir=back, fontsize=10.0];
	BB -> C;
	BB -> CC;
	BB -> CCC;
    }
}
#+END_SRC

#+attr_latex: :width 0.5\linewidth
#+RESULTS:
[[file:./img/tree-abc-a.png]]

Let's look at the Python code for [[https://github.com/tharte/parsearg/blob/master/parsearg/examples/a.py][~a.py~]].

Consider the ~dict~ that represents the View component:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.examples.a import view

show(view)
#+END_SRC

#+RESULTS:
#+begin_example
'A':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c992550>
    '-c':
    {'help': 'A [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A verbosity', 'action': 'store_true'}
'A|B':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bdaf0>
    '-c':
    {'help': 'A B [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A B verbosity', 'action': 'store_true'}
'A|BB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bdb80>
    '-c':
    {'help': 'A BB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB erbosity', 'action': 'store_true'}
'A|BB|C':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bdc10>
    '-c':
    {'help': 'A BB C [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB C verbosity', 'action': 'store_true'}
'A|BB|CC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bdca0>
    '-c':
    {'help': 'A BB CC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB CC verbosity', 'action': 'store_true'}
'A|BB|CCC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bdd30>
    '-c':
    {'help': 'A BB CCC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB CCC verbosity', 'action': 'store_true'}
'A|BBB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5bddc0>
    '-c':
    {'help': 'A BBB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BBB verbosity', 'action': 'store_true'}


#+end_example
and then the tree that the parsed ~dict~ is represented by:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
print(
    ParseArg(d=view, root_name='TODO').tree.show(quiet=True)
)
#+END_SRC

#+RESULTS:
#+begin_example
TODO
    A
        BBB
        B
        BB
            CC
            CCC
            C


#+end_example

** The A-A tree 
The A-AA tree simply extends the A tree one level:
#+headers:  :width 800 :height 500 
#+begin_src dot :exports results :results output graphics :file ./img/tree-abc-a-aa.png
digraph G {
    subgraph {
    edge [penwidth=0.5; arrowsize=0.65];
        root [label="root"; shape=none];
        A    [shape=none];
        AA   [shape=none];
        Aopt [label="", shape=none];
        AAopt [label="", shape=none];
        B1   [label="B";   shape=none];
        BB1  [label="BB";  shape=none];
        BBB1 [label="BBB"; shape=none];
        C1opt [label="";   shape=none];
        C1   [label="C";   shape=none];
        CC1  [label="CC";  shape=none];
        CCC1 [label="CCC";  shape=none];
        B2   [label="B";   shape=none];
        BB2  [label="BB";  shape=none];
        BBB2 [label="BBB"; shape=none];
        C2opt [label="";   shape=none];
        C2   [label="C";   shape=none];
        CC2  [label="CC";  shape=none];
        CCC2 [label="CCC";  shape=none];

	root -> A;
	root -> AA;
	A -> Aopt [minlen=0.1, label="[-f]", dir=back, fontsize=10.0];
	A -> B1;
	A -> BB1;
	A -> BBB1;
	BB1 -> C1;
	BB1 -> C1opt [minlen=0.1, label="[-g]", dir=back, fontsize=10.0];
	BB1 -> CC1;
	BB1 -> CCC1;

	AA -> AAopt [minlen=0.1, label="[-f]", dir=back, fontsize=10.0];
	AA -> B2;
	AA -> BB2;
	AA -> BBB2;
	BB2 -> C2opt [minlen=0.1, label="[-g]", dir=back, fontsize=10.0];
	BB2 -> C2;
	BB2 -> CC2;
	BB2 -> CCC2;
    }
}
#+END_SRC

#+attr_latex: :width 1.0\linewidth
#+RESULTS:
[[file:./img/tree-abc-a-aa.png]]

Let's look at the Python code for [[https://github.com/tharte/parsearg/blob/master/parsearg/examples/a_aa.py][~a_aa.py~]].

Consider the ~dict~ that represents the View component:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.examples.a_aa import view

show(view)
#+END_SRC

#+RESULTS:
#+begin_example
'A':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5d0820>
    '-c':
    {'help': 'A [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A verbosity', 'action': 'store_true'}
'A|B':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5d08b0>
    '-c':
    {'help': 'A B [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A B verbosity', 'action': 'store_true'}
'A|BB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5d0940>
    '-c':
    {'help': 'A BB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB erbosity', 'action': 'store_true'}
'A|BB|C':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5d09d0>
    '-c':
    {'help': 'A BB C [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB C verbosity', 'action': 'store_true'}
'A|BB|CC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5d0a60>
    '-c':
    {'help': 'A BB CC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB CC verbosity', 'action': 'store_true'}
'A|BB|CCC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c05e0>
    '-c':
    {'help': 'A BB CCC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BB CCC verbosity', 'action': 'store_true'}
'A|BBB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0f70>
    '-c':
    {'help': 'A BBB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'A BBB verbosity', 'action': 'store_true'}
'AA':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0ee0>
    '-c':
    {'help': 'AA [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA verbosity', 'action': 'store_true'}
'AA|B':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0e50>
    '-c':
    {'help': 'AA B [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA B verbosity', 'action': 'store_true'}
'AA|BB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0d30>
    '-c':
    {'help': 'AA BB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA BB verbosity', 'action': 'store_true'}
'AA|BB|C':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0ca0>
    '-c':
    {'help': 'AA BB C [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA BB C verbosity', 'action': 'store_true'}
'AA|BB|CC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c0c10>
    '-c':
    {'help': 'AA BB CC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA BB CC verbosity', 'action': 'store_true'}
'AA|BB|CCC':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c00d0>
    '-c':
    {'help': 'AA BB CCC [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA BB CCC verbosity', 'action': 'store_true'}
'AA|BBB':
    'callback':
    <function make_callback.<locals>.func at 0x7f535c5c04c0>
    '-c':
    {'help': 'AA BBB [optional pi]', 'action': 'store_const', 'const': 3.141593}
    '-v|--verbose':
    {'help': 'AA BBB verbosity', 'action': 'store_true'}
#+end_example
and then the tree that the parsed ~dict~ is represented by:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
print(
    ParseArg(d=view, root_name='TODO').tree.show(quiet=True)
)
#+END_SRC

#+RESULTS:
#+begin_example
TODO
    A
        BBB
        B
        BB
            CC
            CCC
            C
    AA
        BBB
        B
        BB
            CC
            CCC
            C
#+end_example


We can now run the A-tree and the A-AA-tree examples, respectively:
#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.examples.a import main
main()
#+END_SRC

#+RESULTS:
#+begin_example
NODE :: 'A':
--------------
usage: ipython A [-h] [-c] [-v] {BBB,B,BB} ...

positional arguments:
  {BBB,B,BB}

optional arguments:
  -h, --help     show this help message and exit
  -c             A [optional pi]
  -v, --verbose  A verbosity


'A':
----
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A' id='139996012374000'>
'A -v':
-------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A' id='139996012374000'>
'A -c':
-------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A' id='139996012374000'>
'A -v -c':
----------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A' id='139996012374000'>


NODE :: 'A B':
----------------
usage: ipython A B [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A B [optional pi]
  -v, --verbose  A B verbosity


'A B':
------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_B' id='139996008444592'>
'A B -v':
---------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_B' id='139996008444592'>
'A B -c':
---------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_B' id='139996008444592'>
'A B -v -c':
------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_B' id='139996008444592'>


NODE :: 'A BB':
-----------------
usage: ipython A BB [-h] [-c] [-v] {CC,CCC,C} ...

positional arguments:
  {CC,CCC,C}

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB [optional pi]
  -v, --verbose  A BB erbosity


'A BB':
-------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB' id='139996008493984'>
'A BB -v':
----------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB' id='139996008493984'>
'A BB -c':
----------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB' id='139996008493984'>
'A BB -v -c':
-------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB' id='139996008493984'>


NODE :: 'A BB C':
-------------------
usage: ipython A BB C [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB C [optional pi]
  -v, --verbose  A BB C verbosity


'A BB C':
---------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_C' id='139996008494176'>
'A BB C -v':
------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_C' id='139996008494176'>
'A BB C -c':
------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_C' id='139996008494176'>
'A BB C -v -c':
---------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_C' id='139996008494176'>


NODE :: 'A BB CC':
--------------------
usage: ipython A BB CC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB CC [optional pi]
  -v, --verbose  A BB CC verbosity


'A BB CC':
----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_CC' id='139996008494368'>
'A BB CC -v':
-------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_CC' id='139996008494368'>
'A BB CC -c':
-------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_CC' id='139996008494368'>
'A BB CC -v -c':
----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_CC' id='139996008494368'>


NODE :: 'A BB CCC':
---------------------
usage: ipython A BB CCC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB CCC [optional pi]
  -v, --verbose  A BB CCC verbosity


'A BB CCC':
-----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_CCC' id='139996008494560'>
'A BB CCC -v':
--------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_CCC' id='139996008494560'>
'A BB CCC -c':
--------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_CCC' id='139996008494560'>
'A BB CCC -v -c':
-----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_CCC' id='139996008494560'>


#+end_example

#+BEGIN_SRC python :session *Python-3.x* :exports both :results output :eval yes 
from parsearg.examples.a_aa import main
main()
#+END_SRC

#+RESULTS:
#+begin_example
NODE :: 'A':
--------------
usage: ipython A [-h] [-c] [-v] {BBB,B,BB} ...

positional arguments:
  {BBB,B,BB}

optional arguments:
  -h, --help     show this help message and exit
  -c             A [optional pi]
  -v, --verbose  A verbosity


'A':
----
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A' id='139996008617584'>
'A -v':
-------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A' id='139996008617584'>
'A -c':
-------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A' id='139996008617584'>
'A -v -c':
----------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A' id='139996008617584'>


NODE :: 'A B':
----------------
usage: ipython A B [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A B [optional pi]
  -v, --verbose  A B verbosity


'A B':
------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_B' id='139996008617728'>
'A B -v':
---------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_B' id='139996008617728'>
'A B -c':
---------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_B' id='139996008617728'>
'A B -v -c':
------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_B' id='139996008617728'>


NODE :: 'A BB':
-----------------
usage: ipython A BB [-h] [-c] [-v] {CC,CCC,C} ...

positional arguments:
  {CC,CCC,C}

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB [optional pi]
  -v, --verbose  A BB erbosity


'A BB':
-------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB' id='139996008616048'>
'A BB -v':
----------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB' id='139996008616048'>
'A BB -c':
----------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB' id='139996008616048'>
'A BB -v -c':
-------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB' id='139996008616048'>


NODE :: 'A BB C':
-------------------
usage: ipython A BB C [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB C [optional pi]
  -v, --verbose  A BB C verbosity


'A BB C':
---------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_C' id='139996008141152'>
'A BB C -v':
------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_C' id='139996008141152'>
'A BB C -c':
------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_C' id='139996008141152'>
'A BB C -v -c':
---------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_C' id='139996008141152'>


NODE :: 'A BB CC':
--------------------
usage: ipython A BB CC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB CC [optional pi]
  -v, --verbose  A BB CC verbosity


'A BB CC':
----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_CC' id='139996008141344'>
'A BB CC -v':
-------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_CC' id='139996008141344'>
'A BB CC -c':
-------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_CC' id='139996008141344'>
'A BB CC -v -c':
----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_CC' id='139996008141344'>


NODE :: 'A BB CCC':
---------------------
usage: ipython A BB CCC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             A BB CCC [optional pi]
  -v, --verbose  A BB CCC verbosity


'A BB CCC':
-----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.A_BB_CCC' id='139996008619792'>
'A BB CCC -v':
--------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.A_BB_CCC' id='139996008619792'>
'A BB CCC -c':
--------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.A_BB_CCC' id='139996008619792'>
'A BB CCC -v -c':
-----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.A_BB_CCC' id='139996008619792'>


NODE :: 'AA':
---------------
usage: ipython AA [-h] [-c] [-v] {BBB,B,BB} ...

positional arguments:
  {BBB,B,BB}

optional arguments:
  -h, --help     show this help message and exit
  -c             AA [optional pi]
  -v, --verbose  AA verbosity


'AA':
-----
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA' id='139996008619504'>
'AA -v':
--------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA' id='139996008619504'>
'AA -c':
--------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA' id='139996008619504'>
'AA -v -c':
-----------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA' id='139996008619504'>


NODE :: 'AA B':
-----------------
usage: ipython AA B [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             AA B [optional pi]
  -v, --verbose  AA B verbosity


'AA B':
-------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA_B' id='139996008619216'>
'AA B -v':
----------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA_B' id='139996008619216'>
'AA B -c':
----------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA_B' id='139996008619216'>
'AA B -v -c':
-------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA_B' id='139996008619216'>


NODE :: 'AA BB':
------------------
usage: ipython AA BB [-h] [-c] [-v] {CC,CCC,C} ...

positional arguments:
  {CC,CCC,C}

optional arguments:
  -h, --help     show this help message and exit
  -c             AA BB [optional pi]
  -v, --verbose  AA BB verbosity


'AA BB':
--------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA_BB' id='139996008619072'>
'AA BB -v':
-----------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA_BB' id='139996008619072'>
'AA BB -c':
-----------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA_BB' id='139996008619072'>
'AA BB -v -c':
--------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA_BB' id='139996008619072'>


NODE :: 'AA BB C':
--------------------
usage: ipython AA BB C [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             AA BB C [optional pi]
  -v, --verbose  AA BB C verbosity


'AA BB C':
----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA_BB_C' id='139996008618928'>
'AA BB C -v':
-------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA_BB_C' id='139996008618928'>
'AA BB C -c':
-------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA_BB_C' id='139996008618928'>
'AA BB C -v -c':
----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA_BB_C' id='139996008618928'>


NODE :: 'AA BB CC':
---------------------
usage: ipython AA BB CC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             AA BB CC [optional pi]
  -v, --verbose  AA BB CC verbosity


'AA BB CC':
-----------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA_BB_CC' id='139996008619456'>
'AA BB CC -v':
--------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA_BB_CC' id='139996008619456'>
'AA BB CC -c':
--------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA_BB_CC' id='139996008619456'>
'AA BB CC -v -c':
-----------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA_BB_CC' id='139996008619456'>


NODE :: 'AA BB CCC':
----------------------
usage: ipython AA BB CCC [-h] [-c] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -c             AA BB CCC [optional pi]
  -v, --verbose  AA BB CCC verbosity


'AA BB CCC':
------------
	args: {'c': None, 'verbose': False}
	<Mock name='mock.AA_BB_CCC' id='139996008618832'>
'AA BB CCC -v':
---------------
	args: {'c': None, 'verbose': True}
	<Mock name='mock.AA_BB_CCC' id='139996008618832'>
'AA BB CCC -c':
---------------
	args: {'c': 3.141593, 'verbose': False}
	<Mock name='mock.AA_BB_CCC' id='139996008618832'>
'AA BB CCC -v -c':
------------------
	args: {'c': 3.141593, 'verbose': True}
	<Mock name='mock.AA_BB_CCC' id='139996008618832'>


#+end_example
