• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pyta-uoft / pyta / 8149440349

05 Mar 2024 01:08AM UTC coverage: 95.775% (-0.09%) from 95.867%
8149440349

push

github

web-flow
Created new PEP8 custom renders for E222 and E262 (#1016)

21 of 21 new or added lines in 1 file covered. (100.0%)

15 existing lines in 1 file now uncovered.

3264 of 3408 relevant lines covered (95.77%)

7.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.11
/python_ta/reporters/node_printers.py
1
"""Specify how errors should be rendered."""
5✔
2
import re
5✔
3
from enum import Enum
5✔
4

5
from astroid import nodes
5✔
6

7
NEW_BLANK_LINE_MESSAGE = "# INSERT NEW BLANK LINE HERE"
5✔
8

9

10
def render_message(msg, node, source_lines):
5✔
11
    """Render a message based on type."""
12
    renderer = CUSTOM_MESSAGES.get(msg.symbol, render_generic)
10✔
13
    yield from renderer(msg, node, source_lines)
10✔
14

15

16
def render_generic(msg, node=None, source_lines=None):
10✔
17
    """Default rendering for a message."""
18
    if node is not None:
5✔
19
        start_line, start_col = node.fromlineno, node.col_offset
5✔
20

21
        if isinstance(node, (nodes.FunctionDef, nodes.ClassDef)):
5✔
22
            end_line, end_col = start_line, None
5✔
23
        else:
24
            end_line, end_col = node.end_lineno, node.end_col_offset
5✔
25

26
        # Display up to 2 lines before node for context:
27
        yield from render_context(start_line - 2, start_line, source_lines)
5✔
28

29
        if start_line == end_line:
5✔
30
            yield (
5✔
31
                start_line,
32
                slice(start_col, end_col),
33
                LineType.ERROR,
34
                source_lines[start_line - 1],
35
            )
36
        else:
37
            yield (start_line, slice(start_col, None), LineType.ERROR, source_lines[start_line - 1])
5✔
38
            yield from (
5✔
39
                (line, slice(None, None), LineType.ERROR, source_lines[line - 1])
40
                for line in range(start_line + 1, end_line)
41
            )
42
            yield (end_line, slice(None, end_col), LineType.ERROR, source_lines[end_line - 1])
5✔
43

44
        # Display up to 2 lines after node for context:
45
        yield from render_context(end_line + 1, end_line + 3, source_lines)
5✔
46

47
    else:
48
        line = msg.line
5✔
49
        yield from render_context(line - 2, line, source_lines)
5✔
50
        yield (line, slice(None, None), LineType.ERROR, source_lines[line - 1])
5✔
51
        yield from render_context(line + 1, line + 3, source_lines)
5✔
52

53

54
def render_missing_docstring(_msg, node, source_lines=None):
5✔
55
    """Render a missing docstring message."""
56
    if isinstance(node, nodes.Module):
10✔
57
        yield (None, slice(None, None), LineType.DOCSTRING, '"""YOUR DOCSTRING HERE"""')
10✔
58
        yield from render_context(1, 3, source_lines)
10✔
59
    elif isinstance(node, nodes.ClassDef) or isinstance(node, nodes.FunctionDef):
10✔
60
        start = node.fromlineno
10✔
61
        end = node.body[0].fromlineno
10✔
62
        yield from render_context(start, end, source_lines)
10✔
63
        # Calculate indentation
64
        body = source_lines[end - 1]
10✔
65
        indentation = len(body) - len(body.lstrip())
10✔
66
        yield (
10✔
67
            None,
5✔
68
            slice(None, None),
5✔
69
            LineType.DOCSTRING,
5✔
70
            body[:indentation] + '"""YOUR DOCSTRING HERE"""',
5✔
71
        )
72
        yield from render_context(end, end + 2, source_lines)
10✔
73

74

75
def render_trailing_newlines(msg, _node, source_lines=None):
10✔
76
    """Render a trailing newlines message."""
77
    start_line = msg.line - 1
5✔
78
    yield from render_context(start_line - 2, start_line, source_lines)
5✔
79
    yield from (
5✔
80
        (line, slice(None, None), LineType.OTHER, source_lines[line - 1])
81
        for line in range(start_line, len(source_lines) + 1)
82
    )
83

84

85
def render_trailing_whitespace(msg, _node, source_lines=None):
5✔
86
    """Render a trailing whitespace message."""
87
    line = msg.line
10✔
88
    start_index, end_index = len(source_lines[line - 1].rstrip()), len(source_lines[line - 1])
10✔
89
    yield from render_context(line - 1, line, source_lines)
10✔
90
    yield (line, slice(start_index, end_index), LineType.ERROR, source_lines[line - 1])
10✔
91
    yield from render_context(line + 1, line + 2, source_lines)
10✔
92

93

94
def render_context(start, stop, source_lines):
10✔
95
    """Helper for rendering context lines."""
96
    start, stop = max(start, 1), min(stop, len(source_lines))
5✔
97
    yield from (
5✔
98
        (line, slice(None, None), LineType.CONTEXT, source_lines[line - 1])
99
        for line in range(start, stop)
100
    )
101

102

103
def render_missing_return_type(_msg, node, source_lines=None):
5✔
104
    """Render a type annotation return message."""
105
    start_line, start_col = node.fromlineno, node.parent.col_offset
10✔
106
    end_line, end_col = node.end_lineno, node.end_col_offset
10✔
107

108
    # Display up to 2 lines before node for context:
109
    yield from render_context(start_line - 2, start_line, source_lines)
10✔
110
    yield from (
10✔
111
        (line, slice(None, end_col + 1), LineType.ERROR, source_lines[line - 1])
5✔
112
        for line in range(start_line, end_line + 1)
5✔
113
    )
114
    # Display up to 2 lines after node for context:
115
    yield from render_context(end_line + 1, end_line + 3, source_lines)
10✔
116

117

118
def render_too_many_arguments(msg, node, source_lines=None):
10✔
119
    """Render a too many arguments message."""
120
    # node is a FunctionDef node so replace it with its Arguments child
121
    yield from render_generic(msg, node.args, source_lines)
5✔
122

123

124
def render_missing_space_in_doctest(msg, _node, source_lines=None):
5✔
125
    """Render a missing space in doctest message"""
126
    line = msg.line
10✔
127

128
    # Display 2 lines before and after the erroneous line
129
    yield from render_context(line - 2, line, source_lines)
10✔
130
    yield (line, slice(None, None), LineType.ERROR, source_lines[line - 1])
10✔
131
    yield from render_context(line + 1, line + 3, source_lines)
10✔
132

133

134
def render_pep8_errors(msg, _node, source_lines=None):
10✔
135
    """Render a PEP8 error message."""
136
    if "E101" in msg.msg or "E123" in msg.msg:
5✔
137
        yield from render_pep8_errors_e101_and_e123(msg, _node, source_lines)
5✔
138
    elif "E115" in msg.msg:
5✔
139
        yield from render_pep8_errors_e115(msg, _node, source_lines)
140
    elif "E116" in msg.msg:
5✔
141
        yield from render_pep8_errors_e116(msg, _node, source_lines)
5✔
142
    elif "E122" in msg.msg or "E127" in msg.msg or "E131" in msg.msg:
5✔
143
        yield from render_pep8_errors_e122_and_e127_and_e131(msg, _node, source_lines)
144
    elif "E124" in msg.msg:
5✔
145
        yield from render_pep8_errors_e124(msg, _node, source_lines)
5✔
146
    elif "E125" in msg.msg or "E129" in msg.msg:
5✔
147
        yield from render_pep8_errors_e125_and_e129(msg, _node, source_lines)
148
    elif "E128" in msg.msg:
5✔
149
        yield from render_pep8_errors_e128(msg, _node, source_lines)
5✔
150
    elif "E201" in msg.msg or "E202" in msg.msg or "E203" in msg.msg:
5✔
151
        yield from render_pep8_errors_e201_e202_e203_e211(msg, _node, source_lines)
5✔
152
    elif "E221" in msg.msg:
5✔
153
        yield from render_pep8_errors_e221(msg, _node, source_lines)
5✔
154
    elif "E222" in msg.msg:
5✔
155
        yield from render_pep8_errors_e222(msg, _node, source_lines)
5✔
156
    elif "E223" in msg.msg:
5✔
157
        yield from render_pep8_errors_e223(msg, _node, source_lines)
158
    elif "E224" in msg.msg or "E273" in msg.msg:
5✔
159
        yield from render_pep8_errors_e224_and_e273(msg, _node, source_lines)
160
    elif "E226" in msg.msg:
5✔
161
        yield from render_pep8_errors_e226(msg, _node, source_lines)
5✔
162
    elif "E227" in msg.msg:
5✔
163
        yield from render_pep8_errors_e227(msg, _node, source_lines)
164
    elif "E228" in msg.msg:
5✔
165
        yield from render_pep8_errors_e228(msg, _node, source_lines)
166
    elif "E251" in msg.msg:
5✔
167
        yield from render_pep8_errors_e251(msg, _node, source_lines)
5✔
168
    elif "E261" in msg.msg:
5✔
169
        yield from render_pep8_errors_e261(msg, _node, source_lines)
5✔
170
    elif "E262" in msg.msg:
5✔
171
        yield from render_pep8_errors_e262(msg, _node, source_lines)
5✔
172
    elif "E265" in msg.msg:
5✔
173
        yield from render_pep8_errors_e265(msg, _node, source_lines)
174
    elif "E266" in msg.msg:
5✔
175
        yield from render_pep8_errors_e266(msg, _node, source_lines)
176
    elif "E272" in msg.msg:
5✔
177
        yield from render_pep8_errors_e272(msg, _node, source_lines)
5✔
178
    elif "E275" in msg.msg:
5✔
179
        yield from render_pep8_errors_e275(msg, _node, source_lines)
180
    elif "E301" in msg.msg:
5✔
181
        yield from render_pep8_errors_e301(msg, _node, source_lines)
182
    elif "E302" in msg.msg:
5✔
183
        yield from render_pep8_errors_e302(msg, _node, source_lines)
5✔
184
    elif "E303" in msg.msg:
5✔
185
        yield from render_pep8_errors_e303(msg, _node, source_lines)
186
    elif "E304" in msg.msg:
5✔
187
        yield from render_pep8_errors_e304(msg, _node, source_lines)
188
    elif "E305" in msg.msg:
5✔
189
        yield from render_pep8_errors_e305(msg, _node, source_lines)
5✔
190
    elif "E306" in msg.msg:
5✔
191
        yield from render_pep8_errors_e306(msg, _node, source_lines)
5✔
192
    else:
193
        yield from render_generic(msg, _node, source_lines)
5✔
194

195

196
def render_blank_line(line):
5✔
197
    """Render a blank line for a PEP8 error message."""
198
    yield (line + 1, slice(None, None), LineType.ERROR, " " * 28)
10✔
199

200

201
def render_pep8_errors_e101_and_e123(msg, _node, source_lines=None):
10✔
202
    """Render a PEP8 indentation contains mixed spaces and tabs message
203
    AND a PEP8 closing bracket does not match indentation of opening bracket's line message."""
204
    line = msg.line
10✔
205
    curr_idx = len(source_lines[line - 1]) - len(source_lines[line - 1].lstrip())
10✔
206
    yield from render_context(line - 2, line, source_lines)
10✔
207
    yield (line, slice(0, curr_idx), LineType.ERROR, source_lines[line - 1])
10✔
208
    yield from render_context(line + 1, line + 3, source_lines)
10✔
209

210

211
def render_pep8_errors_e115(msg, _node, source_lines=None):
10✔
212
    """Render a PEP8 expected an indented block (comment) message."""
213
    line = msg.line
214

215
    yield from render_context(line - 2, line, source_lines)
216
    yield (
217
        line,
218
        slice(0, len(source_lines[line - 1])),
219
        LineType.ERROR,
220
        source_lines[line - 1] + "  # INDENT THIS LINE",
221
    )
222
    yield from render_context(line + 1, line + 3, source_lines)
223

224

225
def render_pep8_errors_e116(msg, _node, source_lines=None):
5✔
226
    """Render a PEP8 unexpected indentation (comment) message"""
227
    line = msg.line
10✔
228
    curr_idx = len(source_lines[line - 1]) - len(source_lines[line - 1].lstrip())
10✔
229
    yield from render_context(line - 2, line, source_lines)
10✔
230
    yield (
10✔
231
        line,
5✔
232
        slice(0, curr_idx),
5✔
233
        LineType.ERROR,
5✔
234
        source_lines[line - 1],
5✔
235
    )
236
    yield from render_context(line + 1, line + 3, source_lines)
10✔
237

238

239
def render_pep8_errors_e122_and_e127_and_e131(msg, _node, source_lines=None):
10✔
240
    """
241
    Render a PEP8 continuation line missing indentation or outdented message, a line over-indented for visual indent
242
    message, and a continuation line unaligned for hanging indent message.
243
    """
244
    line = msg.line
×
245
    curr_line_start_index = len(source_lines[line - 1]) - len(source_lines[line - 1].lstrip())
×
246
    end_index = curr_line_start_index if curr_line_start_index > 0 else len(source_lines[line - 1])
×
247
    yield from render_context(line - 2, line, source_lines)
×
248
    yield (
249
        line,
250
        slice(0, end_index),
251
        LineType.ERROR,
252
        source_lines[line - 1],
253
    )
254
    yield from render_context(line + 1, line + 3, source_lines)
×
255

256

257
def render_pep8_errors_e124(msg, _node, source_lines=None):
10✔
258
    """Render a PEP8 closing bracket does not match visual indentation message."""
259
    line = msg.line
5✔
260
    res = re.search(r"column (\d+)", msg.msg)
5✔
261
    col = int(res.group().split()[-1])
5✔
262
    yield from render_context(line - 2, line, source_lines)
5✔
263
    yield (line, slice(col, col + 1), LineType.ERROR, source_lines[line - 1])
5✔
264
    yield from render_context(line + 1, line + 3, source_lines)
5✔
265

266

267
def render_pep8_errors_e125_and_e129(msg, _node, source_lines=None):
5✔
268
    """Render a PEP8 continuation line with same indent as next logical line message
269
    AND a PEP8 visually indented line with same indent as next logical line messsage"""
270
    line = msg.line
×
271
    curr_idx = len(source_lines[line - 1]) - len(source_lines[line - 1].lstrip())
×
272

273
    yield from render_context(line - 2, line, source_lines)
×
274
    yield (
275
        line,
276
        slice(curr_idx, len(source_lines[line - 1])),
277
        LineType.ERROR,
278
        source_lines[line - 1] + " " * 2 + "# INDENT THIS LINE",
279
    )
280
    yield from render_context(line + 1, line + 3, source_lines)
×
281

282

283
def render_pep8_errors_e128(msg, _node, source_lines):
10✔
284
    """Render a PEP8 continuation line under-indented for visual indent message."""
285
    line = msg.line
5✔
286
    res = re.search(r"column (\d+)", msg.msg)
5✔
287
    col = int(res.group().split()[-1])
5✔
288

289
    yield from render_context(line - 2, line, source_lines)
5✔
290
    yield (line, slice(0, col if col != 0 else None), LineType.ERROR, source_lines[line - 1])
5✔
291
    yield from render_context(line + 1, line + 3, source_lines)
5✔
292

293

294
def render_pep8_errors_e201_e202_e203_e211(msg, _node, source_lines=None):
5✔
295
    """Render a PEP8 whitespace after '(' message,
296
    a PEP8 whitespace before ')' message,
297
    a PEP8 whitespace before ‘,’, ‘;’, or ‘:’ message,
298
    AND a PEP8 whitespace before '(' message.."""
299
    line = msg.line
10✔
300
    res = re.search(r"column (\d+)", msg.msg)
10✔
301
    col = int(res.group().split()[-1])
10✔
302
    curr_idx = col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip())
