Ever dreamed of parallel programming in a few clicks? Meet Tolquane

Community Article
Published September 7, 2026

Parallel programming in Python has a reputation: the GIL, threads that share too much, multiprocessing boilerplate, queues that fill up, and the one bug everybody has met at least once, the program that stops without saying why. Tolquane is my answer to that, and it comes in two parts: a library of composable building blocks that never hangs, and a browser editor where a flow is drawn, run, debugged and scheduled in a few clicks.

Try the editor right now, nothing to install, sign in as demo with the password tolquane:

https://huggingface.co/spaces/robtacconelli/tolquane

A flow running in Tolquane Web: item counts on every card, queue depths on every edge

A function is a node, >> is a pipeline, a farm copies a worker

That is the whole vocabulary. Here is a complete program:

import tolquane as tq

@tq.source
def numbers():
    yield from range(1, 101)

@tq.node
def double(x: int) -> int:
    return x * 2

@tq.sink
def show(x: int) -> None:
    print(x)

tq.run(numbers >> tq.farm(double, workers=4, ordered=True) >> show)

tq.farm runs four copies of double, and ordered=True puts the results back in input order. Return tq.SKIP to drop an item, yield from a node to emit several, take a ctx argument to route items yourself. Beyond farms there are comb (fuse two nodes on one thread), all2all (two farms joined worker to worker), feedback (loops that terminate by rule, no hand-written protocol), and a farm can hold any block as its worker, a pipeline, another farm, a loop.

The vocabulary is the one of the FastFlow building blocks, the C++ library from the Universities of Pisa and Turin that has been running on many-cores and clusters for fifteen years. Tolquane keeps the ideas and puts a Python face on them.

One graph, four runtimes

The graph never changes. Only the runtime argument does.

Runtime Nodes run as Pick it when
threads one thread per node I/O-bound stages, numpy and C extensions, full parallelism on free-threaded 3.14t
processes farm workers in child processes CPU-bound pure Python on a GIL build
async nodes coroutines on an event loop, a farm of them as one pool hundreds of requests in flight on one thread
distributed groups on hosts, TCP between them, from one deploy file two or more machines
sync one node at a time, deterministic tests and debugging

Measured on a 16-thread machine, a farm of eight CPU-bound Python workers: 0.9× on threads with the GIL, 5.5× with processes, 5.6× on free-threaded 3.14t. A million items over loopback TCP take 3.5 s. tolquane launch deploy.toml flow.py starts every host over ssh from one terminal.

It never hangs, and that is a design rule, not a hope

Every channel is bounded. Every node waits in one place. End of stream is a message that cannot be lost or raced. An error in any node cancels the run and names the node. A graph that cannot make progress raises a DeadlockError that names the nodes and what each one waits for, on threads with a watchdog, on the sync runtime at the exact instant. Feedback loops close themselves when the outside input has ended and nothing is in flight. Fifteen such rules are written down in the design document, each with the bug it closes and the test that pins it.

tq.optimize(graph) is the other half of the deal: it fuses a stage before a farm into the farm's emitter, drops collectors the next stage can do without, and flattens farms of farms, so the natural way of writing a flow costs no extra threads.

Tolquane Web: the same flow, in the browser

The canvas: farms and loops as containers

The file stays the artifact. The editor reads flow.py into a model, shows it as block cards on a canvas, farms and loops as containers, and writes it back in the same style when you edit on the canvas. Switch to Code and edit the Python directly: the canvas follows. Every node's body is editable in the properties panel. Nothing you write is ever rewritten by a generator; positions live in a sidecar file. A flow made here runs anywhere with python flow.py and the library alone.

Run and debug. A run is a child process that streams events. Cards colour by state, counters and busy time move, edges show their queue depth, and Taps show the last items that crossed any edge. The drawer holds the console, the report (busy, wait-in, wait-out per node, with the bottleneck named), and the problems, each pointing at its card. A deadlock lands on the canvas as a report, not as a frozen page.

The AI builder. Describe the flow in one sentence in the side panel. It writes the file in the house style, checks the wiring, runs it on a sample, fixes what fails, and hands you a flow card with a diff and an Apply button. Ask it to make a flow faster, or to add a stage, and the same loop runs on your open file. You bring a Claude or GPT key; it never leaves the server.

The AI builder's flow card with a diff and Apply

And the rest of a working tool. Parameters on build() become typed fields in the run popover and in schedules. Schedules use cron presets with a live preview of the next times, and tell you how a run went by webhook or mail, with retries. A git history of every flow: versions, diffs, restore, save with a message. Accounts with admin and member roles for a shared server. Settings for the interpreter, the workspace environment, keys and notifications.

Where to start

Apache-2.0, Python 3.11 and newer. Next on the list: divide-and-conquer and parallel-for helpers, a deploy editor to drag cards onto hosts and launch a cluster from the canvas, and an examples gallery. Try it, break it, and tell me what you would build with it. A ⭐ on the repository helps others find it.

Community

Sign up or log in to comment