gapfill G3: unify all 5 private row_get onto peer_messages.row_get (approvals, automations, marker_tools, todos, wait_ops; resident import retargeted); semantics identical on None/dict/sqlite3.Row, identity tests added
This commit is contained in:
1 parent
b46bf8d7e0
commit
65e8ed3d64
7 files changed
+58
-52
No files matched your search
+1
-10
@@ -74,6 +74,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from cloud.peer_messages import row_get
|
||||||
from cloud.timeouts import APPROVAL_DEFAULT_SECONDS as _APPROVAL_DEFAULT
|
from cloud.timeouts import APPROVAL_DEFAULT_SECONDS as _APPROVAL_DEFAULT
|
||||||
from cloud.timeouts import expires_at_for as _expires_at_for
|
from cloud.timeouts import expires_at_for as _expires_at_for
|
||||||
from cloud.timeouts import resolve_module_timeout as _resolve_timeout
|
from cloud.timeouts import resolve_module_timeout as _resolve_timeout
|
||||||
@@ -86,16 +87,6 @@ MAX_SUMMARY_CHARS = 2000
|
|||||||
MAX_DETAIL_CHARS = 20000
|
MAX_DETAIL_CHARS = 20000
|
||||||
|
|
||||||
|
|
||||||
def row_get(row, name):
|
|
||||||
"""行取值容错:sqlite3.Row 缺列报 IndexError、dict 缺键报
|
|
||||||
KeyError、空行报 TypeError,一律回 None(与 peer_messages
|
|
||||||
的同名函数同款,模块独立所以这里自带一份)。"""
|
|
||||||
try:
|
|
||||||
return row[name]
|
|
||||||
except (KeyError, IndexError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_approvals_table(conn):
|
def ensure_approvals_table(conn):
|
||||||
"""幂等建审批队列表。调用方负责 commit(服务端入口在写
|
"""幂等建审批队列表。调用方负责 commit(服务端入口在写
|
||||||
操作后统一 commit;本函数自己不 commit,避免半截事务)。"""
|
操作后统一 commit;本函数自己不 commit,避免半截事务)。"""
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import time
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
from cloud.peer_messages import row_get
|
||||||
from cloud.quota import decide as _quota_decide
|
from cloud.quota import decide as _quota_decide
|
||||||
from cloud.quota import default_quota_usd_micros as _quota_default
|
from cloud.quota import default_quota_usd_micros as _quota_default
|
||||||
from cloud.quota import monthly_usage as _quota_monthly_usage
|
from cloud.quota import monthly_usage as _quota_monthly_usage
|
||||||
@@ -73,14 +74,9 @@ ACTIVE = "active"
|
|||||||
CANCELLED = "cancelled"
|
CANCELLED = "cancelled"
|
||||||
|
|
||||||
|
|
||||||
def row_get(row, name):
|
# 行取值走 cloud.peer_messages.row_get(A7 全仓统一):旧私有版
|
||||||
"""行取值容错:sqlite3.Row 缺列报 IndexError、dict 缺键报
|
# 与统一版在真实行类型(None / dict / sqlite3.Row)下语义完全
|
||||||
KeyError、空行报 TypeError,一律回 None(老库行缺列时
|
# 相同(缺键/缺列/空行一律回 None),故直接复用,不再自带一份。
|
||||||
读自动化列表不能因此炸掉整页)。"""
|
|
||||||
try:
|
|
||||||
return row[name]
|
|
||||||
except (KeyError, IndexError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
AUTOMATION_COLUMNS = (
|
AUTOMATION_COLUMNS = (
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ from typing import Any
|
|||||||
|
|
||||||
from cloud.gates import RETRYABLE_STATUSES as _GATE_RETRYABLE
|
from cloud.gates import RETRYABLE_STATUSES as _GATE_RETRYABLE
|
||||||
from cloud.gates import TERMINAL_STATUSES as _GATE_TERMINAL
|
from cloud.gates import TERMINAL_STATUSES as _GATE_TERMINAL
|
||||||
|
from cloud.peer_messages import row_get
|
||||||
from cloud.gates import decide_control as _gate_decide_control
|
from cloud.gates import decide_control as _gate_decide_control
|
||||||
from cloud.gates import is_goal_task as _gate_is_goal_task
|
from cloud.gates import is_goal_task as _gate_is_goal_task
|
||||||
from cloud.gates import resolve_inside as _gate_resolve_inside
|
from cloud.gates import resolve_inside as _gate_resolve_inside
|
||||||
@@ -285,14 +286,6 @@ def parse_first_run_at(value):
|
|||||||
return epoch
|
return epoch
|
||||||
|
|
||||||
|
|
||||||
def row_get(row, name):
|
|
||||||
"""与 peer_messages.row_get 同款容错取值。"""
|
|
||||||
try:
|
|
||||||
return row[name]
|
|
||||||
except (KeyError, IndexError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _as_int(value, default):
|
def _as_int(value, default):
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return default
|
return default
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ from urllib.parse import urlsplit
|
|||||||
|
|
||||||
from cloud.automations import _audit_row as _auto_audit_row
|
from cloud.automations import _audit_row as _auto_audit_row
|
||||||
from cloud.automations import _automation_quota_exceeded
|
from cloud.automations import _automation_quota_exceeded
|
||||||
from cloud.automations import row_get
|
from cloud.peer_messages import row_get
|
||||||
from cloud.db import PostgresIntegrityError as _PgIntegrityError
|
from cloud.db import PostgresIntegrityError as _PgIntegrityError
|
||||||
|
|
||||||
MIN_INTERVAL_SECONDS = 60
|
MIN_INTERVAL_SECONDS = 60
|
||||||
|
|||||||
@@ -2,16 +2,24 @@
|
|||||||
|
|
||||||
只用标准库 + 内存 SQLite,不联网。锁定点:
|
只用标准库 + 内存 SQLite,不联网。锁定点:
|
||||||
- 统一身份:auto_memory._row_get 与 lifecycle._row_get 就是
|
- 统一身份:auto_memory._row_get 与 lifecycle._row_get 就是
|
||||||
peer_messages.row_get 本体(不是又一份复写)。
|
peer_messages.row_get 本体(不是又一份复写);gapfill G3
|
||||||
|
之后 approvals、automations、marker_tools、todos、wait_ops
|
||||||
|
的 row_get 以及 resident.py 导入的 row_get 也是同一本体。
|
||||||
- 真实行类型矩阵(None / dict / sqlite3.Row)下取值与回落
|
- 真实行类型矩阵(None / dict / sqlite3.Row)下取值与回落
|
||||||
与三处旧版一致:现值原样回(含 0 / 空串等假值),缺列 /
|
与旧版一致:现值原样回(含 0 / 空串等假值),缺列 /
|
||||||
缺键 / 空行回 default(默认 None)。
|
缺键 / 空行回 default(默认 None)。
|
||||||
- default 回落差异钉住:三处旧版的默认回落都是 None——
|
- default 回落差异钉住:旧版的默认回落都是 None——
|
||||||
auto_memory 旧版 default 参数默认 None,lifecycle 旧版无
|
auto_memory 旧版 default 参数默认 None,lifecycle 旧版无
|
||||||
default 参数一律回 None,peer 旧版回 None;统一后两模块
|
default 参数一律回 None,peer 旧版回 None;统一后各模块
|
||||||
调用处仍不传 default,缺失时仍为 None,不算改语义。
|
调用处仍不传 default,缺失时仍为 None,不算改语义。
|
||||||
- 统一路径实测:lifecycle.snapshot_to_dict 在 dict 缺键行与
|
- 统一路径实测:lifecycle.snapshot_to_dict 在 dict 缺键行与
|
||||||
sqlite3.Row 行上都经统一口出正确字段。
|
sqlite3.Row 行上都经统一口出正确字段。
|
||||||
|
- gapfill G3 全仓收尾:五处旧版与统一版在真实行类型下
|
||||||
|
语义完全相同(缺键/缺列/空行一律回 None,调用处均不传
|
||||||
|
default),故统一不算改语义;todos / wait_ops 模块
|
||||||
|
docstring 里“不 import cloud 其它模块”的表述同步修正为
|
||||||
|
“除行取值复用 peer_messages(本身只依赖标准库)外不
|
||||||
|
import cloud 其它模块”。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -20,13 +28,23 @@ import unittest
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import cloud.approvals as approvals
|
||||||
import cloud.auto_memory as auto_memory
|
import cloud.auto_memory as auto_memory
|
||||||
|
import cloud.automations as automations
|
||||||
import cloud.lifecycle as lifecycle
|
import cloud.lifecycle as lifecycle
|
||||||
|
import cloud.marker_tools as marker_tools
|
||||||
|
import cloud.todos as todos
|
||||||
|
import cloud.wait_ops as wait_ops
|
||||||
from cloud.peer_messages import row_get
|
from cloud.peer_messages import row_get
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||||
|
import cloud.approvals as approvals
|
||||||
import cloud.auto_memory as auto_memory
|
import cloud.auto_memory as auto_memory
|
||||||
|
import cloud.automations as automations
|
||||||
import cloud.lifecycle as lifecycle
|
import cloud.lifecycle as lifecycle
|
||||||
|
import cloud.marker_tools as marker_tools
|
||||||
|
import cloud.todos as todos
|
||||||
|
import cloud.wait_ops as wait_ops
|
||||||
from cloud.peer_messages import row_get
|
from cloud.peer_messages import row_get
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +66,25 @@ class RowGetUnifiedTestCase(unittest.TestCase):
|
|||||||
self.assertIs(auto_memory._row_get, row_get)
|
self.assertIs(auto_memory._row_get, row_get)
|
||||||
self.assertIs(lifecycle._row_get, row_get)
|
self.assertIs(lifecycle._row_get, row_get)
|
||||||
|
|
||||||
|
def test_gapfill_five_modules_are_the_same_function(self):
|
||||||
|
# gapfill G3:五处私有 row_get 已删,模块属性就是统一本体;
|
||||||
|
# resident.py 同样从 peer_messages 导入,故一并钉住。
|
||||||
|
import cloud.resident as resident
|
||||||
|
|
||||||
|
self.assertIs(approvals.row_get, row_get)
|
||||||
|
self.assertIs(automations.row_get, row_get)
|
||||||
|
self.assertIs(marker_tools.row_get, row_get)
|
||||||
|
self.assertIs(todos.row_get, row_get)
|
||||||
|
self.assertIs(wait_ops.row_get, row_get)
|
||||||
|
self.assertIs(resident.row_get, row_get)
|
||||||
|
# 五处调用处均不传 default,缺失回落仍是 None。
|
||||||
|
for fn in (approvals.row_get, automations.row_get,
|
||||||
|
marker_tools.row_get, todos.row_get,
|
||||||
|
wait_ops.row_get):
|
||||||
|
self.assertIsNone(fn({}, "nope"))
|
||||||
|
self.assertIsNone(fn(None, "nope"))
|
||||||
|
self.assertEqual(fn({"a": 0}, "a"), 0)
|
||||||
|
|
||||||
def test_real_row_type_matrix(self):
|
def test_real_row_type_matrix(self):
|
||||||
full, partial = _sqlite_row()
|
full, partial = _sqlite_row()
|
||||||
# 现值原样回(含假值 0,不被当成缺失)。
|
# 现值原样回(含假值 0,不被当成缺失)。
|
||||||
|
|||||||
+5
-9
@@ -27,8 +27,9 @@ pending / in_progress / blocked / completed / cancelled,
|
|||||||
一个项目里多人并行,多项 in_progress 是正常态,强制
|
一个项目里多人并行,多项 in_progress 是正常态,强制
|
||||||
单一会把板子变成单人队列,所以这里不做互斥。
|
单一会把板子变成单人队列,所以这里不做互斥。
|
||||||
|
|
||||||
本模块只依赖标准库,不 import cloud 其它模块,避免循环
|
本模块除行取值复用 cloud.peer_messages(该模块本身只依赖
|
||||||
import;表迁移写法仿 automations(CREATE TABLE IF NOT
|
标准库,故不产生循环 import)外不 import cloud 其它模块;
|
||||||
|
表迁移写法仿 automations(CREATE TABLE IF NOT
|
||||||
EXISTS + 查列补齐,SQLite 与 Postgres 适配层同款:调用方
|
EXISTS + 查列补齐,SQLite 与 Postgres 适配层同款:调用方
|
||||||
仍写 ? 占位符,db.py 适配层转 PG 方言)。
|
仍写 ? 占位符,db.py 适配层转 PG 方言)。
|
||||||
"""
|
"""
|
||||||
@@ -36,6 +37,8 @@ EXISTS + 查列补齐,SQLite 与 Postgres 适配层同款:调用方
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from cloud.peer_messages import row_get
|
||||||
|
|
||||||
STATUSES = ("pending", "in_progress", "blocked", "completed", "cancelled")
|
STATUSES = ("pending", "in_progress", "blocked", "completed", "cancelled")
|
||||||
PRIORITIES = ("low", "medium", "high")
|
PRIORITIES = ("low", "medium", "high")
|
||||||
MAX_TITLE_LEN = 500
|
MAX_TITLE_LEN = 500
|
||||||
@@ -47,13 +50,6 @@ TODO_COLUMNS = (
|
|||||||
"updated_at", "completed_at")
|
"updated_at", "completed_at")
|
||||||
|
|
||||||
|
|
||||||
def row_get(row, name):
|
|
||||||
try:
|
|
||||||
return row[name]
|
|
||||||
except (KeyError, IndexError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _row_dict(row, columns):
|
def _row_dict(row, columns):
|
||||||
if row is None or isinstance(row, dict):
|
if row is None or isinstance(row, dict):
|
||||||
return row
|
return row
|
||||||
|
|||||||
+5
-12
@@ -1,7 +1,8 @@
|
|||||||
"""线程等待与移交操作(对照 codex app 的 8 项工具面)。
|
"""线程等待与移交操作(对照 codex app 的 8 项工具面)。
|
||||||
|
|
||||||
本模块只做控制面纯逻辑,不跑模型、不联网、不 import cloud 其它
|
本模块只做控制面纯逻辑,不跑模型、不联网;行取值复用
|
||||||
模块(与 automations / peer_messages 同款,避免循环 import);
|
cloud.peer_messages(该模块本身只依赖标准库,故不产生循环
|
||||||
|
import),其余不 import cloud 其它模块;
|
||||||
server.py 只负责鉴权、路由与错误码映射。
|
server.py 只负责鉴权、路由与错误码映射。
|
||||||
|
|
||||||
8 项在控制面的落点(详见 docs/thread-wait-ops.md 对照表):
|
8 项在控制面的落点(详见 docs/thread-wait-ops.md 对照表):
|
||||||
@@ -40,6 +41,8 @@ import re
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from cloud.peer_messages import row_get
|
||||||
|
|
||||||
HANDOFF_STATUSES = ("pending", "accepted", "running",
|
HANDOFF_STATUSES = ("pending", "accepted", "running",
|
||||||
"completed", "failed")
|
"completed", "failed")
|
||||||
# report 允许的状态转移:终态(completed / failed)不可再动;
|
# report 允许的状态转移:终态(completed / failed)不可再动;
|
||||||
@@ -74,16 +77,6 @@ SIGNIFICANT_KINDS = ("terminal", "needs_approval", "needs_input",
|
|||||||
"needs_continue")
|
"needs_continue")
|
||||||
|
|
||||||
|
|
||||||
def row_get(row, name):
|
|
||||||
"""行取值容错:sqlite3.Row 缺列报 IndexError、dict 缺键报
|
|
||||||
KeyError、空行报 TypeError,一律回 None(与 automations
|
|
||||||
的 row_get 同款,老库行缺列时读移交列表不能因此炸掉)。"""
|
|
||||||
try:
|
|
||||||
return row[name]
|
|
||||||
except (KeyError, IndexError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _row_dict(row, columns):
|
def _row_dict(row, columns):
|
||||||
if row is None or isinstance(row, dict):
|
if row is None or isinstance(row, dict):
|
||||||
return row
|
return row
|
||||||
|
|||||||
Reference in new issue
Block a user