0

I am implementing a command shell using cmd2, and I wanted to setup a few commands that allows a user to type in objects in json syntax. I thought this would be easy to do with argparse, but I am getting caught up how to parse and present the string to argparse. I am also open to using another module.

I have been looking at attempting to use regex to extract the json, but I am not seeing a straightforward method.

First, here is an example using a command line utility:

import argparse
import json
import re

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--filter', type=json.loads)
    parser.add_argument('-n', '--name', type=str)

    args = parser.parse_args()
    print(args)

Output:

python testparse.py -n device -f '{"comment": "foo space bar"}'
Namespace(filter={u'comment': u'foo space bar'}, name='device')

And then using cmd2:

import argparse
from cmd2 import Cmd
import json

class App(Cmd):
# customized attributes and methods here

    def do_example(self, arg):
        print(arg.parsed.dump() + '\n')
        argString = arg.parsed.args

        parser = argparse.ArgumentParser()
        parser.add_argument('-f', '--filter', type=json.loads)
        parser.add_argument('-n', '--name', type=str)

        # split works when there are no spaces
        args = parser.parse_args(argString.split())
        print(args)


if __name__ == '__main__':

    app = App()

    # split() works with no spaces
    app.onecmd('example -n devices -f {"comment":"foobar"}')
    # it does not work with spaces in your json data, both
    # formats below have similar failures
    app.onecmd('example -n devices -f {"comment":"foo space bar"}')
    # app.onecmd("""example -n devices -f '{"comment":"foo space bar"}'""")

Output:

['example', '-n devices -f {"comment":"foobar"}']
- args: '-n devices -f {"comment":"foobar"}'
- command: 'example'
- raw: 'example -n devices -f {"comment":"foobar"}'
- statement: ['example', '-n devices -f {"comment":"foobar"}']
  - args: '-n devices -f {"comment":"foobar"}'
  - command: 'example'

Namespace(filter={u'comment': u'foobar'}, name='devices')
['example', '-n devices -f {"comment":"foo space bar"}']
- args: '-n devices -f {"comment":"foo space bar"}'
- command: 'example'
- raw: 'example -n devices -f {"comment":"foo space bar"}'
- statement: ['example', '-n devices -f {"comment":"foo space bar"}']
  - args: '-n devices -f {"comment":"foo space bar"}'
  - command: 'example'

usage: testparse2.py [-h] [-f FILTER] [-n NAME]
testparse2.py: error: argument -f/--filter: invalid loads value: '{"comment":"foo'
ytjohn
  • 197
  • 1
  • 7
  • 2
    The problem is that you're using a naive `argString.split()`, which doesn't give you the same thing the arguments parsed from the command line to `sys.argv` would. But I haven't used cmd2, so I don't know how you can better access the arguments. Maybe try using `shlex.split(argString)`, if you're stuck with the string; see https://stackoverflow.com/questions/899276/python-how-to-parse-strings-to-look-like-sys-argv – jonrsharpe Aug 25 '17 at 14:06
  • I'll take a look. To clarify, normally the user would be typing (or pasting) the information in on an interactive shell. For demonstration purposes, I skipped the interactive shell and replicated their input using the `.onecmd()`. If you remove the `app.onecmd()` lines and put in an `app.cmdloop()` statement, you would get a shell. I realize now that in trying to make the question more understandable, I might have made it more confusing to those who aren't familiar with cmd/cmd2. – ytjohn Aug 25 '17 at 19:10
  • 1
    Yes, shlex is exactly what I needed. Perfect! – ytjohn Aug 25 '17 at 19:15

0 Answers0