python3 Execute a function when command line arg is TRUE - python-3.x

I have two functions, (1) running a demo analysis and (2) custom function that is read through a file. I would like to pass a command line argument for the user to select either the demo function or custom function as true or false. No other values needs to be passed. I am not sure what I should add within the function to accept the arg.parse
def demo()
print("This is demo function")
def custom()
print("This is custom function")
def main():
parser = argparse.ArgumentParser(description="Argument Parser is boolean to run Pipeline'")
parser.add_argument("--demo", help='Demo data to show an example')
parser.add_argument("--custom", help='Specify custom analysis, sql query is read from userquery.sql')
args = parser.parse_args()
if args.demo == True:
demoanalysis()
elif args.custom == True:
customanalysis()
else:
print("Don't do anything")
However when I run the script, it goes straight to the third option.
python script.py --demo True --custom False
Don't do anything
I understand something needs to be passed to each function to accept the boolean arguments but unsure how to do it. Any suggestions. Thanks.

I guess, you can use action="store_true" argument (more in the docs):
def main():
parser = argparse.ArgumentParser(description="Argument Parser is boolean to run Pipeline'")
parser.add_argument("--demo", action="store_true", help='Demo data to show an example')
parser.add_argument("--custom", action="store_true", help='Specify custom analysis, sql query is read from userquery.sql')
args = parser.parse_args()
if args.demo: # need no comparison with True, because demo is True or False itself
demoanalysis()
elif args.custom:
customanalysis()
else:
print("Don't do anything")
Edit: If there's no keyword in command line argument, store_true creates False values by default.
Usage examples:
python demo_script.py --demo # prints "This is demo function"
python demo_script.py --custom # prints "This is custom function"
Hope it helps!

You can also build a function to check whether the argument you passed to the function is valid:
def main():
parser = argparse.ArgumentParser(description="Argument Parser is boolean to run Pipeline'")
# Here you add a type definition to your arguments
parser.register('type', 'bool', (lambda x: str(x).lower() == "true") )
parser.add_argument("--demo", type="bool", help='Demo data to show an example')
parser.add_argument("--custom", type="bool", help='Specify custom analysis, sql query is read from userquery.sql')
args = parser.parse_args()
if args.demo:
demoanalysis()
elif args.custom:
customanalysis()
else:
print("Don't do anything")

Related

argparse with multiple flags and arguments

