Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/custom_op/_bridge.py: 96%

158 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-22 15:49 +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# ----------------------------------------------------------------------------------------------------------- 

12 

13"""Bridge-facing Python runtime helpers for GE custom op implementations.""" 

14 

15import inspect 

16import sys 

17import threading 

18from dataclasses import dataclass 

19from typing import Dict, Optional 

20 

21from ._ir_types import InputType, OutputType 

22from ._infer_meta import call_infer_meta # noqa: F401 

23from ._signature import _get_runtime_attr_spec, _validate_args_signature 

24from .base import EagerOpExecutionContext 

25from .bootstrap import ( 

26 get_registered_op_impls, 

27 get_registered_op_protos, 

28 load_custom_op_plugins, 

29) 

30from .context import _declare_launch_args_ctx_scope, _execute_ctx_scope 

31from .registry import ( 

32 INTERFACE_ANNOTATED_ARGS, 

33 INTERFACE_EAGER_EXECUTE, 

34 get_registered_op_impl_by_descriptor_key, 

35) 

36 

37 

38@dataclass 

39class _OpImplHolder: 

40 descriptor_key: str 

41 instance_id: str 

42 instance: object 

43 

44 

45_HOLDER_LOCK = threading.RLock() 

46_OP_IMPL_HOLDERS: Dict[str, _OpImplHolder] = {} 

47 

48 

49def load_and_get_op_impl_descriptors() -> list: 

50 load_custom_op_plugins() 

51 return get_registered_op_impls() 

52 

53 

54def load_and_get_op_descriptors() -> dict: 

55 load_custom_op_plugins() 

56 return { 

57 "protos": get_registered_op_protos(), 

58 "impls": get_registered_op_impls(), 

59 } 

60 

61 

62def _get_holder(instance_id: str) -> _OpImplHolder: 

63 with _HOLDER_LOCK: 

64 holder = _OP_IMPL_HOLDERS.get(instance_id) 

65 if holder is None: 

66 raise KeyError(f"python op impl holder is not created: {instance_id}") 

67 return holder 

68 

69 

70def _get_eager_execute_holder(instance_id: str) -> _OpImplHolder: 

71 holder = _get_holder(instance_id) 

72 if not callable(getattr(holder.instance, "execute", None)): 

73 raise TypeError( 

74 f"python op impl does not implement callable execute: {instance_id}" 

75 ) 

76 return holder 

77 

78 

79def _get_eager_execute_op(instance_id: str) -> object: 

80 return _get_eager_execute_holder(instance_id).instance 

81 

82 

83def create_op_impl_holder(instance_id: str, descriptor_key: str) -> bool: 

84 descriptor = get_registered_op_impl_by_descriptor_key(descriptor_key) 

85 if descriptor is None: 

86 raise KeyError(f"python op impl descriptor_key not found: {descriptor_key}") 

87 with _HOLDER_LOCK: 

88 if instance_id in _OP_IMPL_HOLDERS: 

89 return True 

90 _OP_IMPL_HOLDERS[instance_id] = _OpImplHolder( 

91 descriptor_key=descriptor_key, 

92 instance_id=instance_id, 

93 instance=descriptor.cls(), 

94 ) 

95 return True 

96 

97 

98def destroy_op_impl_holder(instance_id: str) -> bool: 

99 with _HOLDER_LOCK: 

100 return _OP_IMPL_HOLDERS.pop(instance_id, None) is not None 

101 

102 

103def _is_legacy_execute(method) -> bool: 

104 params = list(inspect.signature(method).parameters.values()) 

105 return ( 

106 len(params) == 1 

107 and params[0].name == "ctx" 

108 and params[0].kind 

109 in ( 

110 inspect.Parameter.POSITIONAL_ONLY, 

111 inspect.Parameter.POSITIONAL_OR_KEYWORD, 

112 ) 

113 ) 

114 

115 

116def _get_callback_for_signature(cls, method_name: str): 

117 method = inspect.getattr_static(cls, method_name) 

118 if isinstance(method, staticmethod): 

119 return method.__func__ 

120 if isinstance(method, classmethod): 

121 return method.__get__(None, cls) 

122 if inspect.isfunction(method): 

123 return method.__get__(object(), cls) 

124 return getattr(cls, method_name) 

125 

126 

127def validate_op_impl_descriptor(descriptor_key: str, ir_meta: Optional[dict]) -> bool: 

128 descriptor = get_registered_op_impl_by_descriptor_key(descriptor_key) 

129 if descriptor is None: 

130 raise KeyError(f"python op impl descriptor_key not found: {descriptor_key}") 

131 

132 if INTERFACE_EAGER_EXECUTE in descriptor.interfaces: 

133 method = _get_callback_for_signature(descriptor.cls, "execute") 

134 if not _is_legacy_execute(method): 

135 if ir_meta is None: 

136 raise RuntimeError( 

137 "canonical IR not found for schema-bound execute: " 

138 f"{descriptor.op_type}" 

139 ) 

140 _validate_args_signature(method, ir_meta, descriptor, method_name="execute") 

141 

142 if INTERFACE_ANNOTATED_ARGS in descriptor.interfaces: 

143 if ir_meta is None: 

144 raise RuntimeError( 

145 "canonical IR not found for schema-bound declare_launch_args" 

146 ) 

147 method = _get_callback_for_signature(descriptor.cls, "declare_launch_args") 

148 _validate_args_signature( 

149 method, ir_meta, descriptor, method_name="declare_launch_args" 

150 ) 

151 return True 

152 

153 

154def _build_inputs( 

155 ir_inputs: list, 

156 get_required_input, 

157 get_optional_input, 

158 get_dynamic_input_num, 

159 get_dynamic_input, 

160) -> list: 

