mycorrhizal 0.1.0__py3-none-any.whl → 0.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.
Files changed (45) hide show
  1. mycorrhizal/_version.py +1 -0
  2. mycorrhizal/common/__init__.py +15 -3
  3. mycorrhizal/common/cache.py +114 -0
  4. mycorrhizal/common/compilation.py +263 -0
  5. mycorrhizal/common/interface_detection.py +159 -0
  6. mycorrhizal/common/interfaces.py +3 -50
  7. mycorrhizal/common/mermaid.py +124 -0
  8. mycorrhizal/common/wrappers.py +1 -1
  9. mycorrhizal/hypha/core/builder.py +56 -8
  10. mycorrhizal/hypha/core/runtime.py +242 -107
  11. mycorrhizal/hypha/core/specs.py +19 -3
  12. mycorrhizal/mycelium/__init__.py +174 -0
  13. mycorrhizal/mycelium/core.py +619 -0
  14. mycorrhizal/mycelium/exceptions.py +30 -0
  15. mycorrhizal/mycelium/hypha_bridge.py +1143 -0
  16. mycorrhizal/mycelium/instance.py +440 -0
  17. mycorrhizal/mycelium/pn_context.py +276 -0
  18. mycorrhizal/mycelium/runner.py +165 -0
  19. mycorrhizal/mycelium/spores_integration.py +655 -0
  20. mycorrhizal/mycelium/tree_builder.py +102 -0
  21. mycorrhizal/mycelium/tree_spec.py +197 -0
  22. mycorrhizal/rhizomorph/README.md +82 -33
  23. mycorrhizal/rhizomorph/core.py +308 -82
  24. mycorrhizal/septum/TRANSITION_REFERENCE.md +385 -0
  25. mycorrhizal/{enoki → septum}/core.py +326 -100
  26. mycorrhizal/{enoki → septum}/testing_utils.py +7 -7
  27. mycorrhizal/{enoki → septum}/util.py +44 -21
  28. mycorrhizal/spores/__init__.py +72 -19
  29. mycorrhizal/spores/core.py +907 -75
  30. mycorrhizal/spores/dsl/__init__.py +8 -8
  31. mycorrhizal/spores/dsl/hypha.py +3 -15
  32. mycorrhizal/spores/dsl/rhizomorph.py +3 -11
  33. mycorrhizal/spores/dsl/{enoki.py → septum.py} +26 -77
  34. mycorrhizal/spores/encoder/json.py +21 -12
  35. mycorrhizal/spores/extraction.py +14 -11
  36. mycorrhizal/spores/models.py +75 -20
  37. mycorrhizal/spores/transport/__init__.py +9 -2
  38. mycorrhizal/spores/transport/base.py +36 -17
  39. mycorrhizal/spores/transport/file.py +126 -0
  40. mycorrhizal-0.2.0.dist-info/METADATA +335 -0
  41. mycorrhizal-0.2.0.dist-info/RECORD +54 -0
  42. mycorrhizal-0.1.0.dist-info/METADATA +0 -198
  43. mycorrhizal-0.1.0.dist-info/RECORD +0 -37
  44. /mycorrhizal/{enoki → septum}/__init__.py +0 -0
  45. {mycorrhizal-0.1.0.dist-info → mycorrhizal-0.2.0.dist-info}/WHEEL +0 -0
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Enoki Testing Utilities
3
+ Septum Testing Utilities
4
4
 
5
- Testing infrastructure for the class-method-based Enoki architecture.
5
+ Testing infrastructure for the class-method-based Septum architecture.
6
6
  """
7
7
 
8
8
  import unittest
@@ -13,8 +13,8 @@ import gevent
13
13
  from gevent import sleep
14
14
  import traceback
15
15
 
16
- # Import the Enoki modules
17
- from mycorrhizal.enoki import (
16
+ # Import the Septum modules
17
+ from mycorrhizal.septum import (
18
18
  State,
19
19
  StateMachine,
20
20
  StateConfiguration,
@@ -151,8 +151,8 @@ def simulate_state_lifecycle(
151
151
  return results
152
152
 
153
153
 
154
- class EnokiTestCase(unittest.TestCase):
155
- """Base test case class with assertion methods for Enoki FSM testing."""
154
+ class SeptumTestCase(unittest.TestCase):
155
+ """Base test case class with assertion methods for Septum FSM testing."""
156
156
 
157
157
  def assertStateTransition(
158
158
  self, state_class, expected_transition, context=None, **context_kwargs
@@ -328,7 +328,7 @@ class EnokiTestCase(unittest.TestCase):
328
328
  self.assertIsNotNone(timeout_result, "on_timeout should return a transition")
329
329
 
330
330
 
331
- class StateMachineTestCase(EnokiTestCase):
331
+ class StateMachineTestCase(SeptumTestCase):
332
332
  """Extended test case for testing full StateMachine behavior."""
333
333
 
334
334
  def assertFSMTransition(
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Enoki State Machine Utilities
3
+ Septum State Machine Utilities
4
4
 
5
5
  Utility functions for FSM operations like Mermaid diagram generation.
6
6
  """
