Coverage for src/renaissance/integrations/python/ast/ast_node.py: 83%

46 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 10:34 +0000

1# Copyright (C) 2024-2026 TNO 

2# 

3# Licensed under the terms specified in the LICENSE file in the root directory 

4# of this repository, also available at: 

5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE 

6 

7"""Implementation that patches the native ast using 'traits' mechanism. 

8 

9Requires minimum amount of code to make the matcher work. 

10""" 

11 

12import ast 

13from typing import Any 

14 

15from renaissance.integrations.python.ast.kinds import PYTHON_KIND_MAP 

16from renaissance.syntax_tree.semantic_kind import SemanticKind 

17 

18 

19class ASTExtension: 

20 """AI: Static trait methods patched onto the stdlib ast.AST class to satisfy the node protocol.""" 

21 

22 @staticmethod 

23 def load_from_ast(text, file) -> ast.Module: 

24 """AI: Parse text as Python source (attributed to file) into a native ast.AST tree.""" 

25 root = ast.parse(text, file) 

26 return root 

27 

28 @staticmethod 

29 @property 

30 def ast_node(self) -> ast.AST: 

31 """AI: Return this node itself.""" 

32 return self 

33 

34 @staticmethod 

35 @property 

36 def parser_kind(self) -> str: 

37 """AI: Return this node's ast class name as its parser kind.""" 

38 return type(self).__name__ 

39 

40 @staticmethod 

41 @property 

42 def semantic_kind(self) -> SemanticKind: 

43 """AI: Return the semantic kind mapped from this node's ast class name.""" 

44 return PYTHON_KIND_MAP.get(type(self).__name__, SemanticKind.NODE) 

45 

46 @staticmethod 

47 @property 

48 def ast_properties(self) -> dict[str, Any]: 

49 """AI: Return this node's non-AST fields as a name-to-value dict.""" 

50 return {field: getattr(self, field) for field in self._fields if not isinstance(getattr(self, field), ast.AST)} 

51 

52 @staticmethod 

53 @property 

54 def ast_children(self) -> list[ast.AST]: 

55 """AI: Return this node's direct child AST nodes, including those held in list fields.""" 

56 children = [getattr(self, field) for field in self._fields if isinstance(getattr(self, field), (ast.AST))] 

57 [children.extend(getattr(self, field)) for field in self._fields if isinstance(getattr(self, field), (list))] 

58 return children 

59 

60 @staticmethod 

61 @property 

62 def ast_signature(self) -> str: 

63 """AI: Return the unparsed source text of this node.""" 

64 return ast.unparse(self) 

65 

66 @staticmethod 

67 @property 

68 def ast_name(self) -> str: 

69 """AI: Return this node's identifying name, derived from its specific ast node type.""" 

70 if isinstance(self, ast.arg): 

71 signature = self.arg 

72 elif isinstance(self, ast.Name): 

73 signature = self.id 

74 elif isinstance(self, ast.Expr) and isinstance(self.value, ast.Name): 

75 signature = self.value.id 

76 else: 

77 signature = str(self) 

78 return signature