1
votes

I need to write a command line application, like a shell. So it will include commands etc. The thing is I don't know how to pass parameters to the funcions in a module. For example:

User writes: function1 folder1 Program should now pass the 'folder1' parameter to the function1 function, and run it. But also it has to support other functions with different parameters ex:

User input: function2 folder2 --exampleparam

How to make this to work? I mean, I could just write a module, import it in python and just use the python console, but this is not the case. I need a script that takes command input and runs it.

I tried to use eval(), but that doesn't solve the problem with params. Or maybe it does but I don't see it?

6

6 Answers

7
votes

The first part of your problem -- parsing the command line -- can be solved with argparse.

The second part -- converting the string name of a function into a function call -- can be done with exec or a dispatching dict which maps from strings to function objects.

I would recommend NOT using exec for this, since allowing a user to call arbitrary Python functions from the command line might be dangerous. Instead, make a whitelist of allowable functions:

import argparse


def foo(path):
    print('Running foo(%r)' % (path, ))


def bar(path):
    print('Running bar(%r)' % (path, ))

dispatch = {
    'foo': foo,
    'bar': bar,
}

parser = argparse.ArgumentParser()
parser.add_argument('function')
parser.add_argument('arguments', nargs='*')
args = parser.parse_args()

dispatch[args.function](*args.arguments)

% test.py foo 1
Running foo('1')
% test.py bar 2
Running bar('2')
% test.py baz 3
KeyError: 'baz'

The above works when the command is typed into the command-line itself. If the command is being typed into stdin, then we'll need to do something a bit different.

A simple way would be to call raw_input to grab the string from stdin. We could then parse the string with argparse, as we did above:

shmod.py:

import argparse


def foo(path):
    print('Running foo(%r)' % (path, ))


def bar(path):
    print('Running bar(%r)' % (path, ))

dispatch = {
    'foo': foo,
    'bar': bar,
}

def parse_args(cmd):
    parser = argparse.ArgumentParser()
    parser.add_argument('function')
    parser.add_argument('arguments', nargs='*')
    args = parser.parse_args(cmd.split())
    return args

main.py:

import shmod

while True:
    cmd = raw_input('> ')
    args = shmod.parse_args(cmd)
    try:
        shmod.dispatch[args.function](*args.arguments)
    except KeyError:
        print('Invalid input: {!r}'.format(cmd))

Another, more sophisticated way to handle this is to use the cmd module, as @chepner mentioned in the comments.

from cmd import Cmd


class MyInterpreter(Cmd):

    prompt = '> '

    def do_prompt(self, line):
        "Change the interactive prompt"
        self.prompt = line + ': '

    def do_EOF(self, line):
        return True

    def do_foo(self, line):
        print('Running foo {l}'.format(l=line))

    def do_bar(self, line):
        print('Running bar {l}'.format(l=line))

if __name__ == '__main__':
    MyInterpreter().cmdloop()

For more information on how to use the cmd module, see Doug Hellman's excellent tutorial.


Running the code above yields a result like this:

% test.py
> foo 1
Running foo 1
> foo 1 2 3
Running foo 1 2 3
> bar 2
Running bar 2
> baz 3
*** Unknown syntax: baz 3
2
votes

optparse is deprecated since python 2.7 and anyway argparse is much more flexible. The approach of unutbu is safe, but in case you provide whitelist, I would suggest you to let the user know which functions are accepted

dispatch = {
    'foo': foo,    
    'bar': bar,
}

parser = argparse.ArgumentParser()
parser.add_argument('function', choices=dispatch.keys() )

FYI: if the parsing is not too complicated, docopt looks like a very nice package

1
votes

How about sys.argv? For more advanced stuff check out argsparse. optparse seems depreciated now, but there's a lot of answers here about this question.

1
votes

Take a look at the optparse module in python. It's exactly what you would need:

http://docs.python.org/2/library/optparse.html

Or you can write your own custom opt-parser (minimalistic though)

def getopts(argv):
   opts = {}
   while argv:
      if argv[0][0] == '-': # find "-name value" pairs
         opts[argv[0]] = argv[1] # dict key is "-name" arg
         argv = argv[2:]
      else:
         argv = argv[1:]
   return opts

if __name__ == '__main__':
from sys import argv # example client code
myargs = getopts(argv)
# DO something based on your logic here

But in case your script needs to run on python 3 and beyond, you need to consider argparse module.\

Hope that helps.

0
votes

Take a look at optparse . This can help passing and receiving shell style parameters to python scripts.

Update: Apparently optparse is deprecated now and argparse is now preferred option for parsing command line arguments.

0
votes
import sys

def main(arg):
    return arg

print main(sys.argv[1])

where sys.argv[0] is the .py file you're running, and all the ones after it would be each argument. you could check the length of the list, then iterate through it, and parse them as necessary and pass the correct things to each function