Skip to main content

shade-sdk

O runtime e o framework dos robôs da ShadeOne. O robô gerado pela plataforma importa este pacote, na versão fixada pelo requirements.lock da imagem do sandbox, e o robô escrito à mão importa o mesmo pacote: um runtime só, para os dois. O import é shade_sdk.

from decimal import Decimal

from pydantic import BaseModel, Field
from shade_sdk import Alvo, Item, Pagina, Retentativa, Robo


class Pedido(BaseModel):
    cliente: str = Field(min_length=1)
    valor: Decimal = Field(gt=0)


class TelaDePedido(Pagina):
    url = "/pedidos/novo"  # relativa ao [config] base_url
    pronta = Alvo(papel="heading", nome="Novo pedido")  # abrir() espera por ela
    cliente = Alvo(rotulo="Cliente")
    salvar = Alvo(papel="button", nome="Salvar")


class CadastroDePedido(Robo):
    chave = ("cliente", "pedido")  # a chave de negócio do item
    modelo = Pedido  # o item inválido fica retido antes de abrir o navegador
    retentativas = Retentativa(sistema=3)  # erro de sistema repete; negócio nunca

    def entrar(self, pagina) -> None:  # opcional: ao abrir a sessão e se ela cair
        ...

    def processar(self, item: Item[Pedido], pagina) -> None:
        tela = TelaDePedido(pagina)
        with item.etapa("preencher"):  # tempo, resultado e erro no relatório
            tela.abrir()
            tela.cliente.fill(item.dados.cliente)  # item.dados é o Pedido validado
        with item.efetivar("salvar pedido"):  # o ensaio para aqui; o real registra antes
            tela.salvar.click()

main.py na raiz do projeto: CadastroDePedido.main(). Sem flag, roda em modo real, como o robô gerado; --rehearsal é o ensaio. A entrada é --input <arquivo>, ou data/input.json (onde a plataforma a deixa), ou [itens] exemplo do shade.toml: JSON, CSV (o delimitador é detectado) ou XLSX ([itens] aba escolhe a aba). Planilha chega como texto, e o modelo converte.

