src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/TextVisitor.py

branch
eric7
changeset 11150
73d80859079c
equal deleted inserted replaced
11149:fc45672fae42 11150:73d80859079c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a node visitor for bytes and str instances.
8 """
9
10 import ast
11
12 import AstUtilities
13
14
15 class TextVisitor(ast.NodeVisitor):
16 """
17 Class implementing a node visitor for bytes and str instances.
18
19 It tries to detect docstrings as string of the first expression of each
20 module, class or function.
21 """
22
23 # modeled after the string format flake8 extension
24
25 def __init__(self):
26 """
27 Constructor
28 """
29 super().__init__()
30 self.nodes = []
31 self.calls = {}
32
33 def __addNode(self, node):
34 """
35 Private method to add a node to our list of nodes.
36
37 @param node reference to the node to add
38 @type ast.AST
39 """
40 if not hasattr(node, "is_docstring"):
41 node.is_docstring = False
42 self.nodes.append(node)
43
44 def visit_Constant(self, node):
45 """
46 Public method to handle constant nodes.
47
48 @param node reference to the bytes node
49 @type ast.Constant
50 """
51 if AstUtilities.isBaseString(node):
52 self.__addNode(node)
53 else:
54 super().generic_visit(node)
55
56 def __visitDefinition(self, node):
57 """
58 Private method handling class and function definitions.
59
60 @param node reference to the node to handle
61 @type ast.FunctionDef, ast.AsyncFunctionDef or ast.ClassDef
62 """
63 # Manually traverse class or function definition
64 # * Handle decorators normally
65 # * Use special check for body content
66 # * Don't handle the rest (e.g. bases)
67 for decorator in node.decorator_list:
68 self.visit(decorator)
69 self.__visitBody(node)
70
71 def __visitBody(self, node):
72 """
73 Private method to traverse the body of the node manually.
74
75 If the first node is an expression which contains a string or bytes it
76 marks that as a docstring.
77
78 @param node reference to the node to traverse
79 @type ast.AST
80 """
81 if (
82 node.body
83 and isinstance(node.body[0], ast.Expr)
84 and AstUtilities.isBaseString(node.body[0].value)
85 ):
86 node.body[0].value.is_docstring = True
87
88 for subnode in node.body:
89 self.visit(subnode)
90
91 def visit_Module(self, node):
92 """
93 Public method to handle a module.
94
95 @param node reference to the node to handle
96 @type ast.Module
97 """
98 self.__visitBody(node)
99
100 def visit_ClassDef(self, node):
101 """
102 Public method to handle a class definition.
103
104 @param node reference to the node to handle
105 @type ast.ClassDef
106 """
107 # Skipped nodes: ('name', 'bases', 'keywords', 'starargs', 'kwargs')
108 self.__visitDefinition(node)
109
110 def visit_FunctionDef(self, node):
111 """
112 Public method to handle a function definition.
113
114 @param node reference to the node to handle
115 @type ast.FunctionDef
116 """
117 # Skipped nodes: ('name', 'args', 'returns')
118 self.__visitDefinition(node)
119
120 def visit_AsyncFunctionDef(self, node):
121 """
122 Public method to handle an asynchronous function definition.
123
124 @param node reference to the node to handle
125 @type ast.AsyncFunctionDef
126 """
127 # Skipped nodes: ('name', 'args', 'returns')
128 self.__visitDefinition(node)
129
130 def visit_Call(self, node):
131 """
132 Public method to handle a function call.
133
134 @param node reference to the node to handle
135 @type ast.Call
136 """
137 if isinstance(node.func, ast.Attribute) and node.func.attr == "format":
138 if AstUtilities.isBaseString(node.func.value):
139 self.calls[node.func.value] = (node, False)
140 elif (
141 isinstance(node.func.value, ast.Name)
142 and node.func.value.id == "str"
143 and node.args
144 and AstUtilities.isBaseString(node.args[0])
145 ):
146 self.calls[node.args[0]] = (node, True)
147 super().generic_visit(node)

eric ide

mercurial