How to add arguments to Python script entrypoint based on conditions - python

I have an entry point like so:
ENTRYPOINT [ "python" ,"/usr/local/bin/script.py" ]
I want to be able to add multiple arguments to script.py if a particular container has "MY_ENV_VAR" set to TRUE.
So for instance if:
MY_ENV_VAR = true
MY_ENV_VAR2 = true
MY_ENV_VAR3 = false (or anything other than true)
I want to run
/usr/local/bin/script.py --my-env-var --my-env-var2
I can't find a good working example of how to accomplish this

You will not be able to conditionally do this within the Dockerfile, but you can achieve the desired behavior by configuring the Python script script.py as follows:
import argparse
import os
def main():
parser = argparse.ArgumentParser("Arguments")
if os.environ.get("MY_ENV_VAR") is not None:
parser.add_argument("--my-env-var", required=True, action="store_true")
if os.environ.get("MY_ENV_VAR2") is not None:
parser.add_argument("--my-env-var2", required=True, action="store_true")
parser.parse_args()
if __name__ == "__main__":
main()
The logic is that if MY_ENV_VAR or MY_ENV_VAR2, then a required argparse argument is set.

Related

How to use command line arguments to change variables

I'm developing a game in pygame and I'm trying to use command line arguments.
Here's an example of what I'm trying to do with a description at the top:
# user sets variables with command lines. some options:
# python test.py (all default settings)
# python test.py -f (for fog)
# python test.py -f -m (for fog and multiplayer)
# python test.py -m (for multiplayer)
# default settings
fog = False
mode = 'singleplayer'
if fog == True:
print(1)
else:
print(2)
if mode == 'multiplayer':
print(3)
How do I make it so that the user can type an option as stated in my 4 test cases and it will change the value of my variables?
I've looked at https://docs.python.org/3/library/argparse.html#module-argparse and wasn't able to understand how I can do what I want to do.
See the example in the argparse documentation. You would do something like:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-f', action='store_true')
parser.add_argument('-m', action='store_true')
args = parser.parse_args()
print(args.f)
print(args.m)

How to supply args from the argparse to an external program call via subprocess

