Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/_templates/autosummary/module.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{{ fullname | escape | underline}}

.. automodule:: {{ fullname }}
:no-members:

{% block attributes %}
{% if attributes %}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ readme = "README.md"
requires-python = ">=3.10"
dynamic = ["version"]
dependencies = [
"ezmsg[axisarray]>=3.7.2",
"ezmsg[axisarray]>=3.7.3",
"typing-extensions>=4.0.0",
]

Expand Down
30 changes: 16 additions & 14 deletions src/ezmsg/baseproc/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,21 +171,23 @@ class CompositeProcessor(
typing.Generic[SettingsType, MessageInType, MessageOutType],
):
"""
A processor that chains multiple processor together in a feedforward non-branching graph.
A processor that chains multiple processors together in a feedforward non-branching graph.
The individual processors may be stateless or stateful. The last processor may be a consumer,
otherwise processors must be transformers. Use CompositeProducer if you want the first
processor to be a producer. Concrete subclasses must implement `_initialize_processors`.
Optionally override `_reset_state` if you want adaptive state behaviour.
Example implementation:

class CustomCompositeProcessor(CompositeProcessor[CustomSettings, AxisArray, AxisArray]):
@staticmethod
def _initialize_processors(settings: CustomSettings) -> dict[str, BaseProcessor]:
return {
"stateful_transformer": CustomStatefulProducer(**settings),
"transformer": CustomTransformer(**settings),
}
Where **settings should be replaced with initialisation arguments for each processor.
otherwise processors must be transformers. Use ``CompositeProducer`` if you want the first
processor to be a producer. Concrete subclasses must implement ``_initialize_processors``.
Optionally override ``_reset_state`` if you want adaptive state behaviour.

Example implementation::

class CustomCompositeProcessor(CompositeProcessor[CustomSettings, AxisArray, AxisArray]):
@staticmethod
def _initialize_processors(settings: CustomSettings) -> dict[str, BaseProcessor]:
return {
"stateful_transformer": CustomStatefulProducer(**settings),
"transformer": CustomTransformer(**settings),
}

Where ``**settings`` should be replaced with initialisation arguments for each processor.
"""

def __init__(self, *args, **kwargs) -> None:
Expand Down
9 changes: 5 additions & 4 deletions src/ezmsg/baseproc/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ class BaseProcessor(ABC, typing.Generic[SettingsType, MessageInType, MessageOutT
"""
Base class for processors. You probably do not want to inherit from this class directly.
Refer instead to the more specific base classes.
* Use :obj:`BaseConsumer` or :obj:`BaseTransformer` for ops that return a result or not, respectively.
* Use :obj:`BaseStatefulProcessor` and its children for operations that require state.

Note that `BaseProcessor` and its children are sync by default. If you need async by defualt, then
override the async methods and call them from the sync methods. Look to `BaseProducer` for examples of
* Use :obj:`BaseConsumer` or :obj:`BaseTransformer` for ops that return a result or not, respectively.
* Use :obj:`BaseStatefulProcessor` and its children for operations that require state.

Note that ``BaseProcessor`` and its children are sync by default. If you need async by default, then
override the async methods and call them from the sync methods. Look to ``BaseProducer`` for examples of
calling async methods from sync methods.
"""

Expand Down
29 changes: 16 additions & 13 deletions src/ezmsg/baseproc/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,24 +179,27 @@ class BaseTransformerUnit(
):
"""
Base class for transformer units -- i.e. units that transform input messages into output messages.
Implement a new Unit as follows:

class CustomUnit(BaseTransformerUnit[
CustomTransformerSettings, # SettingsType
AxisArray, # MessageInType
AxisArray, # MessageOutType
CustomTransformer, # TransformerType
]):
SETTINGS = CustomTransformerSettings
Implement a new Unit as follows::

class CustomUnit(BaseTransformerUnit[
CustomTransformerSettings, # SettingsType
AxisArray, # MessageInType
AxisArray, # MessageOutType
CustomTransformer, # TransformerType
]):
SETTINGS = CustomTransformerSettings

... that's all!

Where CustomTransformerSettings and CustomTransformer are custom implementations of:
- ez.Settings for settings
Where ``CustomTransformerSettings`` and ``CustomTransformer`` are custom implementations of:

- ``ez.Settings`` for settings
- One of these transformer types:
* BaseTransformer
* BaseStatefulTransformer
* CompositeProcessor

- ``BaseTransformer``
- ``BaseStatefulTransformer``
- ``CompositeProcessor``
"""

INPUT_SIGNAL = ez.InputStream(MessageInType)
Expand Down
14 changes: 10 additions & 4 deletions src/ezmsg/baseproc/util/typeresolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,23 @@ def resolve_typevar(cls: type, target_typevar: typing.TypeVar) -> type:
def check_message_type_compatibility(type1: TypeLike, type2: TypeLike) -> bool:
"""
Check if two types are compatible for message passing.

Returns True if:

- Both are None/NoneType
- Either is typing.Any
- type1 is a subclass of type2, which includes
- type1 and type2 are concrete types and type1 is a subclass of type2
- type1 is None/NoneType and type2 is typing.Optional, or
- type1 is subtype of the non-None inner type of type2 if type2 is Optional
- type1 is a subclass of type2, which includes:

- type1 and type2 are concrete types and type1 is a subclass of type2
- type1 is None/NoneType and type2 is typing.Optional, or
- type1 is subtype of the non-None inner type of type2 if type2 is Optional

- type1 is a Union/Optional type and all inner types are compatible with type2

Args:
type1: First type to compare
type2: Second type to compare

Returns:
bool: True if the types are compatible, False otherwise
"""
Expand Down