Select destination variable for click parser - python

I'm wondering how to override destination variable for the click.option (Click lib). For example in such piece of code
import click
#click.command()
#click.option('--output', default='data')
def generate_data(output_folder):
print(output_folder)
So I want to use --output flag but pass its value to output_folder argument, kinda this: #click.option('--output', default='data', dest='output_folder')?
Is there is such an ability in click? I know that argparse allow such a behaviour.

Yes, see the section in the click documentation on parameter names, which covers both options and arguments.
If a parameter is not given a name without dashes, a name is generated automatically by taking the longest argument and converting all dashes to underscores. For an option with ('-f', '--foo-bar'), the parameter name is foo_bar. For an option with ('-x',), the parameter is x. For an option with ('-f', '--filename', 'dest'), the parameter is called dest.
Here's your example:
from __future__ import print_function
import click
#click.command()
#click.option('--output', 'data')
def generate_data(data):
print(data)
if __name__ == '__main__':
generate_data()
Running it:
$ python2.7 stack_overflow.py --output some_output
some_output

Related

How to Add Another Name Option for Python click.version_option?

My question is exactly the same as in How to add option name to the Version option in Click
I have the code, and I am able to print out the version of my library using the command "py main.py --version"
See: https://click.palletsprojects.com/en/8.1.x/api/#click.version_option
#click.version_option(version=version)
def main():
pass
However, I would like to add another name option "-V", how can I do this? The documentation and codebase doesn't seem to have this option to add another name option like for the --help argument. (See: https://click.palletsprojects.com/en/8.1.x/api/#click.Context.help_option_names)
I have tried to add the "-v" name option in the param_decls, however I get the following error:
Neither of these work either, since positional arguments can't come after keyword arguments (first line), and I'm not sure why the second line doesn't work:
#click.version_option(version=version, "--version", "-V")
#click.version_option("--version", "-V", version=version)
Please refer to the answer provided by #aaossa below! :D
You should be able to define additional option names using the positional argument param_decls ("One or more option names." according to the docs)
Here's an example:
import click
version = "0.0.1"
#click.version_option(version, "--version", "-V")
#click.command()
def main():
pass
if __name__ == '__main__':
main()

How do I suppress an argument when nothing is input on command line?

#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s')
parser.add_argument('--choice', '-c', default = argparse.SUPPRESS)
args = parser.parse_args()
def main(selection, choice):
print(selection)
print(choice)
if __name__=='__main__':
main(args.selection, args.choice)
The example provided is just to provide something simple and short that accurately articulates the actual problem I am facing in my project. My goal is to be able to ignore an argument within the code body when it is NOT typed into the terminal. I would like to be able to do this through passing the argument as a parameter for a function. I based my code off of searching 'suppress' in the following link: https://docs.python.org/3/library/argparse.html
When I run the code as is with the terminal input looking like so: python3 stackquestion.py -s cheese, I receive the following error on the line where the function is called:
AttributeError: 'Namespace' object has no attribute 'choice'
I've tried adding the following parameter into parser like so:
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
I've also tried the above with
parser.add_argument('--choice', '-c')
But I get the same issue on the same line.
#Barmar answered this question in the comments. Using 'default = None' in parser.add_argument works fine; The code runs without any errors. I selected the anser from #BorrajaX because it's a simple solution to my problem.
According to the docs:
Providing default=argparse.SUPPRESS causes no attribute to be added if the command-line argument was not present:
But you're still assuming it will be there by using it in the call to main:
main(args.selection, args.choice)
A suppressed argument won't be there (i.e. there won't be an args.choice in the arguments) unless the caller specifically called your script adding --choice="something". If this doesn't happen, args.choice doesn't exist.
If you really want to use SUPPRESS, you're going to have to check whether the argument is in the args Namespace by doing if 'choice' in args: and operate accordingly.
Another option (probably more common) can be using a specific... thing (normally the value None, which is what argparse uses by default, anyway) to be used as a default, and if args.choice is None, then assume it hasn't been provided by the user.
Maybe you could look at this the other way around: You want to ensure selection is provided and leave choice as optional?
You can try to set up the arguments like this:
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s', required=True)
parser.add_argument('--choice', '-c')
args = parser.parse_args()
if __name__ == '__main__':
if args.choice is None:
print("No choice provided")
else:
print(f"Oh, the user provided choice and it's: {args.choice}")
print(f"And selection HAS TO BE THERE, right? {args.selection}")

python: add_argument for read data set but unable to read data folder [duplicate]

