Can I give a click option another name? - python

I use click like this:
import click
#click.command(name='foo')
#click.option('--bar', required=True)
def do_something(bar):
print(bar)
So the name of the option and the name of the function parameter is the same. When it is not the same (e.g. when you want --id instead of --bar), I get:
TypeError: do_something() got an unexpected keyword argument 'id'
I don't want the parameter to be called id because that is a Python function. I don't want the CLI parameter to be called different, because it would be more cumbersome / less intuitive. How can I fix it?

You just need to add a non-option (doesn't start with -) argument in the click.option decorator. Click will use this as the name of the parameter to the function. This allows you to use Python keywords as option names.
Here is an example which uses id_ inside the function:
import click
#click.command(name='foo')
#click.option('--id', 'id_', required=True)
def do_something(id_):
print(id_)
There is an official example here in the --from option.

Related

Passing arguments to pytest.mark.parametrize via command line arguments

I am trying to pass command line arguments to pytest tests.
Example from this question
print ("Displaying name: %s" % name)
In conftest
parser.addoption("--name", action="store", default="default name")
def pytest_generate_tests(metafunc):
# This is called for every test. Only get/set command line arguments
# if the argument is specified in the list of test "fixturenames".
option_value = metafunc.config.option.name
if 'name' in metafunc.fixturenames and option_value is not None:
metafunc.parametrize("name", [option_value])
I want to pass name argument to pytest.mark.parametrize.
Now, I am aware that according to the question I've referenced, the answer mentions that I shouldn't use #pytest.mark.parametrize. However, I am working on a code base that already utilizes it and I also need to pass arguments as part of CI process. Is there a work around or am I doomed to re-write all of the tests?
Edit:
Since I wasn't clear enough- I cannot use the command line argument in mark.parametrize with pytest console line args.
Example- I have a function that returns a list and has input for name and I add it to the mark.parametrize:
#pytest.mark.parametrize("return_list", foo(name)):
def test_name(return_list):
pass
The name parameter, like I mentioned needs to come from the command line, and this doesn't work in any method that I've encountered.
I don't know how to use a list in pytest without mark.parametrize, so it's a problem
Ok, so I've found an answer using pytest-lazy-fixture.
It can be pip installed for any version of pytest higher than 3.2.5 (and included).
The Gist is calling a custom fixture (conftest of the test itself) by its keyword.
Will leave a full answer here for future reference:
Full example:
In conftest
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
#pytest.fixture(scope = 'module', autouse = True)
def names(request):
""" main code and manipulation on the data """
return request.config.getoption("--name")
In test.py
#pytest.mark.parametrize('arg1',
[pytest.lazy_fixture('names')]
)
def test_something(arg1):
# do something here
pass
This also works for mark.parametrize if the lazy_fixture is fed into another function.

Parameter 'from' in python3: formal parameter name expected [duplicate]

I am using the Click library but I can't seem to find a behavior similar to dest from argparse.
For example, I have
#click.option('--format', type=click.Choice(['t', 'j']))
def plug(format):
pass
Notice that I am using a flag with --format that gets translated into a built-in Python construct format which is not ideal.
Is there a way to change the argument passed into the click function for options?
While Click doesn't have dest-equivalent of argparse, it has certain argument-naming behavior which can be exploited. Specifically, for parameters with multiple possible names, it will prefer non-dashed to dashed names, and as secondary preference will prioritize longer names over shorter names.
URL: http://click.pocoo.org/dev/parameters/#parameter-names
So if you declare your option as...
#click.option('--format', 'not-format', type=click.Choice(['t', 'j']))
...then Click will prioritize non-dashed variant ('not-format') and call your function with not_format=... argument.
Of course it also means that this alternative spelling can also be used in command line. If that is not desired, then I guess you could add a decorator to rename keyword arguments:
import functools
def rename_kwargs(**replacements):
def actual_decorator(func):
#functools.wraps(func)
def decorated_func(*args, **kwargs):
for internal_arg, external_arg in replacements.iteritems():
if external_arg in kwargs:
kwargs[internal_arg] = kwargs.pop(external_arg)
return func(*args, **kwargs)
return decorated_func
return actual_decorator
Testing code:
if __name__ == '__main__':
#rename_kwargs(different_arg='format')
def tester(different_arg):
print different_arg
tester(format='test value')
Test output:
$ python test_decor.py
test value
In your case, it would look like:
#click.option('--format', type=click.Choice(['t', 'j']))
#replace_kwargs(not_format='format')
def plug(not_format):
pass
Renaming an option to a differently named function argument is possible by decorating the function with
#click.option('--format', '-f', 'format_arg_name')
def plug(format_arg_name):
print(format_arg_name)
then it will remap the option named format and make it available as the format_arg_name parameter.
format_arg_name will not be available as a command line option, but --format and -f are.