161 args = [] 

162 for ir_index, item in enumerate(ir_inputs): 

163 kind = item["kind"] 

164 if kind == InputType.REQUIRED: 

165 args.append(get_required_input(ir_index)) 

166 elif kind == InputType.OPTIONAL: 

167 args.append(get_optional_input(ir_index)) 

168 elif kind == InputType.DYNAMIC: 

169 instance_num = get_dynamic_input_num(ir_index) 

170 args.append( 

171 [ 

172 get_dynamic_input(ir_index, relative_index) 

173 for relative_index in range(instance_num) 

174 ] 

175 ) 

176 else: 

177 raise ValueError( 

178 f"unsupported custom op IR input kind: {kind}, ir index: {ir_index}" 

179 ) 

180 return args 

181 

182 

183def _build_execute_inputs(ctx: EagerOpExecutionContext, ir_inputs: list) -> list: 

184 return _build_inputs( 

185 ir_inputs, 

186 ctx.get_required_input_tensor, 

187 ctx.get_optional_input_tensor, 

188 ctx.get_dynamic_input_num, 

189 ctx.get_dynamic_input_tensor, 

190 ) 

191 

192 

193def _read_runtime_attr(attrs, index: int, ir_type: str): 

194 getter_name, _ = _get_runtime_attr_spec(ir_type, index) 

195 return getattr(attrs, getter_name)(index) 

196 

197 

198def _build_execute_attrs(ctx: EagerOpExecutionContext, ir_attrs: list) -> dict: 

199 if not ir_attrs: 

200 return {} 

201 attrs = ctx.get_attrs() 

202 return { 

203 item["name"]: _read_runtime_attr(attrs, index, item["type"]) 

204 for index, item in enumerate(ir_attrs) 

205 } 

206 

207 

208def _build_declare_inputs(ctx, ir_inputs: list) -> list: 

209 return _build_inputs( 

210 ir_inputs, 

211 ctx._get_required_input_tensor, 

212 ctx._get_optional_input_tensor, 

213 ctx._get_dynamic_input_num, 

214 ctx._get_dynamic_input_tensor, 

215 ) 

216 

217 

218def _build_declare_outputs(ctx, ir_outputs: list) -> list: 

219 args = [] 

220 for ir_index, item in enumerate(ir_outputs): 

221 kind = item["kind"] 

222 if kind == OutputType.REQUIRED: 

223 args.append(ctx._get_required_output_tensor(ir_index)) 

224 elif kind == OutputType.DYNAMIC: 

225 instance_num = ctx._get_dynamic_output_num(ir_index) 

226 args.append( 

227 [ 

228 ctx._get_dynamic_output_tensor(ir_index, relative_index) 

229 for relative_index in range(instance_num) 

230 ] 

231 ) 

232 else: 

233 raise ValueError(f"unsupported custom op IR output kind: {kind}") 

234 return args 

235 

236 

237def _build_declare_attrs(ctx, ir_attrs: list) -> dict: 

238 if not ir_attrs: 

239 return {} 

240 attrs = ctx._get_attrs() 

241 return { 

242 item["name"]: _read_runtime_attr(attrs, index, item["type"]) 

243 for index, item in enumerate(ir_attrs) 

244 } 

245 

246 

247def call_execute( 

248 instance_id: str, 

249 ir_meta: Optional[dict], 

250 ctx: EagerOpExecutionContext, 

251) -> None: 

252 try: 

253 holder = _get_eager_execute_holder(instance_id) 

254 custom_op = holder.instance 

255 method = custom_op.execute 

256 if _is_legacy_execute(method): 

257 method(ctx) 

258 return 

259 if ir_meta is None: 

260 descriptor = custom_op.__ge_op_impl_descriptor__ 

261 raise RuntimeError( 

262 f"canonical IR not found for schema-bound execute: {descriptor.op_type}" 

263 ) 

264 args = _build_execute_inputs(ctx, ir_meta["inputs"]) 

265 kwargs = _build_execute_attrs(ctx, ir_meta["attrs"]) 

266 with _execute_ctx_scope(ctx): 

267 method(*args, **kwargs) 

268 finally: 

269 ctx._invalidate() 

270 

271 

272def call_declare_launch_args(instance_id: str, ir_meta: Optional[dict], ctx) -> None: 

273 try: 

274 holder = _get_holder(instance_id) 

275 method = getattr(holder.instance, "declare_launch_args", None) 

276 if not callable(method): 

277 raise TypeError( 

278 f"python op impl does not implement declare_launch_args: {instance_id}" 

279 ) 

280 if ir_meta is None: 

281 raise RuntimeError( 

282 "canonical IR not found for schema-bound declare_launch_args" 

283 ) 

284 args = _build_declare_inputs(ctx, ir_meta["inputs"]) 

285 args.extend(_build_declare_outputs(ctx, ir_meta["outputs"])) 

286 kwargs = _build_declare_attrs(ctx, ir_meta["attrs"]) 

287 with _declare_launch_args_ctx_scope(ctx): 

288 result = method(*args, **kwargs) 

289 if result is not None: 

290 raise TypeError("declare_launch_args must return None") 

291 finally: 

292 ctx._invalidate() 

293 

294 

295def clear_op_impl_holders() -> None: 

296 with _HOLDER_LOCK: 

297 _OP_IMPL_HOLDERS.clear() 

298 

299 

300def clear_loaded_op_impl_modules() -> None: 

301 """Clear all dynamically loaded op implementation modules from sys.modules to avoid test pollution.""" 

302 keys_to_remove = [key for key in sys.modules if key.startswith("_ge_py_custom_op_")] 

303 for key in keys_to_remove: 

304 del sys.modules[key]