I am trying to debug a script which takes command line arguments as an input. Arguments are text files in the same directory. Script gets file names from sys.argv list. My problem is I cannot launch the script with arguments in pycharm.
I have tried to enter arguments into "Script parameters" field in "Run" > "Edit configuration" menu like so:
-s'file1.txt', -s'file2.txt'
But it did not work. How do I launch my script with arguments?
P.S. I am on Ubuntu
In PyCharm the parameters are added in the Script Parameters as you did but, they are enclosed in double quotes "" and without specifying the Interpreter flags like -s. Those flags are specified in the Interpreter options box.
Script Parameters box contents:
"file1.txt" "file2.txt"
Interpeter flags:
-s
Or, visually:
Then, with a simple test file to evaluate:
if __name__ == "__main__":
import sys
print(sys.argv)
We get the parameters we provided (with sys.argv[0] holding the script name of course):
['/Path/to/current/folder/test.py', 'file1.txt', 'file2.txt']
For the sake of others who are wondering on how to get to this window. Here's how:
You can access this by clicking on Select Run/Debug Configurations (to the left of ) and going to the Edit Configurations. A
gif provided for clarity.
On PyCharm Community or Professional Edition 2019.1+ :
From the menu bar click Run -> Edit Configurations
Add your arguments in the Parameters textbox (for example file2.txt file3.txt, or --myFlag myArg --anotherFlag mySecondArg)
Click Apply
Click OK
In addition to Jim's answer (sorry not enough rep points to make a comment), just wanted to point out that the arguments specified in PyCharm do not have special characters escaped, unlike what you would do on the command line. So, whereas on the command line you'd do:
python mediadb.py /media/paul/New\ Volume/Users/paul/Documents/spinmaster/\*.png
the PyCharm parameter would be:
"/media/paul/New Volume/Users/paul/Documents/spinmaster/*.png"
Notice that for some unknown reason, it is not possible to add command line arguments in the PyCharm Edu version. It can be only done in Professional and Community editions.
Add the following to the top of your Python file.
import sys
sys.argv = [
__file__,
'arg1',
'arg2'
]
Now, you can simply right click on the Python script.
The first parameter is the name of the script you want to run. From the second parameter onwards it is the the parameters that you want to pass from your command line. Below is a test script:
from sys import argv
script, first, second = argv
print "Script is ",script
print "first is ",first
print "second is ",second
from sys import argv
script, first, second = argv
print "Script is ",script
print "first is ",first
print "second is ",second
And here is how you pass the input parameters :
'Path to your script','First Parameter','Second Parameter'
Lets say that the Path to your script is /home/my_folder/test.py, the output will be like :
Script is /home/my_folder/test.py
first is First Parameter
second is Second Parameter
It took me some time to figure out that input parameters are comma separated.
I believe it's included even in Edu version. Just right click the solid green arrow button (Run) and choose "Add parameters".
It works in the edu version for me. It was not necessary for me to specify a -s option in the interpreter options.
In edit configuration of PyCharm when you are giving your arguments as string, you should not use '' (these quotations) for giving your input.
Instead of -s'file1.txt', -s'file2.txt'
simply use:
-s file1.txt, -s file2.txt
you can used -argName"argValue" like -d"rd-demo" to add Pycharm arguments
-d"rd-demo" -u"estate"
Arguments added in Parameters Section after selected edit Configuration from IDE
I'm using argparse, and in order to debug my scripts I also using Edit Configuration. For example below the scripts gets 3 parameters (Path, Set1, N) and an optional parameter (flag):
'Path' and 'Set1' from type str.
'N' from type int.
The optional parameter 'flag' from type boolean.
impor argparse
parser = argparse.ArgumentParser(prog='main.py')
parser.add_argument("Path", metavar="path", type=str)
parser.add_argument("Set1", type=str, help="The dataset's name.")
parser.add_argument("N", type=int, help="Number of images.")
parser.add_argument("--flag", action='store_true')
params = parser.parse_args()
In order to to run this in a debug or not by using command line, all needed is:
bar menu Run-->Edit Configuration
Define the Name for your debug/run script.
Set the parameters section. For the above example enter as follow:
The defualt 3 parameters must me included --> "c:\mypath" "name" 50
For the optional parameter --> "c:\mypath" "name" 50 "--flag"
parameter section

ConfigArgParse ignores abbreviated options?

