2009-08-28 10 views
5

Zajmuję się tworzeniem skryptu zarządzania, który wykonuje dość dużą ilość pracy za pomocą mnóstwa opcji wiersza poleceń. W pierwszych kilku iteracjach skryptu wykorzystano optparse do zbierania danych wprowadzanych przez użytkownika, a następnie po prostu spuszczamy stronę, testując wartość każdej opcji w odpowiedniej kolejności i wykonując akcję w razie potrzeby. Doprowadziło to do dżungli kodu, który jest naprawdę trudny do odczytania i utrzymania.przetwarzanie wiersza komend przez introspekcję

Szukam czegoś lepszego.

Mam nadzieję, że będę miał system, w którym będę mógł pisać funkcje w mniej lub bardziej normalnej formie python, a następnie po uruchomieniu skryptu będę miał opcje (i tekst pomocy) generowane z moich funkcji, parsowane i wykonywane w odpowiednie zamówienie. Dodatkowo, naprawdę chciałbym, aby móc budować interfejsy sub-Command Django stylu, gdzie myscript.py install działa całkowicie niezależnie od myscript.py remove (oddzielne opcje, pomoc, itp)

Znalazłem simon willison's optfunc i robi dużo z tego, ale wydaje się, że po prostu brakuje znaku - chcę napisać każdą OPCJĘ jako funkcję, zamiast próbować skompresować cały zestaw opcji w ogromny ciąg opcji.

Wyobrażam sobie architekturę obejmującą zestaw klas dla głównych funkcji i każdej zdefiniowanej metody klasy odpowiadającej konkretnej opcji w linii poleceń. Ta struktura ma tę zaletę, że każda opcja znajduje się w pobliżu kodu funkcjonalnego, który modyfikuje, co ułatwia utrzymanie. Rzecz, z którą nie wiem, jak sobie poradzić, to porządkowanie poleceń, ponieważ porządkowanie metod klasowych nie jest deterministyczne.

Zanim przejdę do odrodzenia koła: Czy istnieją inne istniejące fragmenty kodu, które zachowują się podobnie? Inne rzeczy, które można łatwo zmodyfikować? Zadawanie tego pytania wyjaśniło moje własne zdanie na temat tego, co byłoby miłe, ale informacja zwrotna na temat tego, dlaczego jest to okropny pomysł, lub jak powinna działać, byłaby mile widziana.

Odpowiedz

4

Nie trać czasu na "introspekcję".

Każde "polecenie" lub "opcja" jest obiektem z dwoma zestawami funkcji lub atrybutów metod.

  1. Podaj informacje o konfiguracji, aby dokonać optymalizacji.

  2. Właściwie wykonuj pracę.

Oto nadklasą dla wszystkich poleceń

class Command(object): 
    name= "name" 
    def setup_opts(self, parser): 
     """Add any options to the parser that this command needs.""" 
     pass 
    def execute(self, context, options, args): 
     """Execute the command in some application context with some options and args.""" 
     raise NotImplemented 

utworzyć sublcasses dla Install i Remove i każdej innej komendy trzeba.

Twoja ogólna aplikacja wygląda mniej więcej tak.

commands = [ 
    Install(), 
    Remove(), 
] 
def main(): 
    parser= optparse.OptionParser() 
    for c in commands: 
     c.setup_opts(parser) 
    options, args = parser.parse() 
    command= None 
    for c in commands: 
     if c.name.startswith(args[0].lower()): 
      command= c 
      break 
    if command: 
     status= command.execute(context, options, args[1:]) 
    else: 
     logger.error("Command %r is unknown", args[0]) 
     status= 2 
    sys.exit(status) 
+0

Dzięki, wydaje się, że to dobra rada. –

+0

To jest doskonałe! –

0

Biblioteka WSGI Werkzeug zapewnia Management Script Utilities co może robić, co chcesz, a przynajmniej daje wskazówkę jak to zrobić introspekcji siebie.

from werkzeug import script 

# actions go here 
def action_test(): 
    "sample with no args" 
    pass 

def action_foo(name=2, value="test"): 
    "do some foo" 
    pass 

if __name__ == '__main__': 
    script.run() 

Który wygeneruje następujący komunikat pomocy:

$ python /tmp/test.py --help 
usage: test.py <action> [<options>] 
     test.py --help 

actions: 
    foo: 
    do some foo 

    --name      integer 2 
    --value      string test 

    test: 
    sample with no args 

Akcja jest funkcją w tym samym module zaczynające się od „action_”, która podejmuje szereg argumentów, gdzie każdy argument domyślny. Typ wartości domyślnej określa typ argumentu.

Argumenty można następnie przekazywać według pozycji lub używając --name = wartość z powłoki.

Powiązane problemy