10✔
303

304
    yield from render_context(line - 2, line, source_lines)
10✔
305
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
10✔
306
    yield from render_context(line + 1, line + 3, source_lines)
10✔
307

308

309
def render_pep8_errors_e221(msg, _node, source_lines=None):
10✔
310
    """Render a PEP8 multiple spaces before operator message."""
311
    line = msg.line
5✔
312
    res = re.search(r"column (\d+)", msg.msg)
5✔
313
    col = int(res.group().split()[-1])
5✔
314
    curr_idx = col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip())
5✔
315

316
    yield from render_context(line - 2, line, source_lines)
5✔
317
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
5✔
318
    yield from render_context(line + 1, line + 3, source_lines)
5✔
319

320

321
def render_pep8_errors_e222(msg, _node, source_lines=None):
5✔
322
    """Render a PEP8 multiple spaces after operator message"""
323
    line = msg.line
10✔
324
    res = re.search(r"column (\d+)", msg.msg)
10✔
325
    col = int(res.group().split()[-1])
10✔
326

327
    curr_idx = col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip())
10✔
328
    yield from render_context(line - 2, line, source_lines)
10✔
329
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
10✔
330
    yield from render_context(line + 1, line + 3, source_lines)
10✔
331

332

333
def render_pep8_errors_e223(msg, _node, source_lines=None):
10✔
334
    """Render a PEP8 tab before operator message."""