@@ -16,6 +16,24 @@ class _TransitionInfo:
16
16
  transition_type: str # "normal", "push", "pop", "again", "retry", etc.
17
17
 
18
18
 
19
+ def _short_state_name(state_name: str) -> str:
20
+ """
21
+ Extract short state name from full qualified name.
22
+
23
+ Converts 'examples.septum.network_protocol_fsm.IdleState' to 'IdleState'.
24
+ Handles edge cases like '<string>.IdleState' gracefully.
25
+
26
+ Args:
27
+ state_name: Full state name with module path
28
+
29
+ Returns:
30
+ Short state name (just the class name)
31
+ """
32
+ if "." in state_name:
33
+ return state_name.rsplit(".", 1)[-1]
34
+ return state_name
35
+
36
+
19
37
  def to_mermaid(fsm) -> str:
20
38
  """
21
39
  Generate Mermaid diagram from a StateMachine instance.
@@ -24,15 +42,15 @@ def to_mermaid(fsm) -> str:
24
42
  including special handling for Push/Pop transitions.
25
43
 
26
44
  Args:
27
- fsm: A StateMachine instance (from mycorrhizal.enoki.core)
45
+ fsm: A StateMachine instance (from mycorrhizal.septum.core)
28
46
 
29
47
  Returns:
30
48
  Mermaid diagram as a string
31
49
 
32
50
  Example:
33
51
  ```python
34
- from mycorrhizal.enoki.core import StateMachine
35
- from mycorrhizal.enoki.util import to_mermaid
52
+ from mycorrhizal.septum.core import StateMachine
53
+ from mycorrhizal.septum.util import to_mermaid
36
54
 
37
55
  fsm = StateMachine(initial_state=MyState)
38
56
  await fsm.initialize()
@@ -43,7 +61,7 @@ def to_mermaid(fsm) -> str:
43
61
  ```
44
62
  """
45
63
  # Import here to avoid circular imports
