configparser - Lavora con i File di Configurazione

Scopo: Leggere/scrivere file di configurazione simili ai file INI di Windows

Il modulo configparser si usa per gestire dei file di configurazione per un'applicazione che siano modificabili dall'utente. Il contenuto dei file di configurazione può essere organizzato in gruppi, e sono supportate parecchie opzioni di tipi di valore, inclusi interi, valori a virgola mobile e booleani. I valori delle opzioni possono essere combinati usando le stringhe di formattazione di Python, per costruire valori più grandi tipo URL da parti degli stessi tipo nomi host e numeri di porte.

Formato del File di Configurazione

Il formato di file usato da configparser è simile a quello usato dalle vecchie versioni di Microsoft Windows. Consiste in una o più sezioni nominative, ognuna delle quali può contenere opzioni individuali con nomi e valori.

Le sezioni del file di configurazione sono identificate cercando delle righe che iniziano con [ e finiscono con ]. Il valore racchiuso tra le parentesi quadre è il nome della sezione, e può contenere qualsiasi carattere ad eccezione delle parentesi quadre.

Le opzioni sono elencate una per riga all'interno di una sezione. La riga inizia con il nome dell'opzione, la quale è separata dal valore dai due punti (:) oppure dall'uguale (=). I caratteri whitespace (spazi, tabulazioni ecc.) attorno al separatore vengono ignorati mentre il file viene elaborato.

Il seguente è un file di configurazione di esempio con una sezione chiamata bug_tracker, con tre opzioni: url, username e password.

# Questo è un semplice esempio con commenti
[bug_tracker]
url = http://localhost:8080/bugs/
username = dhellmann
; Non si dovrebbero memorizzare password in chiaro nei file
; di configurazione.
password = SEGRETO

Leggere i File di Configurazione

L'uso più comune per un file di configurazione è quello di avere un utente od un amministratore di sistema che lo modifichi con un normale editor di testi per impostare i comportamenti predefiniti dell'applicazione, quindi il file vien fatto leggere dall'applicazione, che lo elabora ed agisce in base al suo contenuto. Per leggere il file di configurazione si usi il metodo read() di ConfigParser.

# configparser_read.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('semplice.ini')

print(parser.get('bug_tracker', 'url'))

Il programma legge il file simple.ini visto qui sopra e stampa il valore dell'opzione url dalla sezione bug_tracker.

$ python3 configparser_read.py

http://localhost:8080/bugs/

Il metodo read() accetta anche un elenco di nomi di file. Ciascun nome viene scorso a turno, e se il file esiste viene aperto e letto.

# configparser_read_many.py

from configparser import ConfigParser
import glob

parser = ConfigParser()

candidates = ['non_esiste.ini', 'anche_questo_non_esiste.ini',
              'semplice.ini', 'multisezione.ini']

found = parser.read(candidates)

missing = set(candidates) - set(found)

print('File configurazione trovati:', sorted(found))
print('File mancanti              :', sorted(missing))

read() restituisce un elenco che contiene i nomi dei file caricati con successo, in modo che il programma possa scoprire quali file di configurazione sono mancanti e decidere se ignorarli oppure se trattare questa condizione come un errore.

$ python3 configparser_read_many.py

File configurazione trovati: ['multisezione.ini', 'semplice.ini']
File mancanti              : ['anche_questo_non_esiste.ini', 'non_esiste.ini']

Dati Unicode in Configurazione

I file di configurazione che contengono dati Unicode dovrebbero essere letti usando il valore di codifica appropriato. Nel file di esempio seguente è stato modificato il valore della password originale inserendovi caratteri Unicode, poi il file è stato codificato usando UTF-8.

# unicode.ini

[bug_tracker]
url = http://localhost:8080/bugs/
username = dhellmann
password = ßéç®é†

Il file viene aperto con il decodificatore appropriato, convertendo i dati UTF-8 nelle stringhe Unicode native.

# configparser_unicode.py

from configparser import ConfigParser

parser = ConfigParser()
# Apertura del file con la codifica corretta
parser.read('unicode.ini', encoding='utf-8')

password = parser.get('bug_tracker', 'password')

print('Password:', password.encode('utf-8'))
print('Tipe    :', type(password))
print('repr()  :', repr(password))

