Skip to content

Commit 92b8f7b

Browse files
refactor: deprecate private _advance_pointer in favor of public advance_pointer method
1 parent 490fb7d commit 92b8f7b

5 files changed

Lines changed: 92 additions & 19 deletions

File tree

docs/guide/advanced/built-in_instruction_set/jump_clause.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ That means:
2424
1. **Address resolution** (`_pre_check`): resolve alias names through the alias table, or validate raw addresses.
2525
2. **Jump marker**: call `pc.jump_to(addr)`, which is guarded by `@markup` and sets `_jump_marked=True`.
2626
3. **Pointer replacement**: replace `_pointer` completely with the target address vector.
27-
4. **Interpreter response**: the main loop sees `_jump_marked` and skips normal `_advance_pointer()`, continuing from the jump target.
27+
4. **Interpreter response**: the main loop sees `_jump_marked` and skips normal `advance_pointer()`, continuing from the jump target.
2828

2929
### Key characteristics
3030

docs/reference/api/runtime.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,34 @@ Find a node or rendered composition by absolute address.
120120

121121
Resolve an alias and return the corresponding node object.
122122

123+
#### `advance_pointer(ptr: PointerVector | None = None) -> bool`
124+
125+
Advance the execution pointer to the next node in the workflow graph. This method implements the logic for navigating through nested workflow structures, handling both sequential execution and hierarchical traversal.
126+
127+
**Parameters**
128+
129+
- `ptr`: Optional external pointer vector to advance. When provided, the method advances this pointer **without modifying the interpreter's own `_pointer`**. Defaults to `None`, in which case `self._pointer` is advanced. This enables external systems to preview pointer advancement paths without disturbing interpreter state.
130+
131+
**Returns**
132+
133+
- `True` if the pointer was successfully advanced to the next node.
134+
- `False` if the end of the workflow has been reached.
135+
136+
**Algorithm**
137+
138+
1. Starting from `ptr` (or `self._pointer`), traverse `base_addr` layer-by-layer to locate the container of the current node.
139+
2. If the current node is a **non-empty `NodeComposeRendered`** → enter the nested container (`append(0)`), return `True`.
140+
3. If the current node has a **next sibling**:
141+
- Sibling is a non-empty `NodeComposeRendered` → enter that nested container, return `True`.
142+
- Otherwise → move to the sibling node, return `True`.
143+
4. If no next sibling → **backtrack up** the pointer stack layer-by-layer, looking for a parent container's next sibling.
144+
5. If a next sibling is found during backtracking → apply the same logic, return `True`.
145+
6. If backtracking reaches the top level with no more siblings → return `False` (end of workflow).
146+
147+
**Deprecation**
148+
149+
The `_advance_pointer` property is deprecated since v0.3.0. Use `advance_pointer()` instead. The old property exists only as a compatibility shim and will be removed in a future version.
150+
123151
### Execution behavior
124152

125153
`WorkflowInterpreter` preserves execution atomicity by holding `_interpret_lock` while a single node is executed. It only checks suspend points at safe boundaries:

docs/zh/guide/advanced/built-in_instruction_set/jump_clause.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
1. **地址解析**`_pre_check` 阶段):将别名查表解析为 `list[int]` 绝对地址,或直接验证裸地址的有效性
2525
2. **跳转标记**:调用 `pc.jump_to(addr)`,该方法受 `@markup` 保护,设置 `_jump_marked=True`
2626
3. **指针替换**`_pointer` 被完整替换为目标地址向量
27-
4. **解释器响应**:主循环检测到 `_jump_marked`,跳过常规的 `_advance_pointer()` 步进,下一轮迭代从跳转目标开始执行
27+
4. **解释器响应**:主循环检测到 `_jump_marked`,跳过常规的 `advance_pointer()` 步进,下一轮迭代从跳转目标开始执行
2828

2929
### 关键特性
3030

