Chronaxis用户手册English
本页目录

chronaxis.runner

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""把分析计划交给同一公共入口检查、解释和执行,返回可流式读取的结果。23AnalysisRunner接收已经由load_analysis_plan或AnalysisPlan.from_dict构建的计划。validate和explain4只查看声明,沿用planning.reports,不打开CSV、DBC或旧日志。run才创建独立5RunSession,首次扫描来源、封存新历史并依次运行已有四个分析阶段;这里不重写6读取、数值算法或发布。返回AnalysisResult表示analysis_complete,write另行发布。78显式backend只替换内部执行视图中的计算方式,保留原配置identity、raw_config和9来源对象;清单另记实际后端。AnalysisRunner本身没有输入、FD或跨运行状态,能够重复运行10同一计划,每次使用独立资源。成功前全部输入关闭;失败保原cause及最高运行恢复11owner,取消保原KeyboardInterrupt/SystemExit。暂存与诊断保留,不宣称回滚输入。12"""1314# 只在run时加载实际执行组件;静态检查不创建预算、工作目录或来源adapter。15from dataclasses import replace16from .planning.plan import AnalysisPlan17from .planning.models import ExecutionPlan18from .planning.reports import validate_analysis_plan, explain_analysis_plan19from .results.errors import AnalysisError20from .results.spool import _cleanup_owner212223class AnalysisRunner:24    """保存可选后端覆盖,复用同一静态校验,逐次创建独立运行资源。2526    _backend为None或已支持的明确后端,不自动回退。实例不持有执行结果或活动27    session;结果所有权交给调用者,失败未清完资源通过原异常的最高owner恢复。28    """2930    def __init__(self, backend=None, *, extensions=None):31        """选择是否覆盖计划后端,不读取任何来源或配置文件。3233        参数: backend为None/auto/reference/numpy;None使用各次计划原execution.backend。34            extensions 为创建计划时使用的同一个宿主注册;省略时运行内置操作。35        返回: None;初始化实例状态。异常: TypeError为非文本后端覆盖,或extensions不是ExtensionRegistry;36            ValueError为未知后端。注册对象与计划是否相同,在validate/explain/run收到计划时检查。37        """38        # 明确拒绝任意对象和隐藏回退,不能把拼错的后端当成默认NumPy。39        if backend is not None:40            if type(backend) is not str:41                raise TypeError('AnalysisRunner backend需要auto、reference、numpy或None')42            if backend not in ('reference', 'numpy', 'auto'):43                raise ValueError('AnalysisRunner仅支持auto、reference与numpy后端')44        if extensions is not None:45            from .extensions import ExtensionRegistry46            if type(extensions) is not ExtensionRegistry:47                raise TypeError('extensions需要宿主建立的ExtensionRegistry')48        self._extensions = extensions49        self._backend = backend5051    @property52    def backend(self):53        """读取构造时的覆盖选择,不表示某个尚未运行计划的实际后端。5455        参数: 无。返回: None/auto/reference/numpy。异常: 无。56        """57        return self._backend5859    def _effective_plan(self, plan, process_count=None):60        """保留原声明身份,只为明确覆盖创建内部执行视图。6162        参数: plan为已构建AnalysisPlan;process_count为正整数覆盖,None沿用配置。返回: 原plan或仅执行选项不同的新view。63        异常: TypeError为非计划/执行声明;ValueError为与计划不同的扩展注册或非法process_count覆盖。64            注册对象按身份比较,两个内容相同的注册对象也不能互换;不重新解析配置或复制原输入行。65        """66        if not isinstance(plan, AnalysisPlan) or not isinstance(plan.execution, ExecutionPlan):67            raise TypeError('AnalysisRunner需要load_analysis_plan或AnalysisPlan.from_dict构建的计划')68        if plan.bindings.extensions is not self._extensions:69            raise ValueError('AnalysisRunner与AnalysisPlan必须使用同一个宿主注册')70        # 来源、绑定及raw_config仍借原对象;后端是本次执行选项,不伪造另一份输入。71        if process_count is not None and (type(process_count) is not int or process_count < 1):72            raise ValueError('process_count需要大于等于1的整数,不接受bool')73        # 只复制执行声明,调用者的计划及原始配置保持不变。74        count = plan.execution.process_count if process_count is None else process_count75        backend = plan.execution.backend if self._backend is None else self._backend76        if count == plan.execution.process_count and backend == plan.execution.backend:77            return plan78        return replace(plan, execution=replace(plan.execution, backend=backend, process_count=count))7980    def validate(self, plan, *, process_count=None):81        """报告声明是否合法及执行路径是否已接入,不提前读取数据。8283        参数: plan为已构建AnalysisPlan;process_count为仅关键字的正整数覆盖,None沿用配置。返回: 同一共享ValidationReport,保留实际字段/范围未验警告。84        异常: TypeError为非AnalysisPlan或非法执行模型;ValueError为非法process_count覆盖,或Runner与计划未共享同一扩展注册。85            这些调用错误在返回报告前抛出;正常计划的静态问题写入报告errors,86            不以校验成功证明输入文件内容有效。副作用: 无来源I/O或目录创建。87        """88        # 使用相同有效视图和纯静态报告,避免校验后偷偷换另一套后端能力。89        return validate_analysis_plan(self._effective_plan(plan, process_count))9091    def explain(self, plan, *, process_count=None):92        """列出真实依赖、各节点后端、历史需求及声明预算,不启动分析。9394        参数: plan为已构建AnalysisPlan;process_count为仅关键字的正整数覆盖,None沿用配置。返回: ExplainReport,plan_identity保原声明身份。95        异常: TypeError为非AnalysisPlan或非法执行模型;ValueError为非法process_count覆盖,或Runner与计划未共享同一扩展注册。96            这些调用错误在返回报告前抛出;调用者绕过解析器手工破坏typed97            引用时可能产生AttributeError/KeyError,应重新从完整配置构造计划。98        副作用: 不读取CSV/DBC,也不测量实耗。99        """100        # 规则状态驱动与数组节点分别由同一报告说明,不把覆盖当整个计划标量回退。101        return explain_analysis_plan(self._effective_plan(plan, process_count))102103    def run(self, plan, work_dir=None, *, process_count=None):104        """验证后执行一次真实分析,输入全关才返回独立AnalysisResult。105106        参数: plan为已构建AnalysisPlan;work_dir为全新工作目录或None(唯一专用目录);107            process_count为仅关键字的正整数,覆盖读取解码进程上限;None沿用配置,不修改原计划。108        返回: analysis_complete的AnalysisResult,由调用者close/上下文管理,write另行发布。109        异常: TypeError为错误plan/执行声明;ValueError为非法process_count或不同扩展注册。110            校验或运行失败抛AnalysisError;新包装的运行错误通过__cause__保留原异常,111            直接抛出的AnalysisError可能没有原因链,也没有统一diagnostics属性。112            KeyboardInterrupt/SystemExit原样传播;附加清理信息见__notes__(若有)。113            持续清理失败时,从异常取得getattr(exc, 'runner_cleanup_owner', None),114            排除资源故障后对存在的恢复对象调用close()。115        副作用: 创建本次新暂存;扫描来源一次、后续仅回读新history,不自动删除或发布文件。116        """117        effective = self._effective_plan(plan, process_count)118        validation = validate_analysis_plan(effective)119        if validation.errors or not validation.valid or not validation.execution_ready:120            raise AnalysisError('计划尚不能执行:' + (';'.join(validation.errors) or '所需执行路径不可用'))121        session = None122        try:123            # 惰性加载保证validate/explain没有实际来源组件的创建或调用。124            from .execution.run_session import RunSession125            session = RunSession(effective, work_dir=work_dir)126            if self._extensions is not None:127                from .extensions import using_extension_budget128                with using_extension_budget(session._budget):129                    return session.execute()130            return session.execute()131        except BaseException as exc:132            # 未返回构造有自己的最高owner;正常构造后仍由唯一session关闭全部本次资源。133            owner = getattr(exc, 'runner_cleanup_owner', None)134            if owner is None:135                owner = session136            if owner is not None:137                _cleanup_owner(owner, exc, 'runner_cleanup_owner', '公共分析运行')138            # 保留原分析异常与取消类型;其他业务错误通过cause说明最初失败位置。139            if isinstance(exc, (AnalysisError, KeyboardInterrupt, SystemExit)):140                raise141            error = AnalysisError('分析运行失败:' + str(exc))142            recovery = getattr(exc, 'runner_cleanup_owner', None)143            if recovery is not None:144                error.runner_cleanup_owner = recovery145            for note in getattr(exc, '__notes__', ()):146                error.add_note(note)147            raise error from exc148
Chronaxis 用户手册 · 离线阅读