argshell.argshell

 1import argparse
 2import cmd
 3import shlex
 4import sys
 5from functools import wraps
 6from typing import Any, Callable
 7
 8
 9class Namespace(argparse.Namespace):
10    """Wrapping argparse.Namesapce for convenience."""
11
12
13class ArgShellParser(argparse.ArgumentParser):
14    """Wrapping argparse.ArgumentParser for convenience
15    and to prevent exit on '-h/--help' switch."""
16
17    def exit(self, status=0, message=None):
18        """Override to prevent shell exit when passing -h/--help switches."""
19        if message:
20            self._print_message(message, sys.stderr)
21
22    def parse_args(self, *args, **kwargs) -> Namespace:
23        """Just making the type checker hush."""
24        return super().parse_args(*args, **kwargs)
25
26
27class ArgShell(cmd.Cmd):
28    intro = "Entering argshell..."
29    prompt = "argshell>"
30
31    def do_quit(self, command: str):
32        """Quit shell"""
33        return True
34
35
36def with_parser(
37    parser: Callable[..., ArgShellParser],
38    post_parsers: list[Callable[[Namespace], Namespace]] = [],
39) -> Callable[[Callable[[Any, Namespace], Any]], Callable[[Any, str], Any]]:
40    """Decorate a 'do_*' function in an argshell.ArgShell class with this function to pass an argshell.Namespace object to the decorated function instead of a string.
41
42    :param parser: A function that creates an argshell.ArgShellParser instance, adds arguments to it, and returns the parser.
43
44    :param post_parsers: An optional list of functions to execute where each function takes an argshell.Namespace instance and returns an argshell.Namespace instance.
45        Functions are executed in the order they are supplied.
46
47    >>> def get_parser() -> argshell.ArgShellParser:
48    >>>     parser = argshell.ArgShellParser()
49    >>>     parser.add_argument("names", type=str, nargs="*", help="A list of first and last names to print.")
50    >>>     parser.add_argument("-i", "--initials", action="store_true", help="Print the initials instead of the full name.")
51    >>>     return parser
52    >>>
53    >>> # Convert list of first and last names to a list of tuples
54    >>> def names_list_to_tuples(args: argshell.Namespace) -> argshell.Namespace:
55    >>>     args.names = [(first, last) for first, last in zip(args.names[::2], args.names[1::2])]
56    >>>     if args.initials:
57    >>>         args.names = [(name[0][0], name[1][0]) for name in args.names]
58    >>>     return args
59    >>>
60    >>> def capitalize_names(args: argshell.Namespace) -> argshell.Namespace:
61    >>>     args.names = [name.capitalize() for name in args.names]
62    >>>     return args
63    >>>
64    >>> class NameShell(ArgShell):
65    >>>     intro = "Entering nameshell..."
66    >>>     prompt = "nameshell>"
67    >>>
68    >>>     @with_parser(get_parser, [capitalize_names, names_list_to_tuples])
69    >>>     def do_printnames(self, args: argshell.Namespace):
70    >>>         print(*[f"{name[0]} {name[1]}" for name in args.names], sep="\\n")
71    >>>
72    >>> NameShell().cmdloop()
73    >>> Entering nameshell...
74    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno
75    >>> Karl Marx
76    >>> Fred Hampton
77    >>> Emma Goldman
78    >>> Angela Davis
79    >>> Nestor Makhno
80    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno -i
81    >>> K M
82    >>> F H
83    >>> E G
84    >>> A D
85    >>> N M"""
86
87    def decorator(
88        func: Callable[[Any, Namespace], Any | None]
89    ) -> Callable[[Any, str], Any]:
90        @wraps(func)
91        def inner(self: Any, command: str) -> Any:
92            args = parser().parse_args(shlex.split(command))
93            for post_parser in post_parsers:
94                args = post_parser(args)
95            return func(self, args)
96
97        return inner
98
99    return decorator
class Namespace(argparse.Namespace):
10class Namespace(argparse.Namespace):
11    """Wrapping argparse.Namesapce for convenience."""