Com [itens] fonte = "fila", o run real pega cada item da fila hospedada pelo executor da plataforma, um por vez, e o fecha com o desfecho (a chave do item é o id que o servidor entregou). A queda no meio de um item o devolve pelo efeito: sem efeito, volta para a fila; com o efeito em voo, fica retido para conferência. --input e o ensaio leem o arquivo (o ensaio nunca pega item da fila do cliente), e o run real sem o executor e sem --input para com ErroDeContrato.

  • Robo (uma lista de itens) e Tarefa (uma execução única, executar(item, pagina)).
  • Item: item.dados (a instância do modelo, ou a linha), item["campo"] (a linha como veio), item.chave, with item.efetivar(nome), with item.etapa(nome), item.saida(**campos), item.reter(motivo), item.log(texto).
  • item.pular(motivo) dá o item por feito (Pulada: <motivo>; a retomada não o refaz, nem depois de efetivar(); no ensaio, nada grava) e item.encerrar_lote(motivo) termina o item Interrompido: <motivo> e o lote ali, sem falhar: encerrar roda e o processo sai com 0. Os dois não passam pela retentativa nem pelo ao_falhar ("parar" é a falha: sai com 1, sem encerrar).
  • modelo = <pydantic.BaseModel>: cada item é validado antes de abrir o navegador; o inválido fica retido com o campo e a regra (sem o valor), e sem item válido o navegador nem abre.
  • Pagina (url, pronta, abrir(), esperar_pronta()) e Alvo(papel=, nome=), Alvo(rotulo=), Alvo(test_id=), Alvo(texto=) e, por último, Alvo(css=): o Locator do Playwright (tela.salvar.click(); tela.salvar.locator é o cru). Quando a ação falha, ErroDoAlvo diz o alvo, a URL (sem a query), se não achou nada, se achou mais de um (e quais) ou em que elemento caiu, e os candidatos parecidos. diagnosticar(pagina, alvo) dá o mesmo texto sem agir.
  • shade_sdk.entradas.ler_entrada(caminho, aba=None): os itens de um JSON, CSV ou XLSX. A linha sem valor nenhum (todo valor vazio ou só espaço; 0 e false são valor) não é item em formato nenhum. A entrada que não deu item é um lote vazio: o navegador não abre, entrar e encerrar não rodam, e o run sai com 0.
  • roda_sem_dados = True: o robô que não lê colunas do item. Toda linha da entrada é item e, sem entrada ou sem linhas, ele roda uma vez com item.dados == {}. Não combina com chave, modelo, listar_itens, a fila nem a Tarefa (erro de contrato).
  • Retentativa(sistema=N, espera=Exponencial(base, teto), sessao_perdida="reentrar" | "falhar"): a política por classe de falha. Negócio nunca repete, e nada repete depois do __enter__ de efetivar().
  • ao_falhar = "seguir" | "parar" | "pausar" | "pausar_run": o que o lote faz com o item que falhou depois da Retentativa. "seguir" (o padrão) passa ao próximo; "parar" termina o lote e sai com 1; "pausar" espera RECOVERY_PAUSE_S segundos por uma pessoa e segue; "pausar_run" pausa o run para uma decisão humana na plataforma e sai com 0. Na fila, as duas que saem fecham o item com a falha antes.
  • Os ganchos do lote: ao_terminar_item(item, desfecho) roda uma vez por item, depois que o desfecho está gravado (desfecho.estado, status, efetivou), e é onde ficam a soma entre itens, a marca na planilha de origem e o aviso de falha (a exceção dele só vai ao log); encerrar(pagina) roda uma vez depois do último item, com o navegador aberto, e não roda quando o lote parou. Em entrar e encerrar, with self.efetivar(nome) é o ponto sem volta do lote: no ensaio o bloco não roda e o gancho termina ali; no real roda sem registro.
  • listar_itens(pagina): a fonte dos itens pelo código, para a lista que só existe depois do login (uma tabela na tela, a caixa de e-mail, uma API). O padrão (None) é o arquivo ou a fila. Sobrescrito, o laço o chama uma vez, depois do entrar, no ensaio e no real, e o que ele devolve (uma lista de dicts, vazia se não há o que fazer) é o lote: cada item passa pelo modelo, pela chave e pelo ledger como os do arquivo, e a retomada pula os já feitos. O --input é ignorado, com aviso, e [itens] fonte = "fila" junto com ele é erro de contrato. Marcar o e-mail como lido, por exemplo, é no ao_terminar_item.
  • mascarar_na_saida = ("email",) e omitir_na_saida = ("cpf",): as colunas com dado pessoal da planilha de saída (data/output.xlsx, ou o .csv sem openpyxl). A mascarada sai reconhecível sem expor o dado (joao@x.com vira j***@x.com; documento e telefone, *** e os 2 últimos dígitos; o resto, a inicial e ***); a omitida não sai da planilha nem da saída do item na plataforma. O valor em memória segue real: a máscara é da gravação. Sem as duas, a planilha sai como veio. A mesma coluna nas duas, ou um nome que não é texto, é erro de contrato; o nome é exato, e uma coluna declarada que não aparece na saída ganha um aviso no log.
  • Antes de abrir o navegador, toda credencial de [credenciais] nomes precisa de valor (no .env local ou no cofre do processo). Se alguma falta, o run para com ErroDeContrato (saída 2) e a mensagem lista os nomes, nunca os valores. Vale no ensaio também.
  • sessao_salva = "sessao.json": o navegador nasce do estado salvo (cookies e localStorage), self.sessao_reaproveitada diz ao entrar que pode conferir a sessão e pular o login, e cada entrar que termina bem regrava o arquivo (permissão 0600). O arquivo é segredo: ponha-o no .gitignore (o shade build recusa o que o .gitignore não ignora); ele nunca vai em anexo.
  • O item que falha deixa a tela e o trecho do trace do Playwright em evidencias/<sha256 da chave>/, com teto por item e por run e os dados do item mascarados (SHADE_MASCARAR_PII=0 desliga); o que passa não guarda nada. A sessão (os cabeçalhos Cookie, Set-Cookie, Authorization e afins, os cookies e o localStorage) sai do trace sempre.
  • shade_sdk.http: http.get a qualquer momento; http.post, put, patch e delete só dentro de efetivar() (fora, ErroDeContrato; no ensaio, nada sai), com Idempotency-Key derivada da chave do item, do nome da efetivação e da ordem da escrita no bloco (num Tarefa, com idempotencia=); o item sem chave de negócio, e o self.efetivar do entrar e do encerrar, escrevem sem o cabeçalho, com um aviso por execução (ou com a chave derivada de idempotencia=). Host fora de [hosts] permitidos é recusado antes de qualquer byte, timeout sempre finito, e só repete quando a rede recusou antes do envio. http.NadaSaiu (certamente sem efeito: desfazer_se=(http.NadaSaiu,) devolve o item à fila) e http.ResultadoIncerto (pode ter gravado: o item fica retido). É fidelidade, não segurança: na plataforma, quem barra o host é o proxy da allowlist.
  • self.segredo(nome): só a credencial declarada em [credenciais] nomes; self.config: a tabela [config].
  • Erros: ErroDeNegocio (não repete), SessaoPerdida (entra de novo e refaz), ErroDeContrato, e os sinais ParadaDoEnsaio, ItemRetido, ItemPulado e LoteEncerrado (BaseException: um except Exception não os engole).
  • shade_sdk.plugins: o contrato dos pacotes shade-plugins-<nome>. Plugin(nome, versao, shade_sdk=..., dono=..., hosts=[...], credenciais=[...]) declara o plugin, que se registra no entry point shade_sdk.plugins; @plugin.operacao (sem efeito) e @plugin.efeito (só dentro de efetivar(), fora dele ErroDeContrato; no ensaio nem roda). A operação recebe a Chamada primeiro: chamada.timeout (toda chamada aceita timeout=, sempre finito), chamada.idempotencia (derivada da chave do item, do nome da efetivação, do plugin, da operação e da ordem no bloco: a mesma em toda execução do item), chamada.segredo(nome) (só credencial declarada pelo plugin e pelo projeto) e chamada.destino(host) (para o protocolo que não é o shade_sdk.http). Dentro da operação, o shade_sdk.http recusa o host que o plugin não declara. Cada chamada vira um evento no log do run: plugin, operação, hosts, duração e status, sem argumento, corpo nem mensagem de erro.
  • shade_sdk.testing.plugins.ContratoDePlugin: a suíte de contrato que todo plugin passa (declaração, efeito fora de efetivar(), egresso só para os hosts declarados, timeout, credencial só por segredo(), idempotência, um evento por chamada sem corpo nem credencial e, com o robô em subprocesso, o ensaio sem efeito e o kill no meio do efeito sem duplicata). dentro_de_efetivar(chave) roda uma operação com efeito num teste de unidade.
  • shade_sdk.testing: ExecutorLocal (o lado do executor do protocolo, com o robô em subprocesso, e ex.fila, a fila hospedada do lado do servidor) e pagina_local (um site local que conta os efeitos, do formulário e da API JSON, com a Idempotency-Key de cada chamada).

