Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py

branch
BgService
changeset 3209
c5432abceb25
parent 3145
a9de05d4a22f
child 3228
f489068e51e8
equal deleted inserted replaced
3177:5af61402d74d 3209:c5432abceb25
7 Module implementing the code style checker. 7 Module implementing the code style checker.
8 """ 8 """
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 try: 12 import sys
13 str = unicode # __IGNORE_WARNING__
14 except (NameError):
15 pass
16 13
17 import os 14 import pep8
15 from NamingStyleChecker import NamingStyleChecker
18 16
19 from PyQt4.QtCore import QProcess, QCoreApplication 17 # register the name checker
18 pep8.register_check(NamingStyleChecker, NamingStyleChecker.Codes)
20 19
21 from . import pep8 20 from DocStyleChecker import DocStyleChecker
22 from .NamingStyleChecker import NamingStyleChecker
23 from .DocStyleChecker import DocStyleChecker
24
25 import Preferences
26 import Utilities
27
28 from eric5config import getConfig
29 21
30 22
31 class CodeStyleCheckerPy2(object): 23 def initService():
32 """ 24 """
33 Class implementing the code style checker interface for Python 2. 25 Initialize the service and return the entry point.
26
27 @return the entry point for the background client (function)
34 """ 28 """
35 def __init__(self, filename, lines, repeat=False, 29 return codeStyleCheck
36 select="", ignore="", max_line_length=79, 30
37 hang_closing=False, docType="pep257"): 31
32 class CodeStyleCheckerReport(pep8.BaseReport):
33 """
34 Class implementing a special report to be used with our dialog.
35 """
36 def __init__(self, options):
38 """ 37 """
39 Constructor 38 Constructor
40 39
41 @param filename name of the file to check (string) 40 @param options options for the report (optparse.Values)
42 @param lines source of the file (list of strings) (ignored)
43 @keyparam repeat flag indicating to repeat message categories (boolean)
44 @keyparam select list of message IDs to check for
45 (comma separated string)
46 @keyparam ignore list of message IDs to ignore
47 (comma separated string)
48 @keyparam max_line_length maximum allowed line length (integer)
49 @keyparam hang_closing flag indicating to allow hanging closing
50 brackets (boolean)
51 @keyparam docType type of the documentation strings
52 (string, one of 'eric' or 'pep257')
53 """ 41 """
54 assert docType in ("eric", "pep257") 42 super(CodeStyleCheckerReport, self).__init__(options)
55 43
44 self.__repeat = options.repeat
56 self.errors = [] 45 self.errors = []
57 self.counters = {} 46
47 def error_args(self, line_number, offset, code, check, *args):
48 """
49 Public method to collect the error messages.
58 50
59 interpreter = Preferences.getDebugger("PythonInterpreter") 51 @param line_number line number of the issue (integer)
60 if interpreter == "" or not Utilities.isExecutable(interpreter): 52 @param offset position within line of the issue (integer)
53 @param code message code (string)
54 @param check reference to the checker function (function)
55 @param args arguments for the message (list)
56 @return error code (string)
57 """
58 code = super(CodeStyleCheckerReport, self).error_args(
59 line_number, offset, code, check, *args)
60 if code and (self.counters[code] == 1 or self.__repeat):
61 if code in NamingStyleChecker.Codes:
62 text = NamingStyleChecker.getMessage(code, *args)
63 else:
64 text = pep8.getMessage(code, *args)
61 self.errors.append( 65 self.errors.append(
62 (filename, 1, 1, QCoreApplication.translate( 66 (self.filename, line_number, offset, text)
63 "CodeStyleCheckerPy2", 67 )
64 "Python2 interpreter not configured."))) 68 return code
65 return 69
70
71 def extractLineFlags(line, startComment="#", endComment=""):
72 """
73 Function to extract flags starting and ending with '__' from a line
74 comment.
75
76 @param line line to extract flags from (string)
77 @keyparam startComment string identifying the start of the comment (string)
78 @keyparam endComment string identifying the end of a comment (string)
79 @return list containing the extracted flags (list of strings)
80 """
81 flags = []
82
83 pos = line.rfind(startComment)
84 if pos >= 0:
85 comment = line[pos + len(startComment):].strip()
86 if endComment:
87 comment = comment.replace("endComment", "")
88 flags = [f.strip() for f in comment.split()
89 if (f.startswith("__") and f.endswith("__"))]
90 return flags
91
92
93 def codeStyleCheck(filename, source, args):
94 """
95 Do the code style check and/ or fix found errors.
96
97 @param filename source filename (string)
98 @param source string containing the code to check (string)
99 @param args arguments used by the codeStyleCheck function (list of
100 excludeMessages (str), includeMessages (str), repeatMessages
101 (bool), fixCodes (str), noFixCodes (str), fixIssues (bool),
102 maxLineLength (int), hangClosing (bool), docType (str), errors
103 (list of str), eol (str), encoding (str))
104 @return tuple of stats (dict) and results (tuple for each found violation
105 of style (tuple of lineno (int), position (int), text (str), fixed
106 (bool), autofixing (bool), fixedMsg (str)))
107 """
108 excludeMessages, includeMessages, \
109 repeatMessages, fixCodes, noFixCodes, fixIssues, maxLineLength, \
110 hangClosing, docType, errors, eol, encoding = args
111
112 stats = {}
113 # avoid 'Encoding declaration in unicode string' exception on Python2
114 if sys.version_info[0] == 2:
115 if encoding == 'utf-8-bom':
116 enc = 'utf-8'
117 else:
118 enc = encoding
119 source = [line.encode(enc) for line in source]
120
121 if fixIssues:
122 from CodeStyleFixer import CodeStyleFixer
123 fixer = CodeStyleFixer(
124 filename, source, fixCodes, noFixCodes,
125 maxLineLength, True, eol) # always fix in place
126 else:
127 fixer = None
128
129 if not errors:
130 if includeMessages:
131 select = [s.strip() for s in
132 includeMessages.split(',') if s.strip()]
133 else:
134 select = []
135 if excludeMessages:
136 ignore = [i.strip() for i in
137 excludeMessages.split(',') if i.strip()]
138 else:
139 ignore = []
66 140
67 checker = os.path.join(getConfig('ericDir'), 141 # check coding style
68 "UtilitiesPython2", "CodeStyleChecker.py") 142 styleGuide = pep8.StyleGuide(
143 reporter=CodeStyleCheckerReport,
144 repeat=repeatMessages,
145 select=select,
146 ignore=ignore,
147 max_line_length=maxLineLength,
148 hang_closing=hangClosing,
149 )
150 report = styleGuide.check_files([filename])
151 stats.update(report.counters)
152
153 # check documentation style
154 docStyleChecker = DocStyleChecker(
155 source, filename, select, ignore, [], repeatMessages,
156 maxLineLength=maxLineLength, docType=docType)
157 docStyleChecker.run()
158 stats.update(docStyleChecker.counters)
69 159
70 args = [checker] 160 errors = report.errors + docStyleChecker.errors
71 if repeat: 161
72 args.append("-r") 162 deferredFixes = {}
73 if select: 163 results = []
74 args.append("-s") 164 for error in errors:
75 args.append(select) 165 fname, lineno, position, text = error
76 if ignore: 166 if lineno > len(source):
77 args.append("-i") 167 lineno = len(source)
78 args.append(ignore) 168 if "__IGNORE_WARNING__" not in \
79 args.append("-m") 169 extractLineFlags(source[lineno - 1].strip()):
80 args.append(str(max_line_length)) 170 if fixer:
81 if hang_closing: 171 res, msg, id_ = fixer.fixIssue(lineno, position, text)
82 args.append("-h") 172 if res == -1:
83 args.append("-d") 173 itm = [lineno, position, text]
84 args.append(docType) 174 deferredFixes[id_] = itm
85 args.append("-f")
86 args.append(filename)
87
88 proc = QProcess()
89 proc.setProcessChannelMode(QProcess.MergedChannels)
90 proc.start(interpreter, args)
91 finished = proc.waitForFinished(15000)
92 if finished:
93 output = \
94 str(proc.readAllStandardOutput(),
95 Preferences.getSystem("IOEncoding"),
96 'replace').splitlines()
97 if output[0] == "ERROR":
98 self.errors.append((filename, 1, 1, output[2]))
99 return
100
101 if output[0] == "NO_PEP8":
102 return
103
104 index = 0
105 while index < len(output):
106 if output[index] == "PEP8_STATISTICS":
107 index += 1
108 break
109
110 fname = output[index + 1]
111 lineno = int(output[index + 2])
112 position = int(output[index + 3])
113 code = output[index + 4]
114 arglen = int(output[index + 5])
115 args = []
116 argindex = 0
117 while argindex < arglen:
118 args.append(output[index + 6 + argindex])
119 argindex += 1
120 index += 6 + arglen
121
122 if code in NamingStyleChecker.Codes:
123 text = NamingStyleChecker.getMessage(code, *args)
124 elif code in DocStyleChecker.Codes:
125 text = DocStyleChecker.getMessage(code, *args)
126 else: 175 else:
127 text = pep8.getMessage(code, *args) 176 itm = [lineno, position, text, res == 1, True, msg]
128 self.errors.append((fname, lineno, position, text)) 177 else:
129 while index < len(output): 178 itm = [lineno, position, text, False, False, '']
130 code, countStr = output[index].split(None, 1) 179 results.append(itm)
131 self.counters[code] = int(countStr) 180
132 index += 1 181 if fixer:
133 else: 182 deferredResults = fixer.finalize()
134 self.errors.append( 183 for id_ in deferredResults:
135 (filename, 1, 1, QCoreApplication.translate( 184 fixed, msg = deferredResults[id_]
136 "CodeStyleCheckerPy2", 185 itm = deferredFixes[id_]
137 "Python2 interpreter did not finish within 15s."))) 186 itm.extend([fixed == 1, fixed == 1, msg])
187 fixer.saveFile(encoding)
188
189 return stats, results

eric ide

mercurial