argshell.argshell

  1import argparse
  2import cmd
  3import shlex
  4import sys
  5import traceback
  6from functools import wraps
  7from typing import Any, Callable
  8
  9
 10class Namespace(argparse.Namespace):
 11    """Simple object for storing attributes.
 12
 13    Implements equality by attribute names and values, and provides a simple string representation."""
 14
 15
 16class ArgShellParser(argparse.ArgumentParser):
 17    """***Overrides exit, error, and parse_args methods***
 18
 19    Object for parsing command line strings into Python objects.
 20
 21    Keyword Arguments:
 22        - prog -- The name of the program (default:
 23            ``os.path.basename(sys.argv[0])``)
 24        - usage -- A usage message (default: auto-generated from arguments)
 25        - description -- A description of what the program does
 26        - epilog -- Text following the argument descriptions
 27        - parents -- Parsers whose arguments should be copied into this one
 28        - formatter_class -- HelpFormatter class for printing help messages
 29        - prefix_chars -- Characters that prefix optional arguments
 30        - fromfile_prefix_chars -- Characters that prefix files containing
 31            additional arguments
 32        - argument_default -- The default value for all arguments
 33        - conflict_handler -- String indicating how to handle conflicts
 34        - add_help -- Add a -h/-help option
 35        - allow_abbrev -- Allow long options to be abbreviated unambiguously
 36        - exit_on_error -- Determines whether or not ArgumentParser exits with
 37            error info when an error occurs
 38    """
 39
 40    def exit(self, status=0, message=None):
 41        """Override to prevent shell exit when passing -h/--help switches."""
 42        if message:
 43            self._print_message(message, sys.stderr)
 44
 45    def error(self, message):
 46        raise Exception(f"prog: {self.prog}, message: {message}")
 47
 48    def parse_args(self, *args, **kwargs) -> Namespace:
 49        parsed_args: Namespace = super().parse_args(*args, **kwargs)
 50        return parsed_args
 51
 52
 53class ArgShell(cmd.Cmd):
 54    """Subclass this to create custom ArgShells."""
 55
 56    intro = "Entering argshell..."
 57    prompt = "argshell>"
 58
 59    def do_quit(self, command: str):
 60        """Quit shell."""
 61        return True
 62
 63    def do_help(self, arg):
 64        """List available commands with "help" or detailed help with "help cmd".
 65        If using 'help cmd' and the cmd is decorated with a parser, the parser help will also be printed."""
 66        if arg:
 67            # XXX check arg syntax
 68            try:
 69                func = getattr(self, "help_" + arg)
 70            except AttributeError:
 71                try:
 72                    func = getattr(self, "do_" + arg)
 73                    doc = func.__doc__
 74                    if doc:
 75                        self.stdout.write("%s\n" % str(doc))
 76                    # =========================Modification start=========================
 77                    # Check for decorator and call decorated function with "--help"
 78                    if hasattr(func, "__wrapped__"):
 79                        self.stdout.write(
 80                            f"Parser help for {func.__name__.replace('do_','')}:\n"
 81                        )
 82                        func("--help")
 83                    if doc or hasattr(func, "__wrapped__"):
 84                        return
 85                    # =========================Modification stop=========================
 86                except AttributeError:
 87                    pass
 88                self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
 89                return
 90            func()
 91        else:
 92            names = self.get_names()
 93            cmds_doc = []
 94            cmds_undoc = []
 95            topics = set()
 96            for name in names:
 97                if name[:5] == "help_":
 98                    topics.add(name[5:])
 99            names.sort()