Wrapping argparse.Namesapce for convenience.

Inherited Members
argparse.Namespace
Namespace
class ArgShellParser(argparse.ArgumentParser):
14class ArgShellParser(argparse.ArgumentParser):
15    """Wrapping argparse.ArgumentParser for convenience
16    and to prevent exit on '-h/--help' switch."""
17
18    def exit(self, status=0, message=None):
19        """Override to prevent shell exit when passing -h/--help switches."""
20        if message:
21            self._print_message(message, sys.stderr)
22
23    def parse_args(self, *args, **kwargs) -> Namespace:
24        """Just making the type checker hush."""
25        return super().parse_args(*args, **kwargs)

Wrapping argparse.ArgumentParser for convenience and to prevent exit on '-h/--help' switch.

def exit(self, status=0, message=None):
18    def exit(self, status=0, message=None):
19        """Override to prevent shell exit when passing -h/--help switches."""
20        if message:
21            self._print_message(message, sys.stderr)

Override to prevent shell exit when passing -h/--help switches.

def parse_args(self, *args, **kwargs) -> argshell.argshell.Namespace:
23    def parse_args(self, *args, **kwargs) -> Namespace:
24        """Just making the type checker hush."""
25        return super().parse_args(*args, **kwargs)

Just making the type checker hush.

Inherited Members
argparse.ArgumentParser
ArgumentParser
add_subparsers
parse_known_args
convert_arg_line_to_args
parse_intermixed_args
parse_known_intermixed_args
format_usage
format_help
print_usage
print_help
error
argparse._ActionsContainer
register
set_defaults
get_default
add_argument
add_argument_group
add_mutually_exclusive_group
class ArgShell(cmd.Cmd):
28class ArgShell(cmd.Cmd):
29    intro = "Entering argshell..."
30    prompt = "argshell>"
31
32    def do_quit(self, command: str):
33        """Quit shell"""
34        return True

A simple framework for writing line-oriented command interpreters.

These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface.

A Cmd instance or subclass instance is a line-oriented interpreter framework. There is no good reason to instantiate Cmd itself; rather, it's useful as a superclass of an interpreter class you define yourself in order to inherit Cmd's methods and encapsulate action methods.

def do_quit(self, command: str):
32    def do_quit(self, command: str):
33        """Quit shell"""
34        return True

Quit shell

