Especialização em Sistemas com Python & POO com Type Hints • Capítulo 12


🗺️ Mapa Conceitual do Tópico

flowchart TD
    subgraph PATHLIB["Sistema de Arquivos com pathlib.Path (PEP 428)"]
        PATH["Path('dados/relatorio.json')\nNavegação com Operador / · .glob() · .rglob()"]
        PROPS[".stem · .suffix · .parent · .is_file()"]
        MKDIR[".mkdir(parents=True, exist_ok=True)"]
    end

    subgraph PROTOCOL["Protocolo de Gerenciamento de Contexto"]
        WITH["with open(..., encoding='utf-8') as f:"]
        ENTER["__enter__() -> Adquire Descritor do Sistema Operacional"]
        EXIT["__exit__(exc_type, exc_val, tb) -> Liberação Incondicional"]
        CTXLIB["@contextlib.contextmanager (Geradores de Contexto)"]
    end

    subgraph FORMATS["Formatos Corporativos e Escrita Atômica"]
        CSV_M["Módulo csv\nDictReader & DictWriter com newline=''"]
        JSON_M["Módulo json\ndump/load (Disco) vs dumps/loads (RAM)"]
        ATOMIC["Escrita Atômica de Arquivos\nGravação em .tmp + Renomeação Atômica (.replace())"]
    end

    PATHLIB --> PROTOCOL
    PROTOCOL --> FORMATS

    style PATHLIB fill:#ede7f6,stroke:#512da8,stroke-width:2px
    style PROTOCOL fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
    style FORMATS fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px

📁 1. O Módulo pathlib.Path (PEP 428) no Windows e Linux

Substituindo o antigo módulo procedural os.path, o pathlib.Path oferece uma API orientada a objetos fluida para navegação e inspeção no sistema de arquivos, tratando automaticamente as diferenças de barras entre Windows (\) e sistemas Unix (/).

Operações Essenciais de Engenharia

from pathlib import Path

# Composição semântica de caminhos com sobrecarga do operador /:
diretorio_base: Path = Path.cwd() / "data" / "relatorios"
arquivo_alvo: Path = diretorio_base / "2026_auditoria.json"

# Criação recursiva e idempotente de diretórios (semelhante ao mkdir -p):
diretorio_base.mkdir(parents=True, exist_ok=True)

# Inspeção semântica de componentes do caminho:
print(f"Nome completo : {arquivo_alvo.name}")    # 2026_auditoria.json
print(f"Nome puro     : {arquivo_alvo.stem}")    # 2026_auditoria
print(f"Extensão      : {arquivo_alvo.suffix}")  # .json
print(f"Pasta Pai     : {arquivo_alvo.parent}")  # data/relatorios

# Varredura em profundidade com rglob:
arquivos_csv: list[Path] = list(diretorio_base.rglob("*.csv"))

⚠️ A Armadilha de Codificação do Windows: No Windows, a função open() sem argumento de encoding utiliza o padrão legado ANSI (CP1252), corrompendo acentuações (ç, ã). Em aplicações profissionais, declare sempre encoding="utf-8".


🔒 2. O Protocolo de Gerenciador de Contexto (with)

Em sistemas operacionais, arquivos abertos consom descritores de arquivo (File Descriptors / Handles), limitados por processo. Esquecer de fechar um arquivo causa bloqueios no Windows e esgotamento de handles no Linux. A instrução with garante liberação imediata via protocolo:

  1. __enter__(): Executado antes do corpo do bloco. Abre o recurso e vincula a variável de saída (as arquivo).
  2. __exit__(exc_type, exc_val, exc_tb): Executado incondicionalmente ao sair do bloco, mesmo que ocorra uma exceção grave ou sys.exit(). Libera os buffers e fecha o descritor no SO.

Criando Gerenciadores de Contexto com contextlib

from contextlib import contextmanager
import time
from typing import Iterator

@contextmanager
def cronometrar_operacao(rotulo: str) -> Iterator[None]:
    inicio = time.perf_counter()
    try:
        yield # Entrega controle ao corpo do with
    finally:
        fim = time.perf_counter()
        print(f"[{rotulo}] Duração da I/O: {(fim - inicio) * 1000:.2f} ms")

📊 3. Manipulação de CSV e JSON Corporativos

Processamento Estruturado com csv.DictReader e csv.DictWriter

Para evitar erros de índice por coluna, utilize leitores e escritores baseados em dicionários:

import csv
from pathlib import Path

def exportar_csv_seguro(caminho: Path, linhas: list[dict[str, object]]) -> None:
    # newline="" é OBRIGATÓRIO no Windows para evitar linhas em branco duplicadas
    with caminho.open(mode="w", newline="", encoding="utf-8") as f:
        colunas = list(linhas[0].keys())
        escritor = csv.DictWriter(f, fieldnames=colunas, delimiter=";")
        escritor.writeheader()
        escritor.writerows(linhas)

Serialização JSON com Tratamento de Caracteres Especiais

import json

# Gravação formatada com acentos preservados:
with caminho.open(mode="w", encoding="utf-8") as f:
    json.dump(dados, f, indent=2, ensure_ascii=False)

🛡️ 4. Padrão de Engenharia: Gravação Atômica de Arquivos

Em servidores corporativos, salvar arquivos sobrescrevendo diretamente o arquivo original (open("dados.json", "w")) é arriscado: uma interrupção inesperada (queda de energia, SIGKILL pelo orquestrador de pods) deixará o arquivo vazio ou parcialmente corrompido.

O Padrão de Escrita Atômica resolve isso em três etapas:

  1. Grava os novos dados em um arquivo temporário no mesmo sistema de arquivos (relatorio.json.tmp).
  2. Descarrega os buffers de I/O no disco físico (f.flush(); os.fsync(f.fileno())).
  3. Renomeia o temporário sobre o original via Path.replace(), operação atômica em nível de sistema de arquivos no Windows e Linux.