Argparse use default value instead of argument - python

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.

Related

Python ast - getting function parameters and processing them

I'm trying to use the ast module in Python to parse input code, but am struggling with a lot of the syntax of how to do so. For instance, I have the following code as a testing environment:
import ast
class NodeVisitor(ast.NodeVisitor):
def visit_Call(self, node):
for each in node.args:
print(ast.literal_eval(each))
self.generic_visit(node)
line = "circuit = QubitCircuit(3, True)"
tree = ast.parse(line)
print("VISITOR")
visitor = NodeVisitor()
visitor.visit(tree)
Output:
VISITOR
3
True
In this instance, and please correct me if I'm wrong, the visit_Call will be used if it's a function call? So I can get each argument, however there's no guarantee it will work like this as there are different arguments available to be provided. I understand that node.args is providing my arguments, but I'm not sure how to do things with them?
I guess what I'm asking is how do I check what the arguments are and do different things with them? I'd like to check, perhaps, that the first argument is an Int, and if so, run processInt(parameter) as an example.
The value each in your loop in the method will be assigned to the AST node for each of the arguments in each function call you visit. There are lots of different types of AST nodes, so by checking which kind you have, you may be able to learn things about the argument being passed in.
Note however that the AST is about syntax, not values. So if the function call was foo(bar), it's just going to tell you that the argument is a variable named bar, not what the value of that variable is (which it does not know). If the function call was foo(bar(baz)), it's going to show you that the argument is another function call. If you only need to handle calls with literals as their arguments, then you're probably going to be OK, you'll just look instances of AST.Num and similar.
If you want to check if the first argument is a number and process it if it is, you can do something like:
def visit_Call(self, node):
first_arg = node.args[0]
if isinstance(first_arg, ast.Num):
processInt(first_arg.n)
else:
pass # Do you want to do something on a bad argument? Raise an exception maybe?

Python: Using string stored in variable to create attribute to class