In a task, I need to call an external program that run on terminal via python. This external program has lots of positional and optional arguments.
Here is the sample code developed sofar:
def multi_character_parameter():
parser = argparse.ArgumentParser()
parser.add_argument('b',
help="Execute tests with browser 'firefox' or 'chrome', or 'ALL")
parser.add_argument('C', help="Execute tests in cloud 'aws' or 'azure'")
parser.add_argument(
'c', help="Clear output and temp directory before executing tests")
parser.add_argument('d', help="Check syntax for test data")
parser.add_argument("--upper-case", default=False, action="store_true")
...
args = parser.parse_args()
def my_external_program():
subprocess.call(["external_program"])
Could someone please suggest:
How to supply these args to the external_program called via subprocess.
i.e. upon running say python script.py some_args, these some_args will be supplied to the external_program function for execution as external_program some_args.
What would be the better way to structure the code if the args specified in the code needs to be used at other parts of the code.
Once you set the variable args, you can access any of the arguments using dot notation.
I have edited your code to demonstrate this:
#!/usr/bin/env python3
import argparse
import subprocess
def multi_character_parameter():
parser = argparse.ArgumentParser()
parser.add_argument('--b',
help="Execute tests with browser 'firefox' or 'chrome', or 'ALL",
required=False,
default=False)
parser.add_argument('--C', help="Execute tests in cloud 'aws' or 'azure'",
required=False, default=False)
parser.add_argument(
'--c', help="Clear output and temp directory before executing tests",
required=False, default=False)
parser.add_argument('--d', help="Check syntax for test data", required=False, default=False)
parser.add_argument("--upper-case", action="store_true", required=False, default=False)
args = parser.parse_args()
return args
def my_external_program(args):
print(args)
b = str(args.b)
C = str(args.C)
c = str(args.c)
d = str(args.d)
upper_case = str(args.upper_case)
subprocess.call(["echo", b,C,c,d,upper_case ])
if __name__ == "__main__":
args = multi_character_parameter()
my_external_program(multi_character_parameter())
Now to run it chmod +x test.py
./test.py
The stdout is presented to show access to the args (the external program was subbed out for echo as the program:
Namespace(b=False, C=False, c=False, d=False, upper_case=False)
False False False False False
If you just want to literally pass all the arguments:
import sys
subprocess.call(["external_program"] + sys.argv[1:])
This takes all the arguments passed to the script (except the first, which is the script's name) and just appends them to the argument you already provided, the name of the external program - and probably works as expected.
So, if you were to run python your_script.py 1 2 3 4 (which matches your specified argument requirements), it would call external_program 1 2 3 4.
The more general answer is that you should just provide a list of arguments to subprocess.call, as described in the documentation for subprocess.call - "args should be a sequence of program arguments or else a single string or path-like object."
Since you provide no information on exactly what mapping you're after, or how the arguments to your script translate to arguments for the external program, it's not possible to be a lot more specific - other than saying you need to construct a sequence of arguments to pass to the call.
If you want to limit or restrict the arguments passed to external_program you can do something like this
allowed_args = ['--upper-case', 'b', 'c', 'C', 'd']
subprocess.run(['external_program'] + list(set(sys.argv).intersection(allowed_args)))

How to create conditonal arguments based on arguments using python's argparse?

I'm trying to create small command line tool. My approach was to start off with a list of commands that I would like to run and then create a parser accommodate those commands. Rather than set up the parser and then have the dictate what the input should be.
I'm struggling to figure out how to set up arguments based on previous inputs. Below are a few examples of the commands I am aiming for.
cgen create test-runner
cgen create config --branch branch_name
cgen create guide --branch branch_name
I currently have it set up so that create has set choices as an argument. Then, depending on the inputted argument, I would like to have branch be a required argument if config or guide is run, but not if test-runner is inputted.
I keep tearing things down and trying different approaches so what I have is really basic at the moment but look something like this...
def main():
def run_create(args):
print('run_create')
if args.create_type == 'test-runner':
create_test_runner(args)
if args.create_type == 'config':
print(f'Creating config')
if args.create_type == 'guide':
print(f'Creating guide')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
# create the parser for the "create" command
parser_create = subparsers.add_parser('create')
parser_create.add_argument(choices=['test-runner', 'config', 'guide'], dest='create_type')
parser_create.set_defaults(func=run_create)
args = parser.parse_args()
args.func(args)
When you add arguments you can specify if you want them to be required or not link.
So you can test on the fly and make an argument obligatory.
def arguments_is_given(*args: str) -> bool:
return len(set(args) & set(sys.argv)) > 0
def main():
def run_create(args):
print('run_create')
if args.create_type == 'test-runner':
create_test_runner(args)
if args.create_type == 'config':
print(f'Creating config')
if args.create_type == 'guide':
print(f'Creating guide')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
# create the parser for the "create" command
parser_create = subparsers.add_parser('create')
parser_create.add_argument("--branch",required=not arguments_is_given("config", "guide"))
parser_create.add_argument(choices=['test-runner', 'config', 'guide'], dest='create_type')
parser_create.set_defaults(func=run_create)
args = parser.parse_args()
args.func(args)

Using the '--help' command in argparse

I am using the argparse library but for whatever reason I'm having difficult printing the -h argument. Here is the entire source I have:
# df.py
import argparse
parser = argparse.ArgumentParser(description='Dedupe assets in our library.')
parser.add_argument('--masters', nargs='?', default=None, type=int, help='Enter one or more ids.')
if __name__ == '__main__':
print ('hi')
I was under the impression that entering in the --h flag via:
$ python df.py --help
Would automatically print the help stuff for the file using argparse but I seem to be making false assumptions. It seems like I also have to add in something like this into my code?
if '--help' in sys.argv: print (parser.parse_args(['-h']))
What is the 'proper' way to print out the help args when using the argparse library?
You forgot to actually parse the arguments; if you put parser.parse_args() in after defining the parser, it would respond to -h/--help. Typically, you'd do something like:
args = parser.parse_args()
so that the args object can be used to access the parsed argument data.
I'll also note that the argument parsing should almost certainly be controlled by the if __name__ == '__main__': guard; if you're not being invoked as the main script, parsing the command line is unusual, to say the least. Idiomatic code would look something like:
# df.py
def main():
import argparse # Could be moved to top level, but given it's only used
# in main, it's not a terrible idea to import in main
parser = argparse.ArgumentParser(description='Dedupe assets in our library.')
parser.add_argument('--masters', nargs='?', type=int, help='Enter one or more ids.')
args = parser.parse_args()
print ('hi')
# Do something with args.masters or whatever
if __name__ == '__main__':
main()

Call function based on argparse

I'm new to python and currently playing with it.
I have a script which does some API Calls to an appliance. I would like to extend the functionality and call different functions based on the arguments given when calling the script.
Currently I have the following:
parser = argparse.ArgumentParser()
parser.add_argument("--showtop20", help="list top 20 by app",
action="store_true")
parser.add_argument("--listapps", help="list all available apps",
action="store_true")
args = parser.parse_args()
I also have a
def showtop20():
.....
and
def listapps():
....
How can I call the function (and only this) based on the argument given?
I don't want to run
if args.showtop20:
#code here
if args.listapps:
#code here
as I want to move the different functions to a module later on keeping the main executable file clean and tidy.
Since it seems like you want to run one, and only one, function depending on the arguments given, I would suggest you use a mandatory positional argument ./prog command, instead of optional arguments (./prog --command1 or ./prog --command2).
so, something like this should do it:
FUNCTION_MAP = {'top20' : my_top20_func,
'listapps' : my_listapps_func }
parser.add_argument('command', choices=FUNCTION_MAP.keys())
args = parser.parse_args()
func = FUNCTION_MAP[args.command]
func()
At least from what you have described, --showtop20 and --listapps sound more like sub-commands than options. Assuming this is the case, we can use subparsers to achieve your desired result. Here is a proof of concept:
import argparse
import sys
def showtop20():
print('running showtop20')
def listapps():
print('running listapps')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
# Create a showtop20 subcommand
parser_showtop20 = subparsers.add_parser('showtop20', help='list top 20 by app')
parser_showtop20.set_defaults(func=showtop20)
# Create a listapps subcommand
parser_listapps = subparsers.add_parser('listapps', help='list all available apps')
parser_listapps.set_defaults(func=listapps)
# Print usage message if no args are supplied.
# NOTE: Python 2 will error 'too few arguments' if no subcommand is supplied.
# No such error occurs in Python 3, which makes it feasible to check
# whether a subcommand was provided (displaying a help message if not).
# argparse internals vary significantly over the major versions, so it's
# much easier to just override the args passed to it.
if len(sys.argv) <= 1:
sys.argv.append('--help')
options = parser.parse_args()
# Run the appropriate function (in this case showtop20 or listapps)
options.func()
# If you add command-line options, consider passing them to the function,
# e.g. `options.func(options)`
There are lots of ways of skinning this cat. Here's one using action='store_const' (inspired by the documented subparser example):
p=argparse.ArgumentParser()
p.add_argument('--cmd1', action='store_const', const=lambda:'cmd1', dest='cmd')
p.add_argument('--cmd2', action='store_const', const=lambda:'cmd2', dest='cmd')
args = p.parse_args(['--cmd1'])
# Out[21]: Namespace(cmd=<function <lambda> at 0x9abf994>)
p.parse_args(['--cmd2']).cmd()
# Out[19]: 'cmd2'
p.parse_args(['--cmd1']).cmd()
# Out[20]: 'cmd1'
With a shared dest, each action puts its function (const) in the same Namespace attribute. The function is invoked by args.cmd().
And as in the documented subparsers example, those functions could be written so as to use other values from Namespace.
args = parse_args()
args.cmd(args)
For sake of comparison, here's the equivalent subparsers case:
p = argparse.ArgumentParser()
sp = p.add_subparsers(dest='cmdstr')
sp1 = sp.add_parser('cmd1')
sp1.set_defaults(cmd=lambda:'cmd1')
sp2 = sp.add_parser('cmd2')
sp2.set_defaults(cmd=lambda:'cmd2')
p.parse_args(['cmd1']).cmd()
# Out[25]: 'cmd1'
As illustrated in the documentation, subparsers lets you define different parameter arguments for each of the commands.
And of course all of these add argument or parser statements could be created in a loop over some list or dictionary that pairs a key with a function.
Another important consideration - what kind of usage and help do you want? The different approaches generate very different help messages.
If your functions are "simple enough" take adventage of type parameter https://docs.python.org/2.7/library/argparse.html#type
type= can take any callable that takes a single string argument and
returns the converted value:
In your example (even if you don't need a converted value):
parser.add_argument("--listapps", help="list all available apps",
type=showtop20,
action="store")
This simple script:
import argparse
def showtop20(dummy):
print "{0}\n".format(dummy) * 5
parser = argparse.ArgumentParser()
parser.add_argument("--listapps", help="list all available apps",
type=showtop20,
action="store")
args = parser.parse_args()
Will give:
# ./test.py --listapps test
test
test
test
test
test
test
Instead of using your code as your_script --showtop20, make it into a sub-command your_script showtop20 and use the click library instead of argparse. You define functions that are the name of your subcommand and use decorators to specify the arguments:
import click
#click.group()
#click.option('--debug/--no-debug', default=False)
def cli(debug):
print(f'Debug mode is {"on" if debug else "off"}')
#cli.command() # #cli, not #click!
def showtop20():
# ...
#cli.command()
def listapps():
# ...
See https://click.palletsprojects.com/en/master/commands/
# based on parser input to invoke either regression/classification plus other params
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--path", type=str)
parser.add_argument("--target", type=str)
parser.add_argument("--type", type=str)
parser.add_argument("--deviceType", type=str)
args = parser.parse_args()
df = pd.read_csv(args.path)
df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
if args.type == "classification":
classify = AutoML(df, args.target, args.type, args.deviceType)
classify.class_dist()
classify.classification()
elif args.type == "regression":
reg = AutoML(df, args.target, args.type, args.deviceType)
reg.regression()
else:
ValueError("Invalid argument passed")
# Values passed as : python app.py --path C:\Users\Abhishek\Downloads\adult.csv --target income --type classification --deviceType GPU
You can evaluate using evalwhether your argument value is callable:
import argparse
def list_showtop20():
print("Calling from showtop20")
def list_apps():
print("Calling from listapps")
my_funcs = [x for x in dir() if x.startswith('list_')]
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--function", required=True,
choices=my_funcs,
help="function to call", metavar="")
args = parser.parse_args()
eval(args.function)()

Categories

Resources