src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/PathLib/PathlibChecker.py

branch
eric7
changeset 11150
73d80859079c
parent 11147
dee6e106b4d3
equal deleted inserted replaced
11149:fc45672fae42 11150:73d80859079c
10 10
11 ##################################################################################### 11 #####################################################################################
12 ## adapted from: flake8-use-pathlib v0.3.0 ## 12 ## adapted from: flake8-use-pathlib v0.3.0 ##
13 ## ## 13 ## ##
14 ## Original: Copyright (c) 2021 Rodolphe Pelloux-Prayer ## 14 ## Original: Copyright (c) 2021 Rodolphe Pelloux-Prayer ##
15 ## ##
16 ## License: ##
17 ## Permission is hereby granted, free of charge, to any person obtaining a copy ##
18 ## of this software and associated documentation files (the "Software"), to deal ##
19 ## in the Software without restriction, including without limitation the rights ##
20 ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ##
21 ## copies of the Software, and to permit persons to whom the Software is ##
22 ## furnished to do so, subject to the following conditions: ##
23 ## ##
24 ## The above copyright notice and this permission notice shall be included in all ##
25 ## copies or substantial portions of the Software. ##
26 ## ##
27 ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ##
28 ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ##
29 ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ##
30 ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ##
31 ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ##
32 ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ##
33 ##################################################################################### 15 #####################################################################################
34 16
35 import ast 17 import ast
36 import contextlib 18 import contextlib
37 import copy 19
38 20 from CodeStyleTopicChecker import CodeStyleTopicChecker
39 21
40 class PathlibChecker: 22
23 class PathlibChecker(CodeStyleTopicChecker):
41 """ 24 """
42 Class implementing a checker for functions that can be replaced by use of 25 Class implementing a checker for functions that can be replaced by use of
43 the pathlib module. 26 the pathlib module.
44 """ 27 """
45 28
76 ## Replacements for some Python standard library functions 59 ## Replacements for some Python standard library functions
77 "P-301", 60 "P-301",
78 ## Replacements for py.path.local 61 ## Replacements for py.path.local
79 "P-401", 62 "P-401",
80 ] 63 ]
64 Category = "P"
81 65
82 # map functions to be replaced to error codes 66 # map functions to be replaced to error codes
83 Function2Code = { 67 Function2Code = {
84 "os.chmod": "P-101", 68 "os.chmod": "P-101",
85 "os.mkdir": "P-102", 69 "os.mkdir": "P-102",
129 @param expected list of expected codes 113 @param expected list of expected codes
130 @type list of str 114 @type list of str
131 @param repeat flag indicating to report each occurrence of a code 115 @param repeat flag indicating to report each occurrence of a code
132 @type bool 116 @type bool
133 """ 117 """
134 self.__select = tuple(selected) 118 super().__init__(
135 self.__ignore = tuple(ignored) 119 PathlibChecker.Category,
136 self.__expected = expected[:] 120 source,
137 self.__repeat = repeat 121 filename,
138 self.__filename = filename 122 tree,
139 self.__source = source[:] 123 selected,
140 self.__tree = copy.deepcopy(tree) 124 ignored,
141 125 expected,
142 # statistics counters 126 repeat,
143 self.counters = {} 127 [],
144
145 # collection of detected errors
146 self.errors = []
147
148 self.__checkCodes = (code for code in self.Codes if not self.__ignoreCode(code))
149
150 def __ignoreCode(self, code):
151 """
152 Private method to check if the message code should be ignored.
153
154 @param code message code to check for
155 @type str
156 @return flag indicating to ignore the given code
157 @rtype bool
158 """
159 return code in self.__ignore or (
160 code.startswith(self.__ignore) and not code.startswith(self.__select)
161 ) 128 )
162 129
163 def __error(self, lineNumber, offset, code, *args): 130 checkersWithCodes = [
164 """ 131 (
165 Private method to record an issue. 132 self.__checkPathlibReplacement,
166 133 (
167 @param lineNumber line number of the issue 134 "P-101",
168 @type int 135 "P-102",
169 @param offset position within line of the issue 136 "P-103",
170 @type int 137 "P-104",
171 @param code message code 138 "P-105",
172 @type str 139 "P-106",
173 @param args arguments for the message 140 "P-107",
174 @type list 141 "P-108",
175 """ 142 "P-109",
176 if self.__ignoreCode(code): 143 "P-110",
177 return 144 "P-111",
178 145 "P-112",
179 if code in self.counters: 146 "P-113",
180 self.counters[code] += 1 147 "P-114",
181 else: 148 "P-201",
182 self.counters[code] = 1 149 "P-202",
183 150 "P-203",
184 # Don't care about expected codes 151 "P-204",
185 if code in self.__expected: 152 "P-205",
186 return 153 "P-206",
187 154 "P-207",
188 if code and (self.counters[code] == 1 or self.__repeat): 155 "P-208",
189 # record the issue with one based line number 156 "P-209",
190 self.errors.append( 157 "P-210",
191 { 158 "P-211",
192 "file": self.__filename, 159 "P-212",
193 "line": lineNumber + 1, 160 "P-213",
194 "offset": offset, 161 "P-301",
195 "code": code, 162 "P-401",
196 "args": args, 163 ),
197 } 164 ),
198 ) 165 ]
199 166 self._initializeCheckers(checkersWithCodes)
200 def run(self): 167
201 """ 168 def __checkPathlibReplacement(self):
202 Public method to check the given source against functions 169 """
203 to be replaced by 'pathlib' equivalents. 170 Private method to check for pathlib replacements.
204 """ 171 """
205 if not self.__filename:
206 # don't do anything, if essential data is missing
207 return
208
209 if not self.__checkCodes:
210 # don't do anything, if no codes were selected
211 return
212
213 visitor = PathlibVisitor(self.__checkForReplacement) 172 visitor = PathlibVisitor(self.__checkForReplacement)
214 visitor.visit(self.__tree) 173 visitor.visit(self.tree)
215 174
216 def __checkForReplacement(self, node, name): 175 def __checkForReplacement(self, node, name):
217 """ 176 """
218 Private method to check the given node for the need for a 177 Private method to check the given node for the need for a
219 replacement. 178 replacement.
223 @param name resolved name of the node 182 @param name resolved name of the node
224 @type str 183 @type str
225 """ 184 """
226 with contextlib.suppress(KeyError): 185 with contextlib.suppress(KeyError):
227 errorCode = self.Function2Code[name] 186 errorCode = self.Function2Code[name]
228 self.__error(node.lineno - 1, node.col_offset, errorCode) 187 self.addErrorFromNode(node, errorCode)
229 188
230 189
231 class PathlibVisitor(ast.NodeVisitor): 190 class PathlibVisitor(ast.NodeVisitor):
232 """ 191 """
233 Class to traverse the AST node tree and check for potential issues. 192 Class to traverse the AST node tree and check for potential issues.
280 nameResolver = NameResolver(self.__importAlias) 239 nameResolver = NameResolver(self.__importAlias)
281 nameResolver.visit(node.func) 240 nameResolver.visit(node.func)
282 241
283 self.__checkCallback(node, nameResolver.name()) 242 self.__checkCallback(node, nameResolver.name())
284 243
244 self.generic_visit(node)
245
285 246
286 class NameResolver(ast.NodeVisitor): 247 class NameResolver(ast.NodeVisitor):
287 """ 248 """
288 Class to resolve a Name or Attribute node. 249 Class to resolve a Name or Attribute node.
289 """ 250 """

eric ide

mercurial