Skip to content

redirect_stdout não segura subprocesso nem os.write(1): NDJSON pode voltar a ser corrompido #29

Description

@davidbenal

Limitação conhecida do conserto que entrou na PR #27 (commit 96b7b4d). Não há defeito vivo hoje — é lacuna latente, e nenhum teste a pegaria.

O que o conserto faz

lib/cli.py, em run_cmd: nos modos de máquina (--json e --stream-json), o stdout dos passos é desviado para stderr com contextlib.redirect_stdout(sys.stderr), para que stdout carregue só a saída declarada. Isso existe porque o motor vendorado tem dois print() incondicionais no passo 0 (lib/motores/crystalball_llm.py:1166,1168) e é proibido editá-lo.

O limite

redirect_stdout troca sys.stdout, que é objeto de nível Python. Escapam:

  • subprocess com o stdout do filho herdado do processo pai. O filho escreve no fd 1 direto, e o desvio não o alcança.
  • os.write(1, ...), ou qualquer escrita por descritor.
  • extensões C que escrevam no stdout do C runtime.

Qualquer um dos três, num passo, volta a intercalar lixo no NDJSON e a quebrar a promessa de "uma linha por evento".

Por que não morde hoje

O único subprocesso alcançável a partir de um passo é higgsfield_cli_client._run, que chama subprocess.run(..., capture_output=True) — o filho escreve num pipe, não no fd 1 herdado. Nenhum outro caminho de passo escreve por descritor.

Conserto, quando valer

Trocar o desvio de nível Python por desvio de descritor (os.dup2 do fd 1 para o fd 2 durante a execução dos passos, restaurando depois), que pega subprocesso e os.write junto. Custa mais e exige cuidado com o CliRunner dos testes, que não tem fd real — por isso não entrou agora.

Gatilho para revisitar: o primeiro passo que precise de um subprocesso com stdout herdado.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions