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.
56 lines
1.8 KiB
Python
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
|