100            # There can be duplicates if routines overridden
101            prevname = ""
102            for name in names:
103                if name[:3] == "do_":
104                    if name == prevname:
105                        continue
106                    prevname = name
107                    cmd = name[3:]
108                    if cmd in topics:
109                        cmds_doc.append(cmd)
110                        topics.remove(cmd)
111                    elif getattr(self, name).__doc__:
112                        cmds_doc.append(cmd)
113                    else:
114                        cmds_undoc.append(cmd)
115            self.stdout.write("%s\n" % str(self.doc_leader))
116            self.print_topics(self.doc_header, cmds_doc, 15, 80)
117            self.print_topics(self.misc_header, sorted(topics), 15, 80)
118            self.print_topics(self.undoc_header, cmds_undoc, 15, 80)
119
120    def cmdloop(self, intro=None):
121        """Repeatedly issue a prompt, accept input, parse an initial prefix
122        off the received input, and dispatch to action methods, passing them
123        the remainder of the line as argument.
124
125        """
126
127        self.preloop()
128        if self.use_rawinput and self.completekey:
129            try:
130                import readline
131
132                self.old_completer = readline.get_completer()  # type: ignore
133                readline.set_completer(self.complete)  # type: ignore
134                readline.parse_and_bind(self.completekey + ": complete")  # type: ignore
135            except ImportError:
136                pass
137        try:
138            if intro is not None:
139                self.intro = intro
140            if self.intro:
141                self.stdout.write(str(self.intro) + "\n")
142            stop = None
143            while not stop:
144                if self.cmdqueue:
145                    line = self.cmdqueue.pop(0)
146                else:
147                    if self.use_rawinput:
148                        try:
149                            line = input(self.prompt)
150                        except EOFError:
151                            line = "EOF"
152                    else:
153                        self.stdout.write(self.prompt)
154                        self.stdout.flush()
155                        line = self.stdin.readline()
156                        if not len(line):
157                            line = "EOF"
158                        else:
159                            line = line.rstrip("\r\n")
160                # ===========Modification start===========
161                try:
162                    line = self.precmd(line)
163                    stop = self.onecmd(line)
164                    stop = self.postcmd(stop, line)
165                except Exception as e:
166                    traceback.print_exc()
167                # ===========Modification stop===========
168            self.postloop()
169        finally:
170            if self.use_rawinput and self.completekey:
171                try:
172                    import readline
173
174                    readline.set_completer(self.old_completer)  # type: ignore
175                except ImportError:
176                    pass
177
178
179def with_parser(
180    parser: Callable[..., ArgShellParser],
181    post_parsers: list[Callable[[Namespace], Namespace]] = [],
182) -> Callable[[Callable[[Any, Namespace], Any]], Callable[[Any, str], Any]]:
183    """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.
184
185    :param parser: A function that creates an argshell.ArgShellParser instance, adds arguments to it, and returns the parser.
186
187    :param post_parsers: An optional list of functions to execute where each function takes an argshell.Namespace instance and returns an argshell.Namespace instance.
188        'post_parser' functions are executed in the order they are supplied.
189
190    >>> def get_parser() -> argshell.ArgShellParser:
191    >>>     parser = argshell.ArgShellParser()
192    >>>     parser.add_argument("names", type=str, nargs="*", help="A list of first and last names to print.")
193    >>>     parser.add_argument("-i", "--initials", action="store_true", help="Print the initials instead of the full name.")
194    >>>     return parser
195    >>>
196    >>> # Convert list of first and last names to a list of tuples
197    >>> def names_list_to_tuples(args: argshell.Namespace) -> argshell.Namespace:
198    >>>     args.names = [(first, last) for first, last in zip(args.names[::2], args.names[1::2])]
199    >>>     if args.initials:
200    >>>         args.names = [(name[0][0], name[1][0]) for name in args.names]
201    >>>     return args
202    >>>
203    >>> def capitalize_names(args: argshell.Namespace) -> argshell.Namespace:
204    >>>     args.names = [name.capitalize() for name in args.names]
205    >>>     return args
206    >>>
207    >>> class NameShell(ArgShell):
208    >>>     intro = "Entering nameshell..."
209    >>>     prompt = "nameshell>"
210    >>>
211    >>>     @with_parser(get_parser, [capitalize_names, names_list_to_tuples])
212    >>>     def do_printnames(self, args: argshell.Namespace):
213    >>>         print(*[f"{name[0]} {name[1]}" for name in args.names], sep="\\n")
214    >>>
215    >>> NameShell().cmdloop()
216    >>> Entering nameshell...
217    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno
218    >>> Karl Marx
219    >>> Fred Hampton
220    >>> Emma Goldman
221    >>> Angela Davis
222    >>> Nestor Makhno
223    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno -i
224    >>> K M
225    >>> F H
226    >>> E G
227    >>> A D
228    >>> N M"""
229
230    def decorator(
231        func: Callable[[Any, Namespace], Any | None]
232    ) -> Callable[[Any, str], Any]:
233        @wraps(func)
234        def inner(self: Any, command: str) -> Any:
235            try:
236                args = parser().parse_args(shlex.split(command))
237            except Exception as e:
238                # On parser error, print help and skip post_parser and func execution
239                if "the following arguments are required" not in str(e):
240                    print(f"ERROR: {e}")
241                if "-h" not in command and "--help" not in command:
242                    try:
243                        args = parser().parse_args(["--help"])
244                    except Exception as e:
245                        pass
246                return None
247            # Don't execute function, only print parser help
248            if "-h" in command or "--help" in command:
249                return None
250            for post_parser in post_parsers:
251                args = post_parser(args)
252
253            return func(self, args)
254
255        return inner
256
257    return decorator
class Namespace(argparse.Namespace):
11class Namespace(argparse.Namespace):
12    """Simple object for storing attributes.
13
14    Implements equality by attribute names and values, and provides a simple string representation."""