O runtime:

  • shade_sdk.runtime.laco: o laço por item (ledger, retomada, política de retentativa, efetivar, etapas, resumo).
  • shade_sdk.runtime.evidencia: o trace por item, a tela, a máscara e os tetos.
  • shade_sdk.runtime.ledger: identidade do item por chave de negócio, o incerto gravado antes de efetivar e o canal do registro com o executor.
  • shade_sdk.runtime.fila: o pegar e o fechar da fila hospedada, pelo mesmo canal.
  • shade_sdk.runtime.recovery: classificação da falha, sessão perdida e modal inesperado.
  • shade_sdk.runtime.saida_protegida: só o robô escreve um marcador que a plataforma lê; o texto de uma página num log, print ou traceback sai com o marcador desarmado.
  • shade_sdk.runtime.report: relatório HTML da execução e o histórico.
  • shade_sdk.runtime.config: Settings e a carga do .env.
  • shade_sdk.runtime.output: a planilha de saída, com ou sem mascaramento de PII.
  • shade_sdk.runtime.protocolo: o número e o formato do contrato robô ↔ executor, inclusive a entrada (main.py, argv e ambiente).

Testes do pacote: cd sdk && uv run pytest (Chromium do Playwright instalado).

Licença: Apache-2.0.

Metadata

Release files for shade-sdk 0.22.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for shade-sdk 0.22.0
File Interpreter ABI Platform
shade_sdk-0.22.0-py3-none-any.whl Python 3 none any Details

Release files / shade_sdk-0.22.0-py3-none-any.whl

Download URL shade_sdk-0.22.0-py3-none-any.whl
Size 152.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1af43608ec733b1f13137490ca14d2581e0e3863de7fb5d6c3df9de40f2b15af
BLAKE2b-256 checksum
How to use checksums
2b61e3cc0936932a9f368557bd7eee668674e5e7acb27e2c4d67c9046ff82255
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Security for shade-sdk 0.22.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page