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.
This commit is contained in:
parent
c4538f03ca
commit
2b0873b61b
@ -1,38 +1,55 @@
|
|||||||
"""
|
"""
|
||||||
08_python_subport — tap a C++ node's output from Python using net.read().
|
08_python_subport — drive a *Python* node from Python via write()/read() taps.
|
||||||
|
|
||||||
Graph:
|
Graph:
|
||||||
[ProduceNode] --int--> [DoubleItNode] --int--> (tapped by net.read())
|
(fed by net.write()) --int--> [py_triple] --int--> (tapped by net.read())
|
||||||
|
|
||||||
The sink is Python: instead of connecting a PrintItNode, we call net.read()
|
Unlike 07, there is no C++ source or sink here: the only node in the network is
|
||||||
to pull values out of DoubleItNode's output directly into Python.
|
a pure-Python function, py_triple. Python plays *both* the producer and the
|
||||||
We also demonstrate net.write() by injecting a value into DoubleItNode's input.
|
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
|
import sys
|
||||||
import time
|
sys.path.insert(0, "build/python") # for `python examples/.../example.py` from repo root
|
||||||
import threading
|
|
||||||
sys.path.insert(0, "build/python")
|
|
||||||
|
|
||||||
import kpn_python as kpn
|
import kpn_python as kpn
|
||||||
|
|
||||||
|
|
||||||
|
def py_triple(x: int) -> int:
|
||||||
|
return x * 3
|
||||||
|
|
||||||
|
|
||||||
net = kpn.Network()
|
net = kpn.Network()
|
||||||
|
|
||||||
net.add("src", kpn.make_produce())
|
# The whole network is a single Python node with a tapped input and output.
|
||||||
net.add("dbl", kpn.make_double_it())
|
net.add_node("py", py_triple, inputs=["int"], outputs=["int"])
|
||||||
|
|
||||||
net.connect("src", 0, "dbl", 0)
|
|
||||||
net.build()
|
net.build()
|
||||||
net.start()
|
net.start()
|
||||||
|
|
||||||
# Collect a few values from DoubleItNode's output via Python tap
|
# Push values in from Python and read the Python node's results back out.
|
||||||
|
inputs = [1, 2, 7, 10, 100]
|
||||||
results = []
|
results = []
|
||||||
for _ in range(5):
|
for v in inputs:
|
||||||
val = net.read("dbl", 0)
|
net.write("py", 0, v) # Python -> py_triple input
|
||||||
results.append(val)
|
results.append(net.read("py", 0)) # py_triple output -> Python
|
||||||
|
|
||||||
net.stop()
|
net.stop()
|
||||||
|
|
||||||
print("values read from C++ DoubleItNode output:", results)
|
print("inputs written from Python: ", inputs)
|
||||||
assert all(v == 84 for v in results), f"expected all 84, got {results}"
|
print("outputs read from py_triple:", results)
|
||||||
print("all correct (42 * 2 = 84)")
|
|
||||||
|
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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user