Il valore restituito da get() è una stringa Unicode, quindi per stamparlo in sicurezza deve essere ricodificato come UTF-8.

$ python3 configparser_unicode.py

Password: b'\xc3\x9f\xc3\xa9\xc3\xa7\xc2\xae\xc3\xa9\xe2\x80\xa0'
Tipo    : <class 'str'>
repr()  : 'ßéç®é†'

Accedere alle Impostazioni di Configurazione

ConfigParser comprende metodi per esaminare la struttura della configurazione elaborata, compreso l'elenco delle sezioni ed opzioni, ed il recupero dei loro valori. Questo file di configurazione include due sezioni per servizi web separati.

[bug_tracker]
url = http://localhost:8080/bugs/
username = dhellmann
password = SECRET

[wiki]
url = http://localhost:8080/wiki/
username = dhellmann
password = SECRET

Questo semplice programma applica alcuni dei metodi per cercare i dati di configurazione, inclusi sections(), options() ed items().

# configparser_structure.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multisezione.ini')

for section_name in parser.sections():
    print('Sezione:', section_name)
    print('  Opzioni:', parser.options(section_name))
    for name, value in parser.items(section_name):
        print('  {} = {}'.format(name, value))
    print()

Sia sections() che options() ritornano liste di stringhe, mentre items() ritorna una lista di tuple che contengono coppie di nomi-valori.

$ python3 configparser_structure.py

Sezione: bug_tracker
  Opzioni: ['url', 'username', 'password']
  url = http://localhost:8080/bugs/
  username = dhellmann
  password = SECRET

Sezione: wiki
  Opzioni: ['url', 'username', 'password']
  url = http://localhost:8080/wiki/
  username = dhellmann
  password = SECRET

ConfigParser supporta anche la stessa API di mappatura dei dizionari, ConfigParser funge infatti da dizionario che contiene dizionari separati per ogni sezione.

# configparser_structure_dict.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multisezione.ini')

for section_name in parser:
    print('Sezione:', section_name)
    section = parser[section_name]
    print('  Opzioni:', list(section.keys()))
    for name in section:
        print('  {} = {}'.format(name, section[name]))
    print()

Usando l'API di mappatura per accedere allo stesso file di configurazione si ottiene lo stesso output.

$ python3 configparser_structure_dict.py

Sezione: DEFAULT
  Opzioni: []

Sezione: bug_tracker
  Opzioni: ['url', 'username', 'password']
  url = http://localhost:8080/bugs/
  username = dhellmann
  password = SECRET

Sezione: wiki
  Opzioni: ['url', 'username', 'password']
  url = http://localhost:8080/wiki/
  username = dhellmann
  password = SECRET
Verificare Se Sono Presenti Valori

Per verificare se una sezione esiste, si usi has_section() passando il nome della sezione.

# configparser_has_section.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multisezione.ini')

for candidate in ['wiki', 'bug_tracker', 'dvcs']:
    print('{:<12}: {}'.format(
        candidate, parser.has_section(candidate)))

Se si verifica se una sezione esiste prima di chiamare get() si evitano le eccezioni per dati mancanti.

$ python3 configparser_has_section.py

wiki        : True
bug_tracker : True
dvcs        : False

Si usi has_option() per verificare se una opzione esiste all'interno di una sezione.

# configparser_has_option.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multisezione.ini')

SECTIONS = ['wiki', 'none']
OPTIONS = ['username', 'password', 'url', 'descrizione']

for section in SECTIONS:
    has_section = parser.has_section(section)
    print('{} sezione esiste: {}'.format(section, has_section))
    for candidate in OPTIONS:
        has_option = parser.has_option(section, candidate)
        print('{}.{:<12}  : {}'.format(
            section, candidate, has_option))
    print()

Se la sezione non esiste, has_section() ritorna False.

$ python3 configparser_has_option.py

wiki sezione esiste: True
wiki.username      : True
wiki.password      : True
wiki.url           : True
wiki.descrizione   : False

none sezione esiste: False
none.username      : False
none.password      : False
none.url           : False
none.descrizione   : False
Tipi di Valore

Tutti i nomi di sezione ed opzione sono trattati come stringhe, ma i valori delle opzioni possono essere stringhe, interi, valori a virgola mobile, booleani. Vi è un intervallo di possibili valori booleani che possono essere convertiti a vero o falso. Il file di esempio seguente ne include uno per tipo.

