mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-04 22:36:34 +08:00
Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"""代码内核的真实会话归属、当前身份、预算、回收和输出续读。"""
|
||||
"""
|
||||
代码内核的真实会话归属、当前身份、预算、回收和输出续读。
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
@@ -25,7 +27,9 @@ from app.runtime.tasks import get_task_registry
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def manager(tmp_path):
|
||||
"""每项用例使用独立管理器,真实内核与后台回收任务都在结束时收口。"""
|
||||
"""
|
||||
每项用例使用独立管理器,真实内核与后台回收任务都在结束时收口。
|
||||
"""
|
||||
instance = CodeSessionManager(tmp_path / 'code', max_kernels=2)
|
||||
try:
|
||||
yield instance
|
||||
@@ -38,27 +42,35 @@ async def manager(tmp_path):
|
||||
|
||||
|
||||
def _context(scope, *, admin=True):
|
||||
"""提供宿主实际持有的可变角色引用,不从脚本参数生成身份。"""
|
||||
"""
|
||||
提供宿主实际持有的可变角色引用,不从脚本参数生成身份。
|
||||
"""
|
||||
return ToolPolicyContext(session_id=scope.task_id, user_id=scope.user_id, origin=ToolOrigin.AGENT_INTERACTIVE,
|
||||
principal_type=PrincipalType.HUMAN, auth_source=AuthSource.WEB_SESSION,
|
||||
agent_context={'is_admin': admin})
|
||||
|
||||
|
||||
def _authority(scope, dispatch, context=None):
|
||||
"""使用真实 API 工具类,只有实际业务执行被替换为无出站回执。"""
|
||||
"""
|
||||
使用真实 API 工具类,只有实际业务执行被替换为无出站回执。
|
||||
"""
|
||||
api = MoviePilotApiTool(session_id=scope.task_id, user_id=scope.user_id)
|
||||
object.__setattr__(api, '_agent_tool_source', 'builtin')
|
||||
return CellAuthority(scope, context or _context(scope), allowed_tools([api]), dispatch)
|
||||
|
||||
|
||||
async def _dispatch(_tool, arguments):
|
||||
"""回放只读数据,供真实 Python 子进程循环调用。"""
|
||||
"""
|
||||
回放只读数据,供真实 Python 子进程循环调用。
|
||||
"""
|
||||
return {'success': True, 'data': arguments.get('query', {})}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_persistence_reset_and_object_identity_isolation(manager, tmp_path):
|
||||
"""同 owner 跨轮保持变量;reset 清空;同名会话的新 owner 对象不能继承。"""
|
||||
"""
|
||||
同 owner 跨轮保持变量;reset 清空;同名会话的新 owner 对象不能继承。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'conversation', 'interactive')
|
||||
first = await manager.execute(_authority(scope, _dispatch), 'value = 42', cwd=tmp_path)
|
||||
second = await manager.execute(_authority(scope, _dispatch), 'print(value)', cwd=tmp_path)
|
||||
@@ -73,13 +85,17 @@ async def test_persistence_reset_and_object_identity_isolation(manager, tmp_path
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rpc_rebinds_context_every_cell_and_does_not_freeze_first_role(manager, tmp_path):
|
||||
"""持久 RPC 连接每个 cell 进入当前上下文,角色撤销立即阻止后续工具调用。"""
|
||||
"""
|
||||
持久 RPC 连接每个 cell 进入当前上下文,角色撤销立即阻止后续工具调用。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'live-role', 'interactive')
|
||||
context = _context(scope)
|
||||
marker = contextvars.ContextVar('test_code_marker', default='missing')
|
||||
|
||||
async def dispatch(_tool, _arguments):
|
||||
"""读取当前调用上下文,并在宿主模拟角色撤销。"""
|
||||
"""
|
||||
读取当前调用上下文,并在宿主模拟角色撤销。
|
||||
"""
|
||||
if marker.get() == 'second':
|
||||
context.agent_context['is_admin'] = False
|
||||
return {'marker': marker.get()}
|
||||
@@ -100,12 +116,16 @@ async def test_rpc_rebinds_context_every_cell_and_does_not_freeze_first_role(man
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_budget_and_denied_calls_cannot_be_hidden_by_exit_zero(manager, tmp_path):
|
||||
"""第51次调用不派发,写操作和敏感读取都返回外层失败,即使程序本身成功退出。"""
|
||||
"""
|
||||
第51次调用不派发,写操作和敏感读取都返回外层失败,即使程序本身成功退出。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'budget', 'interactive')
|
||||
dispatched = []
|
||||
|
||||
async def dispatch(tool, arguments):
|
||||
"""记录实际执行次数,而不是仅检查计数字段。"""
|
||||
"""
|
||||
记录实际执行次数,而不是仅检查计数字段。
|
||||
"""
|
||||
dispatched.append(arguments)
|
||||
return await _dispatch(tool, arguments)
|
||||
|
||||
@@ -124,7 +144,9 @@ async def test_budget_and_denied_calls_cannot_be_hidden_by_exit_zero(manager, tm
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_timeout_discards_state_and_next_call_starts_fresh(manager, tmp_path):
|
||||
"""超时明确状态丢失,随后执行不能误用半次 cell 留下的变量。"""
|
||||
"""
|
||||
超时明确状态丢失,随后执行不能误用半次 cell 留下的变量。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'timeout', 'interactive')
|
||||
timed_out = await manager.execute(_authority(scope, _dispatch), 'value = 10\nimport time\ntime.sleep(60)',
|
||||
cwd=tmp_path, timeout=0.05)
|
||||
@@ -136,13 +158,17 @@ async def test_timeout_discards_state_and_next_call_starts_fresh(manager, tmp_pa
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_host_scope_close_cancels_active_python(manager, tmp_path, monkeypatch):
|
||||
"""真实宿主作用域关闭同时收口正在运行的 Python 和全部派发任务。"""
|
||||
"""
|
||||
真实宿主作用域关闭同时收口正在运行的 Python 和全部派发任务。
|
||||
"""
|
||||
monkeypatch.setattr(code_manager, 'code_session_manager', manager)
|
||||
scope = TerminalScope('alice', 'cancel', 'interactive')
|
||||
entered = asyncio.Event()
|
||||
|
||||
async def dispatch(tool, arguments):
|
||||
"""等待子进程已到达 RPC 边界,再触发宿主作用域关闭。"""
|
||||
"""
|
||||
等待子进程已到达 RPC 边界,再触发宿主作用域关闭。
|
||||
"""
|
||||
entered.set()
|
||||
return await _dispatch(tool, arguments)
|
||||
|
||||
@@ -158,7 +184,9 @@ async def test_host_scope_close_cancels_active_python(manager, tmp_path, monkeyp
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_idle_lru_and_autonomous_reaper(manager, tmp_path, monkeypatch):
|
||||
"""容量淘汰最旧空闲会话,停止新调用后后台仍会回收过期内核。"""
|
||||
"""
|
||||
容量淘汰最旧空闲会话,停止新调用后后台仍会回收过期内核。
|
||||
"""
|
||||
scopes = [TerminalScope('alice', str(index), 'interactive') for index in range(3)]
|
||||
for scope in scopes:
|
||||
await manager.execute(_authority(scope, _dispatch), 'pass', cwd=tmp_path)
|
||||
@@ -174,7 +202,9 @@ async def test_idle_lru_and_autonomous_reaper(manager, tmp_path, monkeypatch):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_output_spill_survives_kernel_exit_and_is_sanitized(manager, tmp_path):
|
||||
"""超过内联上限的正文保存在独立目录,退出内核后仍可续读且不泄漏常见凭据。"""
|
||||
"""
|
||||
超过内联上限的正文保存在独立目录,退出内核后仍可续读且不泄漏常见凭据。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'spill', 'interactive')
|
||||
result = await manager.execute(_authority(scope, _dispatch),
|
||||
'print("A" * 1100000)\nprint("api_key=sk-secret0123456789")\nraise SystemExit(0)', cwd=tmp_path)
|
||||
@@ -189,7 +219,9 @@ async def test_output_spill_survives_kernel_exit_and_is_sanitized(manager, tmp_p
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize('ending', ['raise SystemExit(0)', 'import time; time.sleep(60)'])
|
||||
async def test_exit_and_timeout_reap_descendants(manager, tmp_path, ending):
|
||||
"""根进程主动退出与超时都收口继承管道的子进程,不能因根已退出而跳过进程组。"""
|
||||
"""
|
||||
根进程主动退出与超时都收口继承管道的子进程,不能因根已退出而跳过进程组。
|
||||
"""
|
||||
if os.name == 'nt':
|
||||
pytest.skip('真实 POSIX 进程组回收')
|
||||
scope = TerminalScope('alice', 'tree', 'interactive')
|
||||
@@ -204,7 +236,9 @@ async def test_exit_and_timeout_reap_descendants(manager, tmp_path, ending):
|
||||
|
||||
|
||||
def test_stale_staging_cleanup_does_not_follow_links_or_remove_live_paths(tmp_path):
|
||||
"""七天清理只处理固定布局中的过期路径,不跟随链接或删除当前内核。"""
|
||||
"""
|
||||
七天清理只处理固定布局中的过期路径,不跟随链接或删除当前内核。
|
||||
"""
|
||||
parent = tmp_path / 'users' / 'alice'
|
||||
parent.mkdir(parents=True)
|
||||
stale, active = parent / 'kernel-stale', parent / 'kernel-active'
|
||||
@@ -223,7 +257,9 @@ def test_stale_staging_cleanup_does_not_follow_links_or_remove_live_paths(tmp_pa
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_other_user_output_cannot_be_read_as_ordinary_agent(manager, tmp_path, monkeypatch):
|
||||
"""普通用户不能借通用文件工具读取其他用户的 Python 输出缓存。"""
|
||||
"""
|
||||
普通用户不能借通用文件工具读取其他用户的 Python 输出缓存。
|
||||
"""
|
||||
monkeypatch.setattr(base, 'get_runtime_setting', lambda _key: tmp_path)
|
||||
alice = ReadFileTool(session_id='read', user_id='alice')
|
||||
alice.set_agent_context({'is_admin': False})
|
||||
@@ -234,7 +270,9 @@ async def test_other_user_output_cannot_be_read_as_ordinary_agent(manager, tmp_p
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unavailable_rpc_is_visible_even_when_script_ignores_it(manager, tmp_path):
|
||||
"""底层 allowlist 拒绝也进入当前 cell 的失败摘要,脚本忽略返回值不能掩盖。"""
|
||||
"""
|
||||
底层 allowlist 拒绝也进入当前 cell 的失败摘要,脚本忽略返回值不能掩盖。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'unknown', 'interactive')
|
||||
result = await manager.execute(_authority(scope, _dispatch),
|
||||
'from moviepilot_tools import _call\n_call("write_file", {})', cwd=tmp_path)
|
||||
@@ -244,13 +282,17 @@ async def test_unavailable_rpc_is_visible_even_when_script_ignores_it(manager, t
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cell_end_cancels_lingering_rpc_before_reusing_kernel(manager, tmp_path):
|
||||
"""Python 后台线程不能延长已结束 cell 的调用窗口,遗留调用被取消并公开未完成。"""
|
||||
"""
|
||||
Python 后台线程不能延长已结束 cell 的调用窗口,遗留调用被取消并公开未完成。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'lingering', 'interactive')
|
||||
ready = tmp_path / 'ready'
|
||||
canceled = asyncio.Event()
|
||||
|
||||
async def dispatch(_tool, _arguments):
|
||||
"""通知脚本真实 RPC 已经进入宿主,并模拟尚未结束的只读查询。"""
|
||||
"""
|
||||
通知脚本真实 RPC 已经进入宿主,并模拟尚未结束的只读查询。
|
||||
"""
|
||||
ready.touch()
|
||||
try:
|
||||
await asyncio.Event().wait()
|
||||
@@ -269,12 +311,16 @@ async def test_cell_end_cancels_lingering_rpc_before_reusing_kernel(manager, tmp
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queued_cell_after_exit_keeps_new_process_registered(manager, tmp_path):
|
||||
"""前一个 cell 退出时已有排队引用,后一个重建的进程仍必须被 owner 和回收器持有。"""
|
||||
"""
|
||||
前一个 cell 退出时已有排队引用,后一个重建的进程仍必须被 owner 和回收器持有。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'queued', 'interactive')
|
||||
entered, release = asyncio.Event(), asyncio.Event()
|
||||
|
||||
async def dispatch(_tool, _arguments):
|
||||
"""让第二次调用有机会完成准入并排队。"""
|
||||
"""
|
||||
让第二次调用有机会完成准入并排队。
|
||||
"""
|
||||
entered.set()
|
||||
await release.wait()
|
||||
return {'success': True}
|
||||
@@ -308,7 +354,9 @@ async def test_queued_cell_after_exit_keeps_new_process_registered(manager, tmp_
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_long_lived_tasks_do_not_retain_first_request_context(manager, tmp_path):
|
||||
"""持久 socket、管道读取和进程回收任务不能保存首轮历史上下文。"""
|
||||
"""
|
||||
持久 socket、管道读取和进程回收任务不能保存首轮历史上下文。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'context', 'interactive')
|
||||
history = contextvars.ContextVar('test_large_history')
|
||||
token = history.set(object())
|
||||
@@ -323,13 +371,17 @@ async def test_long_lived_tasks_do_not_retain_first_request_context(manager, tmp
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cancel_during_admission_releases_queued_reference(manager, tmp_path, monkeypatch):
|
||||
"""准入期间等待旧内核回收时被取消,也不能留下永久占用的排队引用。"""
|
||||
"""
|
||||
准入期间等待旧内核回收时被取消,也不能留下永久占用的排队引用。
|
||||
"""
|
||||
scope = TerminalScope('alice', 'admission', 'interactive')
|
||||
entered = asyncio.Event()
|
||||
original = manager._sweep_locked
|
||||
|
||||
async def sweep():
|
||||
"""只在新 entry 已登记后的容量清理边界暂停。"""
|
||||
"""
|
||||
只在新 entry 已登记后的容量清理边界暂停。
|
||||
"""
|
||||
if manager._entries:
|
||||
entered.set()
|
||||
await asyncio.Event().wait()
|
||||
|
||||
Reference in New Issue
Block a user