Simple object for storing attributes.

Implements equality by attribute names and values, and provides a simple string representation.

Inherited Members
argparse.Namespace
Namespace
class ArgShellParser(argparse.ArgumentParser):
17class ArgShellParser(argparse.ArgumentParser):
18    """***Overrides exit, error, and parse_args methods***
19
20    Object for parsing command line strings into Python objects.
21
22    Keyword Arguments:
23        - prog -- The name of the program (default:
24            ``os.path.basename(sys.argv[0])``)
25        - usage -- A usage message (default: auto-generated from arguments)
26        - description -- A description of what the program does
27        - epilog -- Text following the argument descriptions
28        - parents -- Parsers whose arguments should be copied into this one
29        - formatter_class -- HelpFormatter class for printing help messages
30        - prefix_chars -- Characters that prefix optional arguments
31        - fromfile_prefix_chars -- Characters that prefix files containing
32            additional arguments
33        - argument_default -- The default value for all arguments
34        - conflict_handler -- String indicating how to handle conflicts
35        - add_help -- Add a -h/-help option
36        - allow_abbrev -- Allow long options to be abbreviated unambiguously
37        - exit_on_error -- Determines whether or not ArgumentParser exits with
38            error info when an error occurs
39    """
40
41    def exit(self, status=0, message=None):
42        """Override to prevent shell exit when passing -h/--help switches."""
43        if message:
44            self._print_message(message, sys.stderr)
45
46    def error(self, message):
47        raise Exception(f"prog: {self.prog}, message: {message}")
48
49    def parse_args(self, *args, **kwargs) -> Namespace:
50        parsed_args: Namespace = super().parse_args(*args, **kwargs)
51        return parsed_args

Overrides exit, error, and parse_args methods

Object for parsing command line strings into Python objects.

Keyword Arguments: - prog -- The name of the program (default: os.path.basename(sys.argv[0])) - usage -- A usage message (default: auto-generated from arguments) - description -- A description of what the program does - epilog -- Text following the argument descriptions - parents -- Parsers whose arguments should be copied into this one - formatter_class -- HelpFormatter class for printing help messages - prefix_chars -- Characters that prefix optional arguments - fromfile_prefix_chars -- Characters that prefix files containing additional arguments - argument_default -- The default value for all arguments - conflict_handler -- String indicating how to handle conflicts - add_help -- Add a -h/-help option - allow_abbrev -- Allow long options to be abbreviated unambiguously - exit_on_error -- Determines whether or not ArgumentParser exits with error info when an error occurs

def exit(self, status=0, message=None):
41    def exit(self, status=0, message=None):
42        """Override to prevent shell exit when passing -h/--help switches."""
43        if message:
44            self._print_message(message, sys.stderr)

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

def error(self, message):
46    def error(self, message):
47        raise Exception(f"prog: {self.prog}, message: {message}")

