0

I am using argparse for a python script I am writing. The purpose of the script is to process a large ascii file storing tabular data. The script just provides a convenient front-end for a class I have written that allows an arbitrary number of on-the-fly cuts to be made on the tabular data. In the class, the user can pass in a variable-name keyword argument with a two-element tuple bound to the variable. The tuple defines a lower and upper bound on whatever column with a name that corresponds to the variable-name keyword. For example:

reader = AsciiFileReducer(fname, mass = (100, float("inf")), spin = (0.5, 1))

This reader instance will then ignore all rows of the input fname except those with mass > 100 and 0.5 < spin < 1. The input fname likely has many other columns, but only mass and spin will have cuts placed on them.

I want the script I am writing to preserve this feature, but I do not know how to allow for arguments with variable names to be added with argparse.add_argument. My class allows for an arbitrary number of optional arguments, each with unspecified names where the string chosen for the name is itself meaningful. The **kwargs feature of python makes this possible. Is this possible with argparse?

aph
  • 1,765
  • 2
  • 19
  • 34
  • It depends how you want your command line parameters to be specified. Would that be `-c "mass > 100" -c "0.5 < spin < 1"? – Hans Then Dec 20 '15 at 19:35
  • After a little more digging, I think I may have figured out a sort of hack-y workaround using a combination of nargs and allowing multiple appearances of the same optional argument. I think if I allow optional arguments `-row_cut_min` and `-row_cut_max`, and set nargs=2, then the first argument can be the string name for the column, and the second argument can be the cut. I think that gives the behavior I wanted, don't you? – aph Dec 20 '15 at 19:38
  • Actually, I think this does not allow what I want because it will not permit multiple appearances of the -row_cut_min or -row_cut_max. So I'm back to the drawing board. – aph Dec 20 '15 at 20:04
  • Possible overlap with http://stackoverflow.com/questions/33712615/using-argparse-with-function-that-takes-kwargs-argument – hpaulj Dec 20 '15 at 23:14

2 Answers2

5

The question of accepting arbitrary key:value pairs via argparse has come up before. For example:

Using argparse with function that takes **kwargs argument

This has a couple of long answers with links to earlier questions.

Another option is to take a string and parse it with JSON.

But here's a quick choice building on nargs, and the append action type:

parser=argparse.ArgumentParser()
parser.add_argument('-k','--kwarg',nargs=3,action='append')

A sample input, produces a namespace with list of lists:

args=parser.parse_args('-k mass 100 inf -k spin 0.5 1.0'.split())

Namespace(kwarg=[['mass', '100', 'inf'], ['spin', '0.5', '1.0']])

they could be converted to a dictionary with an expression like:

vargs={key:(float(v0),float(v1)) for key,v0,v1 in args.kwarg}

which could be passed to your function as:

foo(**vargs)
{'spin': (0.5, 1.0), 'mass': (100.0, inf)}
Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thanks for the reliably crystal clear answer, @hpaulj. Not sure why my so search didn't turn up the answer you had given previously. This directly solves my problem. – aph Dec 21 '15 at 00:08
0

Apologies in advance if I didn't understand exactly what you want to do. But from your description, if I were implementing this, I might try something like the following. Here I make mass and spin optional inputs and set their default values, so the only required input is the file name.

Example code:

# q.py test code

import argparse

parser = argparse.ArgumentParser(prog='q.py')
parser.add_argument('file', help='filename')
parser.add_argument('-m', default=100, type=int, help='integer mass value')
parser.add_argument('-s', nargs=2, default=[0.5,1.0], type=float, help='spin values without comma')

args=parser.parse_args()

print('file:', args.file)
print('mass:', args.m)
print('spin:', tuple(args.s))

Some command line calls:

$ python q.py -h
usage: q.py [-h] [-m M] [-s S S] file

positional arguments:
file        filename

optional arguments:
-h, --help  show this help message and exit
-m M        integer mass value
-s S S      spin values without comma

$ python q.py test.csv
file: test.csv
mass: 100
spin: (0.5, 1.0)

$ python q.py test.csv -m 99 -s 0.2 1.1
file: test.csv
mass: 99
spin: (0.2, 1.1)

And I think you can call initialize your reader like this:

reader = AsciiFileReducer(args.file, mass = (args.m, float("inf")), spin = tuple(args.s))
Steve Misuta
  • 1,033
  • 7
  • 7
  • Thanks for chiming in, Steve, I think I might not have phrased my question clearly enough. The problem was that I needed to avoid having to make an explicit declaration of a the argument name. – aph Dec 21 '15 at 00:09