Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/onnx_plugin/plugin.py: 100%
60 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-28 11:25 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-28 11:25 +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"""Public descriptor and callback decorator for Python ONNX Plugins."""
15import inspect
16from collections.abc import Collection
17from dataclasses import replace
18from typing import Callable, Optional, Tuple
20from .registry import (
21 PARSE_NODE,
22 PARSE_OPERATOR,
23 OnnxPluginDescriptor,
24 register_onnx_plugin,
25 replace_registered_onnx_plugin,
26)
29def _normalize_name(
30 value: str, field_name: str, *, reject_origin_separator=False
31) -> str:
32 if not isinstance(value, str) or not value:
33 raise TypeError(f"onnx_plugin {field_name} must be a non-empty string")
34 if reject_origin_separator and "::" in value:
35 raise TypeError(f"onnx_plugin {field_name} must not contain '::'")
36 return value
39def _normalize_opsets(opsets: Collection[int]) -> Tuple[int, ...]:
40 if isinstance(opsets, (str, bytes)) or not isinstance(opsets, Collection):
41 raise TypeError("onnx_plugin opsets must be a collection of positive integers")
42 if not opsets:
43 raise ValueError("onnx_plugin opsets must not be empty")
44 normalized = set()
45 for opset in opsets:
46 if type(opset) is not int:
47 raise TypeError("onnx_plugin opsets must contain only integers")
48 if opset <= 0:
49 raise ValueError("onnx_plugin opsets must contain only positive integers")
50 normalized.add(opset)
51 return tuple(sorted(normalized))
54class OnnxPlugin:
55 """ONNX source-to-target descriptor for parser callbacks."""
57 __slots__ = ("_descriptor", "_domain", "_opsets", "_source", "_target")
59 def __init__(
60 self, *, source: str, domain: str, opsets: Tuple[int, ...], target: str
61 ) -> None:
62 self._source = source
63 self._domain = domain
64 self._opsets = opsets
65 self._target = target
66 self._descriptor: Optional[OnnxPluginDescriptor] = None
68 def _bind_callback(
69 self, fn: Callable[..., None], callback_kind: str
70 ) -> Callable[..., None]:
71 if not inspect.isfunction(fn):
72 raise TypeError(f"OnnxPlugin {callback_kind} expects a Python function")
74 if self._descriptor is not None:
75 descriptor = self._descriptor
76 if callback_kind in descriptor.callback_kinds:
77 raise ValueError(f"OnnxPlugin {callback_kind} is already bound")
78 descriptor = replace(
79 descriptor,
80 parser_node=fn
81 if callback_kind == PARSE_NODE
82 else descriptor.parser_node,
83 parser_operator=(
84 fn
85 if callback_kind == PARSE_OPERATOR
86 else descriptor.parser_operator
87 ),
88 )
89 replace_registered_onnx_plugin(descriptor)
90 if descriptor.parser_node is not None:
91 setattr(
92 descriptor.parser_node, "__ge_onnx_plugin_descriptor__", descriptor
93 )
94 if descriptor.parser_operator is not None:
95 setattr(
96 descriptor.parser_operator,
97 "__ge_onnx_plugin_descriptor__",
98 descriptor,
99 )
100 self._descriptor = descriptor
101 return fn
103 module_name = fn.__module__
104 callback_name = fn.__qualname__
105 origin_types = tuple(
106 f"{self._domain}::{opset}::{self._source}" for opset in self._opsets
107 )
108 descriptor = register_onnx_plugin(
109 OnnxPluginDescriptor(
110 descriptor_key=(
111 f"{module_name}:{callback_name}:{callback_kind}:"
112 f"{self._domain}:{self._source}:{','.join(map(str, self._opsets))}"
113 ),
114 source=self._source,
115 domain=self._domain,
116 opsets=self._opsets,
117 target=self._target,
118 origin_types=origin_types,
119 module_name=module_name,
120 parser_node=fn if callback_kind == PARSE_NODE else None,
121 parser_operator=fn if callback_kind == PARSE_OPERATOR else None,
122 )
123 )
124 self._descriptor = descriptor
125 setattr(fn, "__ge_onnx_plugin_descriptor__", descriptor)
126 return fn
128 def parse_node(self, fn: Callable[..., None]) -> Callable[..., None]:
129 return self._bind_callback(fn, PARSE_NODE)
131 def parse_operator(self, fn: Callable[..., None]) -> Callable[..., None]:
132 return self._bind_callback(fn, PARSE_OPERATOR)
135def onnx_plugin(
136 *, source: str, domain: str, opsets: Collection[int], target: str
137) -> OnnxPlugin:
138 """Create an ONNX Plugin descriptor for binding a parse_node callback."""
140 return OnnxPlugin(
141 source=_normalize_name(source, "source", reject_origin_separator=True),
142 domain=_normalize_name(domain, "domain", reject_origin_separator=True),
143 opsets=_normalize_opsets(opsets),
144 target=_normalize_name(target, "target"),
145 )