error(message: string)

Prints a usage message incorporating the message to stderr and exits.

If you override this in a subclass, it should not return -- it should either exit or raise an exception.

def parse_args(self, *args, **kwargs) -> argshell.argshell.Namespace:
49    def parse_args(self, *args, **kwargs) -> Namespace:
50        parsed_args: Namespace = super().parse_args(*args, **kwargs)
51        return parsed_args
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
argparse._ActionsContainer
register
set_defaults
get_default
add_argument
add_argument_group
add_mutually_exclusive_group
class ArgShell(cmd.Cmd):
 54class ArgShell(cmd.Cmd):
 55    """Subclass this to create custom ArgShells."""
 56
 57    intro = "Entering argshell..."
 58    prompt = "argshell>"
 59
 60    def do_quit(self, command: str):
 61        """Quit shell."""
 62        return True
 63
 64    def do_help(self, arg):
 65        """List available commands with "help" or detailed help with "help cmd".
 66        If using 'help cmd' and the cmd is decorated with a parser, the parser help will also be printed."""
 67        if arg:
 68            # XXX check arg syntax
 69            try:
 70                func = getattr(self, "help_" + arg)
 71            except AttributeError:
 72                try:
 73                    func = getattr(self, "do_" + arg)
 74                    doc = func.__doc__
 75                    if doc:
 76                        self.stdout.write("%s\n" % str(doc))
 77                    # =========================Modification start=========================
 78                    # Check for decorator and call decorated function with "--help"
 79                    if hasattr(func, "__wrapped__"):
 80                        self.stdout.write(
 81                            f"Parser help for {func.__name__.replace('do_','')}:\n"
 82                        )
 83                        func("--help")
 84                    if doc or hasattr(func, "__wrapped__"):
 85                        return
 86                    # =========================Modification stop=========================
 87                except AttributeError:
 88                    pass
 89                self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
 90                return
 91            func()
 92        else:
 93            names = self.get_names()
 94            cmds_doc = []
 95            cmds_undoc = []
 96            topics = set()
 97            for name in names:
 98                if name[:5] == "help_":
 99                    topics.add(name[5:])
100            names.sort()
101            # There can be duplicates if routines overridden
102            prevname = ""
103            for name in names:
104                if name[:3] == "do_":
105                    if name == prevname:
106                        continue
107                    prevname = name
108                    cmd = name[3:]
109                    if cmd in topics:
110                        cmds_doc.append(cmd)
111                        topics.remove(cmd)
112                    elif getattr(self, name).__doc__:
113                        cmds_doc.append(cmd)
114                    else:
115                        cmds_undoc.append(cmd)
116            self.stdout.write("%s\n" % str(self.doc_leader))
117            self.print_topics(self.doc_header, cmds_doc, 15, 80)
118            self.print_topics(self.misc_header, sorted(topics), 15, 80)
119            self.print_topics(self.undoc_header, cmds_undoc, 15, 80)
120
121    def cmdloop(self, intro=None):
122        """Repeatedly issue a prompt, accept input, parse an initial prefix
123        off the received input, and dispatch to action methods, passing them
124        the remainder of the line as argument.
125
126        """
127
128        self.preloop()
129        if self.use_rawinput and self.completekey:
130            try:
131                import readline
132
133                self.old_completer = readline.get_completer()  # type: ignore
134                readline.set_completer(self.complete)  # type: ignore
135                readline.parse_and_bind(self.completekey + ": complete")  # type: ignore
136            except ImportError:
137                pass
138        try:
139            if intro is not None:
140                self.intro = intro
141            if self.intro:
142                self.stdout.write(str(self.intro) + "\n")
143            stop = None
144            while not stop:
145                if self.cmdqueue:
146                    line = self.cmdqueue.pop(0)
147                else:
148                    if self.use_rawinput:
149                        try:
150                            line = input(self.prompt)
151                        except EOFError:
152                            line = "EOF"
153                    else:
154                        self.stdout.write(self.prompt)
155                        self.stdout.flush()
156                        line = self.stdin.readline()
157                        if not len(line):
158                            line = "EOF"
159                        else:
160                            line = line.rstrip("\r\n")
161                # ===========Modification start===========
162                try:
163                    line = self.precmd(line)
164                    stop = self.onecmd(line)
165                    stop = self.postcmd(stop, line)
166                except Exception as e:
167                    traceback.print_exc()
168                # ===========Modification stop===========
169            self.postloop()
170        finally:
171            if self.use_rawinput and self.completekey:
172                try:
173                    import readline
174
175                    readline.set_completer(self.old_completer)  # type: ignore
176                except ImportError:
177                    pass