335
    line = msg.line
336
    res = re.search(r"column (\d+)", msg.msg)
337
    col = int(res.group().split()[-1])
338
    curr_idx = (
339
        col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip("\t"))
340
    )
341

342
    yield from render_context(line - 2, line, source_lines)
343
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
344
    yield from render_context(line + 1, line + 3, source_lines)
345

346

347
def render_pep8_errors_e224_and_e273(msg, _node, source_lines):
5✔
348
    """Render a PEP8 tab after operator message and a PEP8 tab after keyword message."""
UNCOV
349
    line = msg.line
×
UNCOV
350
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
351
    col = int(res.group().split()[-1])
×
352
    curr_idx = (
353
        col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip("\t"))
354
    )
355

UNCOV
356
    yield from render_context(line - 2, line, source_lines)
×
UNCOV
357
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
×
UNCOV
358
    yield from render_context(line + 1, line + 3, source_lines)
×
359

360

361
def render_pep8_errors_e226(msg, _node, source_lines):
10✔
362
    """Render a PEP8 missing whitespace around arithmetic operator message"""
363
    line = msg.line
5✔
364
    res = re.search(r"column (\d+)", msg.msg)
5✔
365
    col = int(res.group().split()[-1])
5✔
366
    end_idx = col + 1
5✔
367