my config.ini:
banana=original_banana
if I run with full argument name, I get the expected result:
python test_configargparse.py --banana new_banana
new_banana
if I run with abbreviated argument name (--ban instead of --banana), I get unexpected behaviour:
python test_configargparse.py --ban new_banana
original_banana
code for test_configargparse.py
import os, configargparse as ap
parser = ap.ArgumentParser(default_config_files=["config.ini"])
parser.add_argument('--banana',dest='banana')
options = parser.parse_args()
print(options.banana)
versions = ConfigArgParse==0.13.0, Python 2.7.10
is this a bug or am I missing something obvious?? it's a very basic feature in a very established module...
NOTE: this feature is explicitly documented in https://docs.python.org/3/library/argparse.html
allows long options to be abbreviated to a prefix, if the abbreviation is unambiguous (the prefix matches a unique option)
It looks like a bug in ConfigArgParse. When it loads options from the config file, it discards any option that is already on the command line.
discard_this_key = already_on_command_line(
args, action.option_strings)
The bug is that already_on_command_line() only checks for complete argument names, not prefixes.
def already_on_command_line(existing_args_list, potential_command_line_args):
"""Utility method for checking if any of the potential_command_line_args is
already present in existing_args.
"""
return any(potential_arg in existing_args_list
for potential_arg in potential_command_line_args)
That leaves two copies of the argument in the list, with the config file's value second. ArgumentParser takes the second value.

Detecting if any command-line options were specified more than once with optparse or argparse

Python optparse normally allows the user to specify an option more than once and silently ignores all occurrences of the option but the last one. For example, if the action of option --foo is store and the action of option --flag is store_const, store_true or store_false, the following commands will be equivalent:
my-command --foo=bar --foo=another --flag --foo=last --flag
my-command --flag --foo=last
(Update: argparse does just the same thing by default.)
Now, I have a lot of options, and specifying any of them more than once doesn't make sense. If a user specifies the same option more than once I'd like to warn them about the possible error.
What is the most elegant way to detect options that were specified multiple times? Note that the same option can have a short form, a long form and abbreviated long forms (so that -f, --foobar, --foob and --foo are all the same option). It would be even better if it was possible to detect the case when multiple options that have the same destination were specified simultaneously, so that a warning can be given if a user specifies both --quiet and --verbose while both options store a value into the same destination and effectively override each other.
Update: To be more user-friendly, the warning should refer to the exact option names as used on the command line. Using append actions instead of store is possible, but when we detect a conflict, we cannot say which options caused it (was it -q and --verbose or --quiet --quiet?).
Unfortunately I'm stuck with optparse and cannot use argparse because I have to support Python 2.6.
P. S. If you know of a solution that works only with argparse, please post it, too. While I try to minimize the number of external dependencies, using argparse under Python 2.6 is still an option.
I think the correct way would be to "define your action" in some way.
For example, you could use the action callback and implement a function that implement your desired behaviour.
You could write a function that first checks if the destination was already filled, if it is filled then it stores the overlapping options into a list.
When the parsing is finished you should check if these lists are empty, and if they are not raise the appropriate exception.
Another way of doing this could be to define your own action. You can have a look here
A small example that uses the callback:
import sys
import functools
from optparse import OptionParser
bad_option = 'BAD OPTION'
def store(option, opt, value, parser, dest, val):
"""Set option's destination *dest* to *val* if there are no conflicting options."""
list_name = dest + '_options_list'
try:
# if this option is a conflict, save its name and set the value to bad_option
getattr(parser.values, list_name).append(opt)
setattr(parser.values, dest, bad_option)
except AttributeError:
# no conflicts, set the option value and add the options list
setattr(parser.values, dest, val)
setattr(parser.values, list_name, [opt])
store_true = functools.partial(store, val=True)
store_false = functools.partial(store, val=False)
parser = OptionParser()
parser.add_option('-v', '--verbose',
action='callback', callback=store_true,
help='Increase output verbosity',
callback_kwargs={'dest': 'verbose'})
parser.add_option('-q', '--quiet',
action='callback', callback=store_false,
help='Decrease output verbosity',
callback_kwargs={'dest': 'verbose'})
opts, args = parser.parse_args()
# detects all conflicting options for all destinations
found = False
for dest in ('verbose',):
if getattr(opts, dest) == bad_option:
conflicting_opts = ', '.join(getattr(opts, dest + '_options_list'))
print('Conflicting options %s for destination %s'
% (conflicting_opts, dest))
found = True
if found:
parser.print_usage()
sys.exit(2)
And the output:
$ python testing_optparse.py -v -q
Conflicting options -v, -q for destination verbose
Usage: prova_optparse.py [options]
Probably it would be better to raise an OptionValueError when detecting conflicts, even though this would allow to get only couple of conflicting options. If you want to get all conflicting options you have to parse the remaining arguments( in parser.rargs).
You can use action="append" (optparse) and then check the number of appended elements. See http://docs.python.org/library/optparse.html#other-actions

Categories

Resources