Subclass this to create custom ArgShells.

def do_quit(self, command: str):
60    def do_quit(self, command: str):
61        """Quit shell."""
62        return True

Quit shell.

def do_help(self, arg):
 64    def do_help(self, arg):
 65        """List available commands with "help" or detailed help with "help cmd".
 66        If using 'help cmd' and the cmd is decorated with a parser, the parser help will also be printed."""
 67        if arg:
 68            # XXX check arg syntax
 69            try:
 70                func = getattr(self, "help_" + arg)
 71            except AttributeError:
 72                try:
 73                    func = getattr(self, "do_" + arg)
 74                    doc = func.__doc__
 75                    if doc:
 76                        self.stdout.write("%s\n" % str(doc))
 77                    # =========================Modification start=========================
 78                    # Check for decorator and call decorated function with "--help"
 79                    if hasattr(func, "__wrapped__"):
 80                        self.stdout.write(
 81                            f"Parser help for {func.__name__.replace('do_','')}:\n"
 82                        )
 83                        func("--help")
 84                    if doc or hasattr(func, "__wrapped__"):
 85                        return
 86                    # =========================Modification stop=========================
 87                except AttributeError:
 88                    pass
 89                self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
 90                return
 91            func()
 92        else:
 93            names = self.get_names()
 94            cmds_doc = []
 95            cmds_undoc = []
 96            topics = set()
 97            for name in names:
 98                if name[:5] == "help_":
 99                    topics.add(name[5:])
100            names.sort()
101            # There can be duplicates if routines overridden
102            prevname = ""
103            for name in names:
104                if name[:3] == "do_":
105                    if name == prevname:
106                        continue
107                    prevname = name
108                    cmd = name[3:]
109                    if cmd in topics:
110                        cmds_doc.append(cmd)
111                        topics.remove(cmd)
112                    elif getattr(self, name).__doc__:
113                        cmds_doc.append(cmd)
114                    else:
115                        cmds_undoc.append(cmd)
116            self.stdout.write("%s\n" % str(self.doc_leader))
117            self.print_topics(self.doc_header, cmds_doc, 15, 80)
118            self.print_topics(self.misc_header, sorted(topics), 15, 80)
119            self.print_topics(self.undoc_header, cmds_undoc, 15, 80)

List available commands with "help" or detailed help with "help cmd". If using 'help cmd' and the cmd is decorated with a parser, the parser help will also be printed.

def cmdloop(self, intro=None):
121    def cmdloop(self, intro=None):
122        """Repeatedly issue a prompt, accept input, parse an initial prefix
123        off the received input, and dispatch to action methods, passing them
124        the remainder of the line as argument.
125
126        """
127
128        self.preloop()
129        if self.use_rawinput and self.completekey:
130            try:
131                import readline
132
133                self.old_completer = readline.get_completer()  # type: ignore
134                readline.set_completer(self.complete)  # type: ignore
135                readline.parse_and_bind(self.completekey + ": complete")  # type: ignore
136            except ImportError:
137                pass
138        try:
139            if intro is not None:
140                self.intro = intro
141            if self.intro:
142                self.stdout.write(str(self.intro) + "\n")
143            stop = None
144            while not stop:
145                if self.cmdqueue:
146                    line = self.cmdqueue.pop(0)
147                else:
148                    if self.use_rawinput:
149                        try:
150                            line = input(self.prompt)
151                        except EOFError:
152                            line = "EOF"
153                    else:
154                        self.stdout.write(self.prompt)
155                        self.stdout.flush()
156                        line = self.stdin.readline()
157                        if not len(line):
158                            line = "EOF"
159                        else:
160                            line = line.rstrip("\r\n")
161                # ===========Modification start===========
162                try:
163                    line = self.precmd(line)
164                    stop = self.onecmd(line)
165                    stop = self.postcmd(stop, line)
166                except Exception as e:
167                    traceback.print_exc()
168                # ===========Modification stop===========
169            self.postloop()
170        finally:
171            if self.use_rawinput and self.completekey:
172                try:
173                    import readline
174
175                    readline.set_completer(self.old_completer)  # type: ignore
176                except ImportError:
177                    pass

Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument.