368
    yield from render_context(line - 2, line, source_lines)
5✔
369
    yield (line, slice(col, end_idx), LineType.ERROR, source_lines[line - 1])
5✔
370
    yield from render_context(line + 1, line + 3, source_lines)
5✔
371

372

373
def render_pep8_errors_e227(msg, _node, source_lines=None):
5✔
374
    """Render a PEP8 missing whitespace around bitwise or shift operator message."""
UNCOV
375
    line = msg.line
×
UNCOV
376
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
377
    col = int(res.group().split()[-1])
×
378
    # Check which operator to get the correct range of the line to highlight.
379
    # Default highlight is one character, but may be updated to two.
380
    # Note that only binary bitwise operators that are more than one character are included.
UNCOV
381
    operators = {">>", "<<"}
×
UNCOV
382
    end_idx = col + 1
×
UNCOV
383
    end_idx = end_idx + 1 if source_lines[line - 1][col : col + 2] in operators else end_idx
×
384

UNCOV
385
    yield from render_context(line - 2, line, source_lines)
×
UNCOV
386
    yield (line, slice(col, end_idx), LineType.ERROR, source_lines[line - 1])
×
UNCOV
387
    yield from render_context(line + 1, line + 3, source_lines)
×
388

389

390
def render_pep8_errors_e228(msg, _node, source_lines=None):
10✔
391
    """Render a PEP8 missing whitespace around modulo operator message."""
