mycorrhizal 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -0,0 +1 @@
1
+ version = "0.1.2"
@@ -6,7 +6,7 @@ NetBuilder provides the API for declaratively constructing Petri net specificati
6
6
  Used within @pn.net decorated functions.
7
7
  """
8
8
 
9
- from typing import Optional, Callable, List, Dict, Type, Any
9
+ from typing import Optional, Callable, List, Dict, Type, Any, Union
10
10
  from .specs import (
11
11
  NetSpec,
12
12
  PlaceSpec,
@@ -54,14 +54,52 @@ class NetBuilder:
54
54
 
55
55
  def place(
56
56
  self,
57
- name: str,
57
+ name_or_func: Union[str, Callable, None] = None,
58
58
  type: PlaceType = PlaceType.BAG,
59
59
  state_factory: Optional[Callable] = None,
60
- ) -> PlaceRef:
61
- """Declare a regular place"""
62
- place_spec = PlaceSpec(name, type, state_factory=state_factory)
63
- self.spec.places[name] = place_spec
64
- return PlaceRef(name, self.spec)
60
+ ) -> Union[PlaceRef, Callable]:
61
+ """Declare a regular place.
62
+
63
+ Can be used in three ways:
64
+
65
+ 1. As a method call (returns PlaceRef for use in arcs):
66
+ queue = builder.place("queue", type=PlaceType.QUEUE)
67
+
68
+ 2. As a decorator without parens (infers name from function):
69
+ @builder.place
70
+ def queue(bb):
71
+ return bb.tokens
72
+
73
+ 3. As a decorator with custom name:
74
+ @builder.place("custom_name")
75
+ def my_func(bb):
76
+ return bb.tokens
77
+ """
78
+ # Case 1: Used as decorator without parens - first arg is the function
79
+ if callable(name_or_func):
80
+ func = name_or_func
81
+ place_name = func.__name__
82
+ place_spec = PlaceSpec(place_name, type, handler=func, state_factory=state_factory)
83
+ self.spec.places[place_name] = place_spec
84
+ return PlaceRef(place_name, self.spec)
85
+
86
+ # Case 2 & 3: Used with explicit name (as method call or decorator with args)
87
+ # or called without any args (need to return decorator)
88
+ if isinstance(name_or_func, str):
89
+ name = name_or_func
90
+ place_spec = PlaceSpec(name, type, state_factory=state_factory)
91
+ self.spec.places[name] = place_spec
92
+ # Return PlaceRef - it's callable via __call__ for decorator use
93
+ return PlaceRef(name, self.spec)
94
+
95
+ # Case 4: Called without any args - shouldn't happen but handle gracefully
96
+ # This would be like @builder.place() with empty parens
97
+ def decorator(func: Callable) -> PlaceRef:
98
+ place_name = func.__name__
99
+ place_spec = PlaceSpec(place_name, type, handler=func, state_factory=state_factory)
100
+ self.spec.places[place_name] = place_spec
101
+ return PlaceRef(place_name, self.spec)
102
+ return decorator
65
103
 
66
104
  def io_input_place(self):
67
105
  """Decorator for IOInputPlace with async generator"""
@@ -147,11 +147,11 @@ class PlaceRef:
147
147
  def __init__(self, local_name: str, parent_spec: NetSpec):
148
148
  self.local_name = local_name
149
149
  self.parent_spec = parent_spec
150
-
150
+
151
151
  def get_fqn(self) -> str:
152
152
  """Compute FQN dynamically"""
153
153
  return '.'.join(self.get_parts())
154
-
154
+
155
155
  # For backward compatibility
156
156
  @property
157
157
  def fqn(self) -> str:
@@ -164,7 +164,23 @@ class PlaceRef:
164
164
  @property
165
165
  def parts(self) -> List[str]:
166
166
  return self.get_parts()
167
-
167
+
168
+ def __call__(self, func: Callable) -> "PlaceRef":
169
+ """Allow PlaceRef to be used as a decorator to register a handler.
170
+
171
+ Example:
172
+ @builder.place("queue")
173
+ def queue_handler(bb):
174
+ return bb.tokens
175
+
176
+ The PlaceRef is returned for use in arcs, and the handler is registered.
177
+ """
178
+ # Get the place spec and register the handler
179
+ place_spec = self.parent_spec.places.get(self.local_name)
180
+ if place_spec is not None:
181
+ place_spec.handler = func
182
+ return self
183
+
168
184
  def __repr__(self):
169
185
  return f"PlaceRef({self.get_fqn()})"
170
186
 
@@ -1309,9 +1309,36 @@ class _BT:
1309
1309
  self._tracking_stack[-1].append((fn.__name__, fn))
1310
1310
  return fn
1311
1311
 
1312
- def sequence(self, *, memory: bool = True):
1313
- """Decorator to mark a generator function as a sequence composite."""
1312
+ def sequence(self, func_or_none=None, *, memory: bool = True):
1313
+ """Decorator to mark a generator function as a sequence composite.
1314
1314
 
