# 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