The py_trees.ports framework provides the mechanism for typed input/output ports but does not ship concrete port-aware behaviours, decorators, or composites. To make an existing upstream class (like py_trees.decorators.Retry) port-aware, you must create an adapter class that combines PortsMixin with the upstream class.
Implementation Pattern:
- Inheritance: Inherit from both
PortsMixin and the upstream class. - Port Definition: Implement
input_ports() and output_ports() class methods using PortInformation to define the schema. - Initialization: In
__init__, call super().__init__ with safe default values. - Runtime Updates: In
initialise(), use self.get_input("port_name") to read the current port value and apply it to the upstream class's attributes before calling super().initialise(). - Namespace: To avoid naming collisions, place your ported class in a
ports submodule that mirrors the upstream layout (e.g., yourproject.ports.decorators.Retry).
import py_trees
from py_trees.ports import PortInformation, PortsMixin
class Retry(PortsMixin, py_trees.decorators.Retry):
"""Retry that reads its failure budget from an input port."""
@classmethod
def input_ports(cls):
return {"num_failures": PortInformation(data_type=int, required=True)}
@classmethod
def output_ports(cls):
return {}
def __init__(
self,
name: str,
child: py_trees.behaviour.Behaviour,
**kwargs
):
# Start with a safe default; it is overwritten on every initialise().
super().__init__(
name=name, child=child, num_failures=1, **kwargs
)
def initialise(self) -> None:
# Read the port value at tick boundary and apply it before
# the upstream Retry logic runs.
self.num_failures = self.get_input("num_failures")
super().initialise()