Detect an invalid keyword argument - python

I have the following function:
def foo(**kwargs):
if not kwargs:
# No keyword arguments? It's all right. Set defaults here...
elif ('start_index' or 'end_index') in kwargs:
# Do something here...
else:
# Catch unexpected keyword arguments
raise TypeError("%r are invalid keyword arguments" % (kwargs.keys())
Question:
I want to make sure that the only valid keyword arguments are start_index or end_index. Anything else will raise an error, even if mixed with the valid ones. What's the cookbook recipe to make sure that only start_index or end_index are accepted? Yes, I'm looking for a cookbook recipe but I'm not sure how to search for it. I'm not sure if using an if-elif-else structure is the correct way to do it either.

Why do you need **kwargs here? Just
def foo(start_index=None, end_index=None):
and Python will perform all validation for you.

For the sake of completeness, Here's an alternative that still uses **kwargs.
def foo(**kwargs):
start_index = kwargs.pop('start_index', STARTINDEX_DEFAULT)
end_index = kwargs.pop('end_index', ENDINDEX_DEFAULT)
if kwargs:
# Catch unexpected keyword arguments
raise TypeError("%r are invalid keyword arguments" % (kwargs.keys())
# Do something here...
But, you shouldn't want to use this when you don't absolutely need it, use regular parameters with default values (as in Roman Bodnarchuk's answer).
Cases when you might need this is when you also want to use *args, and need a way to distinguish the keyword arguments from arbitrarily man positional arguments. using **kwargs this way forces the keyword arguments to be passed as keywords; A positional argument can never find its way into **kwargs.
Another reason is so that you can really distinguish between a default and an explicit parameter which happens to be the default. None is often used as a default value for arguments to indicate "the argument doesn't apply", but sometimes you actually need to interpret the None as something other than a default. Checking for the presence or absence of a key in the **kwargs dict can accurately distinguish between these cases. (An alternative is to create an instance of a subclass of object whos sole purpose is to be the default value of a specific argument to that specific function)

If you really want to use **kwargs, I'd write that like:
def foo(**kwargs):
# Define default values for all keys
args = {'start_index': 0, 'end_index': -1}
# Get the keys passed in that aren't in args
extraargs = set(kwargs) - set(args)
if extraargs:
raise TypeError("Invalid arguments: %s" % list(extraargs))
# Overwrite the default values with the passed-in values
args.update(kwargs)
# Now, do stuff with the values in args
But all of that is a complicated, slow way to duplicate built-in functionality. Don't do that unless you really need to.

In any case getting keys from a dict is as easy as using .get e.g.
kwargs.get('WIDTH',500)
this way if it doesn't find WIDTH as a key you get 500.

Related

Optional arguments with few mandatory arguments

I am new to python and came across a problem.
I have to get a dictionary from the user via a function that has few elements (key-value). At the same time, if the user is not providing the dictionary, they can provide individual elements as arguments.
How do I check if user has provided dictA, if not, src msg. If the user doesn't provides either one of them, return an error back to the calling function.
Lets say:
def myfunc(dictA, src, msg, e=True)
where dictA is the dictonary and src, msg are arguements if the user didn't provide dictA.
I wouldn't support them passing in dictA or the other arguments. I would just have them pass in dictA as kwargs if that want to use that. Then you can just check if the values have been there
New function prototype might be
def myfunc(src, msg, e=True)
And then usages could be
myfunc(a_source, a_msg)
Or
myfunc(**dictA)
This way your function will automatically check that those values are at least present. And any additional value checking can be done on only one input
For example the following call would fail (without you needing to do anything else) because it would still be missing the positional argument of msg.
myfunc(**{'src': 'a_source'})
Similarly it will fail if you send too many positional arguments as well (i.e. passing in a positional argument and a kwarg via a dictionary)

How to raise exception when function call is missing an argument in Python

If I have the function
def my_function(a,b,c):
and when the user calls the function, they omit the last argument
print(my_function(a,b))
what exception should I raise?
As others have mentioned, Python will raise a TypeError if a function is called with an incorrect number of statically declared arguments. It seems there is no practical reason to override this behavior to raise your own custom error message since Python's:
TypeError: f() takes 2 positional arguments but 3 were given
is quite telling.
However, if you want to do this, and perhaps optionally allow a second argument, you can use *args.
def my_function(a, *args):
b = None
if len(args) > 1:
raise TypeError("More than 2 arguments not allowed.")
elif args:
b = args[0]
# do something with a and possibly b.
Edit: The other answer suggesting a default keyword argument is more appropriate given new additional details in OP’s comment.
After discussion in the comment, it seems that what you want to do is catch an exception to pass a default argument if one was missing.
First of all, Python will already raise a TypeError if an argument is missing.
But you do not need to catch it to have default arguments since Python already provides a way to do this.
def my_function(a, b, c=0):
pass
my_function(1, 2, 3) # This works fine
my_function(1, 2) # This works as well an used 0 as default argument for c

Passing optional arguments from optparse

I'm trying to figure out how to pass optional arguments from optparse. The problem I'm having is if an optparse option is not specified, it defaults to a None type, but if I pass the None type into a function, it yells at me instead of using the default (Which is understandable and valid).
conn = psycopg2.connect(database=options.db, hostname=options.hostname, port=options.port)
The question is, how do I use the function's defaults for optional arguments but still pass in user inputs if there is an input without having a huge number of if statements.
Define a function remove_none_values that filters a dictionary for none-valued arguments.
def remove_none_values(d):
return dict((k,v) for (k,v) in d.iteritems() if not v is None)
kwargs = {
'database': options.db,
'hostname': options.hostname,
...
}
conn = psycopg2.connect(**remove_none_values(kwargs))
Or, define a function wrapper that removes none values before passing the data on to the original function.
def ignore_none_valued_kwargs(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
newkwargs = dict((k,v) for (k,v) in d.iteritems() if not v is None)
return f(*args, **kwargs)
return wrapper
my_connect = ignore_none_valued_kwargs(psycopg2)
conn = my_connect(database=options.db, hostname=options.hostname, port=options.port)
The opo module of my thebops package (pip install thebops, https://bitbucket.org/therp/thebops) contains an add_optval_option function.
This uses an additional keyword argument empty which specifies the value to use if the option is used without a value. If one of the option strings is found in the commandline, this value is injected into the argument list.
This is still hackish, but at least it is made a simple-to-use function ...
It works well under the following circumstances:
The argument vector does already exist when the option is created. This is usually true.
All programs I found which sport arguments with optional values require the given value to be attached as --option=value or -ovalue rather than --option value or -o value.
Maybe I'll tweak thebops.optparse to support the empty argument as well; but I'd like to have a test suite first to prevent regressions, preferably the original Optik / optparse tests.
This is the code:
from sys import argv
def add_optval_option(pog, *args, **kwargs):
"""
Add an option which can be specified without a value;
in this case, the value (if given) must be contained
in the same argument as seen by the shell,
i.e.:
--option=VALUE, --option will work;
--option VALUE will *not* work
Arguments:
pog -- parser or group
empty -- the value to use when used without a value
Note:
If you specify a short option string as well, the syntax given by the
help will be wrong; -oVALUE will be supported, -o VALUE will not!
Thus it might be wise to create a separate option for the short
option strings (in a "hidden" group which isn't added to the parser after
being populated) and just mention it in the help string.
"""
if 'empty' in kwargs:
empty_val = kwargs.pop('empty')
# in this case it's a good idea to have a <default> value; this can be
# given by another option with the same <dest>, though
for i in range(1, len(argv)):
a = argv[i]
if a == '--':
break
if a in args:
argv.insert(i+1, empty_val)
break
pog.add_option(*args, **kwargs)

Python required variable style

What is the best style for a Python method that requires the keyword argument 'required_arg':
def test_method(required_arg, *args, **kwargs):
def test_method(*args, **kwargs):
required_arg = kwargs.pop('required_arg')
if kwargs:
raise ValueError('Unexpected keyword arguments: %s' % kwargs)
Or something else? I want to use this for all my methods in the future so I'm kind of looking for the best practices way to deal with required keyword arguments in Python methods.
The first method by far. Why duplicate something the language already provides for you?
Optional arguments in most cases should be known (only use *args and **kwargs when there is no possible way of knowing the arguments). Denote optional arguments by giving them their default value (def bar(foo = 0) or def bar(foo = None)). Watch out for the classic gotcha of def bar(foo = []) which doesn't do what you expect.
The first method offers you the opportunity to give your required argument a meaningful name; using *args doesn't. Using *args is great when you need it, but why give up the opportunity for clearer expression of your intent?
If you don't want arbitrary keyword arguments, leave out the ** parameter. For the love of all that is holy, if you have something that is required, just make it a normal argument.
Instead of this:
def test_method(*args, **kwargs):
required_arg = kwargs.pop('required_arg')
if kwargs:
raise ValueError('Unexpected keyword arguments: %s' % kwargs)
Do this:
def test_method(required_arg, *args):
pass

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