ezmsg-baseproc 1.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,14 @@ from .clock import (
15
15
  ClockState,
16
16
  )
17
17
 
18
+ # Clock-driven producers
19
+ from .clockdriven import (
20
+ BaseClockDrivenProducer,
21
+ ClockDrivenSettings,
22
+ ClockDrivenSettingsType,
23
+ ClockDrivenState,
24
+ )
25
+
18
26
  # Composite processor classes
19
27
  from .composite import (
20
28
  CompositeProcessor,
@@ -74,15 +82,18 @@ from .stateful import (
74
82
  from .units import (
75
83
  AdaptiveTransformerType,
76
84
  BaseAdaptiveTransformerUnit,
85
+ BaseClockDrivenProducerUnit,
77
86
  BaseConsumerUnit,
78
87
  BaseProcessorUnit,
79
88
  BaseProducerUnit,
80
89
  BaseTransformerUnit,
90
+ ClockDrivenProducerType,
81
91
  ConsumerType,
82
92
  GenAxisArray,
83
93
  ProducerType,
84
94
  TransformerType,
85
95
  get_base_adaptive_transformer_type,
96
+ get_base_clockdriven_producer_type,
86
97
  get_base_consumer_type,
87
98
  get_base_producer_type,
88
99
  get_base_transformer_type,
@@ -116,6 +127,7 @@ __all__ = [
116
127
  "ConsumerType",
117
128
  "TransformerType",
118
129
  "AdaptiveTransformerType",
130
+ "ClockDrivenProducerType",
119
131
  # Decorators
120
132
  "processor_state",
121
133
  # Base processor classes
@@ -131,6 +143,11 @@ __all__ = [
131
143
  "BaseStatefulTransformer",
132
144
  "BaseAdaptiveTransformer",
133
145
  "BaseAsyncTransformer",
146
+ # Clock-driven producers
147
+ "BaseClockDrivenProducer",
148
+ "ClockDrivenSettings",
149
+ "ClockDrivenSettingsType",
150
+ "ClockDrivenState",
134
151
  # Composite classes
135
152
  "CompositeStateful",
136
153
  "CompositeProcessor",
@@ -141,12 +158,14 @@ __all__ = [
141
158
  "BaseConsumerUnit",
142
159
  "BaseTransformerUnit",
143
160
  "BaseAdaptiveTransformerUnit",
161
+ "BaseClockDrivenProducerUnit",
144
162
  "GenAxisArray",
145
163
  # Type resolution helpers
146
164
  "get_base_producer_type",
147
165
  "get_base_consumer_type",
148
166
  "get_base_transformer_type",
149
167
  "get_base_adaptive_transformer_type",
168
+ "get_base_clockdriven_producer_type",
150
169
  "_get_base_processor_settings_type",
151
170
  "_get_base_processor_message_in_type",
152
171
  "_get_base_processor_message_out_type",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.1.0'
32
- __version_tuple__ = version_tuple = (1, 1, 0)
31
+ __version__ = version = '1.2.0'
32
+ __version_tuple__ = version_tuple = (1, 2, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,179 @@
1
+ """Clock-driven producer base classes for generating data synchronized to clock ticks."""
2
+
3
+ import typing
4
+ from abc import abstractmethod
5
+
6
+ import ezmsg.core as ez
7
+ from ezmsg.util.messages.axisarray import AxisArray, LinearAxis
8
+
9
+ from .protocols import StateType, processor_state
10
+ from .stateful import BaseStatefulProcessor
11
+
12
+
13
+ class ClockDrivenSettings(ez.Settings):
14
+ """
15
+ Base settings for clock-driven producers.
16
+
17
+ Subclass this to add your own settings while inheriting fs and n_time.
18
+
19
+ Example::
20
+
21
+ class SinGeneratorSettings(ClockDrivenSettings):
22
+ freq: float = 1.0
23
+ amp: float = 1.0
24
+ """
25
+
26
+ fs: float
27
+ """Output sampling rate in Hz."""
28
+
29
+ n_time: int | None = None
30
+ """
31
+ Samples per block.
32
+ - If specified: fixed chunk size (clock gain is ignored for determining chunk size)
33
+ - If None: derived from clock gain (fs * clock.gain), with fractional sample tracking
34
+ """
35
+
36
+
37
+ # Type variable for settings that extend ClockDrivenSettings
38
+ ClockDrivenSettingsType = typing.TypeVar("ClockDrivenSettingsType", bound=ClockDrivenSettings)
39
+
40
+
41
+ @processor_state
42
+ class ClockDrivenState:
43
+ """
44
+ Internal state for clock-driven producers.
45
+
46
+ Tracks sample counting and fractional sample accumulation.
47
+ Subclasses should extend this if they need additional state.
48
+ """
49
+
50
+ counter: int = 0
51
+ """Current sample counter (total samples produced)."""
52
+
53
+ fractional_samples: float = 0.0
54
+ """Accumulated fractional samples for variable chunk mode."""
55
+
56
+
57
+ class BaseClockDrivenProducer(
58
+ BaseStatefulProcessor[ClockDrivenSettingsType, AxisArray.LinearAxis, AxisArray, StateType],
59
+ typing.Generic[ClockDrivenSettingsType, StateType],
60
+ ):
61
+ """
62
+ Base class for clock-driven data producers.
63
+
64
+ Accepts clock ticks (LinearAxis) as input and produces AxisArray output.
65
+ Handles all the timing/counter logic internally, so subclasses only need
66
+ to implement the data generation logic.
67
+
68
+ This eliminates the need for the Clock → Counter → Generator pattern
69
+ by combining the Counter functionality into the generator base class.
70
+
71
+ Subclasses must implement:
72
+ - ``_reset_state(time_axis)``: Initialize any state needed for production
73
+ - ``_produce(n_samples, time_axis)``: Generate the actual output data
74
+
75
+ Example::
76
+
77
+ @processor_state
78
+ class SinState(ClockDrivenState):
79
+ ang_freq: float = 0.0
80
+
81
+ class SinProducer(BaseClockDrivenProducer[SinSettings, SinState]):
82
+ def _reset_state(self, time_axis: AxisArray.TimeAxis) -> None:
83
+ self._state.ang_freq = 2 * np.pi * self.settings.fs
84
+
85
+ def _produce(self, n_samples: int, time_axis: AxisArray.TimeAxis) -> AxisArray:
86
+ t = (np.arange(n_samples) + self._state.counter) * time_axis.gain
87
+ data = np.sin(self._state.ang_freq * t)
88
+ return AxisArray(data=data, dims=["time"], axes={"time": time_axis})
89
+ """
90
+
91
+ def _hash_message(self, message: AxisArray.LinearAxis) -> int:
92
+ # Return constant hash - state should not reset based on clock rate changes.
93
+ # The producer maintains continuity regardless of clock rate changes.
94
+ return 0
95
+
96
+ def _compute_samples_and_offset(self, clock_tick: AxisArray.LinearAxis) -> tuple[int, float] | None:
97
+ """
98
+ Compute number of samples and time offset from a clock tick.
99
+
100
+ Returns:
101
+ Tuple of (n_samples, offset) or None if no samples to produce yet.
102
+
103
+ Raises:
104
+ ValueError: If clock gain is 0 (AFAP mode) and n_time is not specified.
105
+ """
106
+ if self.settings.n_time is not None:
107
+ # Fixed chunk size mode
108
+ n_samples = self.settings.n_time
109
+ if clock_tick.gain == 0.0:
110
+ # AFAP mode - synthetic offset based on counter
111
+ offset = self._state.counter / self.settings.fs
112
+ else:
113
+ # Use clock's timestamp
114
+ offset = clock_tick.offset
115
+ else:
116
+ # Variable chunk size mode - derive from clock gain
117
+ if clock_tick.gain == 0.0:
118
+ raise ValueError("Cannot use clock with gain=0 (AFAP) without specifying n_time")
119
+
120
+ # Calculate samples including fractional accumulation
121
+ samples_float = self.settings.fs * clock_tick.gain + self._state.fractional_samples
122
+ n_samples = int(samples_float + 1e-9)
123
+ self._state.fractional_samples = samples_float - n_samples
124
+
125
+ if n_samples == 0:
126
+ return None
127
+
128
+ offset = clock_tick.offset
129
+
130
+ return n_samples, offset
131
+
132
+ @abstractmethod
133
+ def _reset_state(self, time_axis: LinearAxis) -> None:
134
+ """
135
+ Reset/initialize state for production.
136
+
137
+ Called once before the first call to _produce, or when state needs resetting.
138
+ Use this to pre-compute values, create templates, etc.
139
+
140
+ Args:
141
+ time_axis: TimeAxis with the output sampling rate (fs) and initial offset.
142
+ """
143
+ ...
144
+
145
+ @abstractmethod
146
+ def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
147
+ """
148
+ Generate output data for this chunk.
149
+
150
+ Args:
151
+ n_samples: Number of samples to generate.
152
+ time_axis: TimeAxis with correct offset and gain (1/fs) for this chunk.
153
+
154
+ Returns:
155
+ AxisArray containing the generated data. The time axis should use
156
+ the provided time_axis or one derived from it.
157
+ """
158
+ ...
159
+
160
+ def _process(self, clock_tick: LinearAxis) -> AxisArray | None:
161
+ """
162
+ Process a clock tick and produce output.
163
+
164
+ Handles all the counter/timing logic internally, then calls _produce.
165
+ """
166
+ result = self._compute_samples_and_offset(clock_tick)
167
+ if result is None:
168
+ return None
169
+
170
+ n_samples, offset = result
171
+ time_axis = AxisArray.TimeAxis(fs=self.settings.fs, offset=offset)
172
+
173
+ # Call subclass production method
174
+ output = self._produce(n_samples, time_axis)
175
+
176
+ # Update counter
177
+ self._state.counter += n_samples
178
+
179
+ return output
ezmsg/baseproc/counter.py CHANGED
@@ -1,47 +1,32 @@
1
1
  """Counter generator for sample counting and timing."""
2
2
 
3
- import ezmsg.core as ez
4
3
  import numpy as np
5
- from ezmsg.util.messages.axisarray import AxisArray, replace
4
+ from ezmsg.util.messages.axisarray import AxisArray, LinearAxis, replace
6
5
 
6
+ from .clockdriven import (
7
+ BaseClockDrivenProducer,
8
+ ClockDrivenSettings,
9
+ ClockDrivenState,
10
+ )
7
11
  from .protocols import processor_state
8
- from .stateful import BaseStatefulTransformer
9
- from .units import BaseTransformerUnit
12
+ from .units import BaseClockDrivenProducerUnit
10
13
 
11
14
 
12
- class CounterSettings(ez.Settings):
15
+ class CounterSettings(ClockDrivenSettings):
13
16
  """Settings for :obj:`Counter` and :obj:`CounterTransformer`."""
14
17
 
15
- fs: float
16
- """Sampling rate in Hz."""
17
-
18
- n_time: int | None = None
19
- """
20
- Samples per block.
21
- - If specified: fixed chunk size (clock gain is ignored)
22
- - If None: derived from clock gain (fs * clock.gain), with fractional sample tracking
23
- """
24
-
25
18
  mod: int | None = None
26
19
  """If set, counter values rollover at this modulus."""
27
20
 
28
21
 
29
22
  @processor_state
30
- class CounterTransformerState:
23
+ class CounterTransformerState(ClockDrivenState):
31
24
  """State for :obj:`CounterTransformer`."""
32
25
 
33
- counter: int = 0
34
- """Current counter value (next sample index)."""
35
-
36
- fractional_samples: float = 0.0
37
- """Accumulated fractional samples for variable chunk mode."""
38
-
39
26
  template: AxisArray | None = None
40
27
 
41
28
 
42
- class CounterTransformer(
43
- BaseStatefulTransformer[CounterSettings, AxisArray.LinearAxis, AxisArray, CounterTransformerState]
44
- ):
29
+ class CounterTransformer(BaseClockDrivenProducer[CounterSettings, CounterTransformerState]):
45
30
  """
46
31
  Transforms clock ticks (LinearAxis) into AxisArray counter values.
47
32
 
@@ -49,80 +34,34 @@ class CounterTransformer(
49
34
  fixed (n_time setting) or derived from the clock's gain (fs * gain).
50
35
  """
51
36
 
52
- def _reset_state(self, message: AxisArray.LinearAxis) -> None:
53
- """Reset state - counter transformer state is simple, just reset values."""
54
- self._state.counter = 0
55
- self._state.fractional_samples = 0.0
37
+ def _reset_state(self, time_axis: LinearAxis) -> None:
38
+ """Reset state - initialize template for counter output."""
56
39
  self._state.template = AxisArray(
57
40
  data=np.array([], dtype=int),
58
41
  dims=["time"],
59
- axes={
60
- "time": AxisArray.TimeAxis(fs=self.settings.fs, offset=message.offset),
61
- },
42
+ axes={"time": time_axis},
62
43
  key="counter",
63
44
  )
64
45
 
65
- def _hash_message(self, message: AxisArray.LinearAxis) -> int:
66
- # Return constant hash - counter state should never reset based on message content.
67
- # The counter maintains continuity regardless of clock rate changes.
68
- return 0
69
-
70
- def _process(self, clock_tick: AxisArray.LinearAxis) -> AxisArray | None:
71
- """Transform a clock tick into counter AxisArray."""
72
- # Determine number of samples for this block
73
- if self.settings.n_time is not None:
74
- # Fixed chunk size mode
75
- n_samples = self.settings.n_time
76
- # Use wall clock or synthetic offset based on clock gain
77
- if clock_tick.gain == 0.0:
78
- # AFAP mode - synthetic offset
79
- offset = self.state.counter / self.settings.fs
80
- else:
81
- # Use clock's timestamp
82
- offset = clock_tick.offset
83
- else:
84
- # Variable chunk size mode - derive from clock gain
85
- if clock_tick.gain == 0.0:
86
- # AFAP with no fixed n_time - this is an error
87
- raise ValueError("Cannot use clock with gain=0 (AFAP) without specifying n_time")
88
-
89
- # Calculate samples including fractional accumulation
90
- # Add small epsilon to avoid floating point truncation errors (e.g., 0.9999999 -> 0)
91
- samples_float = self.settings.fs * clock_tick.gain + self.state.fractional_samples
92
- n_samples = int(samples_float + 1e-9)
93
- self.state.fractional_samples = samples_float - n_samples
94
-
95
- if n_samples == 0:
96
- # Not enough samples accumulated yet
97
- # TODO: Return empty array. What should offset be?
98
- return None
99
-
100
- # Use clock's timestamp for offset
101
- offset = clock_tick.offset
102
-
103
- # Generate counter data
104
- block_samp = np.arange(self.state.counter, self.state.counter + n_samples)
46
+ def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
47
+ """Generate counter values for this chunk."""
48
+ # Generate counter data (using pre-increment counter value)
49
+ block_samp = np.arange(self._state.counter, self._state.counter + n_samples)
105
50
  if self.settings.mod is not None:
106
51
  block_samp = block_samp % self.settings.mod
107
52
 
108
- # Create output AxisArray
109
- result = replace(
53
+ return replace(
110
54
  self._state.template,
111
55
  data=block_samp,
112
- axes={"time": replace(self._state.template.axes["time"], offset=offset)},
56
+ axes={"time": time_axis},
113
57
  )
114
58
 
115
- # Update state
116
- self.state.counter += n_samples
117
-
118
- return result
119
-
120
59
 
121
- class Counter(BaseTransformerUnit[CounterSettings, AxisArray.LinearAxis, AxisArray, CounterTransformer]):
60
+ class Counter(BaseClockDrivenProducerUnit[CounterSettings, CounterTransformer]):
122
61
  """
123
62
  Transforms clock ticks into monotonically increasing counter values as AxisArray.
124
63
 
125
- Receives timing from INPUT_SIGNAL (LinearAxis from Clock) and outputs AxisArray.
64
+ Receives timing from INPUT_CLOCK (LinearAxis from Clock) and outputs AxisArray.
126
65
  """
127
66
 
128
67
  SETTINGS = CounterSettings
ezmsg/baseproc/units.py CHANGED
@@ -7,8 +7,9 @@ from abc import ABC, abstractmethod
7
7
 
8
8
  import ezmsg.core as ez
9
9
  from ezmsg.util.generator import GenState
10
- from ezmsg.util.messages.axisarray import AxisArray
10
+ from ezmsg.util.messages.axisarray import AxisArray, LinearAxis
11
11
 
12
+ from .clockdriven import BaseClockDrivenProducer
12
13
  from .composite import CompositeProcessor
13
14
  from .processor import BaseConsumer, BaseProducer, BaseTransformer
14
15
  from .protocols import MessageInType, MessageOutType, SettingsType
@@ -25,6 +26,7 @@ TransformerType = typing.TypeVar(
25
26
  bound=BaseTransformer | BaseStatefulTransformer | CompositeProcessor,
26
27
  )
27
28
  AdaptiveTransformerType = typing.TypeVar("AdaptiveTransformerType", bound=BaseAdaptiveTransformer)
29
+ ClockDrivenProducerType = typing.TypeVar("ClockDrivenProducerType", bound=BaseClockDrivenProducer)
28
30
 
29
31
 
30
32
  def get_base_producer_type(cls: type) -> type:
@@ -43,6 +45,10 @@ def get_base_adaptive_transformer_type(cls: type) -> type:
43
45
  return resolve_typevar(cls, AdaptiveTransformerType)
44
46
 
45
47
 
48
+ def get_base_clockdriven_producer_type(cls: type) -> type:
49
+ return resolve_typevar(cls, ClockDrivenProducerType)
50
+
51
+
46
52
  # --- Base classes for ezmsg Unit with specific processing capabilities ---
47
53
  class BaseProducerUnit(ez.Unit, ABC, typing.Generic[SettingsType, MessageOutType, ProducerType]):
48
54
  """
@@ -240,6 +246,47 @@ class BaseAdaptiveTransformerUnit(
240
246
  await self.processor.apartial_fit(msg)
241
247
 
242
248
 
249
+ class BaseClockDrivenProducerUnit(
250
+ BaseProcessorUnit[SettingsType],
251
+ ABC,
252
+ typing.Generic[SettingsType, ClockDrivenProducerType],
253
+ ):
254
+ """
255
+ Base class for clock-driven producer units.
256
+
257
+ These units receive clock ticks (LinearAxis) and produce AxisArray output.
258
+ This simplifies the Clock → Counter → Generator pattern by combining
259
+ the counter functionality into the generator.
260
+
261
+ Implement a new Unit as follows::
262
+
263
+ class SinGeneratorUnit(BaseClockDrivenProducerUnit[
264
+ SinGeneratorSettings, # SettingsType (must extend ClockDrivenSettings)
265
+ SinProducer, # ClockDrivenProducerType
266
+ ]):
267
+ SETTINGS = SinGeneratorSettings
268
+
269
+ Where SinGeneratorSettings extends ClockDrivenSettings and SinProducer
270
+ extends BaseClockDrivenProducer.
271
+ """
272
+
273
+ INPUT_CLOCK = ez.InputStream(LinearAxis)
274
+ OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
275
+
276
+ def create_processor(self) -> None:
277
+ """Create the clock-driven producer instance from settings."""
278
+ producer_type = get_base_clockdriven_producer_type(self.__class__)
279
+ self.processor = producer_type(settings=self.SETTINGS)
280
+
281
+ @ez.subscriber(INPUT_CLOCK, zero_copy=True)
282
+ @ez.publisher(OUTPUT_SIGNAL)
283
+ @profile_subpub(trace_oldest=False)
284
+ async def on_clock(self, clock_tick: LinearAxis) -> typing.AsyncGenerator:
285
+ result = await self.processor.__acall__(clock_tick)
286
+ if result is not None:
287
+ yield self.OUTPUT_SIGNAL, result
288
+
289
+
243
290
  # Legacy class
244
291
  class GenAxisArray(ez.Unit):
245
292
  STATE = GenState
@@ -11,6 +11,10 @@ def resolve_typevar(cls: type, target_typevar: typing.TypeVar) -> type:
11
11
  and checks the original bases of each class in the MRO for the TypeVar.
12
12
  If the TypeVar is found, it returns the concrete type bound to it.
13
13
  If the TypeVar is not found, it raises a TypeError.
14
+
15
+ If the resolved type is itself a TypeVar, this function recursively
16
+ resolves it until a concrete type is found.
17
+
14
18
  Args:
15
19
  cls (type): The class to inspect.
16
20
  target_typevar (typing.TypeVar): The TypeVar to resolve.
@@ -30,7 +34,11 @@ def resolve_typevar(cls: type, target_typevar: typing.TypeVar) -> type:
30
34
  index = params.index(target_typevar)
31
35
  args = typing.get_args(orig_base)
32
36
  try:
33
- return args[index]
37
+ resolved = args[index]
38
+ # If the resolved type is itself a TypeVar, resolve it recursively
39
+ if isinstance(resolved, typing.TypeVar):
40
+ return resolve_typevar(cls, resolved)
41
+ return resolved
34
42
  except IndexError:
35
43
  pass
36
44
  raise TypeError(f"Could not resolve {target_typevar} in {cls}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ezmsg-baseproc
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Base processor classes and protocols for ezmsg signal processing pipelines
5
5
  Author-email: Griffin Milsap <griffin.milsap@gmail.com>, Preston Peranich <pperanich@gmail.com>, Chadwick Boulay <chadwick.boulay@gmail.com>, Kyle McGraw <kmcgraw@blackrockneuro.com>
6
6
  License-Expression: MIT
@@ -1,18 +1,19 @@
1
- ezmsg/baseproc/__init__.py,sha256=OLpOqiGBjQSrLPjAtWEHdCS8Sm_2tJ55I7bpCtXntYY,4243
2
- ezmsg/baseproc/__version__.py,sha256=ePNVzJOkxR8FY5bezqKQ_fgBRbzH1G7QTaRDHvGQRAY,704
1
+ ezmsg/baseproc/__init__.py,sha256=cSkyXkfPvIiAxe6maHiZvJpFbdRw3xTEitBClXeehec,4755
2
+ ezmsg/baseproc/__version__.py,sha256=-uLONazCO1SzFfcY-K6A1keL--LIVfTYccGX6ciADac,704
3
3
  ezmsg/baseproc/clock.py,sha256=jRJGxaWWBek503OcGRYsW4Qc7Lt4m-tVl1wn_v-qCIk,3254
4
+ ezmsg/baseproc/clockdriven.py,sha256=ckPZSVHZfYfjFRHDCERUWjDwyQgOu-aRTNsJ3EDFBaI,6161
4
5
  ezmsg/baseproc/composite.py,sha256=Lin4K_rmS2Tnxt-m8daP-PUyeeqL4id5JkVh-AUNrQw,14901
5
- ezmsg/baseproc/counter.py,sha256=3JR4AQ_wwZhdjKQdPlLhjhNVLHE5fj9ggwUPtJhWDS4,4601
6
+ ezmsg/baseproc/counter.py,sha256=kcBPiVxMPULp4ojnVESNw7mn_4v0xSODfASHrL83GtM,2168
6
7
  ezmsg/baseproc/processor.py,sha256=Ir9FtNuVG4yc-frwNxoYrlld99ff1mXwwGWaHxEJ6tY,8056
7
8
  ezmsg/baseproc/protocols.py,sha256=O3Qp0ymE9Ovlmh8t22v-lMmFzuWK0D93REAYMnJV3xA,5106
8
9
  ezmsg/baseproc/stateful.py,sha256=-jjAZIyJA5eiTECi1fSfazfqgv__RtyqPp1ZvLFFIDI,11424
9
- ezmsg/baseproc/units.py,sha256=TRhjDKw0lqUUst7BHYJKP3AhGpRd6mvcdKxULfeWjA0,10283
10
+ ezmsg/baseproc/units.py,sha256=byFijVLEZFO145HE74sZk1_qpCu6nFjB8-vSYz9Grds,12077
10
11
  ezmsg/baseproc/util/__init__.py,sha256=hvMUJOBuqioER50GZ5-GZiQbQ9NtQYEze13ZlR2jbMA,37
11
12
  ezmsg/baseproc/util/asio.py,sha256=0sF5oDc58DSLlcEgoUpNiqjjcbqnZhjSpQrXn6IdosM,4960
12
13
  ezmsg/baseproc/util/message.py,sha256=l_b1b6bXX8N6VF9RbUELzsHs73cKkDURBdIr0lt3CY0,909
13
14
  ezmsg/baseproc/util/profile.py,sha256=MOQDsFsW6ddXT0uAOgytW3aK_AZW5ieA16Pz2hWuE2o,6189
14
- ezmsg/baseproc/util/typeresolution.py,sha256=WCHHYIrMMZ1CfwJWVlJPQgFyY2gnGRNFJVQynAsee7Y,3113
15
- ezmsg_baseproc-1.1.0.dist-info/METADATA,sha256=zv5si0HBsR2t8lzzmwzeuLiik4SeanZvmV-JNWqOnKg,3415
16
- ezmsg_baseproc-1.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
- ezmsg_baseproc-1.1.0.dist-info/licenses/LICENSE,sha256=BDD8rfac1Ur7mp0_3izEdr6fHgSA3Or6U1Kb0ZAWsow,1066
18
- ezmsg_baseproc-1.1.0.dist-info/RECORD,,
15
+ ezmsg/baseproc/util/typeresolution.py,sha256=5on4QcrYd1rxsRoDEqivNjuWT5BkU-Wg7XdTNaOircI,3485
16
+ ezmsg_baseproc-1.2.0.dist-info/METADATA,sha256=H2jTn5VSw0pEwQWLdd3Hu0G1OCbbKM-On4AmKyyyfm4,3415
17
+ ezmsg_baseproc-1.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
+ ezmsg_baseproc-1.2.0.dist-info/licenses/LICENSE,sha256=BDD8rfac1Ur7mp0_3izEdr6fHgSA3Or6U1Kb0ZAWsow,1066
19
+ ezmsg_baseproc-1.2.0.dist-info/RECORD,,