python: call a function with parameter from input

I have a list of tuples consisting of name, phone number and address, and a function called "all" which just shows a list of all the tuples (like in a phonebook). The function is called via input from a user.
I want another function, called "entry" which shows a specific entry of my list. This function should be called via input as well with the index number of the entry (for example, "entry 12") and show just this entry.
Although I can't figure out how to take the number from the input as a parameter for my function and how to call the function. Does it have to contain a variable in the function name which will later be replaced by the number? How can i do that?
Have you looked into argparse?
import argparse
parser = argparse.ArgumentParser(description='your description')
parser.add_argument('-entry', dest="entry")
args = parser.parse_args()
print (args.entry)
You can then call this with python yourfile.py -entry="this is the entry"
That will allow you to take an input when you run the file.
I'm sorry if misunderstood your question, but it seems like you need function arguments. For example: if your `entry' program just prints out what the user put in, your code would look like this:
def entry(user_input): # the variable in the parentheses is your argument--is a local variable ONLY used in the function
print user_input # prints the variable
# now to call the function--use a variable or input() as the function argument
entry(input("Please input the entry number\n >>> ") # see how the return from the input() function call is used as a variable? this basically uses what the user types in as a function argument.
Try running it, and you'll see how it works.
Best of luck and happy coding!

Argparse use default value instead of argument

I'm trying to use the default value in Python argparse instead of the user specified argument. For example, here's an argument:
parser.add_argument('--fin', default='file.txt')
If a user calls --fin dog, that's obviously not a file, so I want to be able to use the default instead. How would I do that? Does argparse have a function that uses the default instead of the input argument? Also sorry for any typing mistakes, I'm doing this on a phone.
There's no way to access default arguments using the return value from parser.parse_args(). More importantly, how is "dog" obviously not a file? That's a perfectly valid filename on any modern operating system; file extensions are common but they are by no means required.
The only way to determine if something is or is not a file is by trying to open it. If it fails, either it wasn't a file or you don't have access to it.
In this case, the best solution may be something like:
DEFAULT_FIN = 'file.txt'
parser.add_argument('--fin', default=DEFAULT_FIN)
And later on:
try:
fd = open(args.fin)
except IOError:
fd = open(DEFAULT_FIN)
Although I would argue that if the user specifies a filename on the command line and you are unable to open it, then you should print an error and exit.
You can:
argument_file = parser.add_argument('--fin', default='file.txt')
args = parser.parse_args()
try:
fo = open(args.fin)
except:
fo = open(argument_file.default)
print fo.read()
But solution by larsks with constant seems better.
It is possible to find the default value of an argument if you hang onto the identifier of the argument when you define it. e.g.
In [119]: parser=argparse.ArgumentParser()
In [120]: arg1 = parser.add_argument('--fin', default='default.txt')
In [121]: arg1.default
Out[121]: 'default.txt'
arg1, the value returned by the add_argument method is the Action object that the parser uses. It has all the information that you provided to the method. arg1.default is just one of its attributes.
So you could use arg1.default in your post parse_args code that checks whether args.fin is a valid file or not.
larsks approach is just as good, since you, the user, are defining the default in the first place.
You could also write a custom Action class, or argument type that does this checking. There is a builtin argparse.FileType that tries to open the file, and raises an ArgumentType error if it can't. That could be modified to use the default instead. But this a more advanced solution, and isn't obviously superior to doing your own checking after parsing.

Default value for file path in function gives SyntaxError. Work around?

for this,
import os.path
def f(data_file_path=os.path.join(os.getcwd(),'temp'),type):
...
return data
I get this,
SyntaxError: non-default argument follows default argument
Is there a way to make this work or do I have to define a variable such as,
rawdata_path = os.path.join(os.getcwd(),'temp')
and then plug that into the function?
Move type before data_file_path
def f(type,data_file_path=os.path.join(os.getcwd(),'temp')):
Assigning values in the function parameter called default arguments, those should come afther non-default arguments
You have to switch the order of the arguments. Mandatory arguments (without default values) must come before arguments with set default values.
Rearrange the parameters:
def f(type, data_file_path=os.path.join(os.getcwd(),'temp')):
pass
The reason for this is, that arguments with default values can be omitted.
But of you call f('foo'), it is not know if you want to set the type and omit data_file_path or not.
Arguments with a default value should be placed after all arguments without a default value.
Change it to:
import os.path
def f(type, data_file_path=os.path.join(os.getcwd(),'temp')):
...
return data
Never mind.
SyntaxError: non-default argument follows default argument
refers to the order of the arguments so,
def f(type,data_file_path=os.path.join(os.getcwd(),'temp')):
works!
me newbie

Categories

Resources