[Python] variabili "globali" tra più moduli.

Daniele Varrazzo piro a develer.com
Lun 20 Maggio 2013 01:48:57 CEST


On 2013-05-18 09:26, Marco Beri wrote:
> Il giorno 18/mag/2013 01:33, "Daniele Varrazzo" <piro a develer.com> ha
> scritto:
>> Guarda optparse o argparse nella libreria python. Entrambi 
>> restituiscono
> un oggetto che contiene tutti i parametri come attributi. Tipicamente
> ottieni quello nel main() e poi passi l'oggetto o solo i valori che 
> servono
> a funzioni o oggetti create nel main.
>
> Ad un talk a djangocon, non nella libreria standard, hanno segnalato 
> questa
> bella libreria standard:
>
> https://github.com/docopt/docopt

"questa bella libreria", non standard.

La conosco. Gneh. Mah. Non mi ha fatto godere. La usa un mio collega: 
il giochino è che tutte le volte prova a ficcarla nei progetti dove 
lavoro anche io gliela rimuovo e la sostituisco con optparse o argparse.

Non amo forzare un messaggio pensato per il consumo umano ad assumere 
tutti i vincoli necessari perché un computer lo capisca. È la stessa 
critica che muovo a doctest. Bello come giochino ma a farci qualcosa di 
serio e mantenerlo la vedo pesa.

In particolare di solito devo mettere due opzioni in croce e per quello 
uso optparse. Un mio progetto (PGXN Client) ha una riga di comando di 
cui sorvolo i dettagli implementativi, ma restando nel semplice ogni 
subcommand è implementato da una classe che richiede certe opzioni: 
anche solo questo con docopt non si può fare, la CLI è "monolitica", non 
dinamica, e va generata in un solo punto.

Un pattern che invece uso molto sul lavoro, dove di script ne creo 
tanti, è quello che mi serve spesso una coppia --quiet/--verbose che 
vanno ad alterare il level di un logger, mi serve spesso una coppia 
--start-date/--end-date che restituiscono oggetti date (prendendo sia 
YYYY-MM-DD sia un numero di "giorni indietro" in modo da poter mettere 
in un crontab --start-date 7 --end-date 1 per elaborare l'ultima 
settimana), mi serve spesso un --dsn per settare il database dove 
lavorare. Usando docopt dovrei ripetere in continuazione sia 
l'impostazione della command line che il parsing delle opzioni ed 
eventualmente farci qualcosa (es. impostare il level del logger). Con 
optparse invece ho scritto delle funzioni che mi permettono di fare:

     def parse_cmdline():
         parser = optparse.OptionParser(usage=...)
         parser.add_option("--roba-specifica-per-lo-script"...)

         @script_utils.with_two_date(parser)
         @script_utils.with_verbosity(parser)
         @script_utils.with_dsn(parser)
         def parse():
             opt, args = parser.parse_args()
             opt.files = args # per esempio
             return opt

         return parse()

Perché coi decoratori? Per la "doppia azione": quando il decoratore 
viene applicato il parser viene popolato. Quando la funzione esce il 
decoratore ha accesso ad opt e può verificare la validità dei valori, o 
qualunque altra operazione (impostare una varabile globale...). Per 
esempio questa è una delle implementazioni:

     def with_verbosity(parser, logger_name=None):
         """Decorator to add --quiet and --verbose options to a script.

         Takes an optional logger_name: if specified set the level on 
that logger,
         otherwise set it on the root logger.

         Set the log level on WARNING if --quiet is specified, on DEBUG 
if
         --verbose is specified, on INFO if none is. Won't tell you what 
happens if
         you specify both: sod off, wise guy.
         """
         def with_verbosity_(f):
             @wraps(f)
             def with_verbosity__(*args, **kwargs):
                 parser.add_option('--quiet', dest='loglevel', 
default=logging.INFO,
                     action='store_const', const=logging.WARNING,
                     help="show less information")
                 parser.add_option('--verbose', dest='loglevel', 
default=logging.INFO,
                     action='store_const', const=logging.DEBUG,
                     help="show more information")

                 opt = f(*args, **kwargs)

                 logging.getLogger(logger_name).setLevel(opt.loglevel)

                 return opt

             return with_verbosity__

         return with_verbosity_

Anche questo non si può fare con docopt: ad ogni script dovrei ripetere 
le stesse dichiarazioni e le stesse operazioni.


-- 
Daniele Varrazzo - Develer S.r.l.
http://www.develer.com


Maggiori informazioni sulla lista Python