# types.ini

[interi]
positive = 1
negative = -5

[virgola mobile]
positive = 0.2
negative = -3.14

[booleani]
number_true = 1
number_false = 0
yn_true = yes
yn_false = no
tf_true = true
tf_false = false
onoff_true = on
onoff_false = false

ConfigParser non effettua alcun tentativo di comprendere il tipo dell'opzione. Spetta all'applicazione di utilizzare il metodo corretto per recuperare il valore nel tipo desiderato. get() ritorna sempre una stringa. Si usi getint() per gli interi, getfloat() per i valori a virgola mobile e getboolean() per i valori booleani.

# configparser_value_types.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('tipi.ini')

print('Interi:')
for name in parser.options('interi'):
    string_value = parser.get('interi', name)
    value = parser.getint('interi', name)
    print('  {:<12} : {!r:<7} -> {}'.format(
        name, string_value, value))

print('\nVirgola Mobile:')
for name in parser.options('virgola_mobile'):
    string_value = parser.get('virgola_mobile', name)
    value = parser.getfloat('virgola_mobile', name)
    print('  {:<12} : {!r:<7} -> {:0.2f}'.format(
        name, string_value, value))

print('\nBooleani:')
for name in parser.options('booleani'):
    string_value = parser.get('booleani', name)
    value = parser.getboolean('booleani', name)
    print('  {:<12} : {!r:<7} -> {}'.format(
        name, string_value, value))

L'esecuzione di questo programma con l'input di esempio produce il seguente risultato.

$ python3 configparser_value_types.py

Interi:
  positivo     : '1'     -> 1
  negativo     : '-5'    -> -5

Virgola Mobile:
  positivo     : '0.2'   -> 0.20
  negativo     : '-3.14' -> -3.14

Booleani:
  numero_vero  : '1'     -> True
  numero_falso : '0'     -> False
  sino_vero    : 'yes'   -> True
  sino_false   : 'no'    -> False
  verofalso_vero : 'true'  -> True
  verofalso_false : 'false' -> False
  accesospento_vero : 'on'    -> True
  accesospento_false : 'false' -> False

E' possibile aggiungere convertitori di tipo personalizzati passando le funzioni di conversione tramite l'argomento converters di ConfigParser. Ogni convertitore riceve un singolo valore in input e dovrebbe trasformare quel valore nell'appropriato tipo di valore da ritornare.

# configparser_custom_types.py

from configparser import ConfigParser
import datetime


def parse_iso_datetime(s):
    print('parse_iso_datetime({!r})'.format(s))
    return datetime.datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f')


parser = ConfigParser(
    converters={
        'datetime': parse_iso_datetime,
    }
)
parser.read('tipi_personalizzati.ini')

string_value = parser['dataora']['data_scadenza']
value = parser.getdatetime('dataora', 'data_scadenza')
print('data_scadenza : {!r} -> {!r}'.format(string_value, value))

L'aggiunta di un convertitore fa sì che ConfigParser crei automaticamente un metodo per quel tipo, usando il nome del tipo come specificato in converters. In questo esempio il convertitore 'datetime' provoca l'aggiunta di un nuovo metodo getdatetime().

$ python3 configparser_custom_types.py

parse_iso_datetime('2015-11-08T11:30:05.905898')
data_scadenza : '2015-11-08T11:30:05.905898' -> datetime.datetime(2015, 11, 8, 11, 30, 5, 905898)

E' anche possibile aggiungere metodi di conversione direttamente ad una sottoclasse di ConfigParser.

Opzioni com Flag

In genere il parser richiede un valore esplicito per ogni opzione, tuttavia impostando il parametro dell'argomento allow_no_value a True di ConfigParser una opzione può presentarsi a se stante in una riga nel file di configurazione ed essere usata come flag.

# configparser_allow_no_value.py

import configparser

# Richiede valori
try:
    parser = configparser.ConfigParser()
    parser.read('consenti_no_valori.ini')
except configparser.ParsingError as err:
    print('Non posso elaborare:', err)

