Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/dttest/api/python/ge/ge/graph/node.py: 89%
179 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# -----------------------------------------------------------------------------------------------------------
5# Copyright (c) 2025 Huawei Technologies Co., Ltd.
6# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
7# CANN Open Software License Agreement Version 2.0 (the "License").
8# Please refer to the License for details. You may not use this file except in compliance with the License.
9# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
10# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
11# See LICENSE in the root of the software repository for the full text of the License.
12# -----------------------------------------------------------------------------------------------------------
14"""Node module for GraphEngine node operations."""
16import ctypes
17from typing import Any, List, Tuple
19from ge._capi.pygraph_wrapper import graph_lib
21from ._attr import _AttrValue
22from .tensor_desc import TensorDesc
25class Node:
26 """Node class for GraphEngine node operations.
28 This class provides a Pythonic interface for node operations
29 using the GraphEngine C API.
31 Example:
32 >>> # Node objects are created internally by Graph operations
33 >>> nodes = graph.get_all_nodes()
34 >>> for node in nodes:
35 ... name = node.name
36 ... node_type = node.type
37 ... attr = node.get_attr("some_attr")
38 ... node.set_attr("new_attr", attr_value)
39 """
41 def __init__(self) -> None:
42 """Prevent direct instantiation of Node objects."""
43 raise RuntimeError("Node objects should not be created directly.")
45 def __del__(self) -> None:
46 """Clean up resources."""
47 if self._owns_handle:
48 graph_lib.GeApiWrapper_GNode_DestroyGNode(self._handle)
49 self._handle = None
51 def __copy__(self) -> None:
52 """Copy is not supported."""
53 raise RuntimeError("Node does not support copy")
55 def __deepcopy__(self, memodict) -> None:
56 """Deep copy is not supported."""
57 raise RuntimeError("Node does not support deepcopy")
59 @classmethod
60 def _create_from(cls, node_handle: ctypes.c_void_p, owns_handle: bool = True) -> "Node":
61 """Create Node object from C++ handle.(internal use only by e.g Graph.get_all_nodes()
62 or TensorHolder._get_node_snapshot(), do not use this method directly)
64 Args:
65 node_handle: C++ GNode object handle.
66 owns_handle: Whether to own the handle.
68 Returns:
69 Node object.
71 Raises:
72 ValueError: If node_handle is None.
73 """
74 if not node_handle:
75 raise ValueError("Node handle cannot be None")
76 instance = cls.__new__(cls)
77 instance._handle = node_handle
78 instance._owns_handle = owns_handle
79 return instance
81 @property
82 def name(self) -> str:
83 """Get node name.
85 Returns:
86 Node name.
88 Raises:
89 RuntimeError: If name retrieval fails.
90 """
91 c_str = graph_lib.GeApiWrapper_GNode_GetName(self._handle)
92 if not c_str:
93 raise RuntimeError("Failed to get Node name")
95 try:
96 return ctypes.string_at(c_str).decode("utf-8")
97 finally:
98 graph_lib.GeApiWrapper_FreeString(c_str)
100 @property
101 def type(self) -> str:
102 """Get node type.
104 Returns:
105 Node type.
107 Raises:
108 RuntimeError: If type retrieval fails.
109 """
110 c_str = graph_lib.GeApiWrapper_GNode_GetType(self._handle)
111 if not c_str:
112 raise RuntimeError("Failed to get Node type")
114 try:
115 return ctypes.string_at(c_str).decode("utf-8")
116 finally:
117 graph_lib.GeApiWrapper_FreeString(c_str)
119 def get_in_control_nodes(self) -> List["Node"]:
120 """Get input control nodes.
122 Returns:
123 List of input control Node objects.
124 """
125 node_num = ctypes.c_size_t()
126 nodes = graph_lib.GeApiWrapper_GNode_GetInControlNodes(self._handle, ctypes.byref(node_num))
127 if not nodes:
128 return []
130 try:
131 return [Node._create_from(nodes[i]) for i in range(node_num.value)]
132 finally:
133 graph_lib.GeApiWrapper_GNode_FreeGNodeArray(nodes)
135 def get_in_data_nodes_and_port_indexes(self, in_index: int) -> Tuple["Node", int]:
136 """Get input data node and port index.
138 Args:
139 in_index: Input index.
141 Returns:
142 Tuple of (input Node, port index).
144 Raises:
145 TypeError: If in_index is not an integer.
146 RuntimeError: If retrieval fails.
147 """
148 if not isinstance(in_index, int):
149 raise TypeError("Input index must be an integer")
151 in_node = ctypes.c_void_p()
152 index = ctypes.c_int32()
154 ret = graph_lib.GeApiWrapper_GNode_GetInDataNodesAndPortIndexes(
155 self._handle, in_index, ctypes.byref(in_node), ctypes.byref(index)
156 )
157 if ret != 0: # GRAPH_SUCCESS
158 raise RuntimeError(f"Failed to get input data node for index {in_index}")
160 return Node._create_from(in_node), index.value
162 def get_out_data_nodes_and_port_indexes(self, out_index: int) -> List[Tuple["Node", int]]:
163 """Get output data nodes and port indexes.
165 Args:
166 out_index: Output index.
168 Returns:
169 List of Tuple of (output Node, port index).
171 Raises:
172 TypeError: If out_index is not an integer.
173 RuntimeError: If retrieval fails.
174 """
175 if not isinstance(out_index, int):
176 raise TypeError("Output index must be an integer")
177 out_nodes = ctypes.POINTER(ctypes.c_void_p)()
178 out_indexes = ctypes.POINTER(ctypes.c_int32)()
179 size = ctypes.c_int()
180 ret = graph_lib.GeApiWrapper_GNode_GetOutDataNodesAndPortIndexes(
181 self._handle,
182 out_index,
183 ctypes.byref(out_nodes),
184 ctypes.byref(out_indexes),
185 ctypes.byref(size),
186 )
187 if ret != 0: # GRAPH_SUCCESS
188 raise RuntimeError(f"Failed to get output data node for index {out_index}")
189 try:
190 return [(Node._create_from(out_nodes[i]), out_indexes[i]) for i in range(size.value)]
191 finally:
192 graph_lib.GeApiWrapper_GNode_FreeGNodeArray(out_nodes)
193 graph_lib.GeApiWrapper_GNode_FreeIntArray(out_indexes)
195 def get_attr(self, key: str) -> Any:
196 """Get node attribute.
198 Args:
199 key: Attribute name.
201 Returns:
202 Attribute value.
204 Raises:
205 TypeError: If key is not a string.
206 RuntimeError: If attribute retrieval fails.
207 """
208 if not isinstance(key, str):
209 raise TypeError("Attribute key must be a string")
211 attr_value = _AttrValue()
212 key_bytes = key.encode("utf-8")
214 ret = graph_lib.GeApiWrapper_GNode_GetAttr(self._handle, key_bytes, attr_value._av_ptr)
215 if ret != 0: # GRAPH_SUCCESS
216 raise RuntimeError(f"Failed to get attribute '{key}' from Node")
218 return attr_value.get_value()
220 def set_attr(self, key: str, value: Any) -> None:
221 """Set node attribute.
223 Args:
224 key: Attribute name.
225 value: Attribute value.
227 Raises:
228 TypeError: If arguments have wrong types.
229 RuntimeError: If attribute setting fails.
230 """
231 if not isinstance(key, str):
232 raise TypeError("Attribute key must be a string")
234 key_bytes = key.encode("utf-8")
235 attr_value = _AttrValue()
236 attr_value.set_value(value)
237 ret = graph_lib.GeApiWrapper_GNode_SetAttr(self._handle, key_bytes, attr_value._av_ptr)
238 if ret != 0: # GRAPH_SUCCESS
239 raise RuntimeError(f"Failed to set attribute '{key}' on Node {self.name}")
241 def get_input_attr(self, attr_name: str, input_index: int) -> Any:
242 """Get input attribute.
244 Args:
245 attr_name: Attribute name.
246 input_index: Input index.
248 Returns:
249 Attribute value.
251 Raises:
252 TypeError: If arguments have wrong types.
253 RuntimeError: If attribute retrieval fails.
254 """
255 if not isinstance(attr_name, str):
256 raise TypeError("Attribute name must be a string")
257 if not isinstance(input_index, int):
258 raise TypeError("Input index must be an integer")
260 attr_value = _AttrValue()
261 attr_name_bytes = attr_name.encode("utf-8")
263 ret = graph_lib.GeApiWrapper_GNode_GetInputAttr(
264 self._handle,
265 attr_name_bytes,
266 ctypes.c_uint32(input_index),
267 attr_value._av_ptr,
268 )
269 if ret != 0: # GRAPH_SUCCESS
270 raise RuntimeError(f"Failed to get Node {self.name} input attribute '{attr_name}' for index {input_index}")
272 return attr_value.get_value()
274 def set_input_attr(self, attr_name: str, input_index: int, value: Any) -> None:
275 """Set input attribute.
277 Args:
278 attr_name: Attribute name.
279 input_index: Input index.
280 value: Attribute value.
282 Raises:
283 TypeError: If arguments have wrong types.
284 RuntimeError: If attribute setting fails.
285 """
286 if not isinstance(attr_name, str):
287 raise TypeError("Attribute name must be a string")
288 if not isinstance(input_index, int):
289 raise TypeError("Input index must be an integer")
291 attr_name_bytes = attr_name.encode("utf-8")
292 attr_value = _AttrValue()
293 attr_value.set_value(value)
294 ret = graph_lib.GeApiWrapper_GNode_SetInputAttr(
295 self._handle,
296 attr_name_bytes,
297 ctypes.c_uint32(input_index),
298 attr_value._av_ptr,
299 )
300 if ret != 0: # GRAPH_SUCCESS
301 raise RuntimeError(f"Failed to set Node {self.name} input attribute '{attr_name}' for index {input_index}")
303 def get_output_attr(self, attr_name: str, output_index: int) -> Any:
304 """Get output attribute.
306 Args:
307 attr_name: Attribute name.
308 output_index: Output index.
310 Returns:
311 Attribute value.
313 Raises:
314 TypeError: If arguments have wrong types.
315 RuntimeError: If attribute retrieval fails.
316 """
317 if not isinstance(attr_name, str):
318 raise TypeError("Attribute name must be a string")
319 if not isinstance(output_index, int):
320 raise TypeError("Output index must be an integer")
322 attr_value = _AttrValue()
323 attr_name_bytes = attr_name.encode("utf-8")
325 ret = graph_lib.GeApiWrapper_GNode_GetOutputAttr(
326 self._handle,
327 attr_name_bytes,
328 ctypes.c_uint32(output_index),
329 attr_value._av_ptr,
330 )
331 if ret != 0: # GRAPH_SUCCESS
332 raise RuntimeError(
333 f"Failed to get Node {self.name} output attribute '{attr_name}' for index {output_index}"
334 )
336 return attr_value.get_value()
338 def set_output_attr(self, attr_name: str, output_index: int, value: Any) -> None:
339 """Set output attribute.
341 Args:
342 attr_name: Attribute name.
343 output_index: Output index.
344 value: Attribute value.
346 Raises:
347 TypeError: If arguments have wrong types.
348 RuntimeError: If attribute setting fails.
349 """
350 if not isinstance(attr_name, str):
351 raise TypeError("Attribute name must be a string")
352 if not isinstance(output_index, int):
353 raise TypeError("Output index must be an integer")
355 attr_name_bytes = attr_name.encode("utf-8")
356 attr_value = _AttrValue()
357 attr_value.set_value(value)
358 ret = graph_lib.GeApiWrapper_GNode_SetOutputAttr(
359 self._handle,
360 attr_name_bytes,
361 ctypes.c_uint32(output_index),
362 attr_value._av_ptr,
363 )
364 if ret != 0: # GRAPH_SUCCESS
365 raise RuntimeError(
366 f"Failed to set Node {self.name} output attribute '{attr_name}' for index {output_index}"
367 )
369 def get_out_control_nodes(self) -> List["Node"]:
370 """Get output control nodes.
372 Returns:
373 List of output control Node objects.
374 """
375 node_num = ctypes.c_size_t()
376 nodes = graph_lib.GeApiWrapper_GNode_GetOutControlNodes(self._handle, ctypes.byref(node_num))
377 if not nodes:
378 return []
380 try:
381 return [Node._create_from(nodes[i]) for i in range(node_num.value)]
382 finally:
383 graph_lib.GeApiWrapper_GNode_FreeGNodeArray(nodes)
385 def get_inputs_size(self) -> int:
386 """Get input size
388 Returns:
389 Number of input size
390 """
391 return graph_lib.GeApiWrapper_GNode_GetInputsSize(self._handle)
393 def get_outputs_size(self) -> int:
394 """Get output size
396 Returns:
397 Number of output size
398 """
399 return graph_lib.GeApiWrapper_GNode_GetOutputsSize(self._handle)
401 def has_attr(self, attr_name: str) -> bool:
402 """Has attribute
404 Args:
405 attr_name: Attribute name.
407 Returns:
408 If node has attribute of this name.
409 """
410 if not isinstance(attr_name, str):
411 raise TypeError("attr_name must be a string")
413 attr_name_bytes = attr_name.encode("utf-8")
414 return graph_lib.GeApiWrapper_GNode_HasAttr(self._handle, attr_name_bytes)
416 def get_input_desc(self, index: int) -> TensorDesc:
417 """Get input tensor descriptor.
419 Args:
420 index: Input index.
422 Returns:
423 TensorDesc object.
424 """
425 if not isinstance(index, int):
426 raise TypeError("Input index must be an integer")
428 desc_handle = graph_lib.GeApiWrapper_GNode_GetInputDesc(self._handle, ctypes.c_int32(index))
429 if not desc_handle:
430 raise RuntimeError(f"Failed to get input desc for index {index}")
431 return TensorDesc._create_from(desc_handle)
433 def update_input_desc(self, index: int, tensor_desc: TensorDesc) -> None:
434 """Update input tensor descriptor.
436 Args:
437 index: Input index.
438 tensor_desc: New tensor descriptor.
439 """
440 if not isinstance(index, int):
441 raise TypeError("Input index must be an integer")
442 if not isinstance(tensor_desc, TensorDesc):
443 raise TypeError("tensor_desc must be a TensorDesc")
445 ret = graph_lib.GeApiWrapper_GNode_UpdateInputDesc(self._handle, ctypes.c_int32(index), tensor_desc._handle)
446 if ret != 0:
447 raise RuntimeError(f"Failed to update input desc for index {index}")
449 def get_output_desc(self, index: int) -> TensorDesc:
450 """Get output tensor descriptor.
452 Args:
453 index: Output index.
455 Returns:
456 TensorDesc object.
457 """
458 if not isinstance(index, int):
459 raise TypeError("Output index must be an integer")
461 desc_handle = graph_lib.GeApiWrapper_GNode_GetOutputDesc(self._handle, ctypes.c_int32(index))
462 if not desc_handle:
463 raise RuntimeError(f"Failed to get output desc for index {index}")
464 return TensorDesc._create_from(desc_handle)
466 def update_output_desc(self, index: int, tensor_desc: TensorDesc) -> None:
467 """Update output tensor descriptor.
469 Args:
470 index: Output index.
471 tensor_desc: New tensor descriptor.
472 """
473 if not isinstance(index, int):
474 raise TypeError("Output index must be an integer")
475 if not isinstance(tensor_desc, TensorDesc):
476 raise TypeError("tensor_desc must be a TensorDesc")
478 ret = graph_lib.GeApiWrapper_GNode_UpdateOutputDesc(self._handle, ctypes.c_int32(index), tensor_desc._handle)
479 if ret != 0:
480 raise RuntimeError(f"Failed to update output desc for index {index}")