392
    line = msg.line
393
    res = re.search(r"column (\d+)", msg.msg)
394
    col = int(res.group().split()[-1])
395

396
    yield from render_context(line - 2, line, source_lines)
397
    yield (
398
        line,
399
        slice(col, col + 1),
400
        LineType.ERROR,
401
        source_lines[line - 1] + "  # INSERT A SPACE BEFORE AND AFTER THE % OPERATOR",
402
    )
403
    yield from render_context(line + 1, line + 3, source_lines)
404

405

406
def render_pep8_errors_e251(msg, _node, source_lines=None):
5✔
407
    """Render a PEP8 unexpected spaces around keyword / parameter equals message."""
408
    line = msg.line
10✔
409
    res = re.search(r"column (\d+)", msg.msg)
10✔
410
    col = int(res.group().split()[-1])
10✔
411
    equals_sign_idx = source_lines[line - 1][col:].find("=")
10✔
412
    code = source_lines[line - 1][col : col + equals_sign_idx if equals_sign_idx != -1 else None]
10✔
413
    end_idx = col + len(code) - len(code.lstrip())
10✔
414

415
    yield from render_context(line - 2, line, source_lines)
10✔
416
    yield (line, slice(col, end_idx), LineType.ERROR, source_lines[line - 1])
10✔
417
    yield from render_context(line + 1, line + 3, source_lines)
