Argparse + cmd2 RecursionError even on simple programs

265 Views Asked by At

Sample code:

import argparse
import cmd2
import sys


def create_subparser(a_parser = None):
    if a_parser is None:
        new_parser = argparse.ArgumentParser()
    else:
        new_parser = a_parser.add_parser("single")
    new_parser.add_argument("--single-param", "-sp")
    return new_parser


def single_print(some_param):
    print("single operation " + str(some_param))


def other_print():
    print("other operation")


class TestCmd(cmd2.Cmd):
    single_parser = create_subparser()

    @cmd2.with_argparser(single_parser)
    def do_single(self, opts):
        print(opts)
        single_print(opts.single_param)

    def do_other(self, opts):
        other_print()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(help="commands", dest="mode")
    create_subparser(subparsers)
    cmd_parser = subparsers.add_parser("cmd", help="shell")

    args = parser.parse_args()
    if args.mode == "single":
        single_print(args.single_param)
    else:
        sys.argv = [sys.argv[0]]  # cmd2 complains about unknown parameters without
        cmd = TestCmd()
        cmd.cmdloop()

In the example, I'm trying to make a program that can either run in single mode (program.py single -sp test) or in cmd/prompt mode (program.py cmd), which then would call single mode or other operation (inside the shell, write single -sp test or other. Anyway, if I try to get all the values for the argparse being used in the do_single inside the cmd2 class, I get EXCEPTION of type 'RecursionError' occurred with message: 'maximum recursion depth exceeded in comparison'. Using cmd2's debug, it goes:

Traceback (most recent call last):
File "/home/.../site-packages/cmd2/cmd2.py", line 2011, in onecmd_plus_hooks
    stop = self.onecmd(statement, add_to_history=add_to_history)
  File "/home/.../site-packages/cmd2/cmd2.py", line 2441, in onecmd
    stop = func(statement)
  File "/home/.../site-packages/cmd2/decorators.py", line 308, in cmd_wrapper
    return func(*args_list, **kwargs)
  File "test.py", line 28, in do_single
    print(opts)
  File "/usr/lib/python3.7/argparse.py", line 131, in __repr__
    arg_strings.append('%s=%r' % (name, value))
  File "/usr/lib/python3.7/argparse.py", line 131, in __repr__
    arg_strings.append('%s=%r' % (name, value))
  File "/usr/lib/python3.7/argparse.py", line 131, in __repr__
    arg_strings.append('%s=%r' % (name, value))
  [Previous line repeated 326 more times]
  File "/usr/lib/python3.7/argparse.py", line 129, in __repr__
    for name, value in self._get_kwargs():
  File "/usr/lib/python3.7/argparse.py", line 139, in _get_kwargs
    return sorted(self.__dict__.items())
RecursionError: maximum recursion depth exceeded in comparison

The reason I'm using the create_subparser function is because I'm creating the subparsers to be used by the cmd2.Cmd subclass, but I want to use the same parser for the modes when invoked from the main shell. This is basically a minimum working example, my code has a lot more flags and whatnot.

One other thing, the problem seems to happen because of print(opts). It seems it tries to do vars(opts) and when it tries enumerating all the variables, it runs out of memory due to recursion somewhere. If I don't try to list all the variables inside the object (for example, if I used just opts.single_param), it would work. It seems to be related to me trying to use the same argparser both inside cmd2 and with the main program, but I don't understand why exactly it's happening and if it can be fixed.

1

There are 1 best solutions below

0
On

Without cmd2 I can't run your code. But I can make a namespace that exhibits your problem.

In [429]: ns = argparse.Namespace(foo='bar', baz=12)                                                 
In [430]: ns.ns = ns                                                                                 
In [431]: print(ns)                                                                                  
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-431-0769451eb86e> in <module>
----> 1 print(ns)

/usr/lib/python3.6/argparse.py in __repr__(self)
    133         for name, value in self._get_kwargs():
    134             if name.isidentifier():
--> 135                 arg_strings.append('%s=%r' % (name, value))
    136             else:
    137                 star_args[name] = value

... last 1 frames repeated, from the frame below ...

/usr/lib/python3.6/argparse.py in __repr__(self)
    133         for name, value in self._get_kwargs():
    134             if name.isidentifier():
--> 135                 arg_strings.append('%s=%r' % (name, value))
    136             else:
    137                 star_args[name] = value

RecursionError: maximum recursion depth exceeded while calling a Python object

You could test the individual attributes until you find the one that's the problem:

In [432]: list(ns.__dict__.keys())                                                                   
Out[432]: ['foo', 'baz', 'ns']