I am trying to follow syntax of the pyparticleio.ParticleCloud package. Using the following command, my code works correctly "particle_cloud.boron1.led('on')" (hardcoded values)
I want to pass portions of the command, "boron1" and "on" as variable. I'm trying to figure out how to use those variables to act in the same way as if i'd hardcoded the values.
My python level is very beginner.
command_list['boron1','on']
device = command_list[0]
function_1 = command_list[1]
access_token = "ak3bidl3xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
particle_cloud = ParticleCloud(username_or_access_token=access_token)
#particle_cloud.boron1.led('on') #hardcoded example that works
particle_cloud.device.led(function_1) #what i would like to work
If you set device to the actual object, you can call methods on the object. Example:
device = particle_cloud.boron1 # Or whatever you like
arg = 'on' # Also make this whatever you like
device.led(arg) # Same as: particle_cloud.boron1.led('on')
Python has a built in function called exec
It allows you to take a string, and have Python execute it as code.
A basic example based on the code you provided would look like this:
command_list['boron1','on']
device = command_list[0]
function_1 = command_list[1]
exec('particle_cloud.' + device + '.led("' + function_1 + '")')
This is a bit ugly, but there are different ways to compose strings in Python such as using join or format so depending on your real code you may be able to build something nice.
Just be careful not to pass raw user input to exec!
I can cause all kinds of trouble from errors to security issues.
I believe you could use getattr() (in Python3: https://docs.python.org/3/library/functions.html#getattr ) :
pcdevice = getattr(particle_cloud, device)
pcdevice.led(function_1)
(BTW, I woudln't name the string 'on' with the label 'function_1' as the variable name implies that this option is a function when it is a string. Also, the above may now work depending on the properties of your library object ParticleCloud.)

Triggering callback on default value in optparse

I'm using Python's optparse to do what it does best, but I can't figure out how to make the option callback trigger on the default argument value if no other is specified via command-line; is this even possible? This would make my code much cleaner.
I can't use argparse unfortunately, as the platform I'm running on has an outdated Python version.
Edit:
To provide more detail, I'm adding an option with a callback and a default value
parser.add_option(
"-f",
"--format",
type = "string",
action = "callback",
callback = format_callback,
default = "a,b,c,d")
The callback function is defined as follows:
def format_callback(option, opt, value, parser):
# some processing
parser.values.V = processed_value
Basically I'm processing the "--format" value and putting the result into the parser. This works fine, when "--format" is specified directly via command-line, but I'd like the callback to be triggered on the default "a,b,c,d" value as well.
It is simply not possible.
The optparse.OptionParser implementation of parse_args starts with:
def parse_args(self, args=None, values=None):
"""
parse_args(args : [string] = sys.argv[1:],
values : Values = None)
-> (values : Values, args : [string])
Parse the command-line options found in 'args' (default:
sys.argv[1:]). Any errors result in a call to 'error()', which
by default prints the usage message to stderr and calls
sys.exit() with an error message. On success returns a pair
(values, args) where 'values' is an Values instance (with all
your option values) and 'args' is the list of arguments left
over after parsing options.
"""
rargs = self._get_args(args)
if values is None:
values = self.get_default_values()
Default values are set before processing any arguments. Actual values then overwrite defaults as options are parsed; the option callbacks are called when a corresponding argument is found.
So callbacks simply cannot be invoked for defaults. The design of the optparse module makes this very hard to change.
You can inject the default when calling parse_args
options, args = parser.parse_args(args=["--option=default"] + sys.argv[1:])
Since flags passed later in the argument list override those passed earlier, this should work. It's possible you may need to modify your callback function to expect this depending on what it is doing.

Is it in Python possible to set the last parameter of several in a function call?

Is it in Python possible to set the last parameter of several in a function call if all have a default value?
Example: in the ftplib.FTP the module defines this:
ftplib.FTP([host[, user[, passwd[, acct[, timeout]]]]])
All these parameters have a default value, so you donĀ“t need to set them all, you could, for example, just call ftp = ftplib.FTP() to get a FTP object. But what if I would like to set only the timeout parameter? How would I achieve this?
You can pass this (or any other) parameter by name:
ftplib.FTP(timeout=10)
Yes, by using named arguments you can specify the value with a name-value pair.
ftp = ftplib.FTP(timeout=100)

How should I convert a Python tuple of strings into dynamically-specified types?

I'm writing a simple Python application using the cmd module to provide a CLI-type interface. The commands provided by my CLI have parameter lists that vary widely. Each command handler receives a string argument containing the portion of the line that contains arguments; I plan to tokenize them into a tuple using shlex.split. Subsequently, I'm looking for the most Pythonic way to take that tuple of strings, validate that they are well-formed, and convert them into a tuple of cleanly-specified numeric types.
Example: I have a function foo that takes 3 arguments: the first is a path to a file on disk, the second is a floating-point value, and the third is an integer, like:
foo /home/jason/file.bin 123.456 123456
I'd like a clean way of specifying this, something akin to using C's sscanf() with a format string of "%s %f %d" (I understand the whitespace-handling issues inherent in that approach; it's just an illustration).
I know that I can accomplish this by writing boilerplate code for each handler function that calls int(), float(), etc. and catches exceptions appropriately. It just seems that there should be a cleaner way of doing this.
I would suggest providing the production rules as functions that parse the arguments, and raise an exception for invalid arguments. so. your example might look like this:
FOO_SYNTAX = (file, float, int)
def foo_cmd(infile, infloat, inint):
pass
def parse_args(rule, args):
if len(rule) != len(args):
raise ValueError, "Wrong number of arguments"
return [rule_item(arg) for rule_item, arg in zip(rule, args)]
COMMANDS = {'foo': (FOO_SYNTAX, foo_cmd)}
def dispatch(line):
cmd, rest = line.split(None, 1)
args = rest.split()
syntax, cmd_func = COMMANDS[cmd]
cmd_func(*parse_args(syntax, args))
Depending on whether you are using Python 2.6 or 2.7, you could use the built in optparse or argparse, respectively.
http://docs.python.org/library/argparse.html
They may be slightly heavyweight, but they'll do conversion to ints,floats, or whatever type you need as part of the parsing, and it can automatically build a usage message and other nice argument parsing things.

Categories

Resources