Cliente Python (provisa-client)¶
Cliente Python para o Provisa. Fornece quatro interfaces:
| Interface | Caso de uso |
|---|---|
ProvisaClient |
Consultas GraphQL, Arrow Flight, saída DataFrame |
DB-API 2.0 (connect) |
Interface padrão de banco de dados Python (PEP 249) (REQ-268) |
| Dialeto SQLAlchemy | Ferramentas de BI, ORM, read_sql do Pandas (REQ-270) |
| ADBC | Streaming colunar Arrow-nativo via Flight (REQ-271) |
Instalação¶
pip install provisa-client # core (ProvisaClient + DB-API)
pip install "provisa-client[pandas]" # adds pandas
pip install "provisa-client[sqlalchemy]" # adds SQLAlchemy dialect
pip install "provisa-client[adbc]" # adds ADBC over Arrow Flight
ProvisaClient¶
Início Rápido¶
from provisa_client import ProvisaClient
client = ProvisaClient(
"http://localhost:8001",
token="provisa_pat_...", # personal access token, or a provider bearer token
role="analyst",
)
ProvisaClient recebe uma credencial, não um nome de usuário e uma senha: ele não tem nenhuma etapa de login própria. Um token de acesso pessoal é a credencial indicada quando um script precisa rodar sem supervisão — é emitido a partir do perfil do próprio usuário, carrega uma expiração e é revogável sem tocar na conta. (REQ-1263) Um token bearer do provedor funciona de forma idêntica. Qualquer um dos dois vai em token, e o cliente o apresenta tanto no caminho HTTP quanto no do Arrow Flight.
Para trocar uma senha por um token, faça POST em /auth/login e leia access_token:
import httpx
body = httpx.post(
"http://localhost:8001/auth/login",
json={"username": "alice", "password": "secret"},
).json()
client = ProvisaClient("http://localhost:8001", token=body["access_token"])
Os pontos de entrada de DB-API e ADBC fazem essa troca por você — veja abaixo.
Consultas GraphQL¶
# Raw response dict
result = client.query("{ orders { id amount region } }")
# With variables
result = client.query(
"query Q($region: String!) { orders(region: $region) { id amount } }",
variables={"region": "west"},
)
# pandas DataFrame (first root field is flattened)
df = client.query_df("{ orders { id amount region } }")
Assíncrono¶
Arrow Flight (colunar de alta vazão)¶
Use Flight para grandes conjuntos de resultados — os dados fazem streaming como batches de registro Arrow sem materializar no servidor. (REQ-143, REQ-145)
import pyarrow as pa
table: pa.Table = client.flight("{ orders { id amount region } }")
df = client.flight_df("{ orders { id amount region } }")
O Flight se conecta à porta 8815 por padrão. (REQ-143) Sobreponha com flight_port=:
Exploração de Catálogo¶
Referência de Conexão¶
| Parâmetro | Padrão | Descrição |
|---|---|---|
url |
http://localhost:8001 |
URL base do servidor Provisa |
token |
None |
Credencial bearer — um token do provedor ou um token de acesso pessoal; omita para autenticação por senha (REQ-606, REQ-1263) |
role |
"admin" |
Função enviada com cada requisição (REQ-273) |
flight_port |
8815 |
Porta gRPC do Arrow Flight (REQ-143) |
Tratamento de Erros¶
query() lança httpx.HTTPStatusError em erros HTTP. (REQ-607)
query_df() lança RuntimeError se a resposta contiver erros GraphQL. (REQ-607)
DB-API 2.0¶
Interface padrão PEP 249. (REQ-268) Funciona com qualquer ferramenta que aceite uma conexão DB-API.
from provisa_client import connect
conn = connect(
"http://localhost:8001",
username="alice",
password="secret",
role="analyst", # optional; omit to run as the role the login returns
)
connect envia o nome de usuário e a senha por POST para /auth/login e guarda o access_token que recebe, de modo que a conexão carrega uma credencial de verdade em vez de um nome. role solicita uma função e o servidor a atende apenas se a identidade a tiver atribuída (REQ-273); se omitida, a conexão roda com a função que o login resolveu.
Executando consultas¶
O cursor aceita GraphQL ou SQL — detectado automaticamente. (REQ-268, REQ-274)
cur = conn.cursor()
# GraphQL
cur.execute("{ orders { id amount region } }")
rows = cur.fetchall() # list of tuples
one = cur.fetchone() # single tuple or None
many = cur.fetchmany(size=50) # up to N tuples
# SQL (routed through Stage 2 governance)
cur.execute("SELECT id, amount FROM orders WHERE region = 'west'")
rows = cur.fetchall()
Metadados de coluna¶
cur.execute("{ orders { id amount } }")
print(cur.description)
# [('id', None, ...), ('amount', None, ...)]
print(cur.rowcount)
Parâmetros nomeados¶
Gerenciadores de contexto¶
with connect("http://localhost:8001", username="alice", password="secret") as conn:
with conn.cursor() as cur:
cur.execute("{ orders { id amount } }")
print(cur.fetchall())
Dialeto SQLAlchemy¶
Esquema de URL: provisa+http:// ou provisa+https:// (REQ-270)
from sqlalchemy import create_engine, text
engine = create_engine("provisa+http://alice:secret@localhost:8001")
with engine.connect() as conn:
result = conn.execute(text("{ orders { id amount region } }"))
for row in result:
print(row)
Com pandas¶
Parâmetros de URL¶
| Parâmetro | Descrição | Padrão |
|---|---|---|
role |
Função a solicitar; validada no servidor (REQ-273) | a função que o login resolve |
Introspecção de esquema¶
O dialeto implementa get_table_names(), get_columns(), e has_table() — ferramentas de catálogo (DBeaver, SQLAlchemy automap) conseguem inspecionar o esquema. (REQ-363, REQ-270)
ADBC¶
Arrow Database Connectivity apoiado por Arrow Flight. (REQ-271) Retorna pyarrow.Table diretamente — sem desserialização JSON. (REQ-271)
from provisa_client.adbc import adbc_connect
conn = adbc_connect(
"http://localhost:8001",
user="alice",
password="secret",
role="analyst", # optional; server validates the requested role
port=8815, # Arrow Flight port (REQ-711)
)
adbc_connect faz login por HTTP primeiro e coloca o token resultante em cada ticket do Flight, de modo que o servidor Flight autentica a conexão do mesmo jeito que a superfície REST. (REQ-1263) O argumento role é uma solicitação, validada no servidor contra as atribuições da identidade — ele nunca se torna a identidade. (REQ-273)
Buscar como Arrow Table¶
with conn.cursor() as cur:
cur.execute("{ orders { id amount region } }")
table = cur.fetch_arrow_table() # pyarrow.Table
df = table.to_pandas()
Buscar como tuplas¶
with conn.cursor() as cur:
cur.execute("{ orders { id amount } }")
rows = cur.fetchall() # list of tuples
one = cur.fetchone() # single tuple or None
Metadados de coluna¶
cur.execute("{ orders { id amount } }")
print(cur.description)
# [('id', None, ...), ('amount', None, ...)]
Gerenciador de contexto¶
with adbc_connect("http://localhost:8001", user="alice", password="secret") as conn:
with conn.cursor() as cur:
cur.execute("{ orders { id amount } }")
table = cur.fetch_arrow_table()
O ADBC se conecta ao servidor Flight na porta 8815 por padrão. (REQ-143) Passe port= para alcançar um servidor Flight vinculado a uma porta não padrão. (REQ-711)