Reputation: 1372
I'd like to provide my user the possibility to add multiple complex objects, as example: a Library object would be:
To do so what I did is to set a parameter --library with an append action and add a type to the parameter so it reads it the right format, what lead to the following command line: --library "library_name=test;library_number=5", the library type function will test and do the work, but this is not realy a pretty way to do it, there is any other way to do what I want ?
I wanted to use the nargs like this:
--library test 5
this look better to me however the type function I want to associate cannot be a complex one as it is runed on each values of the returned array!
Any idea?
I should explain a little bit more what I'm trying to do. I'm working on a workflow manager engine. This one specify a property file where are define all parameters and this parameter list is parsed to build the right argparse object.
As example I can define my library parameter as define before: library_name.name = ... library_name.help = ... library_name.type = str library_name.required = True
library_number.name = ...
library_number.help = ...
library_number.type = int
library_number.required = True
...
But I want this object set as many as library the user want so I could add to those options an append action what would work but the user would have to provide all inputs in the right order:
--library-name lib1 --library-number 1 --library-name lib2 --library-number 2
but this is not that easy for the user, what I did is:
--library "name=lib1;number=1" --library "name=lib2;number=2"
but this is not "pretty", I would prefer something like this but I still want to check if this is the right format:
--library lib1 1 --library lib2 2
Is that clearer?
I should explain a bit more, I changed what you give me so the user can --library library-name=test library-number=5, so all fields are not necessary provided, however I would like some of them to be required, but to do so I need to know once all parameters are checked if the required ones have been settled
class MiltipleParameters(object):
def __init__(self, types):
self.types = types
self.index = None
self.__name__ = "MiltipleParameters"
def __call__(self, arg):
parts = arg.split("=")
if not self.types.has_key(parts[0]):
raise argparse.ArgumentTypeError(parts[0] + " is an invalid flag! Available ones are: "+", ".join(self.types.keys()))
try:
value = self.types[parts[0]](parts[1])
except:
raise argparse.ArgumentTypeError("invalid " + self.types[parts[0]].__name__ + " value: '" + parts[1] + "' for sub parameter '" + parts[0] + "'")
self.index = parts[0]
return (parts[0], value)
this type take a hash table with the param_name:type, I could add the required field so it can be checked. But I do need to know when it is over to check if the required params where set. Is it possible ?
Upvotes: 1
Views: 734
Reputation: 2382
Another solution with a custom action:
import argparse
class LibAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
try:
lib = {'name': values[0], 'type': int(values[1])}
if namespace.library:
namespace.library.append(lib)
else:
namespace.library=[lib]
except ValueError:
parser.error("Problem with library: %s is not int"%(values[1]))
parser = argparse.ArgumentParser()
parser.add_argument("--library", action = LibAction, nargs=2)
args = parser.parse_args()
print args
You obtain:
Namespace(library=[{'type': 1, 'name': 'lib1'}, {'type': 2, 'name': 'lib2'}])
and some error handling if you use: --library libA a
Upvotes: 0
Reputation: 102039
You can create a very simple state machine:
class NargsTypeChecker(object):
def __init__(self, types):
self.types = types
self.index = 0
def __repr__(self):
return self.types[self.index].__name__
def __call__(self, arg):
value = self.types[self.index](arg)
self.index = (self.index + 1) % len(self.types)
return value
Used as:
parser.add_argument('--library', nargs=3, type=NargsTypeChecker((int, float, str)))
And some error outputs:
$python3 complex_type.py --library lib 1 3.6
Namespace(library=['lib', 1, 3.6])
$python3 complex_type.py --library lib other 3.6
usage: complex_type.py [-h] [--library LIBRARY LIBRARY LIBRARY]
complex_type.py: error: argument --library: invalid int value: 'other'
$python3 complex_type.py --library lib 2 other
usage: complex_type.py [-h] [--library LIBRARY LIBRARY LIBRARY]
complex_type.py: error: argument --library: invalid float value: 'other'
Upvotes: 1
Reputation: 2382
If you can accept as a format: --library lib1:1 --library lib2:2
def libType(ls):
name, num = ls.split(':')
return name, int(num)
parser.add_argument("--library", action = "append", type=libType)
# you get: Namespace(library=[('lib1', 1), ('lib2', 2)])
If you prefer to stick with: --library lib1 1 --library lib2 2
parser.add_argument("--library", action = "append", nargs=2)
# you get: Namespace(library=[['lib1', '1'], ['lib2', '2']])
Then you have to perform additional type check to get int instead of str.
Upvotes: 0
Reputation: 1429
If your library names contain no spaces, you could simply use one string for that, then do
library = argstring.split()
This will return a list library
with library[0]
as the name and library[1]
as number.
Upvotes: 0