# Consente nomi di opzioni a se stanti
print('\nRiprovo con allow_no_value=True')
parser = configparser.ConfigParser(allow_no_value=True)
parser.read('consenti_no_valori.ini')
for flag in ['abilita_caratteristica_attivata',
             'abilita_altra_caratteristica_attivata']:
    print('\n', flag)
    exists = parser.has_option('flags', flag)
    print('  has_option:', exists)
    if exists:
        print('         get:', parser.get('flags', flag))

Quando una opzione non ha uno specifico valore, has_option() riporta che l'opzione esiste e get() ritorna None.

$ python3 configparser_allow_no_value.py

Non posso elaborare: Source contains parsing errors: 'consenti_no_valori.ini'
  [line  4]: 'abilita_caratteristica_attivata\n'

Riprovo con allow_no_value=True

 abilita_caratteristica_attivata
  has_option: True
         get: None

 abilita_altra_caratteristica_attivata
  has_option: False
Stringhe Multiriga

I valori stringa possono essere espressi su più righe, se le righe sottostanti sono indentate.

# multiriga.ini

[esempio]
messaggio = Questa è una stringa multiriga.
    Con due paragrafi.

    Essi sono separati da una riga vuota.
# configparser_multiline.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multiriga.ini')

print(parser.get('esempio', 'messaggio'))

All'interno di valori multiriga indentati, la righe vuote sono trattate come parte del valore e preservate.

$ python3 configparser_multiline.py

Questa è una stringa multiriga.
Con due paragrafi.

Essi sono separati da una riga vuota.

Modificare le Impostazioni

Sebbene ConfigParser sia principalmente concepito per la lettura di impostazioni da file, è anche possibile popolare le impostazioni chiamando add_section() per creare una nuova sezione, e set() per aggiungere o modificare un'opzione.

# configparser_populate.py

import configparser

parser = configparser.SafeConfigParser()

parser.add_section('bug_tracker')
parser.set('bug_tracker', 'url', 'http://localhost:8080/bugs')
parser.set('bug_tracker', 'username', 'dhellmann')
parser.set('bug_tracker', 'password', 'secret')

for section in parser.sections():
    print(section)
    for name, value in parser.items(section):
        print('  {} = {!r}'.format(name, value))

Tutte le opzioni devono essere impostate come stringhe, anche se poi saranno recuperate come interi, valori a virgola mobile o booleani.

$ python3 configparser_populate.py

bug_tracker
  url = 'http://localhost:8080/bugs'
  username = 'dhellmann'
  password = 'secret'

Sezioni ed opzioni possono essere rimosse da un oggetto ConfigParser rispettivamente con remove_section() e remove_option().

# configparser_remove.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('multisezione.ini')

print('Lettura valori:\n')
for section in parser.sections():
    print(section)
    for name, value in parser.items(section):
        print('  {} = {!r}'.format(name, value))

parser.remove_option('bug_tracker', 'password')
parser.remove_section('wiki')

print('\nModifica valori:\n')
for section in parser.sections():
    print(section)
    for name, value in parser.items(section):
        print('  {} = {!r}'.format(name, value))

La rimozione di una sezione comporta l'eliminazione di tutte le opzioni in essa contenute.

$ python3 configparser_remove.py

Lettura valori:

bug_tracker
  url = 'http://localhost:8080/bugs/'
  username = 'dhellmann'
  password = 'SECRET'
wiki
  url = 'http://localhost:8080/wiki/'
  username = 'dhellmann'
  password = 'SECRET'

Modifica valori:

bug_tracker
  url = 'http://localhost:8080/bugs/'
  username = 'dhellmann'

Salvare File di Configurazione

Una volta che ConfigParser è stato popolato con i dati desiderati, può essere salvato ad un file chiamando il metodo write(). Questo rende possibile fornire una interfaccia utente per la modifica delle impostazioni di configurazione, senza dover scrivere altro codice per gestire il file.

# configparser_write.py

import configparser
import sys

parser = configparser.ConfigParser()

parser.add_section('bug_tracker')
parser.set('bug_tracker', 'url', 'http://localhost:8080/bugs')
parser.set('bug_tracker', 'username', 'dhellmann')
parser.set('bug_tracker', 'password', 'secret')

parser.write(sys.stdout)

