Chronaxis用户手册English
本页目录

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
Chronaxis 用户手册 · 离线阅读