Node
- class ioiocore.node.Node(name=None, **kwargs)[source]
Bases:
ProcessingElementAbstract base class representing a concrete processing node in a pipeline.
Extends ProcessingElement with setup() and step() methods that define the node’s initialization and processing logic.
- Parameters:
name (str)
- _IMP_CLASS
The implementation class for the Node.
- Type:
type
- _imp
The instance of the implementation class for the Node.
- Type:
- config
The configuration object for the Node.
- Type:
- class Configuration(name=None, **kwargs)[source]
Bases:
ConfigurationConfiguration class for the Node
- Parameters:
name (str)
- __init__(name=None, **kwargs)[source]
Initialize the Node.
- Parameters:
name (
str) – The name of the Node (default is None).kwargs (additional keyword arguments) – Other parameters passed to create the Node configuration.
- config: Configuration
- is_externally_drained()[source]
Report whether this node’s outputs leave the pipeline.
The outlet counterpart of
is_externally_fed(): a chain refuses an IONode at its outlet because its output would be silently dropped. A node that sends its output somewhere outside the graph returns True, and the chain admits it.- Returns:
True if the outputs are consumed externally.
- Return type:
bool
- is_externally_fed()[source]
Report whether this node’s inputs come from outside the pipeline.
A chain refuses an IONode at its inlet, because an IONode there would leave its input ports with nothing connected to them and the chain could never be fed. That is the right default: it catches a real authoring mistake.
It is wrong for one kind of node – one whose input is satisfied from outside the graph entirely, such as a bridge receiving from a socket. Its unconnected input port is not a dangling port; it is the point where external data arrives. Such a node says so by returning True here, and the chain then admits it.
Declared rather than inferred, and False by default, so nothing existing changes and no mistake is silently accepted.
- Returns:
True if the inputs are satisfied externally.
- Return type:
bool
- pre_setup(data, port_context_in)[source]
Adapt the input contexts before setup() sees them.
Called once, immediately before
setup, with the input port contexts thatsetupis about to receive. Modify them in place; the return value is ignored.The setup-time counterpart of
pre_step(). Together they let a consuming package present every node with uniform inputs: this one settles what the inputs are – how many channels, at what rate, with what timing – andpre_stepsupplies frames that match on every cycle. Adapting only the frames would not be enough, because a node decides its own output shape here, from exactly these contexts.The default does nothing.
- Parameters:
data (
dict) – Initial data, as passed tosetup.port_context_in (
dict) – Input port contexts keyed by port name, to be modified in place.
- Return type:
None
- pre_step(data)[source]
Adapt the input frames before step() sees them.
Called once per cycle, immediately before
step, with the dictionary thatstepis about to receive. Modify it in place.Return True to withhold the cycle:
stepis not called and nothing is emitted. A hook that holds frames back needs this – without it, the only way to keep the cycle going is to fabricate a frame, which puts invented samples at the head of the stream and shifts every later one by however many were held. Returning None or False runs the cycle as usual, which is what every hook that does not hold anything back does.This exists so that a consuming package can make the inputs of every node uniform without every node author having to know about it. The engine deliberately holds no opinion about what “uniform” means: it delivers whatever arrived, and this is where a domain layer may reshape it – resample a stream that arrives irregularly onto the grid of one that does not, substitute a value for a port that produced nothing, or reject a combination it cannot express.
The default does nothing, which is the behaviour of every node that does not override it.
Notes
This runs on the per-cycle path, so an override should do as little as possible and decide as much as it can once, in
setup, rather than on every frame.- Parameters:
data (
dict) – Input frames keyed by port name, to be modified in place. A port that produced nothing this cycle may be absent.- Return type:
bool
- process(data)[source]
Process the whole record at once. Override for a batch node.
Called once, with every sample the node will ever see, in place of
step(). This is what a non-causal operation needs – a zero-phase filter, a whole-record statistic – and it is why the node needs no buffering of its own: a batch pipeline delivers one block.- Parameters:
data (
dict) – The complete input data, keyed by port name.- Returns:
The processed data.
- Return type:
dict- Raises:
NotImplementedError – If the node implements only
step(). Reaching this means the caller asked for whole-record processing from a node that declares none, which the pipeline should have refused first.
- abstractmethod setup(data, port_context_in)[source]
Abstract method to setup the Node.
This method should be implemented to define the setup logic for the Node.
- Parameters:
data (
dict) – The input data to configure the Node.port_context_in (
dict) – Context for the input ports.
- Returns:
The configuration of the output ports.
- Return type:
dict
- step(data)[source]
Process one frame. Override for a node that runs in realtime.
Not abstract, because a node may instead implement
process()and run only in a batch pipeline. Exactly one of the two is required, checked in__init__.- Parameters:
data (
dict) – The input data to process.- Returns:
The processed data after applying the step logic.
- Return type:
dict- Raises:
NotImplementedError – If the node implements only
process(). Reaching this means a batch-only node was driven frame by frame.