Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py

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

eric ide

mercurial