Il metodo write() riceve un oggetto di tipo file come argomento. Scrive i dati nel formato INI in modo che possano essere nuovamente elaborati da ConfigParser.

$ python3 configparser_write.py

[bug_tracker]
url = http://localhost:8080/bugs
username = dhellmann
password = secret
I commenti nel file di configurazione originale non sono preservati in lettura, modifica o riscrittura del file di configurazione.

Percorso per la Ricerca delle Opzioni

ConfigParser usa un processo di ricerca a fasi multiple quando cerca una opzione.

Prima di iniziare la ricerca di una opzione, viene verificato il nome della sezione. Se la sezione non esiste, ed il nome non è il valore speciale DEFAULT, viene sollevata l'eccezione NoSectionError.

  1. Se il nome opzione appare nel dizionario vars passato a get(), viene ritornato il valore da vars.
  2. Se il nome opzione appare nella sezione specificata, viene ritornato il valore da quella sezione.
  3. Se il nome opzione appare nella sezione DEFAULT viene ritornato quel valore.
  4. Se il nome opzione appare nel dizionario defaults passato al costruttore, viene ritornato quel valore.

Se il nome non viene trovato in alcuna delle locazioni sopra citate, viene sollevata una eccezione NoOptionError.

Il comportamento del percorso di ricerca può essere verificato usando questo file di configurazione.

# with-defaults.ini

[DEFAULT]
file-only = valore dalla sezione DEFAULT
init-and-file = valore dalla sezione DEFAULT
from-section = valore dalla sezione DEFAULT
from-vars = valore dalla sezione DEFAULT

[sect]
section-only = valore dalla sezione nel file
from-section = valore dalla sezione nel file
from-vars = valore dalla sezione nel file

Il programma di esempio include impostazioni predefinite per le opzioni non specificate nel file di configurazione, e sovrascrive alcuni valori che sono definiti nel file.

# configparser_defaults.py

import configparser

# Definisce i nomi delle opzioni
option_names = [
    'from-default',
    'from-section', 'section-only',
    'file-only', 'init-only', 'init-and-file',
    'from-vars',
]

# Inizializza il parser con qualche valore di default
DEFAULTS = {
    'from-default': 'valore da DEFAULTS passato ad init',
    'init-only': 'valore da DEFAULTS passato ad init',
    'init-and-file': 'valore da DEFAULTS passato ad init',
    'from-section': 'valore da DEFAULTS passato ad init',
    'from-vars': 'valore da DEFAULTS passato ad init',
}
parser = configparser.ConfigParser(defaults=DEFAULTS)

print('Default prima di caricare il file:')
defaults = parser.defaults()
for name in option_names:
    if name in defaults:
        print('  {:<15} = {!r}'.format(name, defaults[name]))

# Carica il file di configurazione
parser.read('with-defaults.ini')

print('\nDefault dopo il caricamento del file:')
defaults = parser.defaults()
for name in option_names:
    if name in defaults:
        print('  {:<15} = {!r}'.format(name, defaults[name]))

# Definisce alcuni valori locali da sovrascriver
vars = {'from-vars': 'valori da vars'}

# Mostra i valori di tutte le opzioni
print('\nRicerca opzioni:')
for name in option_names:
    value = parser.get('sect', name, vars=vars)
    print('  {:<15} = {!r}'.format(name, value))

# Mostra messaggi di errore per opzioni che non esistono
print('\nCasi di errore:')
try:
    print('Opzione non esiste :', parser.get('sect', 'no-option'))
except configparser.NoOptionError as err:
    print(err)

try:
    print('Sezione non esiste:', parser.get('no-sect', 'no-option'))
except configparser.NoSectionError as err:
    print(err)

Il risultato mostra le origini del valore per ciascuna opzione ed illustra il modo nel quale impostazioni predefinite da sorgenti diverse sovrascrivano valori esistenti.

$ python3  configparser_defaults.py

Default prima di caricare il file:
  from-default    = 'valore da DEFAULTS passato ad init'
  from-section    = 'valore da DEFAULTS passato ad init'
  init-only       = 'valore da DEFAULTS passato ad init'
  init-and-file   = 'valore da DEFAULTS passato ad init'
  from-vars       = 'valore da DEFAULTS passato ad init'

