Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/custom_op/registry.py: 99%
81 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 20:50 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 20:50 +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 Any, Dict, List, Optional, Type
20INTERFACE_EAGER_EXECUTE = "eager_execute"
21INTERFACE_ANNOTATED_ARGS = "annotated_args"
22_INTERFACE_SPECS = (
23 (INTERFACE_EAGER_EXECUTE, "execute"),
24 (INTERFACE_ANNOTATED_ARGS, "declare_launch_args"),
25)
28@dataclass(frozen=True)
29class OpImplDescriptor:
30 """Normalized Python custom op implementation descriptor."""
32 descriptor_key: str
33 op_type: str
34 module_name: str
35 class_name: str
36 interfaces: List[str] = field(default_factory=list)
37 cls: Type[Any] = field(compare=False, repr=False, default=object)
39 def to_bridge_dict(self) -> dict:
40 return {
41 "descriptor_key": self.descriptor_key,
42 "op_type": self.op_type,
43 "module_name": self.module_name,
44 "class_name": self.class_name,
45 "interfaces": list(self.interfaces),
46 }
49class _OpImplRegistry:
50 def __init__(self) -> None:
51 self._lock = threading.RLock()
52 self._descriptor_key_to_desc: Dict[str, OpImplDescriptor] = {}
53 self._op_type_to_desc: Dict[str, OpImplDescriptor] = {}
55 def clear(self) -> None:
56 with self._lock:
57 self._descriptor_key_to_desc.clear()
58 self._op_type_to_desc.clear()
60 def register(self, descriptor: OpImplDescriptor) -> OpImplDescriptor:
61 with self._lock:
62 if descriptor.descriptor_key in self._descriptor_key_to_desc:
63 raise ValueError(
64 f"python op impl descriptor_key already exists: {descriptor.descriptor_key}"
65 )
66 if descriptor.op_type in self._op_type_to_desc:
67 raise ValueError(
68 f"python op impl type already exists: {descriptor.op_type}"
69 )
70 self._descriptor_key_to_desc[descriptor.descriptor_key] = descriptor
71 self._op_type_to_desc[descriptor.op_type] = descriptor
72 return descriptor
74 def get_by_descriptor_key(self, descriptor_key: str) -> Optional[OpImplDescriptor]:
75 with self._lock:
76 return self._descriptor_key_to_desc.get(descriptor_key)
78 def get_all(self) -> List[OpImplDescriptor]:
79 with self._lock:
80 return list(self._descriptor_key_to_desc.values())
83_OP_IMPL_REGISTRY = _OpImplRegistry()
86def _build_descriptor_key(module_name: str, class_name: str, op_type: str) -> str:
87 return f"{module_name}:{class_name}:{op_type}"
90def _normalize_op_type(op_type: str) -> str:
91 if not isinstance(op_type, str) or not op_type:
92 raise TypeError("register_op_impl op_type must be a non-empty string")
93 return op_type
96def _collect_interfaces(cls: Type[Any]) -> List[str]:
97 return [
98 name
99 for name, method_name in _INTERFACE_SPECS
100 if callable(getattr(cls, method_name, None))
101 ]
104def _get_interfaces(cls: Type[Any]) -> List[str]:
105 interfaces = _collect_interfaces(cls)
106 if not interfaces:
107 supported_methods = ", ".join(
108 method_name for _, method_name in _INTERFACE_SPECS
109 )
110 class_name = f"{cls.__module__}.{cls.__qualname__}"
111 raise TypeError(
112 f"register_op_impl class '{class_name}' must implement at least one "
113 f"supported method: {supported_methods}"
114 )
115 return interfaces
118def _register_op_impl_class(cls: Type[Any], *, op_type: str) -> Type[Any]:
119 module_name = cls.__module__
120 class_name = cls.__name__
121 descriptor = OpImplDescriptor(
122 descriptor_key=_build_descriptor_key(module_name, class_name, op_type),
123 op_type=op_type,
124 module_name=module_name,
125 class_name=class_name,
126 interfaces=_get_interfaces(cls),
127 cls=cls,
128 )
129 _OP_IMPL_REGISTRY.register(descriptor)
130 setattr(cls, "__ge_op_impl_descriptor__", descriptor)
131 return cls
134def register_op_impl(*, op_type: str) -> callable:
135 """Decorator for Python custom op implementation classes."""
137 normalized_op_type = _normalize_op_type(op_type)
139 def decorator(cls: Type[Any]) -> Type[Any]:
140 if not inspect.isclass(cls):
141 raise TypeError("register_op_impl expects a class")
142 if inspect.isabstract(cls):
143 raise TypeError("register_op_impl expects a concrete class")
144 return _register_op_impl_class(cls, op_type=normalized_op_type)
146 return decorator
149def clear_registered_op_impls() -> None:
150 _OP_IMPL_REGISTRY.clear()
153def get_registered_op_impls() -> List[OpImplDescriptor]:
154 return _OP_IMPL_REGISTRY.get_all()
157def get_registered_op_impl_dicts() -> List[dict]:
158 return [item.to_bridge_dict() for item in get_registered_op_impls()]
161def get_registered_op_impl_by_descriptor_key(
162 descriptor_key: str,
163) -> Optional[OpImplDescriptor]:
164 return _OP_IMPL_REGISTRY.get_by_descriptor_key(descriptor_key)