Execute append-only Python while source is still arriving. Python 3.11+, no runtime dependencies. Experimental API.
uv add 'partial-python==0.1.0a1'Use an Executor for each logical response. Feed source as it arrives, then explicitly finish or abort according to the provider's completion status.
from partial_python import Executor
executor = Executor({"foo": foo, "bar": bar, "baz": baz})
await executor.feed("await foo(1)\n") # Executes now.
await executor.feed("await bar(2)") # Waits: an operator or attribute access may follow.
await executor.feed("\nawait baz(3)") # Executes bar, buffers baz.
await executor.finish() # Successful EOF permits baz.Units execute in source order, at most once each. New input never reruns earlier units. A loop still performs its ordinary iterations, but its whole body must be sealed before execution starts.
feed() accepts new text deltas, not accumulated snapshots. feed() and finish() return completed Execution events. The executor retains events, failed_unit, sealed but unstarted queued units, and an immutable parser_state snapshot containing the unsealed pending tail. Read results from executor.namespace.
finish() declares successful source completion. On disconnects, token limits, cancellation, or failed responses, use abort() for an idle executor; cancel and await an active operation first. Failure is terminal: earlier effects remain, later units do not start, and nothing is retried. Lifecycle and integration.
When your adapter already supplies a validated async text iterator, incremental_exec() is a convenience wrapper. validated_deltas must yield raw source and end normally only after successful generation. Its adapter must raise on truncation, failure, or a missing completion signal.
from partial_python import incremental_exec
namespace = await incremental_exec(validated_deltas, {"foo": foo, "bar": bar})Execution starts while source is arriving; the await completes after execution and returns the namespace used. To consume events as each unit completes, use Executor.stream() and retain the executor for diagnostics. Close a partially consumed event iterator with aclosing in its owning task/context. Streaming adapter.
| Input | Execution boundary |
|---|---|
| Top-level simple statement | A real logical newline or successful EOF. |
foo() at temporary EOF |
Wait; it can still become foo() + bar(). |
for, if, try, def, and other compounds |
Hold the entire statement until an unambiguous top-level successor or successful EOF. |
else, elif, except, finally, or an ambiguous prefix such as e |
Keep clauses attached; wait for ambiguous prefixes to resolve. |
| Blank lines and comments | Preserve them; they do not close a suite. |
| Decorators, brackets, line joins, and strings | Keep their complete logical statement together. |
Parser.feed() / finish() expose the same boundaries as immutable CodeUnit source slices without executing them. pending retains the uncommitted suffix. Every unit preserves original characters and source positions; no repaired text is executed. Precise semantic contract.
from partial_python import Allow, complete
assert complete("ready = 1\nvalue = ['hel").source == "ready = 1\n"
preview = complete("value = ['hel", Allow.STRING | Allow.BRACKET)
assert preview.source == "value = ['hel']"
assert preview.provisionalThe default returns only the stable prefix. Allow flags opt into provisional display; prefix, suffix, and withheld distinguish original text from synthetic text. A preview never authorizes execution, even when its suffix is empty. Preview options and limits.
Units share one namespace and support native top-level await. Execution requires an asyncio event loop; use the asyncio backend with AnyIO. Parser and complete are synchronous.
This uses sequential compilation units, with explicit differences from whole-module execution. Future imports remain in the prologue, only the initial string sets the module docstring, and module-scope global declarations are rejected. On Python 3.14+, module annotations require an explicit from __future__ import annotations.
In-process execution provides no sandbox or rollback. Hosts own permissions, background tasks, source limits, and hard deadlines. The integration examples demonstrate transport completion, concurrent reception, and traceback source lifetime.
uv sync
uv run pytest
uv run basedpyright
uv run ruff check .
uv run ruff format --check .
uv run examples/streaming_tools.py
uv buildAPI and transport · Design · Validation · Related implementations