Giliard Godoi

Machine Learning. Análise e visualização de dados. Inteligência Artificial. Computação Evolutiva. Algoritmos Genéticos. Otimização combinatória. Teoria dos Grafos. Pythonista.

Como manipular arquivos de configuração config.ini



Este artigo faz parte de uma série de três publicações que tratam de arquivos de configurações comuns em projetos desenvolvidos em Python. Esses formatos são config.ini, yaml, e toml.

Serão apresentados as características básicas desses formatos, e quais bibliotecas podem ser utilizadas para manipular os arquivos de cada uma dessas extensões.

O artigo está organizado sob o formatos de perguntas e respostas, com o correspondente exemplo de um código, para facilitar consultas futuras.

Config initialization (config.ini)

Os arquivos .ini foram primeiramente projetados para o sistema Microsoft Windows. Contudo, eles foram adotados por outros softwares (inclusives de código aberto) onde podem aparecer sob a extensão .cfg com implementações ligeiramente diferente.

Basicamente, as informações são guardadas sob o esquema chave-valor e podem ser organizadas em diversas seções. O arquivo no entando deve possuir uma sessão DEFAULT.

A manipulação desses arquivos podem ser feitas pela biblioteca padrão configparser.

As principais características desse formato são:

  1. Ser facilmente lido (e entendido) por um humano
  2. Podem ser utilizados como delimitadores de valor os caracteres ‘= ou ‘:.
  3. Comentários são precedidos pelo caracter ‘#
  4. Todos os valores são convertidos para string, porém existem alguns métodos especiais para converter os valores para determinado tipo;
  5. Ao realizar o parse de um arquivo, a estrutura de dados retornados se assemelha a um dicionário do Python.

O básico sobre configparser

Um exemplo da organização desse arquivo pode ser vista em configuration_string, conforme definido abaixo:

import configparser

config = configparser.ConfigParser()

configuration_string = '''
[DEFAULT]
name  = Giliard Godoi
email = ggodoi@email.com
local = Brasil
secret_agent = True

# This is a comment

[education]
school = Federal University of Technology - Paraná
major  = Software Development
year   = 2018

# This is a list of skills
[skills]
programing :
    Python
    JavaScript
    C
    C++

language :
    Portuguese
    English
'''

Como realizar o parse da string?

config.read_string(configuration_string)

type(
    config
)
configparser.ConfigParser
def __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, *, delimiters=('=', ':'), comment_prefixes=('#', ';'), inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=DEFAULTSECT, interpolation=_UNSET, converters=_UNSET)
ConfigParser implementing interpolation.

O que é uma seção?

config['DEFAULT']
<Section: DEFAULT>

Como saber quais são as demais seções?

config.sections()
['education', 'skills']

Como verificar se uma seção existe dentro de um objeto config?

('education' in config) or config.has_section('education')
True

Como verificar se existe uma opção (chave) dentro de uma seção?

config.has_option(section='education', option='year')
True

Como verificar se existe uma opção para a seção default?

'''
Se especificado `section` igual a None ou string vazia, a seção default é verificada.
'''
config.has_option(section=None, option='company')
False
config.has_option(section=None, option='email')
True

Como acessar um valor na seção default?

config['DEFAULT']['name']
'Giliard Godoi'

Como acessar um valor em uma outra seção?

config['education']['school']
'Federal University of Technology - Paraná'

Ou então, utilizar o método get

config.get(section='education', option='school')
'Federal University of Technology - Paraná'

Qual é o tipo de dado retornado por padrão?

type(
    config['education']['year']
)
str

Isso serve também para listas?

config['skills']['programing']
'\nPython\nJavaScript\nC\nC++'

Como converter os valores para listas?

config['skills']['programing'].split()
['Python', 'JavaScript', 'C', 'C++']

No entanto, a conversão (parse) para listas não é nativamente implementada para a linguagem Python.

Como converter o valor de uma variável para um tipo específico?

print(f'''
-----------------------------------
{config.get(section='education', option='school')} : {type(config.get(section='education', option='school'))}
{config.getint('education', 'year')} : {type(config.getint('education', 'year'))}
{config.getboolean('DEFAULT', 'secret_agent')} : {type(config.getboolean('DEFAULT', 'secret_agent'))}
-----------------------------------
''')
-----------------------------------
Federal University of Technology - Paraná : <class 'str'>
2018 : <class 'int'>
True : <class 'bool'>
-----------------------------------

Ou seja, existem três métodos para converter para tipos específicos:

  1. getint
  2. getfloat
  3. getboolean
type(
    config.getint('education', 'year')
)
int
type(
    config.getboolean('DEFAULT', 'secret_agent')
)
bool

É possível definir um valor fallback nos métodos get?

assert not config.has_option(section='company', option='name')

config.get(section='company', option='name', fallback='Does not exist')
'Does not exist'

Como saber todas as variáveis existentes de uma seção?

config.options('education')
['school', 'major', 'year', 'name', 'email', 'local', 'secret_agent']

Como obter os itens (chave-valor) para uma seção?

config.items('education')
[('name', 'Giliard Godoi'),
 ('email', 'ggodoi@email.com'),
 ('local', 'Brasil'),
 ('secret_agent', 'True'),
 ('school', 'Federal University of Technology - Paraná'),
 ('major', 'Software Development'),
 ('year', '2018')]

Como obter os itens (chave-valor) para a seção default?

config.defaults()
{
    'name': 'Giliard Godoi',
    'email': 'ggodoi@email.com',
    'local': 'Brasil',
    'secret_agent': 'True'
}

Arquivos

As operações de leitura e escrita do arquivo são bem simples, como pode ser visto a seguir.

Como salvar as configurações em um arquivo?

with open('config.ini', 'w') as f:
    config.write(f)

Como ler o arquivo config.ini?

other = configparser.ConfigParser()
other.sections()
[]
other.read('config.ini')
['config.ini']
other.sections()
['education', 'skills']

Referências

  1. https://docs.python.org/3/library/configparser.html
  2. https://en.wikipedia.org/wiki/INI_file