eric6/Plugins/CheckerPlugins/CodeStyleChecker/PathLib/PathlibChecker.py

branch
maintenance
changeset 8273
698ae46f40a4
parent 8243
cc717c2ae956
equal deleted inserted replaced
8190:fb0ef164f536 8273:698ae46f40a4
7 Module implementing the checker for functions that can be replaced by use of 7 Module implementing the checker for functions that can be replaced by use of
8 the pathlib module. 8 the pathlib module.
9 """ 9 """
10 10
11 import ast 11 import ast
12 import sys 12 import copy
13 13 import contextlib
14 14
15 class PathlibChecker(object): 15
16 class PathlibChecker:
16 """ 17 """
17 Class implementing a checker for functions that can be replaced by use of 18 Class implementing a checker for functions that can be replaced by use of
18 the pathlib module. 19 the pathlib module.
19 """ 20 """
20 Codes = [ 21 Codes = [
63 "open": "P301", 64 "open": "P301",
64 65
65 "py.path.local": "P401", 66 "py.path.local": "P401",
66 } 67 }
67 68
68 def __init__(self, source, filename, selected, ignored, expected, repeat): 69 def __init__(self, source, filename, tree, selected, ignored, expected,
70 repeat):
69 """ 71 """
70 Constructor 72 Constructor
71 73
72 @param source source code to be checked 74 @param source source code to be checked
73 @type list of str 75 @type list of str
74 @param filename name of the source file 76 @param filename name of the source file
75 @type str 77 @type str
78 @param tree AST tree of the source code
79 @type ast.Module
76 @param selected list of selected codes 80 @param selected list of selected codes
77 @type list of str 81 @type list of str
78 @param ignored list of codes to be ignored 82 @param ignored list of codes to be ignored
79 @type list of str 83 @type list of str
80 @param expected list of expected codes 84 @param expected list of expected codes
86 self.__ignore = ('',) if selected else tuple(ignored) 90 self.__ignore = ('',) if selected else tuple(ignored)
87 self.__expected = expected[:] 91 self.__expected = expected[:]
88 self.__repeat = repeat 92 self.__repeat = repeat
89 self.__filename = filename 93 self.__filename = filename
90 self.__source = source[:] 94 self.__source = source[:]
95 self.__tree = copy.deepcopy(tree)
91 96
92 # statistics counters 97 # statistics counters
93 self.counters = {} 98 self.counters = {}
94 99
95 # collection of detected errors 100 # collection of detected errors
145 "code": code, 150 "code": code,
146 "args": args, 151 "args": args,
147 } 152 }
148 ) 153 )
149 154
150 def __reportInvalidSyntax(self):
151 """
152 Private method to report a syntax error.
153 """
154 exc_type, exc = sys.exc_info()[:2]
155 if len(exc.args) > 1:
156 offset = exc.args[1]
157 if len(offset) > 2:
158 offset = offset[1:3]
159 else:
160 offset = (1, 0)
161 self.__error(offset[0] - 1, offset[1] or 0,
162 'M901', exc_type.__name__, exc.args[0])
163
164 def __generateTree(self):
165 """
166 Private method to generate an AST for our source.
167
168 @return generated AST
169 @rtype ast.AST
170 """
171 return ast.parse("".join(self.__source), self.__filename)
172
173 def run(self): 155 def run(self):
174 """ 156 """
175 Public method to check the given source against functions 157 Public method to check the given source against functions
176 to be replaced by 'pathlib' equivalents. 158 to be replaced by 'pathlib' equivalents.
177 """ 159 """
179 # don't do anything, if essential data is missing 161 # don't do anything, if essential data is missing
180 return 162 return
181 163
182 if not self.__checkCodes: 164 if not self.__checkCodes:
183 # don't do anything, if no codes were selected 165 # don't do anything, if no codes were selected
184 return
185
186 try:
187 self.__tree = self.__generateTree()
188 except (SyntaxError, TypeError):
189 self.__reportInvalidSyntax()
190 return 166 return
191 167
192 visitor = PathlibVisitor(self.__checkForReplacement) 168 visitor = PathlibVisitor(self.__checkForReplacement)
193 visitor.visit(self.__tree) 169 visitor.visit(self.__tree)
194 170
200 @param node reference to the AST node to check 176 @param node reference to the AST node to check
201 @type ast.AST 177 @type ast.AST
202 @param name resolved name of the node 178 @param name resolved name of the node
203 @type str 179 @type str
204 """ 180 """
205 try: 181 with contextlib.suppress(KeyError):
206 errorCode = self.Function2Code[name] 182 errorCode = self.Function2Code[name]
207 self.__error(node.lineno - 1, node.col_offset, errorCode) 183 self.__error(node.lineno - 1, node.col_offset, errorCode)
208 except KeyError:
209 # name is not in our list of replacements
210 pass
211 184
212 185
213 class PathlibVisitor(ast.NodeVisitor): 186 class PathlibVisitor(ast.NodeVisitor):
214 """ 187 """
215 Class to traverse the AST node tree and check for potential issues. 188 Class to traverse the AST node tree and check for potential issues.
220 193
221 @param checkCallback callback function taking a reference to the 194 @param checkCallback callback function taking a reference to the
222 AST node and the resolved name 195 AST node and the resolved name
223 @type func 196 @type func
224 """ 197 """
225 super(PathlibVisitor, self).__init__() 198 super().__init__()
226 199
227 self.__checkCallback = checkCallback 200 self.__checkCallback = checkCallback
228 self.__importAlias = {} 201 self.__importAlias = {}
229 202
230 def visit_ImportFrom(self, node): 203 def visit_ImportFrom(self, node):
283 Public method to resolve the name. 256 Public method to resolve the name.
284 257
285 @return resolved name 258 @return resolved name
286 @rtype str 259 @rtype str
287 """ 260 """
288 try: 261 with contextlib.suppress(KeyError, IndexError):
289 attr = self.__importAlias[self.__names[-1]] 262 attr = self.__importAlias[self.__names[-1]]
290 self.__names[-1] = attr 263 self.__names[-1] = attr
291 except (KeyError, IndexError):
292 # do nothing if there is no such name or the names list is empty 264 # do nothing if there is no such name or the names list is empty
293 pass
294 265
295 return ".".join(reversed(self.__names)) 266 return ".".join(reversed(self.__names))
296 267
297 def visit_Name(self, node): 268 def visit_Name(self, node):
298 """ 269 """

eric ide

mercurial