Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/custom_op/base.py: 97%
65 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 10:02 +0800
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 10:02 +0800
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# -----------------------------------------------------------------------------------------------------------
4# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").
7# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.
11# -----------------------------------------------------------------------------------------------------------
13"""Base definitions for Python GE custom ops."""
15from __future__ import annotations
16import inspect
17from abc import ABC, abstractmethod
18from functools import wraps
19from typing import Callable, List
21from ._native import EagerOpExecutionContext
24_MISSING = object()
25_POSITIONAL_PARAMETER_KINDS = (
26 inspect.Parameter.POSITIONAL_ONLY,
27 inspect.Parameter.POSITIONAL_OR_KEYWORD,
28)
29_EXECUTE_CONTEXT_ARG_NAME = "ctx"
30_EXECUTE_INPUTS_ARG_NAME = "inputs"
31_SUPPORTED_EXECUTE_ARG_NAMES = (_EXECUTE_CONTEXT_ARG_NAME, _EXECUTE_INPUTS_ARG_NAME)
34def _build_input_tensor_list(ctx: EagerOpExecutionContext) -> List[object]:
35 return [ctx.get_input_tensor(index) for index in range(ctx.get_input_num())]
38def _inject_ctx_methods(instance, ctx: EagerOpExecutionContext) -> dict:
39 original_attrs = {}
40 for name in dir(ctx):
41 if name.startswith("_"):
42 continue
43 ctx_method = getattr(ctx, name)
44 if not callable(ctx_method):
45 continue
46 original_attrs[name] = instance.__dict__.get(name, _MISSING)
47 setattr(instance, name, ctx_method)
48 return original_attrs
51def _restore_ctx_methods(instance, original_attrs: dict) -> None:
52 for name, value in original_attrs.items():
53 if value is _MISSING:
54 instance.__dict__.pop(name, None)
55 else:
56 setattr(instance, name, value)
59def _adapt_inputs_execute(method: Callable) -> Callable:
60 @wraps(method)
61 def wrapper(self, ctx: EagerOpExecutionContext) -> None:
62 inputs = _build_input_tensor_list(ctx)
63 original_attrs = _inject_ctx_methods(self, ctx)
64 try:
65 method(self, inputs)
66 finally:
67 _restore_ctx_methods(self, original_attrs)
69 return wrapper
72def _build_execute_signature_error(method: Callable) -> str:
73 return (
74 "EagerExecuteOp.execute only supports execute(self, ctx) or "
75 f"execute(self, inputs); got execute{inspect.signature(method)}. "
76 "Use 'ctx' for EagerOpExecutionContext or 'inputs' for the input tensor list."
77 )
80def _positional_params(method: Callable) -> List[inspect.Parameter]:
81 return [
82 param
83 for param in inspect.signature(method).parameters.values()
84 if param.kind in _POSITIONAL_PARAMETER_KINDS
85 ]
88def _has_non_positional_params(method: Callable) -> bool:
89 return any(
90 param.kind not in _POSITIONAL_PARAMETER_KINDS
91 for param in inspect.signature(method).parameters.values()
92 )
95def _check_execute_arg_count(method: Callable) -> None:
96 if len(_positional_params(method)) != 2 or _has_non_positional_params(method):
97 raise TypeError(_build_execute_signature_error(method))
100def _get_execute_arg_name(method: Callable) -> str:
101 _check_execute_arg_count(method)
102 arg_name = _positional_params(method)[1].name
103 if arg_name not in _SUPPORTED_EXECUTE_ARG_NAMES:
104 raise TypeError(_build_execute_signature_error(method))
105 return arg_name
108class BaseCustomOp(ABC):
109 """Base class for Python custom ops."""
112class EagerExecuteOp(BaseCustomOp):
113 """Base class for Python eager execute custom ops."""
115 def __init_subclass__(cls, **kwargs):
116 super().__init_subclass__(**kwargs)
117 method = cls.__dict__.get("execute")
118 if callable(method):
119 arg_name = _get_execute_arg_name(method)
120 if arg_name == _EXECUTE_INPUTS_ARG_NAME:
121 cls.execute = _adapt_inputs_execute(method)
123 @abstractmethod
124 def execute(self, ctx: EagerOpExecutionContext) -> None:
125 raise NotImplementedError