Analysis arguments that are not declared
I'm writing a utility to run bash commands that essentially take a string and an optional list argument as input, and use the optional arguments to interpolate the string.
I would like it to work like this:
interpolate.py Hello {user_arg} my name is {computer_arg} %% --user_arg=john --computer_arg=hal
%%
is a delimiter, it separates the string to be interpolated from the arguments. Following are the arguments used for string interpolation. In this case, the user chose user_arg
and computer_arg
as arguments. The program cannot know in advance which argument names the user will choose.
My problem is how to parse the arguments? I can trivially split the input arguments on a delimiter, but I can't figure out how to get optparse to just provide a list of optional arguments as a dictionary without specifying them beforehand. Does anyone know how to do this without writing a lot of regex?
source to share
For something like this, you don't really need optparse or argparse - the benefits of such libraries are of little use in this case (things like single type arguments -v
, invalid parameter checking, validation, etc.)
def partition_list(lst, sep):
"""Slices a list in two, cutting on index matching "sep"
>>> partition_list(['a', 'b', 'c'], sep='b')
(['a'], ['c'])
"""
if sep in lst:
idx = lst.index(sep)
return (lst[:idx], lst[idx+1:])
else:
return (lst[:], )
def args_to_dict(args):
"""Crudely parses "--blah=123" type arguments into dict like
{'blah': '123'}
"""
ret = {}
for a in args:
key, _, value = a.partition("=")
key = key.replace("--", "", 1)
ret[key] = value
return ret
if __name__ == '__main__':
import sys
# Get stuff before/after the "%%" separator
string, args = partition_list(sys.argv[1:], "%%")
# Join input string
string_joined = " ".join(string)
# Parse --args=stuff
d = args_to_dict(args)
# Do string-interpolation
print string_joined.format(**d)
source to share
Well, if you use '-' to separate options from arguments instead of %%, optparse / argparse will just give you the arguments as a simple list (treating them as positional arguments instead of toggle arguments). After that, it's not "many" regexes, but just splitting:
for argument in args:
if not argument.startswith("--"):
# decide what to do in this case...
continue
arg_name, arg_value = argument.split("=", 1)
arg_name = arg_name[2:]
# use the argument any way you like
source to share
With argparse, you can use parse_known_args
predefined arguments and any additional arguments. For example using the following script
import sys
import argparse
def main(argv=None):
parser = argparse.ArgumentParser()
parser.add_argument('string', type=str, nargs='*',
help="""String to process. Optionally with interpolation
(explain this here...)""")
args, opt_args = parser.parse_known_args(argv)
print args
print opt_args
return 0
if __name__=='__main__':
sys.exit(main(sys.argv[1:]))
and calling with
python script.py Hello, my name is {name} --name=chris
outputs the following output:
Namespace(string=['Hello,' 'my', 'name', 'is', '{name}'])
['--name=chris']
All that's left to do is skip the namespace args
, which looks for form strings {...}
and replaces them with the corresponding element in opt_args
, if any. (I'm not sure if argparse can automatically use argument interpolation, the example above is the only immediate solution that comes to mind).
source to share