Duncan Tourolle 2b0873b61b examples: make 08_python_subport run a real Python node
The example was named "python subport" but its graph was entirely C++
(ProduceNode -> DoubleItNode); Python only tapped the output, leaving a
dangling "#todo: return value to network".

Rewrite so the only node in the graph is a pure-Python py_triple, driven
from both ends via the subport taps: net.write() injects inputs and
net.read() pulls results back, closing the round trip. Also del the
network at the end so its callable cycle is reclaimed deterministically.
2026-07-04 14:44:24 +02:00

56 lines
1.8 KiB
Python

"""
08_python_subport — drive a *Python* node from Python via write()/read() taps.
Graph:
(fed by net.write()) --int--> [py_triple] --int--> (tapped by net.read())
Unlike 07, there is no C++ source or sink here: the only node in the network is
a pure-Python function, py_triple. Python plays *both* the producer and the
consumer by using the subport taps:
* net.write("py", 0, v) injects v into py_triple's input (Python -> network)
* net.read("py", 0) pulls py_triple's output back out (network -> Python)
This closes the loop the old version left as a "#todo": a value flows from
Python, through a Python node running inside the network, and back to Python.
"""
import sys
sys.path.insert(0, "build/python") # for `python examples/.../example.py` from repo root
import kpn_python as kpn
def py_triple(x: int) -> int:
return x * 3
net = kpn.Network()
# The whole network is a single Python node with a tapped input and output.
net.add_node("py", py_triple, inputs=["int"], outputs=["int"])
net.build()
net.start()
# Push values in from Python and read the Python node's results back out.
inputs = [1, 2, 7, 10, 100]
results = []
for v in inputs:
net.write("py", 0, v) # Python -> py_triple input
results.append(net.read("py", 0)) # py_triple output -> Python
net.stop()
print("inputs written from Python: ", inputs)
print("outputs read from py_triple:", results)
expected = [v * 3 for v in inputs]
assert results == expected, f"expected {expected}, got {results}"
print("all correct (x * 3 computed by a Python node inside the network)")
# Drop the network deterministically. The network holds the Python callable,
# which (via globals) forms a reference cycle; deleting the global breaks it so
# the network is reclaimed promptly instead of lingering to interpreter exit.
del net