5

I have a parser with 3-4 arguments, which works well. I want to supply an unknown number of extra arguments to the script, which would get loaded into a template. I've read the argparse documentation, but I'm not sure it's possible. I can parse_known_arguments(), but I still have to handle the ["--placeholder1", "value1", "--placeholder2", "value2", ...] array myself. Should I go ahead with that, or is there a more pythonic way?

Just from the top of my head:

parser = argparse.ArgumentParser()
parser.add_argument("--template", required=True)
parser.add_argument("--location", required=True)
args,unknown = parser.parse_known_arguments()
tpl = LoadTemplate(args.template)

# Missing part where I transform unknown into an dict or namespace called extraarguments
raw = tpl.render(extraarguments)

# Print into args.location raw
render.py --template template1 --location /path/to/invoices --author John --customer Customer1 --title "Your invoice is ready!"
render.py --template template2 --location /path/to/newsletter --customer Customer2 --sender [email protected] --subject "Weekly newsletter"

In both cases, the template and location have to be present, but the extra arguments should be unpacked and sent into the template rendering function. It looks like a one-liner, but is there a more pythonic way of doing it?

6
  • Just plain extra arguments, or a bunch of unknown options with (possibly) arguments? Give some examples of command line input you're expecting, to clarify your question. Commented May 9, 2015 at 12:10
  • 2
    possible duplicate of argparse accept everything Commented May 9, 2015 at 12:41
  • If you illustrate how you would handle that list of extras, we might be able to suggest a better way. Commented May 9, 2015 at 15:08
  • Can you give us some examples of command arguments that would like argparse parse? Commented May 9, 2015 at 15:26
  • Just use docopt, it will make you smile. Commented May 9, 2015 at 17:00

2 Answers 2

6

Assuming the unknown list is an alternating list of keys and values, it can be turned into a dictionary with:

adict = dict(zip(unknown[:-1:2],unknown[1::2]))

The zip part turns the list in a list of pairs, which then is turned into a dictionary. You may want process the values a bit more to remove the '--' prefix from the keys. You may need a more explicit iteration if you need to check for errors, such as mismatch 'key value' sequences.

Here's a version that leaves the '--' intact:

templates = {'template1': "from: {--author} to: {--customer} re: {--title}",
             'template2': "from: {--sender} to: {--customer} re: {--subject}"}

def parser():
    parser = argparse.ArgumentParser()
    parser.add_argument("--template", required=True, choices=templates)
    parser.add_argument("--location", required=True)
    args,unknown = parser.parse_known_args()
    extraarguments = dict(zip(unknown[::2], unknown[1::2]))
    tpl = templates[args.template]
    raw = tpl.format(**extraarguments)
    return raw
print parser()

With your 2 samples this produces:

In [25]: run stack30139426.py --template template1 --location /path/to/invoices --author John --customer Customer1 --title "Your invoice is ready!"
from: John to: Customer1 re: Your invoice is ready!

In [26]: run stack30139426.py --template template2 --location /path/to/newsletter --customer Customer2 --sender [email protected] --subject "Weekly newsletter"
from: [email protected] to: Customer2 re: Weekly newsletter

There have been other SO questions about inputing a dictionary or other unknown key/value pairs.

One suggestion has been to use a key:value syntax, and then a simple [kv.split(':') for kv in unknowns] to produce a list of pairs:

run stack30139426.py --template template2 --location /path/to/newsletter customer:Customer2 sender:[email protected] subject:"Weekly newsletter"

Another is to use a JSON syntax

run stack30139426.py --template template2 --location /path/to/newsletter '{"customer":"Customer2", "sender":"[email protected]", "subject":"Weekly newsletter"}'
Sign up to request clarification or add additional context in comments.

1 Comment

appending a JSON string to the call seems like a better idea.
1

A way to partially automate the process can be to reuse argparse by creating another parser using strings from unknown that start with '--' as argument names:

argument_names = [arg for arg in unknown if arg[:2] == '--']
parser = argparse.ArgumentParser()
for arg in argument_names:
    parser.add_argument(arg)
args = parser.parse_args(unknown)

This however will fail if anything but name-value pairs is provided.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.