Skip to content

Retomada devolve o texto vazio, o Literal com None e as coleções como gravados - #265

Open
bdcdo wants to merge 3 commits into
mainfrom
fix/retomada-texto-vazio-e-colecoes
Open

bdcdo wants to merge 3 commits into
mainfrom
fix/retomada-texto-vazio-e-colecoes

Conversation

@bdcdo

@bdcdo bdcdo commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Problema

Em quatro casos, a retomada pelo checkpoint recusava uma linha já paga ou devolvia um valor diferente do que tinha sido gravado. No caso da issue, um campo obs: str respondido "" numa execução com checkpoint CSV interrompida pela cota faz a segunda chamada levantar ValueError: O DataFrame contém linhas processadas incompatíveis com o modelo atual: campos incompatíveis ['obs']. Inclua-os em reprocess_columns=['obs'], e seguir a sugestão reprocessaria a coluna inteira.

Na main de agora (b48bd03), depois do #246, do #248 e do #251, os quatro itens ainda reproduzem. A mesma medição mostrou dois casos da mesma classe que a issue não lista: Mapping[str, int] é recusado como o TypedDict, e no parquet o Sequence[str] também é recusado, enquanto set[str] e TypedDict voltam como array do numpy e como dict com 2024.0 no lugar de 2024.

O que muda

Texto vazio no CSV e no XLSX. Os dois formatos gravam "" e None na mesma célula vazia, e o leitor do pandas devolve as duas ausentes. Na retomada pelo checkpoint_path, a assinatura garante que o checkpoint foi gravado com o mesmo modelo, e nele um campo que só aceita texto, não admite None, não tem None por padrão e sai no model_dump só fica None quando a condição dele é falsa. Para esses campos, a validação da linha processada passa a ler a célula vazia como "", salvo quando a condição do campo é falsa na linha, caso em que ela continua None. O str obrigatório e o str = "padrão" respondidos "" voltam "", em vez de serem acusados (o primeiro) ou trocados pelo default (o segundo).

A correção não segue a forma literal da proposta, que relê com keep_default_na=False dentro do read_df. Feito ali, o "" apareceria também na linha com erro e na linha pendente, que guardam None, e no campo condicional pulado, que a validação aceitaria como "" válido. O read_df público não sabe qual é a coluna de status nem avalia condições. A restauração ficou na validação da retomada, que conhece as duas coisas, e o read_df continua devolvendo ausente a célula vazia.

Fórmula no XLSX. O openpyxl toma por fórmula todo texto que começa com =, e o pandas relê vazia a fórmula que nunca foi calculada. O checkpoint .xlsx passa a ser gravado pelo openpyxl, que ele já exige, e toda célula marcada como fórmula volta a ser texto, em qualquer coluna.

Coleções. is_complex_type reconhece as origens abstratas Sequence, AbstractSet e Mapping e as versões mutáveis, e as classes set e frozenset, além das subclasses de list, tuple, dict e BaseModel, o que inclui TypedDict e NamedTuple. A busca pelas abstratas é por identidade, porque str e bytes também são Sequence para o collections.abc. Esses campos entram nas estruturas do checkpoint, gravadas em JSON, e a retomada os devolve ao tipo declarado. O conjunto vai ao JSON em ordem fixa, a do texto JSON de cada item, também dentro de dict e de modelo aninhado: a ordem em que um set de textos é percorrido muda de um processo para outro, e a lista relida só diz o mesmo que o conjunto validado quando os dois saem na mesma ordem.

Literal com None. accepts_only_text ignora o None entre os argumentos do Literal, e o "NA" de Literal["NA", "deferido", None] é relido como texto, como já acontecia com Optional[Literal["NA", "deferido"]].

Reprocessamento interrompido. Só o aviso muda. Ele diz que repetir a chamada com reprocess_columns refaz todas as linhas, inclusive as já reprocessadas, que é o que acontece: numa execução de 4 linhas interrompida depois de 2, a repetição faz as 4 chamadas.

Itens da issue

Item Estado
Texto vazio em CSV e XLSX, campo str obrigatório Corrigido
Texto vazio em CSV e XLSX, campo que admite None Declarado: a célula vazia volta None, porque o arquivo não distingue "" de None; o parquet guarda a diferença. A documentação diz isso
Resposta que começa com = no XLSX Corrigido
Sequence[str], set[str], AbstractSet e TypedDict Corrigido, nos três formatos
Literal que inclui None Corrigido
Reprocessamento interrompido Só o aviso muda, por decisão do Bruno

Decisões

  • O checkpoint carregado à mão com read_df e passado como entrada continua acusando o "" do campo obrigatório, como o teste test_texto_obrigatorio_vazio_em_csv_continua_acusado já fixava. Sem a assinatura, com outro modelo a célula vazia pode ser o None de um campo que o admitia.
  • Mapping, MutableMapping, MutableSequence, MutableSet e NamedTuple entram junto, porque o defeito é o mesmo e o model_dump os devolve como lista, conjunto, dict ou tupla. O deque fica de fora: o model_dump o devolve como deque, que a gravação não serializa.
  • O campo com exclude=True fica de fora da restauração do texto vazio, porque o model_dump não o traz e a célula dele guarda o None que a execução pôs ao criar a coluna.
  • O repr de conjunto que um checkpoint CSV gravado pela versão anterior traz ("{'a'}") continua sem ser lido como conjunto. Aceitá-lo em normalize_value mudaria também o read_df sem modelo, que normaliza toda coluna com cara de JSON.

Closes #196

bdcdo added 3 commits October 21, 2026 22:17
… gravados

Texto vazio: o CSV e o XLSX gravam "" e None na mesma célula vazia. Na
retomada pelo checkpoint, cuja assinatura garante o mesmo modelo, a
célula vazia de linha processada num campo que só aceita texto, não
admite None, não tem None por padrão e sai no model_dump volta "",
salvo quando a condição do campo é falsa na linha. O checkpoint
carregado à mão e o campo que admite None ficam como estavam.

XLSX: o texto que começa com "=" é gravado como texto, e não como
fórmula, que o pandas relia vazia. O engine fica fixo no openpyxl, que o
checkpoint .xlsx já exige.

Coleções: is_complex_type reconhece Sequence, AbstractSet, Mapping e as
mutáveis, set, frozenset e as subclasses de dict e tuple, como TypedDict
e NamedTuple. Esses campos entram nas estruturas do checkpoint, gravadas
em JSON, e a retomada os devolve ao tipo do campo. O conjunto vai ao
JSON em ordem fixa, também dentro de dict e de modelo aninhado.

Literal com None: accepts_only_text ignora o None dos argumentos, e o
"NA" de Literal["NA", "deferido", None] é relido como texto.

Aviso do reprocessamento interrompido: diz que repetir a chamada com
reprocess_columns refaz todas as linhas, inclusive as já reprocessadas.
…e e coleções

O guia de desempenho e a referência da API, nas duas árvores, descrevem os campos de estrutura que o checkpoint grava em JSON, o texto vazio que a retomada pelo checkpoint devolve ao campo de texto que não admite None, o texto que começa com "=" no XLSX e o custo de repetir o reprocessamento interrompido.
…ir a lista

A enumeração entre parênteses copiava o conteúdo da constante definida logo acima.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retomada recusa ou altera valores já gravados

1 participant