falsegreen (Python)¶
O scanner determinístico para Python/pytest. Uma passagem AST sem dependências que valida cada teste contra os códigos de falso-positivo que um parser consegue provar. Achados ALTO bloqueiam o commit, os BAIXO avisam, e um grupo de diagnóstico/acoplamento é opcional.
- Repositório: github.com/vinicq/falsegreen
- Catálogo: códigos Python
Install¶
Use¶
falsegreen path/to/tests # scan
falsegreen --staged # only staged files (pre-commit)
falsegreen --json # machine-readable report
falsegreen --diagnostics # include the opt-in F8 group
falsegreen --config-audit # read pytest/coverage config for project-level false-green
Achados ALTO encerram com código diferente de zero, então a ferramenta entra em CI e pre-commit sem ajuste. O relatório numera cada achado com seu código, julgamento, nível da pirâmide, localização, evidência e uma dica de correção.
First finding¶
Salve um teste que sempre passa:
Rode o scanner sobre ele:
Ele reporta:
test_demo.py:3 [C5] always-true check (assert True / tuple / or True)
level: unit fix: assert the real behaviour, not a constant or tautology
Summary: 1 high, 0 low.
Reading a finding¶
Cada linha carrega os mesmos campos:
test_demo.py:3- o arquivo e a linha que disparou o achado.[C5]- o código do catálogo.C5é a checagem sempre-verdadeira. Cada código está explicado no catálogo Python.level: unit- em qual nível da pirâmide de testes o arquivo está; isso muda o que conta como checagem de verdade.fix:- uma dica de uma linha. Aqui: asserte o comportamento real, não uma constante.
Códigos de saída integram em CI: 0 limpo, 10 só baixa confiança, 20 ao
menos um achado de alta confiança. Bloqueie o build no 20.
Complete usage and configuration¶
O guia inicial acima é o caminho de cinco minutos. Esta seção é a referência completa: cada canal de instalação, cada formato de saída, cada chave de configuração, o contrato de código de saída e a integração com CI. Espelha o que o README do projeto documenta.
Install channels¶
Python 3.8 ou mais novo, sem dependências de runtime de terceiros.
pip install falsegreen # instalação no projeto ou virtualenv
uvx falsegreen tests/ # roda uma vez sem instalar (uv)
pipx run falsegreen tests/ # roda uma vez sem instalar (pipx)
python -m falsegreen ... é equivalente ao comando falsegreen quando o ponto de entrada não
está no PATH.
Invocation¶
falsegreen # escaneia o diretório atual
falsegreen tests/ # escaneia uma pasta ou um arquivo
falsegreen --staged # só os arquivos de teste no stage do git (pre-commit)
falsegreen --summary # uma linha "N escaneados, M sinalizados" no stderr
O scanner lê apenas os arquivos de teste, nunca os importa nem os executa, então um teste quebrado ou hostil não roda através dele. Cada achado carrega seu nível na pirâmide (unit / integration / e2e, lido dos imports do arquivo) e uma dica de correção de uma linha; o resumo em texto separa os achados por nível e lista as correções mais comuns.
Output formats¶
--format text|json|sarif|junit seleciona o formato do relatório (padrão text). --json
continua como alias de --format json.
falsegreen tests/ --json # JSON legível por máquina
falsegreen tests/ --format sarif # SARIF 2.1.0
falsegreen tests/ --format junit # JUnit XML
falsegreen tests/ --output report.sarif # grava em arquivo
falsegreen tests/ --output .falsegreen/ # grava report.<ext> num diretório
jsoncarrega o envelope completo: tool, version, judgments e a lista de achados.sarifemite SARIF 2.1.0, mapeando ALTO paraerrore BAIXO parawarning, para o GitHub code scanning e anotações inline no pull request.junitemite JUnit XML, onde achados ALTO viram<failure>para que um reporter de CI os mostre como suíte falhando.
--output aceita arquivo ou diretório: um caminho sem extensão ou com barra final (.falsegreen/)
recebe report.<ext> no formato escolhido. Relatórios são artefatos de execução, então mantenha o
diretório de saída no gitignore.
Configuration¶
Desabilitar códigos (CLI). --disable C6,C2b desliga códigos específicos numa execução.
Supressão inline. Um comentário na linha do achado silencia um achado justificado sem desligar o código na suíte inteira:
assert user.id == user.id # falsegreen: ignore[C7] silencia só o C7 nesta linha
assert x # falsegreen: ignore silencia todos os códigos nesta linha
Só o token exato falsegreen: suprime; um # ignore puro não.
Arquivo de config do projeto. [tool.falsegreen] no pyproject.toml, ou um .falsegreen.toml
plano na raiz do repo (.falsegreen.toml vence se ambos existirem). Aponte para um arquivo
específico com --config PATH.
[tool.falsegreen]
disable = ["C13b"] # desliga estes códigos em todo lugar
exclude = ["tests/legacy/*"] # ignora arquivos que casam com estes globs
long_test_threshold = 30 # limite de linhas para M2 (padrão: 50)
inline_setup_threshold = 3 # limite de statements para D5 (padrão: 5)
[tool.falsegreen.severity]
C8 = "high" # promove: agora bloqueia o commit (exit 20)
C6 = "off" # equivale a adicionar C6 ao disable
C22 = "low" # habilita a checagem async-never-awaits
D1 = "info" # habilita Assertion Roulette (diagnóstico, nunca bloqueia)
M2 = "info" # habilita Long Test Method (diagnóstico)
Valores de severity: high, low, info ou off. Achados info aparecem nas seções
DIAGNOSTIC / COUPLING e não afetam o código de saída, o grupo opcional F8. As chaves
long_test_threshold e inline_setup_threshold ficam direto sob [tool.falsegreen], não dentro
de [severity]. Precedência, da maior para a menor: --disable na CLI, inline
# falsegreen: ignore, o arquivo de config, o padrão embutido.
Auditoria de config. --config-audit é um modo separado. Em vez de escanear arquivos de
teste, lê a config de pytest e coverage do projeto (pyproject.toml, pytest.ini, tox.ini,
setup.cfg) e reporta as formas de nível de projeto pelas quais uma suíte fica verde por
configuração:
PL1-python -O/PYTHONOPTIMIZEremove todoassertem runtime.PL2-filterwarningsnão promove warnings a erros.PL7- sem trava de coverage (--cov-fail-underausente).PL8-addoptsinterrompe a execução cedo com-x/--maxfail, mascarando a contagem.
Baseline (adotar num repo legado). Registre os achados que você já tem e falhe só nos novos:
falsegreen --write-baseline tests/ # grava .falsegreen-baseline.json, exit 0
falsegreen --baseline tests/ # suprime os achados registrados, falha nos novos
Um achado é fingerprintado por caminho relativo, código, detalhe e a linha-fonte normalizada
(não o número da linha), então adicionar código antes não redispara um achado já no baseline.
Comite .falsegreen-baseline.json e a catraca só aperta.
Exit codes¶
| Código | Significado |
|---|---|
0 |
limpo, nenhum achado que afete o gate |
10 |
só achados de baixa confiança |
20 |
ao menos um achado de alta confiança |
Bloqueie o build no 20. O hook de pre-commit respeita o mesmo contrato.
CI integration¶
GitHub Actions. Um job que falha no exit 20:
name: falsegreen
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.x" }
- run: pip install falsegreen
- run: falsegreen tests/ # exit 20 falha o job
Upload de SARIF para o GitHub code scanning. Emita SARIF e passe para a action do CodeQL para que os achados apareçam inline no pull request:
- run: falsegreen tests/ --format sarif --output falsegreen.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: falsegreen.sarif
Hook de pre-commit. Adicione ao .pre-commit-config.yaml:
Depois pre-commit install. A entrada do hook é falsegreen --staged com pass_filenames: false,
então ele lê sozinho os arquivos de teste em stage; não adicione argumentos de arquivo nem
reative pass_filenames, ou alguns arquivos escaneiam duas vezes. Fixe uma tag (nunca um branch)
para que execuções locais e CI usem o mesmo scanner; pre-commit autoupdate reescreve o rev.
Pule uma vez com git commit --no-verify, ou defina FALSEGREEN_BLOCK=0 para o hook só avisar.
Para rodar no push em vez do commit, defina stages: [pre-push] sob o hook. Um git hook cru, sem
o framework:
python -m falsegreen.hook_install --repo . # instala
python -m falsegreen.hook_install --uninstall # remove
Para os casos semânticos que um parser não alcança, combine o scanner com a passagem LLM falsegreen-skill.
What it covers¶
O scanner mais completo da família: é a referência que os outros espelham. O detalhe completo por código está no catálogo Python.
| Grupo | Códigos | Efeito |
|---|---|---|
| Falso-positivo (F1-F6) | ~45 códigos C* ativos + CC |
ALTO bloqueia, BAIXO avisa |
| Diagnóstico / acoplamento (F8) | D1, D3, D4, D5, D6, M2 |
opcional, nunca bloqueia |
Projeto / CI (F5, --config-audit) |
PL2 (filterwarnings não está em error), PL7 (sem --cov-fail-under), PL8 (-x/--maxfail mascara a contagem) |
lê a config, reporta |
What it does not cover, and why¶
Fora de escopo (o eixo errado)¶
Fragilidade/falso-vermelho, higiene, lentidão, design, nomenclatura, duplicação, runtime/cultura não são false-green. Veja cobertura vs a literatura para o limite completo.
Códigos deixados de fora de propósito¶
Estes foram avaliados contra o catálogo consolidado e deixados de fora, cada um por um motivo. Deixá-los de fora é a política de precisão em primeiro lugar: um falso positivo é pior que um escape.
| Código | O que sinalizaria | Por que não |
|---|---|---|
| C40 | assert mock.attr sem spec (sempre verdadeiro) |
sem análise de spec a taxa de falso-positivo é alta; o conceito vive na skill (F7) |
| C46 | rede/DB real sem dublê (requests, socket) |
legítimo num teste de integração; sinalizá-lo exige saber o nível, então roteia para a skill / --config-audit |
| C47 | asserção depende da ordenação de dict/set | falso positivo alto (a maioria das coleções é determinística no uso); em vez disso, uma nota na skill |
Reservados para a passagem semântica (F7)¶
Mockar a unidade sob teste (caso 10), asserir o valor passado ao mock (caso 11), reimplementar a
fórmula de produção (caso 12), um valor esperado que contradiz a intenção (caso 18), estado
compartilhado emprestado (caso 15). Nenhum AST prova intenção ou fluxo interprocedural. Estes
vivem na falsegreen-skill. C14 (snapshot da própria saída do código) é o único canto
codificável.
Precisa de runtime (não prometido estaticamente)¶
python -O removendo assert, um erro de coleta reportado como "0 tests passed", um passo de CI
que roda um subconjunto e reporta verde (PL1/PL4/PL6). O PL1 agora tem uma fatia
detectável por configuração: o --config-audit sinaliza python -O/-OO ou PYTHONOPTIMIZE=1
em tox.ini/addopts do pytest como aviso de nível de projeto. O resto só aparece quando a suíte
roda; são documentados, não reivindicados. O caminho honesto é teste de mutação (mutmut,
cosmic-ray), que fica fora de banda.
Veja escopo e honestidade para o limite entre as camadas.