1315
+ Can be used with or without parentheses:
1316
+ @bt.sequence
1317
+ def root():
1318
+ ...
1319
+
1320
+ @bt.sequence(memory=False)
1321
+ def root():
1322
+ ...
1323
+ """
1324
+ # Case 1: Used as @bt.sequence (no parens)
1325
+ if callable(func_or_none):
1326
+ # Apply decorator directly with defaults
1327
+ spec = NodeSpec(
1328
+ kind=NodeSpecKind.SEQUENCE,
1329
+ name=_name_of(func_or_none),
1330
+ payload={"factory": func_or_none, "memory": memory},
1331
+ )
1332
+ func_or_none.node_spec = spec # type: ignore
1333
+ if self._tracking_stack:
1334
+ self._tracking_stack[-1].append((func_or_none.__name__, func_or_none))
1335
+ return func_or_none
1336
+
1337
+ # Case 2: Used as @bt.sequence() or @bt.sequence(memory=False)
1338
+ return self._sequence_impl(memory=memory)
1339
+
1340
+ def _sequence_impl(self, memory: bool):
1341
+ """Implementation of sequence decorator."""
1315
1342
  def deco(
1316
1343
  factory: Callable[..., Generator[Any, None, None]],
1317
1344
  ) -> Callable[..., Generator[Any, None, None]]:
@@ -1327,9 +1354,40 @@ class _BT:
1327
1354
 
1328
1355
  return deco
1329
1356
 
1330
- def selector(self, *, memory: bool = True, reactive: bool = False):
1331
- """Decorator to mark a generator function as a selector composite."""
1357
+ def selector(self, func_or_none=None, *, memory: bool = True, reactive: bool = False):
1358
+ """Decorator to mark a generator function as a selector composite.
1359
+
1360
+ Can be used with or without parentheses:
1361
+ @bt.selector
1362
+ def root():
1363
+ ...
1364
+
1365
+ @bt.selector(memory=False)
1366
+ def root():
1367
+ ...
1368
+
1369
+ @bt.selector(reactive=True)
1370
+ def root():
1371
+ ...
1372
+ """
1373
+ # Case 1: Used as @bt.selector (no parens)
1374
+ if callable(func_or_none):
1375
+ # Apply decorator directly with defaults
1376
+ spec = NodeSpec(
1377
+ kind=NodeSpecKind.SELECTOR,
1378
+ name=_name_of(func_or_none),
1379
+ payload={"factory": func_or_none, "memory": memory, "reactive": reactive},
1380
+ )
1381
+ func_or_none.node_spec = spec # type: ignore
1382
+ if self._tracking_stack:
1383
+ self._tracking_stack[-1].append((func_or_none.__name__, func_or_none))
1384
+ return func_or_none
1385
+
1386
+ # Case 2: Used as @bt.selector() or @bt.selector(memory=False)
1387
+ return self._selector_impl(memory=memory, reactive=reactive)
1332
1388
 
1389
+ def _selector_impl(self, memory: bool, reactive: bool):
1390
+ """Implementation of selector decorator."""
1333
1391
  def deco(
1334
1392
  factory: Callable[..., Generator[Any, None, None]],
1335
1393
  ) -> Callable[..., Generator[Any, None, None]]:
@@ -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
@@ -32,12 +63,15 @@ DSL Adapters:
32
63
  RhizomorphAdapter - Log behavior tree node execution with status
33
64
  EnokiAdapter - Log state machine execution and lifecycle events
34
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
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,7 +111,7 @@ 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 (
@@ -81,7 +121,10 @@ from .dsl import (
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,7 +164,11 @@ __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',