Python Library "Click" pass arguments to a function - python

I am using the click library and I have a function "agent_diff" that takes in three arguments...and I would like to take in arguments from the CLI and pass that into my function. But I am not entirely sure how to pass those values into "agent_diff". Here is the current code that I have:
#!/usr/bin/env python
import os
from sys import path
path.append(os.getcwd())
from src.main.scripts.bitbucket import *
import click
#click.command()
#click.argument('arg')
#click.option('--repository')
#click.option('tag1')
#click.option('tag2')
def main(arg):
print(os.path.expanduser("~"))
if arg.upper() == 'ORB-LIST':
print('Printing List of Orbs:')
orb_list()
if arg.upper() == 'AGENT-LIST':
print('Printing List of Agents:')
agent_list()
if arg.upper() == 'AGENT-DIFF':
agent_diff()
if __name__ == '__main__':
main()
Any insight would be wonderful! Thank you.

The click arguments and options must correspond to the arguments of the function. See the example below.
#click.command()
#click.argument('arg')
#click.option('--repository')
#click.option('tag1')
#click.option('tag2')
def main(arg, repository, tag1, tag2):
# do stuff...
pass
The order of the arguments does not matter.

Related

Display full help text in python click

I am having the following problem and I am fearful there isn't a straghtforward way to solve it so I am asking here. I am using Click to implement a CLI and I have created several grouped commands under the main command. This is the code:
#click.group()
def main():
pass
#main.command()
def getq():
'''Parameters: --questionnaire_id, --question_id, --session_id, --option_id'''
click.echo('Question Answers')
When I type the main command alone in my terminal it lists all the subcommands with the help text next to each one. However, the text is not displayed fully for the case of getq. Instead, it displays only "Parameters: --questionnaire_id, --question_id,... ."
Is there a way to display it all?
Thank You
The easiest way to do this is to use the command's short_help argument:
#click.group()
def main():
pass
#main.command(short_help='Parameters: --questionnaire_id, --question_id, --session_id, --option_id')
def getq():
click.echo('Question Answers')
If you insist to use the docstring for this and want to override the automatic shortening of it, then you could use a custom Group class overriding the format_commands method to directly use cmd.help instead of the get_short_help_str method:
import click
from gettext import gettext as _
class FullHelpGroup(click.Group):
def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""Extra format methods for multi methods that adds all the commands
after the options.
"""
commands = []
for subcommand in self.list_commands(ctx):
cmd = self.get_command(ctx, subcommand)
# What is this, the tool lied about a command. Ignore it
if cmd is None:
continue
if cmd.hidden:
continue
commands.append((subcommand, cmd))
# allow for 3 times the default spacing
if len(commands):
limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands)
rows = []
for subcommand, cmd in commands:
help = cmd.help if cmd.help is not None else ""
rows.append((subcommand, help))
if rows:
with formatter.section(_("Commands")):
formatter.write_dl(rows)
#click.group(cls=FullHelpGroup)
def main():
pass
#main.command()
def getq():
'''Parameters: --questionnaire_id, --question_id, --session_id, --option_id'''
click.echo('Question Answers')
if __name__ == "__main__":
main()
You most probably want to override the max_content_width (at most 80 columns by default) also. You could do this by overriding the context settings:
import shutil
#click.group(cls=FullHelpGroup,
context_settings={'max_content_width': shutil.get_terminal_size().columns - 10})
def main():
pass

Running python main with arguments programmatically

Hello I am having a piece of code that is like this root/main.py
def main():
parser = argparse.ArgumentParser(prog="my prog")
parser.add_argument('--param-1')
parser.add_argument('--param-2')
parser_result, unknown = parser.parse_known_args()
...(do stuff with params)
if __name__ == '__main__':
main()
and root/folder1/folder2/util.py
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../../../")
import main
...
main.main(param1, param2) //errors here for wrong arguments
My question is how do I trigger my main with params, there is a way to use java like reflection to trigger and read correct in main my arguments? (Ideally I need as less changes on main as possible)
Also used and worked:
os.system("python /root/main.py --param-1 '{param1}' --param-2'{param2}'")
But I prefer a solution that keeps the same context and is cleaner.
There is probably a way to do it like that, but the simplest way would be to add default parameters to main:
main.py
def main(param1="anything", param2="anything"):
# do something with parameters
if __name__ == "__main__":
main()
util.py
import main
main.main(param1, param2)

Click: NameError: name not defined

I'm trying to use click to pass command-line arguments to a function but having difficulty. I'm trying to pass two command-line arguments with this:
python script.py --first-a hi --second-a there
Here's my attempt:
import click
#click.command()
#click.option("--first-a")
#click.option("--second-a")
def main(first_a, second_a):
print first_a, second_a
if __name__ == "__main__":
main(first_a, first_a)
This fails with:
NameError: name 'first_a' is not defined
I thought this had to do with dashes vs. underscores but removing the dashes and underscores (just using firsta and seconda) also fails with the same issue.
What am I doing incorrectly?
You need to call main() either without any arguments, or with a list of parameters as would normally be found in sys.argv.
Code:
if __name__ == "__main__":
main()
Test Code:
import click
#click.command()
#click.option("--first-a")
#click.option("--second-a")
def main(first_a, second_a):
print(first_a, second_a)
if __name__ == "__main__":
# test code
import sys
sys.argv[1:] = ['--first-a', 'hi', '--second-a', 'there']
# actual call to click command
main()
Results:
hi there

python calling custom function from bash shell

How can I call the custom-defined function in a python script from a bash shell?
I tried to use sys.argv[1], but not working properly.
for example,
import sys
if __name__=='__main__':
try:
func = sys.argv[1]
except: func = None
def function1():
~~~~~~~~
return a
def function2():
~~~~~~~~
return b
here, I want to call the function 1 or function 2 by typing like
$ script.py function1
$ script.py function2
You are getting the name of function , but you are not running it. You should check first if the func name is one of your functions than execute it:
if __name__=='__main__':
try:
func = sys.argv[1]
except:
func = None
functions = {
"function1": function1,
"function2": function2
}
if func in functions:
functions[func]()
A simpler solution:
if func == "function1":
function1()
elif func == "function2":
function2()
I suggest to use argparse module: https://docs.python.org/3/library/argparse.html#module-argparse
You will thank yourself later.
For your case - since you need to call only 1 function at the time - you can use positional arguments:
import argparse
def function1():
print("1")
def function2():
print("2")
parser = argparse.ArgumentParser()
F_MAP = {'function1': function1,
'function2': function2}
parser.add_argument('function', choices=F_MAP.keys())
args = parser.parse_args()
F_MAP[args.function]()
As a bonus you get a nice help page when calling with -h argument :)
#bigOTHER's answer is good and correct, but if you're looking to build a relatively complex text UI, maybe have a look at something like Click?
You can refer Display a list of user defined functions in the Python IDLE session
import types
list_function = [f for f in globals().values() if type(f) == types.FunctionType]
This will return list of available functions, Then you can check if any of these contain sys.argv[1], If yes then you can call your function as
list_function[index]()

Access function from within scripts and from commandline

I want to do the following:
I have a class which should provide several functions, which need different inputs. And I would like to use these functions from within other scripts, or solely from commandline.
e.g. I have the class "test". It has a function "quicktest" (which basically justs prints something). (From commandline) I want to be able to
$ python test.py quicktest "foo" "bar"
Whereas quicktest is the name of the function, and "foo" and "bar" are the variables.
Also (from within another script) I want to
from test import test
# this
t = test()
t.quicktest(["foo1", "bar1"])
# or this
test().quicktest(["foo2", "bar2"])
I just can't bring that to work. I managed to write a class for the first request and one for the second, but not for both of them. The problem is that I sometimes have to call the functions via (self), sometimes not, and also I have to provide the given parameters at any time, which is also kinda complicated.
So, does anybody have an idea for that?
This is what I already have:
Works only from commandline:
class test:
def quicktest(params):
pprint(params)
if (__name__ == '__main__'):
if (sys.argv[1] == "quicktest"):
quicktest(sys.argv)
else:
print "Wrong call."
Works only from within other scripts:
class test:
_params = sys.argv
def quicktest(self, params):
pprint(params)
pprint(self._params)
if (__name__ == '__main__'):
if (sys.argv[1] == "quicktest"):
quicktest()
else:
print "Wrong call"
try the following (note that the different indentation, the if __name__ part is not part of class test anymore):
class test:
def quicktest(params):
pprint(params)
if __name__ == '__main__':
if sys.argv[1] == "quicktest":
testObj = test()
testObj.quicktest(sys.argv)
else:
print "Wrong call."
from other scripts:
from test import test
testObj = test()
testObj.quicktest(...)
The if __name__ == '__main__': block needs to be at the top level:
class Test(object): # Python class names are capitalized and should inherit from object
def __init__(self, *args):
# parse args here so you can import and call with options too
self.args = args
def quicktest(self):
return 'ret_value'
if __name__ == '__main__':
test = Test(sys.argv[1:])
You can parse the command line with the help of argparse to parse the value from the command line.
Your class which has the method and associate methods to arguments.

Categories