10✔
418

419

420
def render_pep8_errors_e261(msg, _node, source_lines=None):
10✔
421
    """Render a PEP8 at least two spaces before inline comment message."""
422
    line = msg.line
5✔
423
    res = re.search(r"column (\d+)", msg.msg)
5✔
424
    col = int(res.group().split()[-1])
5✔
425

426
    yield from render_context(line - 2, line, source_lines)
5✔
427
    yield (
5✔
428
        line,
429
        slice(col, len(source_lines[line - 1])),
430
        LineType.ERROR,
431
        source_lines[line - 1] + "  # INSERT TWO SPACES BEFORE THE '#'",
432
    )
433
    yield from render_context(line + 1, line + 3, source_lines)
5✔
434

435

436
def render_pep8_errors_e262(msg, _node, source_lines=None):
5✔
437
    """Render a PEP8 inline comment should start with '# ' message"""
438
    line = msg.line
10✔
439
    res = re.search(r"column (\d+)", msg.msg)
10✔
440
    col = int(res.group().split()[-1])
10✔
441

442
    keyword = source_lines[line - 1][col:].split()[1]
10✔
443
    keyword_idx = source_lines[line - 1][col:].index(keyword) + col
10✔
444

445
    yield from render_context(line - 2, line, source_lines)
10✔
446
    yield (line, slice(col, keyword_idx), LineType.ERROR, source_lines[line - 1])
10✔
447
    yield from render_context(line + 1, line + 3, source_lines)
10✔
448

449

450
def render_pep8_errors_e265(msg, _node, source_lines=None):
10✔
451
    """Render a PEP8 block comment should start with '# ' message."""
452
    line = msg.line
453
    yield from render_context(line - 2, line, source_lines)
454
    yield (
455
        line,
456
        slice(0, len(source_lines[line - 1])),
457
        LineType.ERROR,
458
        source_lines[line - 1] + "  # INSERT SPACE AFTER THE '#'",
459
    )