docs/zh/reference/api/runtime.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(
5151
- `_graph: NodeComposeRendered`:编译后的只读工作流图,解释器从中读取节点
5252
- `_pointer: PointerVector`:当前执行位置。解释器主循环始终以它指向的节点作为执行目标
5353
- `_ret_addr_stack: Stack[PointerVector]`:返回地址栈。`call_sub``CALL` 指令压入返回地址,执行完毕弹栈恢复
54-
- `_jump_marked: bool`:跳转标记。当 `True` 时,主循环跳过本次的 `_advance_pointer()` 步进,下一轮直接从跳转目标继续
54+
- `_jump_marked: bool`:跳转标记。当 `True` 时,主循环跳过本次的 `advance_pointer()` 步进,下一轮直接从跳转目标继续
5555
- `_interpret_lock: aiologic.Lock`:解释锁。每次迭代获取一次,保证单个节点的执行原子性。同时也是外部安全调用的互斥锁
5656
- `_ava_args / _ava_kwargs`:执行期可用参数池,供依赖注入系统从中匹配节点的参数签名
5757
- `_exc_ignored: tuple[type[BaseException], ...]`:运行时自动包含 `InterruptNotice``BreakLoop`。这些异常不会被任何 `CATCH` 块捕获,直接穿透到顶层
@@ -137,6 +137,36 @@ def __init__(
137137

138138
装饰器的类型注解使用 `fun_T` TypeVar 保留原始方法签名。在 `TYPE_CHECKING` 下,它会返回原始函数以避免混淆静态类型检查器。所有被装饰的方法必须是返回 `None` 的实例方法。
139139

140+
#### 指针推进
141+
142+
**`advance_pointer(ptr: PointerVector | None = None) -> bool`**
143+
144+
推进执行指针到工作流图中的下一个节点。此方法实现了嵌套工作流结构的导航逻辑,处理顺序执行和层级遍历。
145+
146+
**参数**
147+
148+
- `ptr`:可选的外部指针向量。传入后,方法将推进此参数所指的指针,而**不会改变解释器自身的 `_pointer`**。默认为 `None` 时,推进解释器自身的 `_pointer`。此参数使外部系统可以在不破坏解释器状态的前提下,预演指针推进路径。
149+
150+
**返回值**
151+
152+
- `True`:指针成功推进到下一个节点
153+
- `False`:已到达工作流末尾,无更多节点可执行
154+
155+
**推进算法**
156+
157+
1.`ptr`(或 `self._pointer`)开始,沿 `base_addr` 逐层定位到当前节点所在容器
158+
2. 若当前节点是**非空 `NodeComposeRendered`** → 指针进入嵌套容器(`append(0)`),返回 `True`
159+
3. 若当前节点有**后继兄弟节点**
160+
- 兄弟节点是非空 `NodeComposeRendered` → 进入该嵌套容器,返回 `True`
161+
- 否则 → 移动到兄弟节点,返回 `True`
162+
4. 若当前节点无后继 → 沿指针栈**逐层向上回溯**,寻找父容器的下一个兄弟
163+
5. 回溯中寻得后继 → 按相同逻辑处理,返回 `True`
164+
6. 回溯到顶层仍未找到 → 返回 `False`(工作流结束)
165+
166+
**弃用说明**
167+
168+
`_advance_pointer` 属性已在 v0.3.0 弃用,请使用 `advance_pointer()` 方法。旧属性仅作为兼容性委托存在,将在未来版本中移除。
169+
140170
### 执行行为
141171

142172
`WorkflowInterpreter` 通过持有 `_interpret_lock` 保证单个节点的执行原子性。它仅在安全边界检查挂起点:
@@ -185,7 +215,7 @@ await pc.run()
185215
3.`PC_CHECKPOINT` 断点等待外部挂起
186216
4. 执行当前节点(`_call()`
187217
5.`_jump_marked`,重置标记并跳过指针推进
188-
6. 否则调用 `_advance_pointer()` 推进指针
218+
6. 否则调用 `advance_pointer()` 推进指针
189219
7. 指针推进失败(到达末尾)则退出
190220

191221
外层 `try` 捕获 `InterruptNotice` 后清理调用栈和指针,干净退出。

src/amrita_sense/runtime/workflow.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
99

1010
import aiologic
11+
from typing_extensions import deprecated
1112

1213
from amrita_sense.exceptions import (
1314
BreakLoop,
@@ -129,9 +130,9 @@ def find_addr_alias(self, alias: str) -> list[int]:
129130
Raises:
130131
NullPointerException: If the alias does not exist in the graph.
131132
"""
132-
if alias not in self._graph.alias2vector_map:
133+
if alias not in self.get_graph().alias2vector_map:
133134
raise NullPointerException(f"{alias} is not a valid alias")
134-
return self._graph.alias2vector_map[alias]
135+
return self.get_graph().alias2vector_map[alias]
135136

136137
def find_node_alias(self, alias: str) -> BaseNode | NodeComposeRendered:
137138
"""Find a node by its alias and return the node object.
@@ -381,11 +382,12 @@ async def run_step_by(self) -> AsyncGenerator[Any, None]:
381382
):
382383
raise RuntimeError("Runtime arguments cannot be resolved")
383384
self._ava_args = tuple(session_args)
385+
graph = self.get_graph()
384386
while True:
385387
await self.object_io._wait_for_continue(PC_CHECKPOINT)
386388
async with self._interpret_lock:
387389
if not self._pointer:
388-
if not self._graph:
390+
if not graph:
389391
break
390392
self._pointer.append(0)
391393
yield (
@@ -397,7 +399,7 @@ async def run_step_by(self) -> AsyncGenerator[Any, None]:
397399
self._jump_marked = False
398400
continue
399401

400-
if not self._advance_pointer():
402+
if not self.advance_pointer():
401403
break
402404
except InterruptNotice as e:
403405
logger.info(f"Interrupt notice at {self._pointer} :{e.message}")
@@ -406,24 +408,35 @@ async def run_step_by(self) -> AsyncGenerator[Any, None]:
406408
self._pointer.clear()
407409
self._jump_marked = False
408410

409-
def _advance_pointer(self) -> bool:
410-
"""Advance the execution pointer to the next node in the workflow.
411+
@deprecated(
412+
"Method of `_advance_pointer` is now `advance_pointer`, this compile method will be removed in `v0.3.0`",
413+
category=DeprecationWarning,
414+
)
415+
@property
416+
def _advance_pointer(self):
417+
return self.advance_pointer
418+
419+
def advance_pointer(self, ptr: PointerVector | None = None) -> bool:
420+
"""Advance the execution pointer to the next node with ptr arg by the graph.
411421
412-
This internal method implements the complex logic for navigating through
422+
This method implements the logic for navigating through
413423
nested workflow structures, handling both sequential execution and
414424
hierarchical traversal.
415425
426+
Args:
427+
ptr (PointerVector, optional): ptr to advance to. If None, advance to the next node in the current container of interpreter.
428+
416429
Returns:
417-
True if the pointer was successfully advanced, False if at the end of workflow.
430+
bool: True if the pointer was successfully advanced, False if at the end of workflow.
418431
"""
419-
if not self._pointer:
432+
pointer: PointerVector = ptr if ptr is not None else self._pointer
433+
if not pointer:
420434
logger.debug("Pointer is empty, cannot advance")
421435
return False
422436

423-
pointer: PointerVector = self._pointer
424437
logger.debug(f"Advancing pointer from {pointer}")
425-
426-
current_container: BaseNode | NodeComposeRendered = self._graph
438+
graph: NodeComposeRendered = self.get_graph()
439+
current_container: BaseNode | NodeComposeRendered = graph
427440
for idx in pointer.base_addr[:-1]:
428441
if isinstance(current_container, NodeComposeRendered):
429442
current_container = current_container[idx]
@@ -464,7 +477,7 @@ def _advance_pointer(self) -> bool:
464477
return False
465478

466479
parent_path: list[int] = pointer.base_addr[:-1]
467-
parent_container: BaseNode | NodeComposeRendered = self._graph
480+
parent_container: BaseNode | NodeComposeRendered = graph
468481
for idx in parent_path:
469482
if isinstance(parent_container, NodeComposeRendered):
470483
parent_container = parent_container[idx]
@@ -508,7 +521,7 @@ def _find_addr_or_none(
508521
Returns:
509522
The node at the specified address, or None if it doesn't exist.
510523
"""
511-
graph: NodeComposeRendered = self._graph
524+
graph: NodeComposeRendered = self.get_graph()
512525
current_chunk: NodeComposeRendered | BaseNode = graph
513526

514527
for i, chunk in enumerate(addr):
@@ -570,7 +583,9 @@ async def _call(
570583
addr_getter = addr_getter or self.find_addr
571584
node: BaseNode | NodeComposeRendered = addr_getter(self._pointer.base_addr)
572585
if isinstance(node, NodeComposeRendered):
573-
raise RuntimeError("Cannot call a NodeCompose.")
586+
raise RuntimeError(
587+
f"Cannot call a NodeCompose in addr {self._pointer.base_addr}."
588+
)
574589
await self.object_io._wait_for_continue(node.tag)
575590

576591
node._pre_check(self)

0 commit comments

Comments
 (0)