Default dopo il caricamento del file:
  from-default    = 'valore da DEFAULTS passato ad init'
  from-section    = 'valore dalla sezione DEFAULT'
  file-only       = 'valore dalla sezione DEFAULT'
  init-only       = 'valore da DEFAULTS passato ad init'
  init-and-file   = 'valore dalla sezione DEFAULT'
  from-vars       = 'valore dalla sezione DEFAULT'

Ricerca opzioni:
  from-default    = 'valore da DEFAULTS passato ad init'
  from-section    = 'valore dalla sezione nel file'
  section-only    = 'valore dalla sezione nel file'
  file-only       = 'valore dalla sezione DEFAULT'
  init-only       = 'valore da DEFAULTS passato ad init'
  init-and-file   = 'valore dalla sezione DEFAULT'
  from-vars       = 'valori da vars'

Casi di errore:
No option 'no-option' in section: 'sect'
No section: 'no-sect'

Combinare Valori con Interpolazione

ConfigParser fornisce una caratteristica chiamata interpolazione che può essere usata per combinare valori insieme. Valori che contengono stringhe di formattazione standard Python attivano l'interpolazione quando sono recuperati. Nomi di opzione espressi all'interno del valore che si sta acquisendo sono sostituiti con i loro valori, uno per uno, fino a che non sono più necessarie altre sostituzioni.

Gli esempi di URL dal file INI mostrati in precedenza possono essere riscritti usando l'interpolazione in modo che sia più facile modificarne solo una parte del valore. Ad esempio, in questo file di configurazione il protocollo, il nome host e la porta di un URL vengono espressi come opzioni separate.

# interpolazione.ini

[bug_tracker]
protocol = http
server = localhost
port = 8080
url = %(protocol)s://%(server)s:%(port)s/bugs/
username = dhellmann
password = SECRET

L'interpolazione viene eseguita in modalità predefinita ogni volta che viene chiamato get(). Si passi True come parametro per l'argomento raw per ottenere il valore originale, senza interpolazione.

# configparser_interpolation.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('interpolazione.ini')

print('Valore originale       :', parser.get('bug_tracker', 'url'))

parser.set('bug_tracker', 'port', '9090')
print('Valore porta modificato:', parser.get('bug_tracker', 'url'))

print('Senza interpolazione   :', parser.get('bug_tracker', 'url',
                                           raw=True))

Visto che il valore viene calcolato da get(), la modifica di una delle impostazioni utilizzate per il valore url modificherà il valore ritornato.

$ python3 configparser_interpolation.py

Valore originale       : http://localhost:8080/bugs/
Valore porta modificato: http://localhost:9090/bugs/
Senza interpolazione   : %(protocol)s://%(server)s:%(port)s/bugs/

Usare Opzioni Predefinite

I valori per l'interpolazione non devono apparire nella stessa sezione dell'opzione originale. Le opzioni predefinite possono essere mescolate a valori sovrascritti.

# interpolazione_predefiniti.ini

[DEFAULT]
url = %(protocol)s://%(server)s:%(port)s/bugs/
protocol = http
server = bugs.example.com
port = 80

[bug_tracker]
server = localhost
port = 8080
username = dhellmann
password = SECRET

Con questa configurazione, il valore per url proviene dalla sezione DEFAULT, e il processo di sostituzione inizia cercando in bug_tracker poi arriva a DEFAULT per le parti che non trova.

# configparser_interpolation_defaults.py

from configparser import ConfigParser

parser = ConfigParser()
parser.read('interpolazione_predefiniti.ini')

print('URL:', parser.get('bug_tracker', 'url'))

I valori del nome dell'host e della porta provengono dalla sezione bug_tracker, ma protocol proviene da DEFAULT.

$ python3 configparser_interpolation_defaults.py

URL: http://localhost:8080/bugs/
Errori in Sostituzione

Le sostituzioni si interrompono dopo il numero di passi definito in MAX_INTERPOLATION_DEPTH per evitare problemi dovuti a riferimenti ricorsivi.

# configparser_interpolation_recursion.py

import configparser

parser = configparser.ConfigParser()

parser.add_section('sect')
parser.set('sect', 'opt', '%(opt)s')

try:
    print(parser.get('sect', 'opt'))
except configparser.InterpolationDepthError as err:
    print('ERRORE:', err)