460
    yield from render_context(line + 1, line + 3, source_lines)
461

462

463
def render_pep8_errors_e266(msg, _node, source_lines=None):
5✔
464
    """Render a PEP8 too many leading ‘#’ for block comment message."""
465
    line = msg.line
×
466
    res = re.search(r"column (\d+)", msg.msg)
×
467
    col = int(res.group().split()[-1])
×
468
    curr_idx = (
469
        col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip("#"))
470
    )
471

472
    yield from render_context(line - 2, line, source_lines)
×
473
    yield (
474
        line,
475
        slice(col, curr_idx),
476
        LineType.ERROR,
477
        source_lines[line - 1] + "  # THERE SHOULD ONLY BE ONE '#'",
478
    )
479
    yield from render_context(line + 1, line + 3, source_lines)
×
480

481

482
def render_pep8_errors_e272(msg, _node, source_lines=None):
10✔
483
    """Render a PEP8 multiple spaces before keyword message."""
484
    line = msg.line
5✔
485
    res = re.search(r"column (\d+)", msg.msg)
5✔
486
    col = int(res.group().split()[-1])
5✔
487
    curr_idx = col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip())
5✔
488

489
    yield from render_context(line - 2, line, source_lines)
5✔
490
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
5✔
491
    yield from render_context(line + 1, line + 3, source_lines)
5✔
492

493

494
def render_pep8_errors_e275(msg, _node, source_lines=None):
5✔
495
    """Render a PEP8 missing whitespace after keyword message."""
496
    line = msg.line
×
497
    res = re.search(r"column (\d+)", msg.msg)
×
498
    col = int(res.group().split()[-1])
×
499

500
    # Get the range for highlighting the corresponding keyword.
501
    keyword = source_lines[line - 1][:col].split()[-1]
×
502
    keyword_idx = source_lines[line - 1].index(keyword)
×
503

504
    yield from render_context(line - 2, line, source_lines)
×
505
    yield (
506
        line,
507
        slice(keyword_idx, col),
508
        LineType.ERROR,
509
        source_lines[line - 1] + "  # INSERT SPACE AFTER KEYWORD",
510
    )
511
    yield from render_context(line + 1, line + 3, source_lines)
×
512

513

514
def render_pep8_errors_e301(msg, _node, source_lines=None):
10✔
515
    """Render a PEP8 expected 1 blank line message."""
516
    line = msg.line - 1
517
    yield from render_context(line - 1, line + 1, source_lines)
518
    body = source_lines[line]
519
    indentation = len(body) - len(body.lstrip())
520
    yield (
521
        None,
522
        slice(None, None),
523
        LineType.ERROR,
524
        body[:indentation] + NEW_BLANK_LINE_MESSAGE,
525
    )
526
    yield from render_context(msg.line, msg.line + 2, source_lines)
527

528

529
def render_pep8_errors_e302(msg, _node, source_lines=None):
5✔
530
    """Render a PEP8 expected 2 blank lines message."""
531
    line = msg.line - 1
10✔
532
    if "found 0" in msg.msg:
10✔
533
        yield from render_context(line - 1, line + 1, source_lines)
10✔
534
        yield from (
10✔
535
            (
5✔
536
                None,
5✔
537
                slice(None, None),
5✔
538
                LineType.ERROR,
5✔
539
                NEW_BLANK_LINE_MESSAGE,
5✔
540
            )
541
            for _ in range(0, 2)
5✔
542
        )
543
    else:
544
        line -= 1
10✔
545
        yield from render_context(line - 1, line + 1, source_lines)
10✔
546
        yield from render_blank_line(line)
10✔
547
        yield (None, slice(None, None), LineType.ERROR, NEW_BLANK_LINE_MESSAGE)
10✔
548
    yield from render_context(msg.line, msg.line + 2, source_lines)
10✔
549

550

551
def render_pep8_errors_e303(msg, _node, source_lines=None):
10✔
552
    """Render a PEP8 too many blank lines message."""
