0

I want to call the functions using arguments, But I am confused because I am using two arguments by Sys.arg[1] and Sys.arg[2] for file input-output functions.

My script works as a $ python script.py inputfile.txt outputfile.txt

import argparse
import sys

input_file = sys.argv[1]
out_file = sys.argv[2]

def fuction1():
    #Stuff

def fucntion2():
    #Stuff

I am using input_file and out_file in both of these functions for different tasks! Now, I want to call these function from the command line argument for example $ python script.py runfuction1 inputfile.txt outputfile.txt $ python script.py runfuction2 inputfile.txt outputfile.txt

I have tried some solutions from other posts like the question in post number: 27529610 but it's not working, I am confused in using system arguments and argparse together.

Thanks!

Captain-Robot
  • 71
  • 2
  • 8
  • https://stackoverflow.com/questions/27529610/call-function-based-on-argparse – hpaulj Feb 10 '19 at 19:13
  • It's easy enough to define an `argparse` that takes 3 positional arguments, with a usage that looks like "python myscript.py cmd input output". The `args` namespace will have 3 attributes, essentially the same as the 3 elements of `sys.argv[1:]`. The focus of the linked SO is how to constrain the `cmd` attribute with `choices`, and convert that into a function call (with a function map). – hpaulj Feb 10 '19 at 19:18

2 Answers2

2

You shouldn't need argparse for this. Try:

import sys

to_run = int(sys.argv[1][-1])
input_file = sys.argv[2]
out_file = sys.argv[3]

def function1():
    # Stuff
    pass

def function2():
    # Stuff
    pass

(function1, function2)[to_run - 1]()

Even better, you could just create two separate scripts!

You can also use a dictionary, which is a bit more flexible:

import sys

input_file = sys.argv[2]
out_file = sys.argv[3]

def function1():
    # Stuff
    pass

def function2():
    # Stuff
    pass

funcs = {'runfunction1': function1, 'runfunction2': function2}    
funcs[sys.argv[1]]()
iz_
  • 15,923
  • 3
  • 25
  • 40
  • Yeah, I have created two sperate scripts for that as well, But wanted to learn how could I do it in the single script as well. Thanks for an answer let me try that! – Captain-Robot Feb 10 '19 at 07:01
  • Your solution works perfectly! But just for the sake of learning how could I use defined arguments in place of running functions with 1, 2 argument. – Captain-Robot Feb 10 '19 at 07:11
  • @Captain-Robot I added another option that is likely better for your situation. Please be sure to accept the answer if it helped! – iz_ Feb 10 '19 at 07:16
  • Thanks, @Tomothy32. it's perfect! I have just one question if you please don't mind me asking. Why you prefer to run functions using sys.argv and why not used argparse? – Captain-Robot Feb 10 '19 at 07:20
  • @Captain-Robot For more complex stuff, definitely use `argparse`. However, this case is quite simple, so it's not needed. There is a way to accomplish this with `argparse`, but it's just complicated and unnecessary. – iz_ Feb 10 '19 at 07:22
  • one more confusion please, when I changed `runfunction1` to `runfunction` it gave me the error `ValueError: invalid literal for int() with base 10: 'e'` Is that because of the auto-sorting of the dictionary in python? – Captain-Robot Feb 10 '19 at 07:34
  • @Captain-Robot That is because you are using the first code with the `int` conversion. The second code doesn't have the `to_run` variable or `int` conversion. Also, there is no such thing as "auto-sorting" of a dictionary. – iz_ Feb 10 '19 at 07:37
0

Here's a version using argparse and 3 positional arguments:

import argparse
import sys

def function1(input, output):
    print(f'1: {input} to {output}')

def function2(input, output):
    print(f'2: {input} to {output}')

adict = {'runfunction1': function1, 'runfunction2': function2}

parser = argparse.ArgumentParser()
parser.add_argument('cmd', choices=adict)
parser.add_argument('input')
parser.add_argument('output')

args = parser.parse_args()
print(sys.argv[1:])
print(args)
adict[args.cmd](args.input, args.output)

Some sample runs:

1445:~/mypy$ python3 stack54614049.py -h
usage: stack54614049.py [-h] {runfunction1,runfunction2} input output

positional arguments:
  {runfunction1,runfunction2}
  input
  output

optional arguments:
  -h, --help            show this help message and exit

1445:~/mypy$ python3 stack54614049.py 
usage: stack54614049.py [-h] {runfunction1,runfunction2} input output
stack54614049.py: error: the following arguments are required: cmd, input, output

1446:~/mypy$ python3 stack54614049.py foo
usage: stack54614049.py [-h] {runfunction1,runfunction2} input output
stack54614049.py: error: argument cmd: invalid choice: 'foo' (choose from 'runfunction1', 'runfunction2')

1446:~/mypy$ python3 stack54614049.py runfunction1 in out
['runfunction1', 'in', 'out']
Namespace(cmd='runfunction1', input='in', output='out')
1: in to out

1446:~/mypy$ python3 stack54614049.py runfunction2 in out
['runfunction2', 'in', 'out']
Namespace(cmd='runfunction2', input='in', output='out')
2: in to out

With 3 required positional arguments like this argparse doesn't do much fancier parsing than looking at

cmd, input, output = sys.argv[1:]

With choices it objects if the cmd string isn't in the approved list. It adds a help display. Delegation from cmd string to function uses some sort of mapping.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • `argparse` becomes more valuable when you add a bunch of optionals - values that only need to supplied if a default doesn't apply, such as verbosity, output format, – hpaulj Feb 11 '19 at 22:32