Your first Python call

Verified this session

Every command and output below was run against main: the Windows path was validated against the checked-in evidence in examples/python-math/README.md; the Linux path (build, runtime, provider, invocation, overflow rejection, no-provider handling, and graceful shutdown) was independently re-run end to end in this session inside WSL2 Ubuntu. add(2, 3) really does return 5, computed by the Python process — there is no addition logic in the C runtime or the CLI.

This walks through examples/python-math/: a real Python process implementing math.add, registered against the shipped root Polycallfile, invoked through the polycall CLI over the real wire protocol (LPV1-06).

Prerequisites

  • Built binaries: run make all (Linux) or .\build-windows.ps1 -Target rebuild (Windows) first — see Getting started.
  • Python 3.8 or later, standard library only. No pip install is needed; provider.py uses only socket, struct, json, subprocess, and (on Windows) ctypes.
  • Run everything from the repository root — the shipped Polycallfile and request.json live there.

The three terminals

Terminal Runs Role
1 polycall runtime start the runtime: binds runtime.listen, accepts connections, authenticates, dispatches calls
2 python examples/python-math/provider.py the provider: registers math.add, serves invocations
3 polycall call invoke the caller: sends one typed request, prints the typed result

Windows PowerShell

Terminal 1

.\bin\polycall.exe runtime start .\Polycallfile
runtime ready, listening
press Ctrl+C to stop

Terminal 2

python .\examples\python-math\provider.py .\Polycallfile
provider: registered math.add (generation 1); serving (Ctrl+C to stop)

.\Polycallfile here is a real positional argument that provider.py accepts and passes straight to polycall.exe config show --json as a subprocess — the script never parses Polycallfile syntax itself (see Core concepts). [security] mode = "local-auto" in the shipped file means no manual token setup: the runtime auto-generates a shared credential into Windows Credential Manager on start, and the provider reads that same entry.

Terminal 3

.\bin\polycall.exe call invoke --service math --method add --input .\request.json --json .\Polycallfile
{"ok":true,"command":"call","subcommand":"invoke","status":"completed","data":{"result":{"type":"i64","value":"5"}},"error":null}

Terminal 2 prints provider: add(2, 3) = 5 at the same moment — that value came from the Python process.

Linux

make all                                                                                    # once
./bin/polycall runtime start ./Polycallfile                                                 # terminal 1
python3 ./examples/python-math/provider.py ./Polycallfile                                   # terminal 2
./bin/polycall call invoke --service math --method add --input ./request.json --json ./Polycallfile   # terminal 3

local-auto on Linux stores the credential in ~/.polycall-credentials/<runtime name> (or $XDG_RUNTIME_DIR if set) instead of an OS credential manager; both the runtime and the provider resolve it the same way automatically.

request.json (already at the repository root):

{"args":[{"type":"i64","value":"2"},{"type":"i64","value":"3"}]}

What else was tested, and what actually happens

Overflow is rejected, not wrapped or truncated. Requesting add(9223372036854775807, 1) returns a structured error instead of a silently wrong number:

{"ok":false,"command":"call","subcommand":"invoke","status":"provider_error","data":null,"error":{"code":"provider_error","message":"result is out of the signed 64-bit range"}}

exit code 2. Python integers do not overflow the way C's do, so this is a check against the wire's declared i64 range, performed before the value is ever put on the wire — not an accident of Python's arbitrary precision.

No provider connected is reported honestly, never faked. Before the Python process registers (or after it disconnects), the same invocation returns:

{"ok":false,"command":"call","subcommand":"invoke","status":"unavailable","data":null,"error":{"code":"unavailable","message":"no eligible provider"}}

exit code 7.

Clean shutdown, on both platforms. polycall runtime stop --json .\Polycallfile (note: options must come before the Polycallfile path — see CLI reference) or Ctrl+C in terminal 1 drains and exits the runtime, and the provider notices its connection close and exits on its own with provider: stopped.

Bug found and fixed this session

Before this session, a clean runtime stop on Linux hung indefinitely: the shutdown code closed the listening socket and each connection's socket to unblock threads waiting in accept()/recv(), which reliably wakes those calls on Windows but not on Linux. Verified hung for 15+ seconds with strace-visible futex_do_wait, then fixed in src/transport/polycall_server.c by calling shutdown() before close() on every connection socket, and forcing the blocked accept thread to return with a local loopback self-connect before closing the listening socket. Re-verified: both platforms now exit within ~1 second of a stop request, with a provider connected.

Stopping cleanly

Press Ctrl+C in terminal 1 (or run polycall runtime stop --json ./Polycallfile from a fourth terminal), then Ctrl+C in terminal 2 if the provider hasn't already exited on its own.

Also available: the C provider

examples/quickstart-math/ is the same demonstration using the public polycall_provider_* C API instead of a hand-written wire client — see Language integration.