Una eccezione InterpolationDepthError viene sollevata se ci sono troppi passi di sostituzione.

$ python3 configparser_interpolation_recursion.py

ERRORE: Recursion limit exceeded in value substitution: option 'opt' in section 'sect' contains an interpolation key which cannot be substituted in 10 steps. Raw value: '%(opt)s'

Valori mancanti danno luogo ad una eccezione InterpolationMissingOptionError.

# configparser_interpolation_error.py

import configparser

parser = configparser.ConfigParser()

parser.add_section('bug_tracker')
parser.set('bug_tracker', 'url',
           'http://%(server)s:%(port)s/bugs')

try:
    print(parser.get('bug_tracker', 'url'))
except configparser.InterpolationMissingOptionError as err:
    print('ERRORE:', err)

Visto che non è stato definito alcun valore server, url non può essere costruito.

$ python3 configparser_interpolation_error.py

ERRORE: Bad value substitution: option 'url' in section 'bug_tracker' contains an interpolation key 'server' which is not a valid option name. Raw value: 'http://%(server)s:%(port)s/bugs'
Caratteri Speciali

Visto che % fa partire le istruzioni di interpolazione, se è necessario un % nel valore dell'opzione occorre farlo precedere da un altro carattere di percentuale (%%).

# escape.ini

[escape]
value = un %% nel valore ritornato si esprime raddoppiandolo

Leggere il valore non richiede altre considerazioni particolari.

# configparser_escape.py

from configparser import ConfigParser
import os

filename = 'escape.ini'
config = ConfigParser()
config.read([filename])

value = config.get('escape', 'value')

print(value)

Quando viene letto il valore, %% viene automaticamente convertito in %.

$ python3 configparser_escape.py

un % nel valore ritornato si esprime raddoppiandolo
Interpolazione Estesa

ConfigParser supporta implementazioni alternative di interpolazione. Passando un oggetto che supporta l'API definita da Interpolation nel parametro interpolation. Ad esempio, usando ExtendedInterpolation in luogo della predefinita BasicInterpolation si ha accesso a diverse sintassi usando ${} come indicatore di variabile.

# configparser_extendedinterpolation.py

from configparser import ConfigParser, ExtendedInterpolation

parser = ConfigParser(interpolation=ExtendedInterpolation())
parser.read('interpolazione_estesa.ini')

print('Valore originale     :', parser.get('bug_tracker', 'url'))

parser.set('intranet', 'port', '9090')
print('Valore porta alterato:', parser.get('bug_tracker', 'url'))

print('Senza interpolazione :', parser.get('bug_tracker', 'url',
                                           raw=True))

L'interpolazione estesa supporta l'accesso a valori da altri sezioni del file di configurazione prefissando il nome della variabile dal nome della sezione e dal simbolo dei due punti (:).

# interpolazione_estesa.ini

[intranet]
server = localhost
port = 8080

[bug_tracker]
url = http://${intranet:server}:${intranet:port}/bugs/
username = dhellmann
password = SECRET

Facendo riferimento a valori in altre sezioni del file rende possibile condividere una gerarchia di valori, senza piazzare tutti quelli predefiniti nella sezione DEFAULTS.

$ python3 configparser_extendedinterpolation.py

Valore originale     : http://localhost:8080/bugs/
Valore porta alterato: http://localhost:9090/bugs/
Senza interpolazione : http://${intranet:server}:${intranet:port}/bugs/
Disabilitare l'Interpolazione

Per disabilitare l'interpolazione, si passi None in luogo di un oggetto Interpolation.

# configparser_nointerpolation.py

from configparser import ConfigParser

parser = ConfigParser(interpolation=None)
parser.read('interpolazione.ini')

print('Senza interpolazione:', parser.get('bug_tracker', 'url'))

Questo fa sì che qualsiasi sintassi che possa essere elaborata dall'oggetto di interpolazione venga ignorata in sicurezza.

$ python3 configparser_nointerpolation.py

Senza interpolazione: %(protocol)s://%(server)s:%(port)s/bugs/

Vedere anche:

configparser
La documentazione della libreria standard per questo modulo.
ConfigObj
Un avanzato elaboratore di file di configurazione con supporto di caratteristiche tipo la validazione del contenuto.
Note di portabilità per configparser.