Inherited Members
cmd.Cmd
Cmd
precmd
postcmd
preloop
postloop
parseline
onecmd
emptyline
default
completedefault
completenames
complete
get_names
complete_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]]:
180def with_parser(
181    parser: Callable[..., ArgShellParser],
182    post_parsers: list[Callable[[Namespace], Namespace]] = [],
183) -> Callable[[Callable[[Any, Namespace], Any]], Callable[[Any, str], Any]]:
184    """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.
185
186    :param parser: A function that creates an argshell.ArgShellParser instance, adds arguments to it, and returns the parser.
187
188    :param post_parsers: An optional list of functions to execute where each function takes an argshell.Namespace instance and returns an argshell.Namespace instance.
189        'post_parser' functions are executed in the order they are supplied.
190
191    >>> def get_parser() -> argshell.ArgShellParser:
192    >>>     parser = argshell.ArgShellParser()
193    >>>     parser.add_argument("names", type=str, nargs="*", help="A list of first and last names to print.")
194    >>>     parser.add_argument("-i", "--initials", action="store_true", help="Print the initials instead of the full name.")
195    >>>     return parser
196    >>>
197    >>> # Convert list of first and last names to a list of tuples
198    >>> def names_list_to_tuples(args: argshell.Namespace) -> argshell.Namespace:
199    >>>     args.names = [(first, last) for first, last in zip(args.names[::2], args.names[1::2])]
200    >>>     if args.initials:
201    >>>         args.names = [(name[0][0], name[1][0]) for name in args.names]
202    >>>     return args
203    >>>
204    >>> def capitalize_names(args: argshell.Namespace) -> argshell.Namespace:
205    >>>     args.names = [name.capitalize() for name in args.names]
206    >>>     return args
207    >>>
208    >>> class NameShell(ArgShell):
209    >>>     intro = "Entering nameshell..."
210    >>>     prompt = "nameshell>"
211    >>>
212    >>>     @with_parser(get_parser, [capitalize_names, names_list_to_tuples])
213    >>>     def do_printnames(self, args: argshell.Namespace):
214    >>>         print(*[f"{name[0]} {name[1]}" for name in args.names], sep="\\n")
215    >>>
216    >>> NameShell().cmdloop()
217    >>> Entering nameshell...
218    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno
219    >>> Karl Marx
220    >>> Fred Hampton
221    >>> Emma Goldman
222    >>> Angela Davis
223    >>> Nestor Makhno
224    >>> nameshell>printnames karl marx fred hampton emma goldman angela davis nestor makhno -i
225    >>> K M
226    >>> F H
227    >>> E G
228    >>> A D
229    >>> N M"""
230
231    def decorator(
232        func: Callable[[Any, Namespace], Any | None]
233    ) -> Callable[[Any, str], Any]:
234        @wraps(func)
235        def inner(self: Any, command: str) -> Any:
236            try:
237                args = parser().parse_args(shlex.split(command))
238            except Exception as e:
239                # On parser error, print help and skip post_parser and func execution
240                if "the following arguments are required" not in str(e):
241                    print(f"ERROR: {e}")
242                if "-h" not in command and "--help" not in command:
243                    try:
244                        args = parser().parse_args(["--help"])
245                    except Exception as e:
246                        pass
247                return None
248            # Don't execute function, only print parser help
249            if "-h" in command or "--help" in command:
250                return None
251            for post_parser in post_parsers:
252                args = post_parser(args)
253
254            return func(self, args)
255
256        return inner
257
258    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. 'post_parser' 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