46
- from mycorrhizal.enoki.core import (
64
+ from mycorrhizal.septum.core import (
47
65
  StateSpec,
48
66
  StateRef,
49
67
  LabeledTransition,
@@ -77,12 +95,12 @@ def to_mermaid(fsm) -> str:
77
95
 
78
96
  # Add initial state marker
79
97
  initial_name = fsm.initial_state.name if isinstance(fsm.initial_state, StateSpec) else str(fsm.initial_state)
80
- lines.append(f" start((start)) --> {nid(initial_name)}")
98
+ lines.append(f' start((start)) --> {nid(initial_name)}')
81
99
 
82
100
  # Add error state if exists
83
101
  if fsm.error_state:
84
102
  error_name = fsm.error_state.name if isinstance(fsm.error_state, StateSpec) else str(fsm.error_state)
85
- lines.append(f" {nid(error_name)}(((ERROR)))")
103
+ lines.append(f' {nid(error_name)}{{"ERROR"}}')
86
104
 
87
105
  # Collect all transitions
88
106
  transitions: Dict[str, List[_TransitionInfo]] = {}
@@ -135,8 +153,9 @@ def to_mermaid(fsm) -> str:
135
153
  # Push transitions - show all states being pushed
136
154
  for push_state in p.push_states:
137
155
  push_name = push_state.name if isinstance(push_state, StateSpec) else str(push_state)
156
+ # Use -> instead of unicode arrow for better compatibility
138
157
  transitions[state_name].append(
139
- _TransitionInfo(f"{label.name}push", push_name, "push")
158
+ _TransitionInfo(f"{label.name}->push", push_name, "push")
140
159
  )
141
160
  case StateSpec() as target_state:
142
161
  transitions[state_name].append(
@@ -182,39 +201,43 @@ def to_mermaid(fsm) -> str:
182
201
  continue
183
202
 
184
203
  state_id = nid(state_name)
204
+ short_name = _short_state_name(state_name)
185
205
 
186
206
  # Determine node shape based on state properties
207
+ # All nodes use quotes to handle special characters
187
208
  if state.config.terminal:
188
- shape = f'[{state_name} (**terminal**)]'
209
+ # Terminal state: use double parentheses and note terminal status
210
+ lines.append(f' {state_id}[["{short_name}<br/>terminal"]]')
189
211
  elif state_name == initial_name:
190
- shape = f'[{state_name}]'
212
+ # Initial state: normal shape
213
+ lines.append(f' {state_id}["{short_name}"]')
191
214
  elif fsm.error_state and state_name == (fsm.error_state.name if isinstance(fsm.error_state, StateSpec) else str(fsm.error_state)):
192
- shape = f'[{state_name}]'
215
+ # Error state: use rhombus shape
216
+ lines.append(f' {state_id}{{"{short_name}"}}')
193
217
  else:
194
- shape = f'[{state_name}]'
195
-
196
- lines.append(f" {state_id}{shape}")
218
+ # Normal state
219
+ lines.append(f' {state_id}["{short_name}"]')
197
220
 
198
221
  # Add transitions
199
222
  for trans in transitions.get(state_name, []):
200
223
  if trans.transition_type in ("again", "retry", "repeat", "restart", "unhandled"):
201
- # Self-loop
202
- label = f'"{trans.transition_type}"' if trans.transition_type else ""
203
- lines.append(f" {state_id} -->|{label}| {state_id}")
224
+ # Self-loop with transition type
225
+ lines.append(f' {state_id} -->|"{trans.transition_type}"| {state_id}')
204
226
  elif trans.transition_type == "pop":
205
227
  # Pop goes to a special pop node
206
- lines.append(f" {state_id} -->|\"{trans.label or 'pop'}\"| pop((pop))")
228
+ label = trans.label if trans.label else "pop"
229
+ lines.append(f' {state_id} -->|"{label}"| pop((pop))')
207
230
  elif trans.transition_type == "push":
208
231
  # Push transition
209
232
  target_id = nid(trans.target)
210
- lines.append(f" {state_id} -->|\"{trans.label}\"| {target_id}")
233
+ lines.append(f' {state_id} -->|"{trans.label}"| {target_id}')
211
234
  else:
212
235
  # Normal transition
213
236
  target_id = nid(trans.target)
214
- lines.append(f" {state_id} -->|\"{trans.label}\"| {target_id}")
237
+ lines.append(f' {state_id} -->|"{trans.label}"| {target_id}')
215
238
 
216
239
  # Add pop node if any pop transitions exist
217
240
  if any(t.transition_type == "pop" for transs in transitions.values() for t in transs):
218
- lines.append(" pop((pop))")
241
+ lines.append(' pop((pop))')
219
242
 
220
243
  return "\n".join(lines)
@@ -6,22 +6,53 @@ OCEL (Object-Centric Event Log) compatible logging system for tracking events
6
6
  and objects in Mycorrhizal systems. Provides automatic extraction of events
7
7
  and objects from DSL execution.
8
8
 
9
- Usage:
10
- from mycorrhizal.spores import configure, spore, EventAttr, ObjectRef
11
-
12
- # Configure logging
13
- configure(
14
- enabled=True,
15
- object_cache_size=100,
16
- transport=FileTransport("logs/ocel.jsonl"),
9
+ Usage (Standalone Event and Object Logging):
10
+ from mycorrhizal.spores import configure, get_spore_sync, get_spore_async
11
+ from mycorrhizal.spores.transport import SyncFileTransport, AsyncFileTransport
12
+ from mycorrhizal.spores.models import SporesAttr
13
+ from pydantic import BaseModel
14
+ from typing import Annotated
15
+
16
+ # Mark domain model fields for object attribute logging
17
+ class Order(BaseModel):
18
+ id: str
19
+ status: Annotated[str, SporesAttr]
20
+ total: Annotated[float, SporesAttr]
21
+
22
+ # Sync code
23
+ configure(transport=SyncFileTransport("logs/ocel.jsonl"))
24
+ spore = get_spore_sync(__name__)
25
+
26
+ @spore.log_event(
27
+ event_type="OrderCreated",
28
+ relationships={"order": ("return", "Order")},
29
+ )
30
+ def create_order(customer: Customer, items: list) -> Order:
31
+ return Order(...)
32
+
33
+ # Async code
34
+ configure(transport=AsyncFileTransport("logs/ocel.jsonl"))
35
+ aspore = get_spore_async(__name__)
36
+
37
+ @aspore.log_event(
38
+ event_type="OrderCreated",
39
+ relationships={"order": ("return", "Order")},
17
40
  )
41
+ async def create_order_async(customer: Customer, items: list) -> Order:
42
+ return Order(...)
43
+
44
+ Usage (DSL Adapters):
45
+ from mycorrhizal.spores import configure, spore
46
+ from mycorrhizal.spores.models import EventAttr, ObjectRef, ObjectScope
47
+ from pydantic import BaseModel
48
+ from typing import Annotated
18
49
 
19
- # Mark model fields for automatic extraction
50
+ # Mark blackboard fields for event attribute extraction
20
51
  class MissionContext(BaseModel):
21
52
  mission_id: Annotated[str, EventAttr]
22
53
  robot: Annotated[Robot, ObjectRef(qualifier="actor", scope=ObjectScope.GLOBAL)]
23
54
 
24
- # Mark object types
55
+ # Mark object types for logging
25
56
  @spore.object(object_type="Robot")
26
57
  class Robot(BaseModel):
27
58
  id: str
@@ -30,14 +61,17 @@ Usage:
30
61
  DSL Adapters:
31
62
  HyphaAdapter - Log Petri net transitions with token relationships
32
63
  RhizomorphAdapter - Log behavior tree node execution with status
33
- EnokiAdapter - Log state machine execution and lifecycle events
64
+ SeptumAdapter - Log state machine execution and lifecycle events
65
+
66
+ Annotation Types:
67
+ EventAttr - Mark blackboard fields for automatic event attribute extraction (DSL adapters)
68
+ SporesAttr - Mark domain model fields for automatic object attribute logging (log_event)
69
+ ObjectRef - Mark blackboard fields as object references with scope
34
70
 
35
71
  Key Concepts:
36
72
  Event - Something that happens at a point in time with attributes
37
73
  Object - An entity with a type and attributes
38
74
  Relationship - Link between events and objects (e.g., "actor", "target")
39
- EventAttr - Mark blackboard fields for automatic event attribute extraction
40
- ObjectRef - Mark blackboard fields as object references with scope
41
75
 
42
76
  Object Scopes:
43
77
  EVENT - Object exists only for this event (default)
@@ -48,8 +82,13 @@ from .core import (
48
82
  configure,
49
83
  get_config,
50
84
  get_object_cache,
85
+ get_spore_sync,
86
+ get_spore_async,
51
87
  spore,
52
88
  SporesConfig,
89
+ EventLogger,
90
+ AsyncEventLogger,
91
+ SyncEventLogger,
53
92
  )
54
93
 
55
94
  from .models import (
@@ -62,6 +101,7 @@ from .models import (
62
101
  ObjectRef,
63
102
  ObjectScope,
64
103
  EventAttr,
104
+ SporesAttr,
65
105
  generate_event_id,
66
106
  generate_object_id,
67
107
  attribute_value_from_python,
@@ -71,17 +111,20 @@ from .models import (
71
111
  from .cache import ObjectLRUCache
72
112
 
73
113
  from .encoder import Encoder, JSONEncoder
74
- from .transport import Transport
114
+ from .transport import SyncTransport, AsyncTransport, Transport, SyncFileTransport, AsyncFileTransport
75
115
 
76
116
  # DSL adapters
77
117
  from .dsl import (
78
118
  HyphaAdapter,
79
119
  RhizomorphAdapter,
80
- EnokiAdapter,
120
+ SeptumAdapter,
81
121
  )
82
122
 
83
123
 
84
- __version__ = "0.1.0"
124
+ try:
125
+ from mycorrhizal._version import version as __version__
126
+ except ImportError:
127
+ __version__ = "0.0.0+dev"
85
128
 
86
129
 
87
130
  __all__ = [
@@ -89,7 +132,12 @@ __all__ = [
89
132
  'configure',
90
133
  'get_config',
91
134
  'get_object_cache',
92
- 'spore',
135
+ 'get_spore_sync',
136
+ 'get_spore_async',
137
+ 'EventLogger',
138
+ 'AsyncEventLogger',
139
+ 'SyncEventLogger',
140
+ 'spore', # For DSL adapters and @spore.object() decorator
93
141
  'SporesConfig',
94
142
 
95
143
  # Models
@@ -102,6 +150,7 @@ __all__ = [
102
150
  'ObjectRef',
103
151
  'ObjectScope',
104
152
  'EventAttr',
153
+ 'SporesAttr',
105
154
  'generate_event_id',
106
155
  'generate_object_id',
107
156
  'attribute_value_from_python',
@@ -115,10 +164,14 @@ __all__ = [
115
164
  'JSONEncoder',
116
165
 
117
166
  # Transport
118
- 'Transport',
167
+ 'SyncTransport',
168
+ 'AsyncTransport',
169
+ 'Transport', # Alias for AsyncTransport (backward compatibility)
170
+ 'SyncFileTransport',
171
+ 'AsyncFileTransport',
119
172
 
120
173
  # DSL Adapters
121
174
  'HyphaAdapter',
122
175
  'RhizomorphAdapter',
123
- 'EnokiAdapter',
176
+ 'SeptumAdapter',
124
177
  ]