18 · Módulos
Python com extern
Funções Python tipadas, pacotes nativos e chamadas async com CPython.
Nesta página
Importe o provider Python e declare a função com tipos Zolo. O corpo usa Python, incluindo indentação, comentários e imports locais. A chamada segue a sintaxe normal de uma função Zolo. O CPython executa dentro do processo.
Use um alias importado, agrupe funções pequenas e capture um valor Zolo em uma
expressão Python. O import math local executa quando a função é chamada.
Strings com aspas triplas e chaves em comentários mantêm seu significado nativo.
// In-process CPython: ordinary Zolo headers, native Python bodies.
use plugin python::{python as py}
extern py fn add(a: int, b: int) -> int {
# Braces in comments stay inside Python: } {
return a + b
}
extern py {
fn normalize(text: str) -> str {
return text.strip().lower()
}
fn factorial(n: int) -> int {
result = 1
for value in range(2, n + 1):
result *= value
return result
}
}
let original = " Zolo "
let clean: str = extern py {
return original.strip().upper()
}
let greeting: str = extern py { return f"Hi {original.strip()}" }
extern py fn native_import(value: float) -> float {
import math
return math.sqrt(value)
}
extern py fn literal() -> str {
value = '''Braces { } and "quotes"'''
return value
}
print(add(20, 22))
print(normalize(original))
print(factorial(5))
print(clean)
print(greeting)
print(native_import(81.0))
print(literal())
// expected:
// 42
// zolo
// 120
// ZOLO
// Hi Zolo
// 9
// Braces { } and "quotes"
Copie records tipados, preserve inteiros de 64 bits e slots nulos em arrays, transfira bytes, passe uma closure Zolo e chame uma closure devolvida pelo Python. Um objeto Python mantém seus atributos e estado entre chamadas.
// Exact values and live Python callables/objects share the ordinary syntax.
use plugin python::{python}
struct Payload { value: int, optional: int?, items: [int] }
extern python {
fn echo(value: Payload) -> Payload {
assert "optional" in value and value.optional is None
return value
}
fn nullable(values: [int?]) -> [int?] { return values }
fn empty() -> [int] { return [] }
fn binary() -> bytes { return b"\x00\xff\x2a" }
fn call(callback: fn(int) -> int) -> int { return callback(41) }
fn factory(delta: int) -> fn(int) -> int { return lambda value: value + delta }
fn counter() -> Any {
class Counter:
def __init__(self):
self.value = 0
def add(self, delta):
self.value += delta
return self.value
return Counter()
}
}
let copy = echo(Payload {
value: 9223372036854775807,
optional: nil,
items: [7, 8, 9],
})
print(copy.value)
print(copy.optional == nil)
print(copy.items.len())
let values = nullable([7, nil, nil])
print(values.len())
print(values[1] == nil)
print(empty().len())
print(binary().len())
print(call(|value: int| value + 1))
let increment = factory(1)
print(increment(41))
let live = counter()
print(live.add(40))
live.value = 41
print(live.add(1))
print(live.value)
// expected:
// 9223372036854775807
// true
// 3
// 3
// true
// 0
// 3
// 42
// 42
// 40
// 42
// 42
Imports de fontes nativos expõem funções e classes anotadas. Caminhos relativos partem do arquivo Zolo que faz o import. Um módulo nativo inline também pode expor funções pelo seu namespace Zolo.
Importe funções e uma classe de tools.py, use um alias para a classe, aguarde
um método async e acesse um campo anotado ou uma propriedade.
use plugin python::{python}
extern python from "./python_fixtures/tools.py" use {twice, later, Counter as Count}
extern python mod Tools {
def greet(name: str) -> str:
return f"Hello {name}"
}
print(twice(21))
print(await later(41))
let counter = Count(40)
print(counter.add(1))
print(await counter.add_later(1))
print(counter.value)
print(counter.total)
print(Tools.greet("Zolo"))
// expected:
// 42
// 42
// 41
// 42
// 42
// 42
// Hello Zolo
Aguarde coroutines Python e callbacks que retornam Futures Zolo. Combine
chamadas com Promise.all, capture falhas e solicite o cancelamento de uma tarefa.
use plugin python::{python}
use std::async::Promise
extern python async fn transform(value: int, callback: fn(int) -> Future<int>) -> int {
import asyncio
await asyncio.sleep(0.001)
return await callback(value)
}
extern python async fn fail() -> int {
import asyncio
await asyncio.sleep(0.001)
raise ValueError("Python failed")
}
extern python async fn wait_for_cancel() -> int {
import asyncio
await asyncio.sleep(60)
return 0
}
// Completing the entry function keeps its spawned tasks alive.
extern python async fn schedule(callback: fn(int) -> int) -> int {
import asyncio
async def later():
await asyncio.sleep(0.01)
callback(42)
asyncio.create_task(later())
return 42
}
async fn increment(value: int) -> int {
sleep 2ms
return value + 1
}
async fn reject(value: int) -> int {
sleep 1ms
panic("callback failed")
return value
}
async fn cancel_soon(future: Future<int>) -> bool {
sleep 5ms
future.cancel()
return future.is_done()
}
let values = await Promise.all([
transform(41, increment),
transform(42, increment),
])
print(values[0])
print(values[1])
print(try { await transform(0, reject) } catch error { "callback caught" })
print(try { await fail() } catch error { "Python caught" })
let pending = wait_for_cancel()
print(await cancel_soon(pending))
let background: [int] = []
print(await schedule(|value: int| {
background.push(value)
return value
}))
sleep 40ms
print(background[0])
// expected:
// 42
// 43
// callback caught
// Python caught
// true
// 42
// 42
Use Python e C# no mesmo processo. Um callback de qualquer uma das linguagens pode voltar ao Zolo e chamar o outro provider.
use plugin dotnet::{csharp}
use plugin python::{python}
extern csharp fn managed_add(a: int, b: int) -> int { return a + b; }
extern python fn python_add(a: int, b: int) -> int { return a + b }
extern python fn call_python(callback: fn(int) -> int) -> int {
return callback(41)
}
extern csharp fn call_managed(callback: fn(int) -> int) -> int {
return callback(41);
}
print(managed_add(20, 22))
print(python_add(20, 22))
print(call_python(|value: int| managed_add(value, 1)))
print(call_managed(|value: int| python_add(value, 1)))
print(python_add(managed_add(20, 1), 21))
// expected:
// 42
// 42
// 42
// 42
// 42
Selecione uma instalação CPython convencional e completa, da versão 3.9 à 3.14
com o GIL, usando ZOLO_PYTHON_HOME antes do
primeiro uso e instale dependências nesse ambiente. NumPy precisa da instalação
normal; 27-extern-python-numpy.zolo exercita sua extensão nativa. O build retém
os fontes necessários, enquanto CPython e pacotes externos continuam como
dependências da aplicação. PyTorch ainda não foi validado aqui.
Erros, completion, hover e ajuda de parâmetros analisam o texto atual sem executar código do usuário. VM, native e LLVM compartilham o contrato de execução. Um target wasm sem CPython rejeita o binding. O provider é experimental e foi validado no Windows com CPython 3.12, incluindo NumPy e executáveis sem os fontes originais. Linux/macOS e outras versões do interpretador ainda não foram validados em execução.
Veja também