Introduzione

Spesso capita di dover implemetare un applicazione a linea di comando in tempi molto rapidi, quello che segue è un esempio di codice di partenza; questo template risponde all'esigienza di disporre in breve tempo di un implemetazione flessibile ed estensibile.

Il codice è pubblicco ed è disponibile su github

Template

In generale quando bisogna scrivere un applicazione a linea di comando serve:

  • un main in cui inizialezzare tutti i componenti
  • un logger per poter debuggare eventuali problemi
  • un parser per interpretare i parametri da linea di comando
  • un pattern per gestire tutte le opzioni che si hanno in mente di implementare

Questo template cerca di rispondere a tutti i punti precedenti.

Struttura

Files presenti nel progetto

$ tree
.
├── app    # questo file contiene il main dell'applicazione
├── app_modules
│   ├── commands
│   │   ├── commandline_parser.py    # parser della linea di comando
│   │   ├── command.py    # classe base per gestire i comandi
│   │   ├── __init__.py
│   │   ├── runner
│   │   │   ├── __init__.py
│   │   │   └── run_version.py    # esempio di comando che stampa la versione
│   │   └── setter
│   │       ├── __init__.py
│   │       ├── set_conf.py
│   │       └── set_env.py
│   ├── core
│   │   ├── config.py
│   │   ├── constants.py
│   │   ├── __init__.py
│   │   └── mlogger.py
│   └── __init__.py
├── build.sh
├── LICENSE
├── README.md
└── requirements.txt

5 directories, 18 files

Diagramma di flusso delle classi

Durante l'esecuzione l'applicazione seguirà i seguenti passi:

diagramma di flusso

  1. la classe app inizia l'esecuzione e prepara il logger
  2. la classe app instanzia la classe commandline_parser
  3. la classe commandline_parser inizializza tutti i comandi
  4. la classe commandline_parser analizza gli argomenti passati da linea e costruisce l'elenco dei comandi da eseguire
  5. la classe commandline_parse ritorna alla classe app l'elenco dei comandi
  6. la classe app esegue tutti i comandi gestendo eventuali eccezzioni

Estendere il template

Per estendere il template bisognerà per prima cosa inserire i codici specifici dell'applicazione dentro la cartella app_module, e poi iniziare a creare i nuovi comandi.

I comandi vengono distinit in :

  • setter: comandi utilizzati per passare valori all'applicazione
  • runner : comandi utilizzati per specificare quale azione compiere

Esempio di nuovo comando

Supponiamo di voler creare l'opzione --say-hello, bisognerà creare il file :

app_modules/commands/runner/sey_hello.py

from app_modules.core import LoggerFactory
from app_modules.core import SingleConfig
from app_modules.core import AppConstants
from app_modules.commands.command import Command

class Say_hello ( Command ):
    short_arg   = None
    long_arg    = 'say-hello'
    cmd_help    = 'print hello world'
    cmd_type    = None
    cmd_action  = 'store_true'

    def __init__( self, param = None ):
        super().__init__()
        self.logger = LoggerFactory.getLogger( str( self.__class__ ))

    def run( self ):
        print('hello world')

successivamente è necessario aggiornare il file

app_modules/commands/runner/__init__.py

in maniera che il nuovo comando sia richiamabile:

__all__ = [
    'Run_version',
    'Say_hello' 
    ]

# deprecated to keep older scripts who import this from breaking
from app_modules.commands.runner.run_version   import Run_version
from app_modules.commands.runner.say_hello   import Say_hello


A questo punto è necessario registrare nel parser il nuovo comando :

# file app_modules/commands/commandline_parser.py
# linea 38

        self.rcl = [ 
            Run_version,
            Say_hello
            ]

Infine è possibile provare il nuovo comando:

$ python app -h
usage: App [-h] [--version] [--say-hello] [--conf CONF]

A sample python app

optional arguments:
  -h, --help            show this help message and exit
  --version, -V         print version
  --say-hello           print hello world
  --conf CONF, -c CONF  pass a configuration file
 
$ python app --say-hello
hello world

Conclusine

Il codice dell'esempio è disponibile sul branch hello-world.

Invito tutti a forcare il mio codice per migliorarlo o fornirmi un feedback sulle scelte fatte.

Riferimenti