I have a program with a function that takes a class initializer and list of objects. Each object consists of 3 variables id, value, and tag.
class Package():
def __init__(self, id, value, name):
if (value <= 0):
raise ValueError("Amount must be greater than 0")
self.id = id
self.value = value
self.tag = tag
class Purchase():
def submit(some_list):
//Do stuff
def main():
//Help here!
parser = argparse.ArgumentParser()
parser.add_argument("id", help="ID")
parser.add_argument("value", help="Value")
parser.add_argument("tag", help="Tag")
args = parser.parse_args()
some_list = [args.id, args.value, args.tag]
submit(some_list)
I'm trying to implement argparse in main() so I can run the program by doing something like: python foo.py "int0 [(int1, float1, int2), (int3, float2, int4) ....]"
. The number of objects in the list is variable and depends on the user input.
initializer = num0
//First package object
package.id = num1
package.value = num2
package.tag = num3
//Second package object
package.id = num4
package.value = num5
package.tag = num6
You can make a custom argument type and use ast.literal_eval()
to parse the value.
Working sample:
import argparse
from ast import literal_eval
class Package():
def __init__(self, id, value, tag):
if (value <= 0):
raise ValueError("Amount must be greater than 0")
self.id = id
self.value = value
self.tag = tag
def packages(s):
try:
data = literal_eval(s)
except: # TODO: avoid bare except and handle more specific errors
raise argparse.ArgumentTypeError("Invalid 'packages' format.")
return [Package(*item) for item in data]
parser = argparse.ArgumentParser()
parser.add_argument('--packages', dest="packages", type=packages, nargs=1)
args = parser.parse_args()
print(args.packages)
Now if you would run the script, you would get a list of Package
class instances printed:
$ python test.py --packages="[(1, 1.02, 3), (40, 2.32, 11)]"
[[<__main__.Package instance at 0x10a20d368>, <__main__.Package instance at 0x10a20d4d0>]]
I would prefer to be a bit more explicit and use a custom action:
import argparse
class PackageAction(argparse.Action):
def __init__(self, *args, **kwargs):
super(PackageAction, self).__init__(*args, **kwargs)
self.nargs = 3
def __call__(self, parser, namespace, values, option_string):
lst = getattr(namespace, self.dest, []) or []
a, b, c = values
lst.append(Package(int(a), float(b), int(c)))
setattr(namespace, self.dest, lst)
class Package(object):
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def __repr__(self):
return 'Package(%r, %r, %r)' % (self.foo, self.bar, self.baz)
parser = argparse.ArgumentParser()
parser.add_argument('--package', action=PackageAction)
print(parser.parse_args())
The usage here would look something like:
$ python packager.py --package 1 2 3 --package 4 5 6
Namespace(package=[Package(1, 2.0, 3), Package(4, 5.0, 6)])
One benefit is that you get slightly better default error handling ... e.g.:
$ python ~/sandbox/test.py --package 1 2 3 --package 4 5
usage: test.py [-h] [--package PACKAGE PACKAGE PACKAGE]
test.py: error: argument --package: expected 3 argument(s)
Of course, you can modify to suit your purposes -- specifically it would probably be good to provide a little extra error handling to __call__
. e.g. you could do something like
parser.error('--package requires an int float and int')
if the user passed bad strings. You could also provide better variable names :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With