Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/custom_op/registry.py: 99%
77 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 10:03 +0800
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 10:03 +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"""Python custom op implementation registry and decorators."""
15import inspect
16import threading
17from dataclasses import dataclass, field
18from typing import Dict, List, Optional, Type
20from .base import BaseCustomOp, EagerExecuteOp
22INTERFACE_EAGER_EXECUTE = "eager_execute"
23_INTERFACE_SPECS = ((INTERFACE_EAGER_EXECUTE, EagerExecuteOp),)
26@dataclass(frozen=True)
27class OpImplDescriptor:
28 """Normalized Python custom op implementation descriptor."""
30 descriptor_key: str
31 op_type: str
32 module_name: str
33 class_name: str
34 interfaces: List[str] = field(default_factory=list)
35 cls: Type[BaseCustomOp] = field(compare=False, repr=False, default=BaseCustomOp)
37 def to_bridge_dict(self) -> dict:
38 return {
39 "descriptor_key": self.descriptor_key,
40 "op_type": self.op_type,
41 "module_name": self.module_name,
42 "class_name": self.class_name,
43 "interfaces": list(self.interfaces),
44 }
47class _OpImplRegistry:
48 def __init__(self) -> None:
49 self._lock = threading.RLock()
50 self._descriptor_key_to_desc: Dict[str, OpImplDescriptor] = {}
51 self._op_type_to_desc: Dict[str, OpImplDescriptor] = {}
53 def clear(self) -> None:
54 with self._lock:
55 self._descriptor_key_to_desc.clear()
56 self._op_type_to_desc.clear()
58 def register(self, descriptor: OpImplDescriptor) -> OpImplDescriptor:
59 with self._lock:
60 if descriptor.descriptor_key in self._descriptor_key_to_desc:
61 raise ValueError(
62 f"python op impl descriptor_key already exists: {descriptor.descriptor_key}"
63 )
64 if descriptor.op_type in self._op_type_to_desc:
65 raise ValueError(
66 f"python op impl type already exists: {descriptor.op_type}"
67 )
68 self._descriptor_key_to_desc[descriptor.descriptor_key] = descriptor
69 self._op_type_to_desc[descriptor.op_type] = descriptor
70 return descriptor
72 def get_by_descriptor_key(self, descriptor_key: str) -> Optional[OpImplDescriptor]:
73 with self._lock:
74 return self._descriptor_key_to_desc.get(descriptor_key)
76 def get_all(self) -> List[OpImplDescriptor]:
77 with self._lock:
78 return list(self._descriptor_key_to_desc.values())
81_OP_IMPL_REGISTRY = _OpImplRegistry()
84def _build_descriptor_key(module_name: str, class_name: str, op_type: str) -> str:
85 return f"{module_name}:{class_name}:{op_type}"
88def _normalize_op_type(op_type: str) -> str:
89 if not isinstance(op_type, str) or not op_type:
90 raise TypeError("register_op_impl op_type must be a non-empty string")
91 return op_type
94def _collect_interfaces(cls: Type[BaseCustomOp]) -> List[str]:
95 return [name for name, base_cls in _INTERFACE_SPECS if issubclass(cls, base_cls)]
98def _get_interfaces(cls: Type[BaseCustomOp]) -> List[str]:
99 interfaces = _collect_interfaces(cls)
100 if not interfaces:
101 raise TypeError("register_op_impl expects a supported BaseCustomOp subclass")
102 return interfaces
105def _register_op_impl_class(
106 cls: Type[BaseCustomOp], *, op_type: str
107) -> Type[BaseCustomOp]:
108 module_name = cls.__module__
109 class_name = cls.__name__
110 descriptor = OpImplDescriptor(
111 descriptor_key=_build_descriptor_key(module_name, class_name, op_type),
112 op_type=op_type,
113 module_name=module_name,
114 class_name=class_name,
115 interfaces=_get_interfaces(cls),
116 cls=cls,
117 )
118 _OP_IMPL_REGISTRY.register(descriptor)
119 setattr(cls, "__ge_op_impl_descriptor__", descriptor)
120 return cls
123def register_op_impl(*, op_type: str) -> callable:
124 """Decorator for Python custom op implementation classes."""
126 normalized_op_type = _normalize_op_type(op_type)
128 def decorator(cls: Type[BaseCustomOp]) -> Type[BaseCustomOp]:
129 if not inspect.isclass(cls) or not issubclass(cls, BaseCustomOp):
130 raise TypeError("register_op_impl expects a BaseCustomOp subclass")
131 return _register_op_impl_class(cls, op_type=normalized_op_type)
133 return decorator
136def clear_registered_op_impls() -> None:
137 _OP_IMPL_REGISTRY.clear()
140def get_registered_op_impls() -> List[OpImplDescriptor]:
141 return _OP_IMPL_REGISTRY.get_all()
144def get_registered_op_impl_dicts() -> List[dict]:
145 return [item.to_bridge_dict() for item in get_registered_op_impls()]
148def get_registered_op_impl_by_descriptor_key(
149 descriptor_key: str,
150) -> Optional[OpImplDescriptor]:
151 return _OP_IMPL_REGISTRY.get_by_descriptor_key(descriptor_key)