feat(filter): apply comment limit after filtering - #2
Conversation
othonhugo
left a comment
There was a problem hiding this comment.
A implementação ficou muito boa! Deixei só alguns comentários com sugestões que acho que podem melhorar a legibilidade e a organização do código. Ainda preciso testar o comportamento em runtime pra validar tudo, mas a implementação parece bem consistente :)
| COMMENTS_MAX_SECONDS = 300 | ||
|
|
||
|
|
||
| def _with_deadline(records: Iterator[dict[str, object]], max_seconds: int) -> Iterator[dict[str, object]]: | ||
| deadline = time.monotonic() + max_seconds | ||
|
|
||
| for record in records: | ||
| if time.monotonic() >= deadline: | ||
| return | ||
|
|
||
| yield record | ||
|
|
There was a problem hiding this comment.
Boa função! Acho que também faz sentido criar um novo módulo utils e movê-la pra lá pra poder ser reusada em outras partes do código.
Além disso, seria melhor ainda se puder user um tipo genérico no lugar de object, preservando o mesmo tipo de entrada e saída da função.
Já a constante COMMENTS_MAX_SECONDS poderia continuar neste módulo e ser passada como argumento pro utilitário.
Exemplo:
# src/prawler/utils/iterators.py
from typing import TypeVar
T = TypeVar("T")
def with_deadline(records: Iterator[T], max_seconds: int) -> Iterator[T]:
deadline = time.monotonic() + max_seconds
for record in records:
if time.monotonic() >= deadline:
return
yield recordThere was a problem hiding this comment.
Acho que vale ajustar o nome dos arquivos de teste para seguir uma convenção mais comum, como test_cli_commands_comments.py. A ideia é usar um único _ para separar os diretórios do caminho do módulo testado, em vez de __.
There was a problem hiding this comment.
Outro ponto é que o diretório tests geralmente fica na raiz do repositório, no mesmo nível de src e docs. Acho que vale a pena seguir essa convenção e alinhar a estrutura do projeto com o que é mais comum na comunidade.
| @@ -0,0 +1,22 @@ | |||
| from prawler.pipeline.filters import make_filter_stage | |||
|
|
|||
| def test_filtro_ia_ignora_verbo_ir(): | |||
There was a problem hiding this comment.
Acho que vale renomear test_filtro_ia_ignora_verbo_ir para inglês e para descrever o comportamento que está sendo testado. Como o foco é validar o filtro usando um operador de regex (~=), algo como test_filter_regex_operator_matches_expected_records (ou outro nome equivalente) tornaria a intenção do teste mais explícita.
MR: Aplicar
--limitapós o filtro, não antesContexto / Problema
Hoje, ao rodar:
prawler comments /brdev \ --filter "body ~= \b(?-i:IA)\b|\bChatGPT\b|\bClaude\s*Code\b|\bintelig[êe]ncia\s+artificial\b" \ --format table \ --limit 20a expectativa é receber 20 comentários que atendem ao filtro. O que acontece hoje é diferente:
limité passado direto para o crawler, que o repassa para a chamada do PRAW. Isso corta o stream antes do filtro rodar. Resultado: buscamos 20 comentários brutos, filtramos esse subconjunto pequeno, e o total final quase sempre fica abaixo do que foi pedido.A causa raiz é um único parâmetro (
limit) sendo usado para dois papéis distintos: quantidade buscada na fonte e quantidade desejada após aplicar regra de negócio. Este MR separa esses dois papéis.Solução
SubmissionCommentConfig/UserCommentConfigagora sempre recebemlimit=None, o crawler entrega todo o stream lazy disponível na fonte, sem opinar sobre quantidade final.itertools.islice(filtered_records, limit). Como tudo éIterator, oislicesó continua "puxando" registros da fonte até acumularlimititens já aprovados pelo filtro, ou até a fonte se esgotar._with_deadlineencerra o consumo apósCOMMENTS_MAX_SECONDS(300s), devolvendo o que já foi coletado até ali, sem travar indefinidamente em filtros muito restritivos.Mudanças por arquivo
src/prawler/cli/commands/comments.py_with_deadline(records, max_seconds): novo estágio consumidor que interrompe a iteração ao atingir o deadline.from_user/from_submissionagora chamados comlimit=None, delegando o corte de quantidade para depois do pipeline.crawler → filtros → deadline → islice(limit) → formatter → sink.docs/usage/commands.md--limitfiltra e só então corta; para uma única submission, o resultado ainda depende de aquele post ter comentários suficientes que batam no critério.tests/test__cli__commands__comments.py(novo)test_comments_limit_applies_after_filter: crawler fake devolve 4 comentários (1 rejeitado, 3 aprovados),--limit 2e filtrobody ~= keepvalida que o corte respeita o filtro e que o crawler foi chamado comlimit=None.test_comments_user_source_is_unbounded: garante que a busca poru/usernametambém segue sem teto na fonte.tests/test__pipeline__filters.py(novo)Como testar manualmente