2012-11-02 15 views
5

Napisz skrypt, który pobiera jeden lub więcej plików wejściowych. Chcę móc określić opcje skryptu (A i B) i oddzielnie dla każdego pliku wejściowego (C i D).Python argparse: oddzielne opcje dla argumentów pozycyjnych

Wykorzystanie powinna wyglądać następująco:

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

Jak można to zrobić z argparse?

Dziękujemy!

+0

Wiesz ile wejście pliki będą masz? – mgilson

+0

Jeden lub więcej :)) – Roux

Odpowiedz

1

Jak zwykle, pytanie wydaje się nie być dokładnie to, co jest potrzebne .. Przepraszam. Oto mój wynik pracy (zainspirowany odpowiedzią unutbu), który pozwala również na opcje pliku z argumentami.

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 

Wywołanie to z:

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

plonów:

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

Teraz jedyny problem polega na tym, jak rozmieścić uzasadnione użycie i komunikaty ... – Roux

4

Jeśli to możliwe, spróbuj docopt. Jest znacznie łatwiejszy w użyciu i ma wystarczającą liczbę przykładów, aby zacząć.

+0

Dzięki! Imponujące podejście. Jednak mój skrypt powinien działać na kilku komputerach z zainstalowanym dość starym pythonem.Tak więc, niestety, standardowe rozwiązania są preferowane w stosunku do eleganckich ... – Roux

1

My, ta odpowiedź jest zagmatwana:

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 

Wywołanie to z:

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

plonów:

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

ja chciałem zrobić to na chwilę, ale nigdy nie wymyśliłem przypadku użycia. Znalazłeś: dziękuję!

Możesz to zrobić z dwuetapowym argparsingiem. Na pierwszym etapie szukasz tylko opcji -A lub -B.

W drugim etapie, podzielić się pozostałe argumenty na kawałki (w tym przypadku za pomocą funkcji generatora) i wywołać parse_args na fragmentach:

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) 

następnie test.py -A -B -C -D file1 -C file2 daje

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

Dziękuję! Jasne, proste i dokładnie odpowiada na zadane pytanie. Rzeczywiście potrzebowałem również użyć argumentów opcji dla plików, ale to w jakiś sposób odpadło od sformułowania pytań. Przepraszam. Proszę zobaczyć również moją odpowiedź, która została znaleziona po wypróbowaniu kodu ... – Roux

0

Prawdopodobnie pomoże opcja action=append. Umożliwi to wielokrotne podanie opcji, a wszystkie argumenty z konkretną opcją będą przechowywane na odpowiednich listach.

... oto przykład, pozwala nazwać 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'] 

Check out: http://docs.python.org/2/library/argparse.html#action ... Nadzieja to pomaga ...

+0

z niestandardowymi akcjami, coś takiego może być stworzone do pracy dość łatwo, ale zmusza cię do określenia niepotrzebnego '-f" przed każdym plikiem. – mgilson

Powiązane problemy