Skip to content
Open
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
56 changes: 54 additions & 2 deletions examples/ezmsg_generator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""
.. deprecated::
This example demonstrates deprecated generator-based patterns from
``ezmsg.util.generator``. New code should use ``ezmsg.baseproc``
(BaseTransformer, BaseProducer, etc.) instead.

This ezmsg example showcases a design pattern where core computational
logic is encapsulated within a Python generator. This approach is
beneficial for several reasons:
Expand All @@ -22,14 +27,61 @@
"""

import asyncio
import traceback
import ezmsg.core as ez
import numpy as np
from typing import Any
from collections.abc import Generator
from collections.abc import AsyncGenerator, Callable, Generator
from functools import wraps, reduce
from ezmsg.util.messages.axisarray import AxisArray, replace
from ezmsg.util.debuglog import DebugLog
from ezmsg.util.gen_to_unit import gen_to_unit
from ezmsg.util.generator import consumer, compose, Gen


# --- Inlined helpers (previously from ezmsg.util.generator, now deprecated) ---

def consumer(func):
"""Prime a generator by advancing it to the first yield statement."""
@wraps(func)
def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return wrapper


def compose(*funcs):
"""Compose a chain of generator functions into a single callable."""
return lambda x: reduce(lambda f, g: g.send(f), list(funcs), x)


class GenState(ez.State):
gen: Generator[Any, Any, None]


class Gen(ez.Unit):
STATE = GenState

INPUT = ez.InputStream(Any)
OUTPUT = ez.OutputStream(Any)

async def initialize(self) -> None:
self.construct_generator()

def construct_generator(self):
raise NotImplementedError

@ez.subscriber(INPUT)
@ez.publisher(OUTPUT)
async def on_message(self, message: Any) -> AsyncGenerator:
try:
ret = self.STATE.gen.send(message)
if ret is not None:
yield self.OUTPUT, ret
except (StopIteration, GeneratorExit):
ez.logger.debug(f"Generator closed in {self.address}")
except Exception:
ez.logger.info(traceback.format_exc())


@consumer
Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Key modules:
- :mod:`ezmsg.util.debuglog`: Debug logging utilities
- :mod:`ezmsg.util.generator`: Generator-based message processing decorators
- :mod:`ezmsg.util.generator`: Generator-based message processing decorators (deprecated — use ``ezmsg.baseproc`` instead)
- :mod:`ezmsg.util.messagecodec`: JSON encoding/decoding for message logging
- :mod:`ezmsg.util.messagegate`: Message flow control and gating
- :mod:`ezmsg.util.messagelogger`: File-based message logging
Expand Down
17 changes: 17 additions & 0 deletions src/ezmsg/util/generator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
"""
.. deprecated::
This module is deprecated. Use ``ezmsg.baseproc`` (ezmsg-baseproc) instead.
The ``consumer``, ``compose``, ``GenState``, and ``Gen`` APIs in this module
will be removed in a future release.
"""

import warnings

warnings.warn(
"ezmsg.util.generator is deprecated. "
"Use ezmsg.baseproc (BaseTransformer, BaseProducer, etc.) instead. "
"This module will be removed in a future release.",
DeprecationWarning,
stacklevel=2,
)

import ezmsg.core as ez
import traceback
from collections.abc import AsyncGenerator, Callable, Generator
Expand Down
2 changes: 2 additions & 0 deletions tests/test_generator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# NOTE: This file tests the deprecated ezmsg.util.generator module.
# The @consumer usages here are the subject of the tests and should remain.
from collections.abc import AsyncGenerator, Generator
import copy
import json
Expand Down