Inherited Members
cmd.Cmd
Cmd
cmdloop
precmd
postcmd
preloop
postloop
parseline
onecmd
emptyline
default
completedefault
completenames
complete
get_names
complete_help
do_help
print_topics
columnize
def with_parser( parser: Callable[..., argshell.argshell.ArgShellParser], post_parsers: list[typing.Callable[[argshell.argshell.Namespace], argshell.argshell.Namespace]] = []) -> Callable[[Callable[[Any, argshell.argshell.Namespace], Any]], Callable[[Any, str], Any]]:
 37def with_parser(
 38    parser: Callable[..., ArgShellParser],
 39    post_parsers: list[Callable[[Namespace], Namespace]] = [],
 40) -> Callable[[Callable[[Any, Namespace], Any]], Callable[[Any, str], Any]]:
 41    """Decorate a 'do_*' function in an argshell.ArgShell class with this function to pass an argshell.Namespace object to the decorated function instead of a string.
 42
 43    :param parser: A function that creates an argshell.ArgShellParser instance, adds arguments to it, and returns the parser.
 44
 45    :param post_parsers: An optional list of functions to execute where each function takes an argshell.Namespace instance and returns an argshell.Namespace instance.
 46        Functions are executed in the order they are supplied.
 47
 48    >>> def get_parser() -> argshell.ArgShellParser:
 49    >>>     parser = argshell.ArgShellParser()
 50    >>>     parser.add_argument("names", type=str, nargs="*", help="A list of first and last names to print.")
 51    >>>     parser.add_argument("-i", "--initials", action="store_true", help="Print the initials instead of the full name.")
 52    >>>     return parser
 53    >>>
 54    >>> # Convert list of first and last names to a list of tuples
 55    >>> def names_list_to_tuples(args: argshell.Namespace) -> argshell.Namespace:
 56    >>>     args.names = [(first, last) for first, last in zip(args.names[::2], args.names[1::2])]
 57    >>>     if args.initials:
 58    >>>         args.names = [(name[0][0], name[1][0]) for name in args.names]
 59    >>>     return args
 60    >>>
 61    >>> def capitalize_names(args: argshell.Namespace) -> argshell.Namespace:
 62    >>>     args.names = [name.capitalize() for name in args.names]
 63    >>>     return args
 64    >>>
 65    >>> class NameShell(ArgShell):
 66    >>>     intro = "Entering nameshell..."
 67    >>>     prompt = "nameshell>"
 68    >>>
 69    >>>     @with_parser(get_parser, [capitalize_names, names_list_to_tuples])
 70    >>>     def do_printnames(self, args: argshell.Namespace):
 71    >>>         print(*[f"{name[0]} {name[1]}" for name in args.names], sep="\\n")
 72    >>>
 73    >>> NameShell().cmdloop()
 74    >>> Entering nameshell...
 75    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno
 76    >>> Karl Marx
 77    >>> Fred Hampton
 78    >>> Emma Goldman
 79    >>> Angela Davis
 80    >>> Nestor Makhno
 81    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno -i
 82    >>> K M
 83    >>> F H
 84    >>> E G
 85    >>> A D
 86    >>> N M"""
 87
 88    def decorator(
 89        func: Callable[[Any, Namespace], Any | None]
 90    ) -> Callable[[Any, str], Any]:
 91        @wraps(func)
 92        def inner(self: Any, command: str) -> Any:
 93            args = parser().parse_args(shlex.split(command))
 94            for post_parser in post_parsers:
 95                args = post_parser(args)
 96            return func(self, args)
 97
 98        return inner
 99
100    return decorator

Decorate a 'do_*' function in an argshell.ArgShell class with this function to pass an argshell.Namespace object to the decorated function instead of a string.

Parameters
  • parser: A function that creates an argshell.ArgShellParser instance, adds arguments to it, and returns the parser.

  • post_parsers: An optional list of functions to execute where each function takes an argshell.Namespace instance and returns an argshell.Namespace instance. Functions are executed in the order they are supplied.

>>> def get_parser() -> argshell.ArgShellParser:
>>>     parser = argshell.ArgShellParser()
>>>     parser.add_argument("names", type=str, nargs="*", help="A list of first and last names to print.")
>>>     parser.add_argument("-i", "--initials", action="store_true", help="Print the initials instead of the full name.")
>>>     return parser
>>>
>>> # Convert list of first and last names to a list of tuples
>>> def names_list_to_tuples(args: argshell.Namespace) -> argshell.Namespace:
>>>     args.names = [(first, last) for first, last in zip(args.names[::2], args.names[1::2])]
>>>     if args.initials:
>>>         args.names = [(name[0][0], name[1][0]) for name in args.names]
>>>     return args
>>>
>>> def capitalize_names(args: argshell.Namespace) -> argshell.Namespace:
>>>     args.names = [name.capitalize() for name in args.names]
>>>     return args
>>>
>>> class NameShell(ArgShell):
>>>     intro = "Entering nameshell..."
>>>     prompt = "nameshell>"
>>>
>>>     @with_parser(get_parser, [capitalize_names, names_list_to_tuples])
>>>     def do_printnames(self, args: argshell.Namespace):
>>>         print(*[f"{name[0]} {name[1]}" for name in args.names], sep="\n")
>>>
>>> NameShell().cmdloop()
>>> Entering nameshell...
>>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno
>>> Karl Marx
>>> Fred Hampton
>>> Emma Goldman
>>> Angela Davis
>>> Nestor Makhno
>>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno -i
>>> K M
>>> F H
>>> E G
>>> A D
>>> N M