Esistono vari modi per gestire l’interfaccia da linea di comando in Python, in particolare il modulo argparse
. In questo articolo vedremo un’alternativa per essere subito operativi: python-fire.
Command Line Interface
L’interfaccia da linea di comando è certamente la più amata dagli utenti esperti e in particolare da coloro che utilizzano sistemi Unix like. Essa permette di inserire e personalizzare le esecuzioni dei nostri programmi in script più complessi o integrarli in altri in maniera estremamente semplice.
Per provare le funzionalità di python-fire userò un semplice esempio che simula uno script per gestire i backup. Potremo richiedere l’archiviazione, il backup e il restore, oltre che vedere il contenuto di un backup.
Python Fire
Fire è un modulo che permette di mappare quasi automaticamente le funzioni Python nella CLI. Sebbene presente nel github di Google, “Python Fire is not an official Google product.”
Come sempre, il modo più rapido per avere il modulo è installarlo via pip:
pip install fire
La versione che ho provato io è la 0.2.1 con interprete python 3.6 su linux.
Subito Operativi
Nel suo funzionamento di base, Fire è davvero sorprendente. Tutto quello che serve fare è definire le funzioni e i parametri in python e richiamare Fire()
. Il suo funzionamento è talmente immediato che bastano gli esempi per descriverlo.
import fire
def backup(src, dest):
"""Start to copy the files from src into dest"""
print(f"Backup of {src} into {dest}")
def main():
fire.Fire(backup)
if __name__ == '__main__':
main()
Il suo utilizzo è immediato. Mette già a disposizione l’opzione --help
(sfruttando le doc string) e la gestione degli errori di sintassi.
$ python3.6 cli-simple.py /partenza /arrivo Backup of /partenza into /arrivo
Funzioni diverse
Passare ad una gestione più complessa, con comandi e parametri opzionali è molto semplice e prevede unicamente di riservare lo script di lancio per la CLI. Ci penserà Fire a estrarre nomi e opzioni direttamente dalle funzioni.
import fire
def backup(src, dest='/backup'):
print(f"Backup of {src} into {dest}")
def restore(backup, dest):
print(f"Restoring backup from {backup} into {dest}")
def list(backup):
print(f"{backup} contains...")
if __name__ == '__main__':
fire.Fire()
In questo caso ogni funzione rappresenta un sotto-comando, gli argomenti posizionali sono obbligatori mentre quelli chiave-valore vengono impostati con la sintassi --flag
$ python3.6 simple-timemachine.py backup /tmp --dest=/home Backup of /tmp into /home
Pipeline
I comandi possono essere anche inclusi in classi per venire organizzati in gruppi. Inoltre è possibile definire delle pipeline per eseguire più comandi di seguito, tutto in modo intuitivo e pythonic.
import fire
class Archive():
def full(self, src, dest='/tmp'):
print(f"Archiving all {src} into {dest}")
def delta(self, src, dest='/tmp'):
print(f"Archiving delta from {src} into {dest}")
class Backup():
def start(self, src, dest='/backup'):
print(f"Backup of {src} into {dest}")
def list(self, backup):
print(f"{backup} contains...")
class Restore():
def all(self, backup, dest):
print(f"Restoring full backup from {backup} into {dest}")
def pick(self, backup, file, dest):
print(f"Restoring {file} of backup from {backup} into {dest}")
class Pipeline():
def __init__(self):
self.archive = Archive()
self.backup = Backup()
def delta(self, src, archive='/tmp', dest='/backup'):
self.archive.delta(src, archive)
self.backup.start(src, dest)
def full(self, src, archive='/tmp', dest='/backup'):
self.archive.full(src, archive)
self.backup.start(src, dest)
if __name__ == '__main__':
fire.Fire({
'run' : Pipeline,
'archive' : Archive,
'backup' : Backup,
'restore' : Restore
})
In questo caso sono stati definiti manualmente i comandi possibili, creando al volo un dict. L’uso della pipeline è lineare
$ python3.6 timemachine.py run delta /home --archive /arch --dest /dest Archiving delta from /home into /arch Backup of /home into /dest
Conclusioni
Python-fire è sicuramente uno strumento di grandissima utilità che permette di ottenere risultati professionali con pochi sforzi. Soprattutto all’avvio di un progetto o nella scrittura di script d’utilità può accelerare di molto la messa in opera.
Oltre alle funzioni essenziali qui proposte, permette di creare interfacce più sofisticate e di elaborare vari formati di input, ma già con le funzionalità di base si presta ad essere un tool essenziale!