I want my code to be able to call different functions according to the flag and then use the argument passed after the flag as the input to the function.
Example of the expected output:
$ python3 test.py -a 2
4
$ python3 test.py -a human123
<Error message>
$ python3 test.py -h human123
Hello, human123
Here's my sample code:
class Test:
def __init__(self):
pass
def add(self, a):
return a+a
def hello(self, name):
return f"Hello, {name}"
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--add', dest='command', action='store_consts', const='add', nargs=1, help='add a to itself')
parser.add_argument('-h', '--hello', dest='command', action='store_consts', const='hello', nargs=1, help='hello!')
args = parser.parse_args()
t = Test()
if args.command=='add':
print(t.add(args.add))
elif args.command=='sub':
print(t.hello(args.hello))
This sample code currently is doing what I want to achieve. I tried many things to fix the issue from removing the 'consts', changing the action to 'store', changing the value of nargs to '?', etc., however, it keeps giving me different kinds of errors like TypeError, etc.
Simplifying your arguments:
import argparse
class Test:
def __init__(self):
pass
def add(self, a):
return a+a
def hello(self, name):
return f"Hello, {name}"
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--add', help='add a to itself')
parser.add_argument('-b', '--hello', help='hello!') # -h is already taken
args = parser.parse_args()
print(args)
t = Test()
if args.add: # or 'args.add is not None:'
print(t.add(args.add))
elif args.hello:
print(t.hello(args.hello))
test runs:
1936:~/mypy$ python3 stack62702524.py -a testing
Namespace(add='testing', hello=None)
testingtesting
1937:~/mypy$ python3 stack62702524.py -b other
Namespace(add=None, hello='other')
Hello, other
1937:~/mypy$ python3 stack62702524.py
Namespace(add=None, hello=None)
===
Your errors, which you did not show :{ When you get errors, don't just throw up your hands and randomly try alternatives. Read the docs and try to understand the error.
parser.add_argument('-c', action='store_consts', const='hello')
ValueError: unknown action "store_consts"
parser.add_argument('-c', action='store_const', const='hello', nargs=1)
TypeError: __init__() got an unexpected keyword argument 'nargs'
'store_consts' is an name error; with 'store_const' nargs is fixed at 0; you can't change that.
If I add 3 arguments - two store_const and one positional:
parser.add_argument('-c', dest='command', action='store_const', const='add')
parser.add_argument('-d', dest='command', action='store_const', const='hello')
parser.add_argument('foo')
Note the two new command and foo attributes, which you could use in your function call:
1945:~/mypy$ python3 stack62702524.py -c bar
Namespace(add=None, command='add', foo='bar', hello=None)
1945:~/mypy$ python3 stack62702524.py -d bar
Namespace(add=None, command='hello', foo='bar', hello=None)
Typically I use the dest parameter of add_argument to specify the variable name.
For example:
parser.add_argument("-a", "--add", dest="add", required=False, type=str help="some help")
Could be accessed by:
args = parser.parse_args()
print(args.add == "something")
I believe that you need one unique dest per argument.
Also, -h is reserved for help. You may wish to change this to -e or something.
parser.add_argument('-h', '--hello', ...)

python argparse if argument selected then another argument required =True

Is there a way to make an argument required to be true if another specific argument choice is present otherwise argument required is false?
For example the following code if argument -act choice select is 'copy' then the argument dp required is true otherwise required is false:
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("-act", "--act", required=True, choices=['tidy','copy'],type=str.lower,
help="Action Type")
ap.add_argument("-sp", "--sp", required=True,
help="Source Path")
args = vars(ap.parse_args())
if args["act"] == 'copy':
ap.add_argument("-dp", "--dp", required=True,
help="Destination Path")
else:
ap.add_argument("-dp", "--dp", required=False,
help="Destination Path")
args = vars(ap.parse_args())
### Tidy Function
def tidy():
print("tidy Function")
### Copy Function
def copy():
print("copy Function")
### Call Functions
if args["act"] == 'tidy':
tidy()
if args["act"] == 'copy':
copy()
I am currently getting an error unrecognized arguments: -dp with the above code.
The expected result would be to move on to call function. Thanks
I would use ArgumentParser.add_subparsers to define the action type {tidy, copy} and give the command specific arguments. Using a base parser with parents allows you to define arguments that are shared by both (or all) your sub-commands.
import argparse
parser = argparse.ArgumentParser(
prog='PROG',
epilog="See '<command> --help' to read about a specific sub-command."
)
base_parser = argparse.ArgumentParser(add_help=False)
base_parser.add_argument("--sp", required=True, help="source")
subparsers = parser.add_subparsers(dest='act', help='Sub-commands')
A_parser = subparsers.add_parser('copy', help='copy command', parents=[base_parser])
A_parser.add_argument('--dp', required=True, help="dest, required")
B_parser = subparsers.add_parser('tidy', help='tidy command', parents=[base_parser])
B_parser.add_argument('--dp', required=False, help="dest, optional")
args = parser.parse_args()
if args.act == 'copy':
pass
elif args.act == 'tidy':
pass
print(args)
Which produces the following help pages, note that instead of needing to use -act the command is given as a positional parameter.
~ python args.py -h
usage: PROG [-h] {tidy,copy} ...
positional arguments:
{tidy,copy} Sub-commands
tidy tidy command
copy copy command
optional arguments:
-h, --help show this help message and exit
See '<command> --help' to read about a specific sub-command.
~ python args.py copy -h
usage: PROG copy [-h] --sp SP [--dp DP]
optional arguments:
-h, --help show this help message and exit
--sp SP source
--dp DP dest, optional
~ python args.py tidy -h
usage: PROG tidy [-h] --sp SP --dp DP
optional arguments:
-h, --help show this help message and exit
--sp SP source
--dp DP dest, required
ap.add_argument("-dp", "--dp", help="Destination Path")
args = parser.parse_args()
if args.copy is in ['copy']:
if args.dp is None:
parser.error('With copy, a dp value is required')
Since a user can't set a value to None, is None is a good test for arguments that haven't been used.
The parser has a list of defined arguments, parser._actions. Each has a required attribute. During parsing it maintains a set of seen-actions. At the end of parsing it just checks this set against the actions for which required is True, and issues the error message if there are any.
I would argue that testing after parsing is simpler than trying to set the required parameter before hand.
An alternative is to provide dp with a reasonable default. Then you won't care whether the user provides a value or not.
I can imagine defining a custom Action class for copy that would set the required attribute of dp, but overall that would be more work.

Function that to exit from other function

I wanted to create to a function which, when called in other function it exits the previous function based on the first function's input.
def function_2(checking):
if checking == 'cancel':
# I wanted to exit from the function_1
def function_1():
the_input = input("Enter the text: ")
function_2(the_input)
I wanted the code for the function_2 so that it exits from function_1, I know that I can put the if statement in function_1 itself but ill use this to check more than one in input in the same function or even in different function I cant put the if block everywhere it will look unorganized so i want to create a function and it will be convenient to check for more than one word like if cancel is entered i wanted to exit the programm if hello is entered i wanted to do something, to do something its ok but to exit from the current function with the help of other function code please :) if any doubts ask in the comment ill try to give you more info im using python 3.x.x on Windows8.
Why not simply:
def should_continue(checking):
if checking == 'cancel':
return False
return True
def function_1():
the_input = input("Enter the text: ")
if not should_continue(the_input):
return
This is the best solution I think.
Another alternative is to raise an Exception, for example:
def function_2(checking):
if checking == 'cancel':
raise KeyboardInterrupt
def function_1():
the_input = input("Enter the text: ")
function_2(the_input)
try:
function_1()
except KeyboardInterrupt:
print("cancelled by user")