553
    line = msg.line - 1
554
    while source_lines[line - 1].strip() == "":
555
        line -= 1
556
    yield from render_context(line - 1, line + 1, source_lines)
557
    body = source_lines[msg.line - 1]
558
    indentation = len(body) - len(body.lstrip())
559
    yield from (
560
        (curr_line, slice(None, None), LineType.ERROR, " " * (indentation + 28))
561
        for curr_line in range(line + 1, msg.line)
562
    )
563
    yield from render_context(msg.line, msg.line + 2, source_lines)
564

565

566
def render_pep8_errors_e304(msg, _node, source_lines=None):
5✔
567
    """Render a PEP8 blank lines found after function decorator message."""
568
    line = msg.line - 1
×
569
    while source_lines[line - 1].strip() == "":
×
570
        line -= 1
×
571
    yield from render_context(line - 1, line + 1, source_lines)
×
572
    yield from (
573
        (curr_line, slice(None, None), LineType.ERROR, " " * 28)
574
        for curr_line in range(line + 1, msg.line)
575
    )
576
    yield from render_context(msg.line, msg.line + 2, source_lines)
×
577

578

579
def render_pep8_errors_e305(msg, _node, source_lines=None):
10✔
580
    """Render a PEP8 expected 2 blank lines after class or function definition message."""
581
    line = msg.line - 1
5✔
582
    if "found 0" in msg.msg:
5✔
583
        yield from render_context(line - 1, line + 1, source_lines)
584
        yield from (
585
            (
586
                None,
587
                slice(None, None),
588
                LineType.ERROR,
589
                NEW_BLANK_LINE_MESSAGE,
590
            )
591
            for _ in range(0, 2)
592
        )
593
    else:
594
        line -= 1
5✔
595
        yield from render_context(line - 1, line + 1, source_lines)
5✔
596
        yield from render_blank_line(line)
5✔
597
        yield (None, slice(None, None), LineType.ERROR, NEW_BLANK_LINE_MESSAGE)
5✔
598
    yield from render_context(msg.line, msg.line + 2, source_lines)
5✔
599

600

601
def render_pep8_errors_e306(msg, _node, source_lines=None):
5✔
602
    """Render a PEP8 expected 1 blank line before a nested definition message."""
603
    line = msg.line - 1
10✔
604
    yield from render_context(line - 1, line + 1, source_lines)
10✔
605
    body = source_lines[line]
10✔
606
    indentation = len(body) - len(body.lstrip())
10✔
607
    yield (
10✔
608
        None,
5✔
609
        slice(None, None),
5✔
610
        LineType.ERROR,
5✔
611
        body[:indentation] + NEW_BLANK_LINE_MESSAGE,
5✔
612
    )
613
    yield from render_context(msg.line, msg.line + 2, source_lines)
10✔
614

615

616
CUSTOM_MESSAGES = {
10✔
617
    "missing-module-docstring": render_missing_docstring,
5✔
618
    "missing-class-docstring": render_missing_docstring,
5✔
619
    "missing-function-docstring": render_missing_docstring,
5✔
620
    "trailing-newlines": render_trailing_newlines,
5✔
621
    "trailing-whitespace": render_trailing_whitespace,
5✔
622
    "missing-return-type": render_missing_return_type,
5✔
623
    "too-many-arguments": render_too_many_arguments,
5✔
624
    "missing-space-in-doctest": render_missing_space_in_doctest,
5✔
625
    "pep8-errors": render_pep8_errors,
5✔
626
}
627

628

629
class LineType(Enum):
10✔
630
    """An enumeration for _add_line method line types."""
5✔
631

632
    ERROR = 1  # line with error
5✔
633
    CONTEXT = 2  # non-error/other line added for context
5✔
634
    OTHER = 3  # line included in source but not error
5✔
635
    ELLIPSIS = 5  # code replaced with ellipsis
5✔
636
    DOCSTRING = 6  # docstring needed warning
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc