91 lines
3.5 KiB
Python
91 lines
3.5 KiB
Python
# Importa o módulo padrão do Python para ler arquivos INI (.ini)
|
|
import configparser
|
|
|
|
# Importa Path, que facilita o trabalho com caminhos de arquivos (mais seguro que strings)
|
|
from pathlib import Path
|
|
|
|
# Importa tipos para anotações — Dict (dicionário) e Any (qualquer tipo)
|
|
from typing import Dict, Any
|
|
|
|
|
|
# Define uma classe chamada ConfigIni
|
|
class ConfigIni:
|
|
|
|
@staticmethod
|
|
def read(path: str) -> Dict[str, Any]:
|
|
"""
|
|
Lê um arquivo INI (ignorando comentários iniciados por ';')
|
|
e retorna um dicionário com suas seções e pares chave=valor.
|
|
|
|
Tenta múltiplas codificações comuns no Windows:
|
|
- utf-8
|
|
- utf-8-sig
|
|
- latin-1
|
|
- cp1252
|
|
"""
|
|
|
|
# Converte o caminho recebido (string) em um objeto Path (mais seguro e portável)
|
|
config_path = Path(path)
|
|
|
|
# Verifica se o arquivo realmente existe no caminho informado
|
|
if not config_path.exists():
|
|
# Caso não exista, lança uma exceção com uma mensagem explicativa
|
|
raise FileNotFoundError(f"Arquivo não encontrado: {path}")
|
|
|
|
# Lista de codificações a tentar, em ordem de preferência
|
|
encodings_to_try = ["utf-8", "utf-8-sig", "latin-1", "cp1252"]
|
|
|
|
last_error: Exception | None = None
|
|
config: configparser.ConfigParser | None = None
|
|
|
|
# Tenta ler o arquivo usando diferentes encodings
|
|
for enc in encodings_to_try:
|
|
try:
|
|
tmp_config = configparser.ConfigParser()
|
|
# Garante que as chaves mantenham o mesmo formato de maiúsculas/minúsculas
|
|
# Por padrão, o configparser converte tudo para minúsculas.
|
|
tmp_config.optionxform = str
|
|
|
|
# Tenta ler o arquivo com a codificação atual
|
|
tmp_config.read(config_path, encoding=enc)
|
|
|
|
# Se chegou aqui sem UnicodeDecodeError, consideramos que deu certo
|
|
config = tmp_config
|
|
break
|
|
except UnicodeDecodeError as e:
|
|
# Guarda o último erro para caso nenhuma codificação funcione
|
|
last_error = e
|
|
continue
|
|
|
|
# Se nenhuma codificação funcionou, levanta um erro mais amigável
|
|
if config is None:
|
|
msg = (
|
|
f"Não foi possível decodificar o arquivo INI '{path}' "
|
|
f"usando as codificações: {', '.join(encodings_to_try)}"
|
|
)
|
|
if last_error:
|
|
raise UnicodeDecodeError(
|
|
last_error.encoding or "utf-8",
|
|
last_error.object,
|
|
last_error.start,
|
|
last_error.end,
|
|
msg,
|
|
)
|
|
raise RuntimeError(msg)
|
|
|
|
# Cria um dicionário vazio que armazenará os dados lidos do INI
|
|
data: Dict[str, Dict[str, Any]] = {}
|
|
|
|
# Percorre cada seção do arquivo INI (exemplo: [Geral], [Banco], etc.)
|
|
for section in config.sections():
|
|
# Cria um dicionário interno para armazenar as chaves e valores dessa seção
|
|
data[section] = {}
|
|
|
|
# Percorre todas as chaves e valores da seção atual
|
|
for key, value in config.items(section):
|
|
# .strip() remove espaços no início/fim
|
|
# .strip("'\"") remove aspas simples ou duplas em volta do valor (se existirem)
|
|
data[section][key] = value.strip().strip("'\"")
|
|
|
|
# Retorna o dicionário completo contendo todas as seções e seus pares chave=valor
|
|
return data
|