Using cmd module: how to have sub-commands documented in `help` and integrated with autocomplete?

I'm using the cmd module and have a command called server (method do_server()) which has autocomplete out of the box (I'm on macOS). That's all fine and works like expected. But if i want to additionally also use a sub-command on the server command, things don't work out like i need it.
I would need the following:
- sub-commands should also be integrated with the help command (shouldn't be shown when entering help since they are not first level commands but should be shown when entering help server)
- sub-commands should also integrate with auto-complete
Currently i don't see a way to define sub-commands out-of-the-box. I need to implement them as arguments for a command which isn't ideal.
My question would be, how can i achieve automatic documentation of the sub-commands with help and have autocomplete so that it integrates as good as possible with cmd?
For this example, i'd like to better integrate connect as a sub-command:
from cmd import Cmd
class Tansanit(Cmd):
def do_server(self, args):
""" Show server info """
print("Some server details")
if args and args == "connect":
print("Connect to the server")
def do_quit(self, args):
""" Quit CLI """
raise SystemExit
if __name__ == '__main__':
t = Tansanit()
t.prompt = "> "
t.cmdloop()
I would prefer to have something like this:
from cmd import Cmd
class Tansanit(Cmd):
def do_server(self, args):
""" Show server info """
print("Some server details")
def do_server_connect(self, args):
""" Connect to server """
print("Connect to the server")
def do_quit(self, args):
""" Quit CLI """
raise SystemExit
if __name__ == '__main__':
t = Tansanit()
t.prompt = "> "
t.cmdloop()
Unfortunately that's not possible.
Relavant infos can be found here
Autocomplete for sub-commands
The interpreter is able to process completion for commands names, but for commands arguments you will have to help it. For the command xxx, this is done by defining a complete_xxx method. For example, if you have defined a color command, the completion method for this command could be:
_AVAILABLE_COLORS = ('blue', 'green', 'yellow', 'red', 'black')
def complete_color(self, text, line, begidx, endidx):
return [i for i in _AVAILABLE_COLORS if i.startswith(text)]
The complete_xxx method takes four arguments:
text is the string we are matching against, all returned matches must begin with it
line is is the current input line
begidx is the beginning index in the line of the text being matched
endidx is the end index in the line of the text being matched
It should return a list (possibly empty) of strings representing the possible completions. The arguments begidx and endidx are useful when completion depends on the position of the argument.
help for sub-commands
You can also define help for topics that are not related to commands:
def help_introduction(self):
print 'introduction'
print 'a good place for a tutorial'
This isn't perfect since the help will be categorized as undocumented commands but it's for an argument. But maybe still better then nothing.

how to pass many arguments through a command function (do_)

I want to code a simple command that could take 3 arguments for a text adventure game.
Basically at the prompt, I would type 'use key unlock door' and it would run a particular block.
Here is what I coded but it does not work:
def do_use(self, tool, action, object):
if tool == 'key':
if action == 'unlock':
if object == 'door':
print("seems like it works!")
else:
print("nope 1")
else:
print("nope 2")
else:
print("nope 3")
Notes: the rest of the commands work fine. I imported cmd
and the following is the main code:
class Game(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
....
if __name__ == "__main__":
g = Game()
g.cmdloop()
At the prompt, when I type:
>>> use key unlock door
I get the following error message:
TypeError: do_use() takes exactly 4 arguments (2 given)
The code would work if it would print:
seems like it works!
Any help will be appreciated.
Reading that documentation, it looks like all of the commands just take in a single string, and you have to parse the string yourself. Your command is defined as taking 4 arguments (including self), and cmd is calling it with self, input, which is 2. I think could get the result you want with the following:
def do_use(self, user_input):
args = user_input.split()
if len(args) != 3:
print "*** invalid number of arguments"
else:
tool, action, obj = args
# Do the rest of your code here

Resources