Usando a CLI¶
O que você vai aprender
A CLI tempestweb é o seu painel de controle: cria o projeto, roda ele
localmente nos três modos, builda o artefato e o empacota para produção.
Esta página segue o caminho de um app do zero ao deploy, um comando por vez. 🚀
A CLI vem no extra cli:
pip install "tempestweb[cli]" # ou: uv add "tempestweb[cli]"
pip install "tempestweb[server,cli]" # se você também vai rodar o Modo B
Confirme que instalou:
-V é atalho de --version
tempestweb -V imprime a mesma coisa. E tempestweb --help (ou -h) mostra
todos os subcomandos, com um bloco de exemplos no rodapé.
O modelo mental¶
Antes dos comandos, três ideias que valem para todos eles:
- Um modo, escolhido na CLI — nunca no app. O seu
app.pynunca nomeia um modo. Você escolhe--mode wasm(A),--mode server(B) ou--mode transpile(C) na hora de rodar/buildar. O mesmo código roda nos três. Omitir--modeusa o[dev].modedotempestweb.toml(e, sem esse campo,wasm); um--modeexplícito sobrescreve. Vale paradev,builderun. --pathrecebe o diretório do projeto, nunca um arquivo.py. O default é o diretório atual (.). A CLI descobre o entrypoint sozinha.- O entrypoint default é
app.py. É um módulo Python que expõe duas callables —make_state()(o estado inicial) eview(app)(a árvore de widgets). O nome é configurável notempestweb.toml.
O tempestweb.toml
Todo campo tem default, então o arquivo é opcional. Ele serve para mudar um default:
[project]
name = "todolist"
entrypoint = "app.py" # troque para "main.py", "src/app.py", etc.
[dev]
mode = "wasm" # modo usado quando você omite --mode (dev/build/run)
port = 8000
Sem o módulo expor make_state() e view(app), o build falha com
must define a callable make_state/view.
1. Criar o projeto — new¶
Não escreva os arquivos na mão. O new monta um projeto rodável:
Isso escreve quatro arquivos — um counter funcional, a config, um README e um
.gitignore:
todolist/
├── app.py # a UI: make_state() + view() — o counter inicial
├── tempestweb.toml # config do projeto
├── README.md
└── .gitignore
Opções úteis:
| Flag | Para quê |
|---|---|
--into <dir> |
Cria o projeto dentro de outro diretório (default: cwd). |
--template <default\|pwa> |
default é o counter de dois modos; pwa é um PWA instalável/offline (Modo C). |
--force |
Escreve num diretório existente não vazio. |
--no-verify |
Pula a renderização de prova (mais rápido, menos garantia). |
Scaffold na pasta atual — tempestweb new .
Passe . como nome para scaffoldar dentro do diretório atual, em vez de
criar um subdiretório:
O projeto é nomeado pelo basename do diretório (aqui, meuapp). O new .
tolera arquivos pré-existentes não conflitantes (um .git/, um LICENSE,
etc.), mas recusa sobrescrever os arquivos de scaffold (app.py,
tempestweb.toml, README.md, .gitignore) se algum já existir — passe
--force para sobrescrever mesmo assim.
Template PWA
tempestweb new meuapp --template pwa já vem com manifest, service worker e o
esqueleto de offline/WebPush. O new te diz o próximo passo no final —
para o template PWA ele sugere tempestweb dev --mode transpile.
2. Rodar localmente — dev¶
O dev é o comando único de desenvolvimento — ele bulda e serve o app
localmente com hot-reload. E, desde a v0.52, serve os três modos:
Python roda no browser via Pyodide. Salvou um arquivo? O browser recarrega sozinho (livereload). Ótimo para iterar na UI sem servidor.
Sobe o host FastAPI real sob uvicorn (Python no servidor + cliente fino por
WebSocket). A cada edição a CLI faz rebuild e reinicia o servidor
automaticamente. Precisa do extra server.
Todos abrem em http://127.0.0.1:8000 por padrão. Sobrescreva com --host e
--port:
dev para desenvolver, run para servir
O dev serve todos os modos com watch + reload — é o comando do dia a dia
enquanto você escreve código. O run builda uma vez e serve o app como
está, sem watcher (produção-like) — é o que o Dockerfile gerado pelo
tempestweb deploy executa. Mesmos modos, mesmas flags; só o watcher muda.
dev nunca serve cache velho
Nos modos estáticos (wasm/transpile) o app é um PWA com service worker
cache-first. Em dev isso atrapalharia — você veria o bundle antigo. Por
isso o tempestweb dev não registra o service worker: ele injeta um
kill-switch que desregistra qualquer worker existente e limpa os caches, de
modo que todo reload serve o build recém-gerado. O run/build/deploy
mantêm o service worker cacheador — em produção o cache é o que deixa o app
rápido no segundo load.
Feito quando
O terminal mostra tempestweb dev: serving <app> at http://127.0.0.1:8000
(mode=wasm); edit a file to reload. Ctrl-C to stop., você abre no browser e vê
o counter. Editar app.py recarrega a página (A/C) ou reinicia o servidor (B).
3. Gerar o artefato — build¶
Quando for publicar, o build gera o artefato do modo escolhido em
dist/<modo>/:
tempestweb build --mode wasm # bundle estático Pyodide → dist/wasm/
tempestweb build --mode transpile # JS nativo, servível por CDN → dist/transpile/
tempestweb build --mode server # app FastAPI → dist/server/
Opções:
| Flag | Para quê |
|---|---|
--path <dir> |
Diretório do projeto (default: cwd). |
--out <dir> |
Diretório de saída (default: <projeto>/dist/<modo>). |
--offline |
(só wasm) vendora o runtime Pyodide + wheels para bootar offline. |
Modo A offline
tempestweb build --mode wasm --offline baixa o Pyodide e as wheels no momento
do build, então o bundle final não depende de CDN em runtime — abre até sem
rede.
4. Empacotar para produção — deploy¶
Os modos estáticos (A/C) são só arquivos: publique dist/ em qualquer CDN. O
Modo B precisa de reverse-proxy e TLS — o deploy escreve esses arquivos pra
você (nginx + Dockerfile + docker-compose + um DEPLOY.md):
Opções:
| Flag | Para quê |
|---|---|
--out <dir> |
Onde escrever os arquivos (default: <projeto>/deploy). |
--server-name <host> |
server_name do nginx (seu domínio; default: _). |
--tls |
Emite bloco TLS (443) + redirect HTTP→HTTPS. |
--replicas <n> |
Número de réplicas do app no upstream do nginx (default: 1). |
--no-sticky |
Remove ip_hash (use com um RedisSessionRouter para escalar SSE). |
--force |
Sobrescreve arquivos de deploy existentes. |
Depois é só seguir o DEPLOY.md gerado. Veja o guia completo em
Deploy em produção.
5. Comandos utilitários¶
vapid — chaves para WebPush¶
O WebPush (Trilho P) precisa de um par de chaves VAPID. Gere um:
tempestweb vapid # imprime public_key / private_key legível
tempestweb vapid --env # imprime linhas VAPID_PUBLIC_KEY= / VAPID_PRIVATE_KEY=
Guarde a chave privada
Exporte a privada como VAPID_PRIVATE_KEY (secret do servidor) e compartilhe
só a pública com o cliente. Veja PWA e offline.
sync — dependências puras no Modo A¶
O Modo A roda no Pyodide, que não tem seu ambiente completo. O sync preenche
[wasm].modules no tempestweb.toml com as dependências puro-Python instaladas
no projeto, para que sejam empacotadas no bundle:
tempestweb sync # escreve tempestweb.toml
tempestweb sync --dry-run # só mostra o que seria adicionado
gen api — cliente tipado a partir do OpenAPI¶
Gera models @dataclass + classes de serviço a partir de um spec OpenAPI 3.x
(o /openapi.json de um backend FastAPI, ou um arquivo):
tempestweb gen api http://127.0.0.1:8000/openapi.json --out api
tempestweb gen api ./openapi.json --out api # a partir de um arquivo
Um pacote por tag, cada um com schemas.py + service.py. Detalhes em
Gerar um cliente do OpenAPI.
6. Qualidade de código¶
Você escreve Python tipado — então a CLI também cuida da qualidade desse
Python. Sete comandos rodam o ruff, o mypy e o pytest no seu projeto,
com uma camada de opinião por cima que você pode afrouxar ou apertar. 🚀
Rode do diretório do projeto
Como tudo na CLI, esses comandos recebem o diretório do projeto via
--path (default: o diretório atual). Rode-os da raiz do seu app — eles
escopam o ruff/mypy/pytest a esse caminho.
Precisa do ruff, mypy e pytest instalados
Os comandos fazem shell-out para essas ferramentas — não trazem uma cópia
própria. A CLI prefere o binário no seu PATH; se não achar, tenta
uv run <tool>. Instale-os no seu ambiente (pip install ruff mypy pytest)
ou tenha o uv no PATH.
Comece pelo gate — check¶
O check é o gate completo: roda as quatro etapas em sequência e para no
primeiro erro, tudo escopado ao --path do projeto.
A ordem é sempre a mesma:
1. ruff check # lint
2. ruff format --check # formatação (read-only)
3. mypy # tipagem
4. pytest # testes
Antes de cada etapa o check ecoa o comando (em stderr) e mostra a saída da
própria ferramenta. Num projeto limpo:
$ ruff check .
All checks passed!
$ ruff format --check .
1 file already formatted
$ mypy .
Success: no issues found in 1 source file
$ pytest .
no tests ran in 0.01s
Quando uma etapa falha, o check para ali (retorna o código de saída dela) —
as seguintes não rodam:
$ ruff check .
All checks passed!
$ ruff format --check .
Would reformat: app.py
1 file would be reformatted
Sem testes ainda? Tudo bem
O pytest retorna "nenhum teste coletado" (exit 5) num projeto recém-criado;
o check trata isso como sucesso, então o gate passa antes de você
escrever o primeiro teste.
É o mesmo gate do CI
Rode tempestweb check antes de cada commit. É a versão de um comando do que a
esteira de CI faz — se passa localmente, passa lá.
Os comandos individuais¶
Quando você quer só uma etapa (ou quer corrigir, não só apontar), use os comandos individuais:
| Comando | Roda | Escreve arquivos? |
|---|---|---|
tempestweb lint |
ruff check |
Não (só reporta) |
tempestweb fix |
ruff check --fix + ruff format |
Sim |
tempestweb format |
ruff format |
Sim |
tempestweb fmt-check |
ruff format --check |
Não (read-only) |
tempestweb type |
mypy |
Não |
tempestweb test |
pytest |
Não |
O fluxo mais comum: aponte com lint, corrija com fix.
tempestweb lint # o que está errado?
tempestweb fix # conserta o que dá pra consertar automaticamente
O fix aplica os autofixes seguros do ruff e reformata. Para também aplicar
os autofixes marcados como unsafe pelo ruff, passe --unsafe:
O test filtra os testes pelo --path, e trata o exit code 5 do pytest
("nenhum teste coletado") como sucesso — um projeto sem testes ainda não é
um projeto quebrado:
tempestweb test # roda a suíte do projeto
tempestweb test --path ./meuapp # escopado a um subdiretório
Exit 5 do pytest = sucesso
Se o pytest não encontra nenhum teste (exit 5), o tempestweb test (e o
tempestweb check) tratam isso como ✅. Assim o gate não quebra num app que
ainda não escreveu testes.
Os três níveis de strictness¶
lint, fix, type e check respeitam um modelo de strictness — uma camada
de opinião por cima da sua config de ruff/mypy. Ela só adiciona regras;
nunca afrouxa o que você já configurou.
| Nível | ruff adiciona (--extend-select) |
mypy adiciona |
|---|---|---|
lenient |
nenhuma regra ANN extra | nenhum flag extra |
standard (default) |
ANN001, ANN201, ANN202, ANN205, ANN206 |
--ignore-missing-imports |
strict |
ANN001, ANN002, ANN003, ANN201, ANN202, ANN204, ANN205, ANN206 |
--strict |
Em português: o standard exige tipos nos parâmetros e nos retornos das funções
públicas; o strict cobra também *args/**kwargs e métodos dunder, e liga o
mypy --strict; o lenient não adiciona nada — fica só com a sua config.
Any é sempre válido — ANN401 nunca é ligado
Nenhum nível liga a regra ANN401 (proibir typing.Any). Any é uma
anotação legítima — usá-la de propósito é tipar, não deixar de tipar. Você
escolhe Any quando precisa; a CLI nunca vai te punir por isso.
Configurar o nível — tempestweb.toml [quality]¶
O default é standard. Para mudar o nível do projeto inteiro, escreva no
tempestweb.toml:
O tempestweb new já escreve esse bloco no tempestweb.toml scaffoldado, com
"standard".
Para uma invocação, sobrescreva com --strictness sem tocar no arquivo:
tempestweb check --strictness strict # aperta só nessa rodada
tempestweb lint --strictness lenient # afrouxa só nessa rodada
Feito quando
tempestweb check sai com código 0 e cada etapa reporta limpo. Esse é o
mesmo verde que o CI espera — commite com confiança.
Referência de subcomandos¶
Todos os comandos que buldam/servem recebem o diretório do projeto via
--path (default: diretório atual) — nunca um arquivo .py posicional.
| Comando | O que faz | Flags principais |
|---|---|---|
tempestweb new <nome> |
Scaffold de um projeto rodável. | --into, --template <default\|pwa>, --force, --no-verify |
tempestweb dev |
Desenvolve localmente com watch + reload — serve todos os modos. | --mode <wasm\|server\|transpile>, --path, --host, --port |
tempestweb build |
Gera o artefato em dist/<modo>/. |
--mode, --path, --out, --offline |
tempestweb run |
Serve o app como buildado, sem watcher (produção-like). | --mode, --path, --host, --port, --offline |
tempestweb deploy |
Escreve os arquivos de deploy (nginx + Docker + guia). | --out, --server-name, --tls, --replicas, --no-sticky, --force |
tempestweb vapid |
Gera um par de chaves VAPID para WebPush. | --env |
tempestweb sync |
Preenche [wasm].modules com as deps puro-Python. |
--path, --dry-run |
tempestweb gen api <src> |
Gera um cliente tipado (dataclasses + serviços) de um OpenAPI. | --out |
tempestweb lint |
ruff check no projeto. |
--path, --strictness <lenient\|standard\|strict> |
tempestweb fix |
ruff check --fix + ruff format (escreve). |
--path, --strictness, --unsafe |
tempestweb format |
ruff format (escreve). |
--path |
tempestweb fmt-check |
ruff format --check (read-only). |
--path |
tempestweb type |
mypy no projeto. |
--path, --strictness |
tempestweb test |
pytest (exit 5 = sucesso). |
--path |
tempestweb check |
Gate: ruff check → ruff format --check → mypy → pytest. |
--path, --strictness |
tempestweb --version / -V |
Imprime a versão instalada. | — |
Recap¶
newcria;devdesenvolve (os três modos, com watch + reload);runserve como buildado (sem watcher, produção-like);buildgera o artefato;deployempacota o Modo B para produção.- O modo é escolhido na CLI (
--mode), nunca noapp.py. --pathrecebe o diretório; o entrypoint default éapp.py(configurável), e precisa expormake_state()+view(app).checké o gate de qualidade (ruff check→ruff format --check→mypy→pytest, para no primeiro erro);lint/fix/format/fmt-check/type/testsão as etapas individuais. O nível[quality] typing_strictness(lenient/standard/strict, defaultstandard) adiciona regras por cima da sua config, nunca afrouxa — eANN401nunca é ligado (Anyé válido).
Pronto? Vá para o Tutorial e construa o counter. 🚀