2012-11-02 15 views
5

Scrivo uno script che accetta uno o più file di input. Voglio essere in grado di specificare le opzioni per lo script (A e B) e per ciascuno dei file di input separatamente (C e D).Python argparse: opzioni separate per argomenti posizionali

uso dovrebbe essere simile:

script.py [-A] [-B] [-C] [-D] file1 [[-C] [-D] file2] ... 

Come può essere fatto con argparse?

Grazie!

+0

Sai quanti file di input si avrà? – mgilson

+0

Uno o più :)) – Roux

risposta

1

Come al solito, la questione sembrava non essere esattamente ciò che è necessario .. Scusa. Ecco il mio risultato di lavoro (ispirato alla risposta di unutbu) che consente anche opzioni per file con argomenti.

import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument('-A', action = 'store_true') 
parser.add_argument('-B', action = 'store_true') 
args, unk = parser.parse_known_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('-V', "--variable-list") 
file_parser.add_argument('file') 

fargs=[] 
n=len(unk) 
while True: 
     i=0 
     for i in range(n): # finding longest fully parsable tail 
      Parsed, unkf = file_parser.parse_known_args(unk[i:n]) 
      if not unkf: break 
     if i==n: # did not found parsable tail 
      file_parser.parse_args(unk[0:n]) # cause error 
     else: 
      fargs.append(Parsed) 
      n=i 
     if (n<=0): break 
fargs.reverse() 

print args 
for argl in fargs: 
     print argl 

chiamata a questo con:

myscript.py -A -B -C -D file1 -C -V a,b,c file 

rendimenti:

Namespace(A=True, B=True) 
Namespace(C=True, D=True, file='file1', variable_list=None) 
Namespace(C=True, D=False, file='file2', variable_list='a,b,c') 
+0

Ora, l'unico problema è come organizzare un uso ragionevole e aiutare i messaggi .... – Roux

4

Se possibile, provare docopt. È molto più facile da usare e ha abbastanza esempi per iniziare.

+0

Grazie! Approccio impressionante Tuttavia, il mio script dovrebbe funzionare su diversi computer con python piuttosto antico installato.Quindi, sfortunatamente, le soluzioni standard sono preferite ad eleganti ... – Roux

1

mia, questa risposta è complicata:

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 

di chiamare questo con:

~ $ python test.py -A foo bar -A -B -C qux 

rendimenti:

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') 
4

ho voluto fare questo per un po ', ma mai immaginato un caso d'uso. Ne hai trovato uno: grazie!

È possibile farlo con due stadi di argparsing. Nella prima fase, si cercano solo le opzioni -A o -B.

Nella seconda fase, si dividono i restanti argomenti in frammenti (in questo caso utilizzando una funzione generatore) e chiamare parse_args sui frammenti:

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) 

poi test.py -A -B -C -D file1 -C file2 cede

Namespace(A=True, B=True) 
Namespace(C=True, D=True, file='file1') 
Namespace(C=True, D=False, file='file2') 
+0

Grazie! Chiaro, semplice e risponde esattamente alla domanda fatta. Anzi, avevo bisogno anche di usare gli argomenti delle opzioni per i file, ma che in qualche modo si erano ritirati dalla formulazione delle domande. Scusate. Per favore vedi anche la mia risposta, che è stata trovata dopo aver provato il tuo codice ... – Roux

0

L'opzione action=append probabilmente aiuterà. Ciò ti consentirà di specificare un'opzione più volte e tutti gli argomenti con un'opzione specifica verranno archiviati nei rispettivi elenchi.

... ecco un esempio, consente di chiamare sample.py:

if __name__ == '__main__': 
    parser = argparse.ArgumentParser() 
    parser.add_argument("-c", "--cfilein", action="append") 
    parser.add_argument("-d", "--dfilein", action="append") 
    args = parser.parse_args() 
    print args.cfilein 
    print args.dfilein 

Execute: ./sample.py -c f1 -d f2 -c f3 -d f4 
Output: ['f1', 'f3'] 
     ['f2', 'f4'] 

Partenza: http://docs.python.org/2/library/argparse.html#action ... Spero che questo aiuti ...

+0

con azioni personalizzate, qualcosa del genere potrebbe essere fatto funzionare abbastanza facilmente, ma ti costringe a specificare un '-f' non necessario prima di ogni file. – mgilson

Problemi correlati