i write a script that takes one or more input files. I want to be able to specify options for the script (A and B) and for each of the input files separately (C and D).
Usage should look like:
script.py [-A] [-B]  [-C] [-D] file1 [[-C] [-D] file2] ...
How can it be done with argparse?
Thank you!
To specify optional positional parameters, use square [] brackets.
Optional Arguments To add an optional argument, simply omit the required parameter in add_argument() . args = parser.
argv list. Arguments that are expected to show up at an exact location on the command-line (a particular index of sys. argv ) are know as positional arguments.
If possible try docopt. It is much easier to use and has enough examples to get started.
I've wanted to do this for a while, but never dreamt up a use case. You've found one: thank you!
You can do this with two-stages of argparsing.
In the first stage, you only look for -A or -B options.
In the second stage, you split up the remaining arguments into fragments (in this case using a generator function) and call parse_args on the fragments:
import argparse
def fileargs(args):
    result = []
    for arg in args:
        result.append(arg)
        if not arg.startswith('-'):
            yield result
            result = []
parser = argparse.ArgumentParser()
parser.add_argument('-A', action = 'store_true')
parser.add_argument('-B', action = 'store_true')
args, unk = parser.parse_known_args()
print(args)
file_parser = argparse.ArgumentParser()
file_parser.add_argument('-C', action = 'store_true')
file_parser.add_argument('-D', action = 'store_true')
file_parser.add_argument('file')
for filearg in fileargs(unk):
    fargs = file_parser.parse_args(filearg)
    print(fargs)
then test.py -A -B -C -D file1 -C file2 yields
Namespace(A=True, B=True)
Namespace(C=True, D=True, file='file1')
Namespace(C=True, D=False, file='file2')
                        My, this answer is convoluted:
import sys
#Unforunately, you can't split up positional arguments in a reasonable way if you
#don't know about all of them...  Count positional arguments (files)
def how_many_files(lst):
    return sum(1 for x in lst if not x.startswith('-'))
args = sys.argv[1:]
Nfiles = how_many_files(args)
import argparse
#Create our own NameSpace class so that we can have an easy handle on the
#attributes that the namespace actually holds.
class MyNameSpace(argparse.Namespace,dict):
    def __init__(self):
        argparse.Namespace.__init__(self)
        dict.__init__(self)
    def __setattr__(self,k,o):
        argparse.Namespace.__setattr__(self,k,o)
        self[k] = o
class MyParser(argparse.ArgumentParser):
    def __init__(self,*args,**kwargs):
        self.my_parents = kwargs.get('parents',[])
        argparse.ArgumentParser.__init__(self,*args,**kwargs)
class FooAction(argparse.Action):
    def __call__(self,parser,namespace,value,option_string=None):
        ref = namespace.pop('accumulated',{})
        try:
            del namespace.accumulated
        except AttributeError:
            pass
        #get a new namespace and populate it with the arguments we've picked up
        #along the way        
        new_namespace = self.__default_namespace(parser)
        for k,v in namespace.items():
            setattr(new_namespace,k,v)
            delattr(namespace,k)  #delete things from `namespace.__dict__`
        namespace.clear() #also remove things from the dictionary side.
        namespace.accumulated = ref
        new_namespace.file = value
        ref[value] = new_namespace
    def __default_namespace(self,parser):
        n = argparse.Namespace()
        for parent in parser.my_parents:
            parent.parse_args([],namespace=n)
        return n
parser = argparse.ArgumentParser()
parser.add_argument('-A',action='store_true')
parser.add_argument('-B',action='store_true')
parser.add_argument('-C',action='store_true')
parser.add_argument('-D',action='store_true')
parser2 = MyParser(parents=[parser],conflict_handler='resolve')
for i in range(Nfiles):
    parser2.add_argument('files%d'%i,action=FooAction,default=argparse.SUPPRESS)
n = parser2.parse_args(args,namespace = MyNameSpace())
for k,v in n.accumulated.items():
    print k,v
Calling this with:
~ $ python test.py -A foo bar -A -B -C qux
yields:
qux Namespace(A=True, B=True, C=True, D=False, file='qux')
foo Namespace(A=True, B=False, C=False, D=False, file='foo')
bar Namespace(A=False, B=False, C=False, D=False, file='bar')
                        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