chronaxis.execution.static_metadata
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""按实际对象身份估算一次运行借用的静态声明,不展开重复引用。23输入只限已经校验的声明模型;返回浅对象及其独有子对象的字节估计。4此函数用于计划、编译树的固定费用,不改变样本、历史或动态工作区的计费。5已有父资源持有的对象可显式排除;这里只计算费用,不接管它们的生命周期。6"""78from collections.abc import Mapping9from dataclasses import fields, is_dataclass10from enum import Enum11from sys import getsizeof1213from ..core.expression import ExprNode14from ..extensions import ExtensionRegistry, FunctionSpec, ReductionSpec, TransformSpec15from ..results.errors import ResultFormatError161718def static_metadata_bytes(value, *, borrowed=()):19 """遍历不可变声明图,一次调用内每个实际对象只计一次。2021 参数: value为已校验声明,borrowed为仍由父资源计费的实际对象。22 返回: 对象、容器和实例属性字典的保守字节数;只读映射另计背后字典槽。23 异常: ResultFormatError为循环、未知结构或普通容器超过32层。24 表达式args用显式栈处理,不将合法表达式长度当成外部协议深度。25 """26 seen = set()27 if borrowed:28 # 真正新建的定义可能仍借用旧choices/单位字符串,父声明的共享子对象同样排除。29 _static_metadata_cost(borrowed, seen)30 return _static_metadata_cost(value, seen)313233def _static_metadata_cost(value, seen):34 """累加尚未登记的对象并更新本次身份集合。3536 参数: value为冻结声明;seen包含仍由父声明持有或本次已计费的对象身份。37 返回: 新增费用。异常: 循环、深度或类型错误。集合只活在本次估算调用内。38 """39 active, pending, total = set(), [(value, 0, False)], 040 while pending:41 item, depth, leaving = pending.pop()42 identity = id(item)43 if leaving:44 active.remove(identity)45 seen.add(identity)46 continue47 if identity in active:48 raise ResultFormatError('静态声明存在循环引用')49 if identity in seen:50 continue51 if depth > 32:52 raise ResultFormatError('静态声明容器超过32层')53 # 回调与注册表由宿主持有,不遍历任意Python函数及其全局环境。54 if type(item) in (ExtensionRegistry, FunctionSpec, ReductionSpec, TransformSpec):55 total += 25656 seen.add(identity)57 continue58 if isinstance(item, Enum) or item is None or type(item) in (str, int, bool, float, bytes):59 total += getsizeof(item)60 seen.add(identity)61 continue62 if is_dataclass(item) and not isinstance(item, type):63 # slots实例的字段槽已在实例本体中;普通实例还持有自己的属性字典。64 attributes = getattr(item, '__dict__', None)65 total += getsizeof(item) + (0 if attributes is None else getsizeof(attributes))66 children = []67 for field in fields(item):68 member = getattr(item, field.name)69 if type(item) is ExprNode and field.name == 'args':70 if type(member) is not tuple or any(type(child) is not ExprNode for child in member):71 raise ResultFormatError('静态表达式参数结构错误')72 # args容器本身独立计费,子节点延续表达式外的普通深度。73 if id(member) not in seen:74 total += getsizeof(member)75 seen.add(id(member))76 children.extend((child, depth, False) for child in member)77 else:78 children.append((member, depth + 1, False))79 elif isinstance(item, Mapping):80 total += getsizeof(item)81 if type(item) is not dict:82 # 冻结模型使用只读proxy,getsizeof只含代理,不含紧凑背后dict。83 # 单独覆盖其桶和引用槽;键值对象随后按身份计费,不再复制整棵树。84 total += 256 + 64 * len(item)85 children = [(member, depth + 1, False) for pair in item.items() for member in pair]86 elif type(item) is tuple:87 total += getsizeof(item)88 children = [(member, depth + 1, False) for member in item]89 else:90 raise ResultFormatError('静态声明类型无法估算')91 # DFS离开时才登记seen,保证共享子树去重不会掩盖环。92 active.add(identity)93 pending.append((item, depth, True))94 pending.extend(reversed(children))95 return total96