1

See here for related discussion on the merits of the various option and arg parsing choices we have in Python.

I'm making a diff script that does neat things with the output of Python's difflib, and part of what that involves is handling the different ways that it can be called. For instance Git will send 7 args (the second and fifth being the files you want to diff) to the diff program you configure it with, and most differs are also expected to accept input as two file args. Interestingly, git's difftool's --extcmd= flag invokes the differ you specify with only the two args.

So, it's really easy to use OptionParser to do this since it just gives you a list of the args and I could grab the second and fifth ones and send them to fileinput.

I did notice the big banner on the pydoc that says it's deprecated, so I was looking at argparse.

It was not clear to me at all if it is even possible to configure argparse to let your program accept a series of positional arguments without an option to "start it off". That is what I needed since I can't change the way e.g. Git would invoke the differ.

Anyway, I ended up doing some really trivial manipulation of sys.argv, which after all is what I should have been doing to begin with in this particular situation.

if len(sys.argv) == 8:
        # assume this was passed to git; we can of course do
        # some parsing to check if we got valid git style args
        args = [sys.argv[2], sys.argv[5]]
elif len(sys.argv) == 3:
        args = sys.argv[:1]
else:
        sys.exit("Not a valid number of args (2 or 7) to this diff program")
print "Files: " + ' '.join(args)

How might one use argparse to implement a program that simply attempts to open and read all of its arguments?

The reasoning is that for argparse to deprecate parseopt it must be possible to replicate all its functionality (within reason).

Community
  • 1
  • 1
Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • Please post the code you are using to manipulate `sys.argv`. Then maybe we could suggest how to do the same with `argparse`. – unutbu Jun 12 '13 at 20:50

3 Answers3

1

Just add an argument that has no dashes in front of it.

import argparse
parser = argparse.ArgumentParser()
# You can change nargs to '+' for at least one argument
parser.add_argument('positional', nargs=2) # Positionals have no dashes
parser.add_argument('second_positional', nargs=1) 
parser.add_arguemnt('--optional', '-o')    # Optionals have dashes
args = parser.parse_args()

print args['positional'] # Your first two positional arguments
print args['second_positional'] # Second set
print args['optional'] # An optional argument
SethMMorton
  • 45,752
  • 12
  • 65
  • 86
1

I just made this example that takes zero or more positional arguments:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('args', metavar='arg', type=str, nargs='*',
    help='zero or more positional arguments')

args = parser.parse_args()
print args.arguments
Charl Botha
  • 4,373
  • 34
  • 53
0

You could define a custom Action for this, though its really not that different than post-processing args yourself unless you have many such arguments that would require this action:

import argparse

class TwoOrSeven(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if len(values) not in (2,7):
            raise argparse.ArgumentTypeError('Not a valid number of args (2 or 7)')
        try:
            values = values[2], values[5]
        except IndexError:
            values = values[0]
        setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('args', metavar='arg', action=TwoOrSeven, nargs='+',
    help='Must be supplied 2 or 7 arguments')

args = parser.parse_args('1 2 3 4 5 6 7'.split())
print(args)
# Namespace(args=('3', '6'))
args = parser.parse_args('1 2'.split())
print(args)
# Namespace(args='1')
args = parser.parse_args('1 2 3 4 5 6'.split())
# argparse.ArgumentTypeError: Not a valid number of args (2 or 7)
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677