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

pyta-uoft / pyta / 7999215313

22 Feb 2024 03:55AM UTC coverage: 95.867% (+0.2%) from 95.668%
7999215313

push

github

web-flow
Added new PEP8 custom renderer for error E123 (#1015)

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

28 existing lines in 1 file now uncovered.

3247 of 3387 relevant lines covered (95.87%)

7.31 hits per line

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

87.54
/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 "E223" in msg.msg:
5✔
155
        yield from render_pep8_errors_e223(msg, _node, source_lines)
156
    elif "E224" in msg.msg or "E273" in msg.msg:
5✔
157
        yield from render_pep8_errors_e224_and_e273(msg, _node, source_lines)
158
    elif "E226" in msg.msg:
5✔
159
        yield from render_pep8_errors_e226(msg, _node, source_lines)
5✔
160
    elif "E227" in msg.msg:
5✔
161
        yield from render_pep8_errors_e227(msg, _node, source_lines)
162
    elif "E228" in msg.msg:
5✔
163
        yield from render_pep8_errors_e228(msg, _node, source_lines)
164
    elif "E251" in msg.msg:
5✔
165
        yield from render_pep8_errors_e251(msg, _node, source_lines)
5✔
166
    elif "E261" in msg.msg:
5✔
167
        yield from render_pep8_errors_e261(msg, _node, source_lines)
5✔
168
    elif "E265" in msg.msg:
5✔
169
        yield from render_pep8_errors_e265(msg, _node, source_lines)
170
    elif "E266" in msg.msg:
5✔
171
        yield from render_pep8_errors_e266(msg, _node, source_lines)
172
    elif "E272" in msg.msg:
5✔
173
        yield from render_pep8_errors_e272(msg, _node, source_lines)
5✔
174
    elif "E275" in msg.msg:
5✔
175
        yield from render_pep8_errors_e275(msg, _node, source_lines)
176
    elif "E301" in msg.msg:
5✔
177
        yield from render_pep8_errors_e301(msg, _node, source_lines)
178
    elif "E302" in msg.msg:
5✔
179
        yield from render_pep8_errors_e302(msg, _node, source_lines)
5✔
180
    elif "E303" in msg.msg:
5✔
181
        yield from render_pep8_errors_e303(msg, _node, source_lines)
182
    elif "E304" in msg.msg:
5✔
183
        yield from render_pep8_errors_e304(msg, _node, source_lines)
184
    elif "E305" in msg.msg:
5✔
185
        yield from render_pep8_errors_e305(msg, _node, source_lines)
5✔
186
    elif "E306" in msg.msg:
5✔
187
        yield from render_pep8_errors_e306(msg, _node, source_lines)
5✔
188
    else:
189
        yield from render_generic(msg, _node, source_lines)
5✔
190

191

192
def render_blank_line(line):
5✔
193
    """Render a blank line for a PEP8 error message."""
194
    yield (line + 1, slice(None, None), LineType.ERROR, " " * 28)
10✔
195

196

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

206

207
def render_pep8_errors_e115(msg, _node, source_lines=None):
10✔
208
    """Render a PEP8 expected an indented block (comment) message."""
209
    line = msg.line
210

211
    yield from render_context(line - 2, line, source_lines)
212
    yield (
213
        line,
214
        slice(0, len(source_lines[line - 1])),
215
        LineType.ERROR,
216
        source_lines[line - 1] + "  # INDENT THIS LINE",
217
    )
218
    yield from render_context(line + 1, line + 3, source_lines)
219

220

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

234

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

252

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

262

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

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

278

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

285
    yield from render_context(line - 2, line, source_lines)
5✔
286
    yield (line, slice(0, col if col != 0 else None), LineType.ERROR, source_lines[line - 1])
5✔
287
    yield from render_context(line + 1, line + 3, source_lines)
5✔
288

289

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

300
    yield from render_context(line - 2, line, source_lines)
10✔
301
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
10✔
302
    yield from render_context(line + 1, line + 3, source_lines)
10✔
303

304

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

312
    yield from render_context(line - 2, line, source_lines)
5✔
313
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
5✔
314
    yield from render_context(line + 1, line + 3, source_lines)
5✔
315

316

317
def render_pep8_errors_e223(msg, _node, source_lines=None):
5✔
318
    """Render a PEP8 tab before operator message."""
UNCOV
319
    line = msg.line
×
UNCOV
320
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
321
    col = int(res.group().split()[-1])
×
322
    curr_idx = (
323
        col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip("\t"))
324
    )
325

UNCOV
326
    yield from render_context(line - 2, line, source_lines)
×
UNCOV
327
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
×
UNCOV
328
    yield from render_context(line + 1, line + 3, source_lines)
×
329

330

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

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

344

345
def render_pep8_errors_e226(msg, _node, source_lines):
5✔
346
    """Render a PEP8 missing whitespace around arithmetic operator message"""
347
    line = msg.line
10✔
348
    res = re.search(r"column (\d+)", msg.msg)
10✔
349
    col = int(res.group().split()[-1])
10✔
350
    end_idx = col + 1
10✔
351

352
    yield from render_context(line - 2, line, source_lines)
10✔
353
    yield (line, slice(col, end_idx), LineType.ERROR, source_lines[line - 1])
10✔
354
    yield from render_context(line + 1, line + 3, source_lines)
10✔
355

356

357
def render_pep8_errors_e227(msg, _node, source_lines=None):
10✔
358
    """Render a PEP8 missing whitespace around bitwise or shift operator message."""
359
    line = msg.line
360
    res = re.search(r"column (\d+)", msg.msg)
361
    col = int(res.group().split()[-1])
362
    # Check which operator to get the correct range of the line to highlight.
363
    # Default highlight is one character, but may be updated to two.
364
    # Note that only binary bitwise operators that are more than one character are included.
365
    operators = {">>", "<<"}
366
    end_idx = col + 1
367
    end_idx = end_idx + 1 if source_lines[line - 1][col : col + 2] in operators else end_idx
368

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

373

374
def render_pep8_errors_e228(msg, _node, source_lines=None):
5✔
375
    """Render a PEP8 missing whitespace around modulo operator message."""
UNCOV
376
    line = msg.line
×
UNCOV
377
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
378
    col = int(res.group().split()[-1])
×
379

UNCOV
380
    yield from render_context(line - 2, line, source_lines)
×
381
    yield (
382
        line,
383
        slice(col, col + 1),
384
        LineType.ERROR,
385
        source_lines[line - 1] + "  # INSERT A SPACE BEFORE AND AFTER THE % OPERATOR",
386
    )
UNCOV
387
    yield from render_context(line + 1, line + 3, source_lines)
×
388

389

390
def render_pep8_errors_e251(msg, _node, source_lines=None):
10✔
391
    """Render a PEP8 unexpected spaces around keyword / parameter equals message."""
392
    line = msg.line
5✔
393
    res = re.search(r"column (\d+)", msg.msg)
5✔
394
    col = int(res.group().split()[-1])
5✔
395
    equals_sign_idx = source_lines[line - 1][col:].find("=")
5✔
396
    code = source_lines[line - 1][col : col + equals_sign_idx if equals_sign_idx != -1 else None]
5✔
397
    end_idx = col + len(code) - len(code.lstrip())
5✔
398

399
    yield from render_context(line - 2, line, source_lines)
5✔
400
    yield (line, slice(col, end_idx), LineType.ERROR, source_lines[line - 1])
5✔
401
    yield from render_context(line + 1, line + 3, source_lines)
5✔
402

403

404
def render_pep8_errors_e261(msg, _node, source_lines=None):
5✔
405
    """Render a PEP8 at least two spaces before inline comment message."""
406
    line = msg.line
10✔
407
    res = re.search(r"column (\d+)", msg.msg)
10✔
408
    col = int(res.group().split()[-1])
10✔
409

410
    yield from render_context(line - 2, line, source_lines)
10✔
411
    yield (
10✔
412
        line,
5✔
413
        slice(col, len(source_lines[line - 1])),
5✔
414
        LineType.ERROR,
5✔
415
        source_lines[line - 1] + "  # INSERT TWO SPACES BEFORE THE '#'",
5✔
416
    )
417
    yield from render_context(line + 1, line + 3, source_lines)
10✔
418

419

420
def render_pep8_errors_e265(msg, _node, source_lines=None):
10✔
421
    """Render a PEP8 block comment should start with '# ' message."""
422
    line = msg.line
423
    yield from render_context(line - 2, line, source_lines)
424
    yield (
425
        line,
426
        slice(0, len(source_lines[line - 1])),
427
        LineType.ERROR,
428
        source_lines[line - 1] + "  # INSERT SPACE AFTER THE '#'",
429
    )
430
    yield from render_context(line + 1, line + 3, source_lines)
431

432

433
def render_pep8_errors_e266(msg, _node, source_lines=None):
5✔
434
    """Render a PEP8 too many leading ‘#’ for block comment message."""
UNCOV
435
    line = msg.line
×
UNCOV
436
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
437
    col = int(res.group().split()[-1])
×
438
    curr_idx = (
439
        col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip("#"))
440
    )
441

UNCOV
442
    yield from render_context(line - 2, line, source_lines)
×
443
    yield (
444
        line,
445
        slice(col, curr_idx),
446
        LineType.ERROR,
447
        source_lines[line - 1] + "  # THERE SHOULD ONLY BE ONE '#'",
448
    )
UNCOV
449
    yield from render_context(line + 1, line + 3, source_lines)
×
450

451

452
def render_pep8_errors_e272(msg, _node, source_lines=None):
10✔
453
    """Render a PEP8 multiple spaces before keyword message."""
454
    line = msg.line
5✔
455
    res = re.search(r"column (\d+)", msg.msg)
5✔
456
    col = int(res.group().split()[-1])
5✔
457
    curr_idx = col + len(source_lines[line - 1][col:]) - len(source_lines[line - 1][col:].lstrip())
5✔
458

459
    yield from render_context(line - 2, line, source_lines)
5✔
460
    yield (line, slice(col, curr_idx), LineType.ERROR, source_lines[line - 1])
5✔
461
    yield from render_context(line + 1, line + 3, source_lines)
5✔
462

463

464
def render_pep8_errors_e275(msg, _node, source_lines=None):
5✔
465
    """Render a PEP8 missing whitespace after keyword message."""
UNCOV
466
    line = msg.line
×
UNCOV
467
    res = re.search(r"column (\d+)", msg.msg)
×
UNCOV
468
    col = int(res.group().split()[-1])
×
469

470
    # Get the range for highlighting the corresponding keyword.
UNCOV
471
    keyword = source_lines[line - 1][:col].split()[-1]
×
UNCOV
472
    keyword_idx = source_lines[line - 1].index(keyword)
×
473

UNCOV
474
    yield from render_context(line - 2, line, source_lines)
×
475
    yield (
476
        line,
477
        slice(keyword_idx, col),
478
        LineType.ERROR,
479
        source_lines[line - 1] + "  # INSERT SPACE AFTER KEYWORD",
480
    )
UNCOV
481
    yield from render_context(line + 1, line + 3, source_lines)
×
482

483

484
def render_pep8_errors_e301(msg, _node, source_lines=None):
10✔
485
    """Render a PEP8 expected 1 blank line message."""
486
    line = msg.line - 1
487
    yield from render_context(line - 1, line + 1, source_lines)
488
    body = source_lines[line]
489
    indentation = len(body) - len(body.lstrip())
490
    yield (
491
        None,
492
        slice(None, None),
493
        LineType.ERROR,
494
        body[:indentation] + NEW_BLANK_LINE_MESSAGE,
495
    )
496
    yield from render_context(msg.line, msg.line + 2, source_lines)
497

498

499
def render_pep8_errors_e302(msg, _node, source_lines=None):
5✔
500
    """Render a PEP8 expected 2 blank lines message."""
501
    line = msg.line - 1
10✔
502
    if "found 0" in msg.msg:
10✔
503
        yield from render_context(line - 1, line + 1, source_lines)
10✔
504
        yield from (
10✔
505
            (
5✔
506
                None,
5✔
507
                slice(None, None),
5✔
508
                LineType.ERROR,
5✔
509
                NEW_BLANK_LINE_MESSAGE,
5✔
510
            )
511
            for _ in range(0, 2)
5✔
512
        )
513
    else:
514
        line -= 1
10✔
515
        yield from render_context(line - 1, line + 1, source_lines)
10✔
516
        yield from render_blank_line(line)
10✔
517
        yield (None, slice(None, None), LineType.ERROR, NEW_BLANK_LINE_MESSAGE)
10✔
518
    yield from render_context(msg.line, msg.line + 2, source_lines)
10✔
519

520

521
def render_pep8_errors_e303(msg, _node, source_lines=None):
10✔
522
    """Render a PEP8 too many blank lines message."""
523
    line = msg.line - 1
524
    while source_lines[line - 1].strip() == "":
525
        line -= 1
526
    yield from render_context(line - 1, line + 1, source_lines)
527
    body = source_lines[msg.line - 1]
528
    indentation = len(body) - len(body.lstrip())
529
    yield from (
530
        (curr_line, slice(None, None), LineType.ERROR, " " * (indentation + 28))
531
        for curr_line in range(line + 1, msg.line)
532
    )
533
    yield from render_context(msg.line, msg.line + 2, source_lines)
534

535

536
def render_pep8_errors_e304(msg, _node, source_lines=None):
5✔
537
    """Render a PEP8 blank lines found after function decorator message."""
UNCOV
538
    line = msg.line - 1
×
UNCOV
539
    while source_lines[line - 1].strip() == "":
×
UNCOV
540
        line -= 1
×
UNCOV
541
    yield from render_context(line - 1, line + 1, source_lines)
×
542
    yield from (
543
        (curr_line, slice(None, None), LineType.ERROR, " " * 28)
544
        for curr_line in range(line + 1, msg.line)
545
    )
UNCOV
546
    yield from render_context(msg.line, msg.line + 2, source_lines)
×
547

548

549
def render_pep8_errors_e305(msg, _node, source_lines=None):
10✔
550
    """Render a PEP8 expected 2 blank lines after class or function definition message."""
551
    line = msg.line - 1
5✔
552
    if "found 0" in msg.msg:
5✔
553
        yield from render_context(line - 1, line + 1, source_lines)
554
        yield from (
555
            (
556
                None,
557
                slice(None, None),
558
                LineType.ERROR,
559
                NEW_BLANK_LINE_MESSAGE,
560
            )
561
            for _ in range(0, 2)
562
        )
563
    else:
564
        line -= 1
5✔
565
        yield from render_context(line - 1, line + 1, source_lines)
5✔
566
        yield from render_blank_line(line)
5✔
567
        yield (None, slice(None, None), LineType.ERROR, NEW_BLANK_LINE_MESSAGE)
5✔
568
    yield from render_context(msg.line, msg.line + 2, source_lines)
5✔
569

570

571
def render_pep8_errors_e306(msg, _node, source_lines=None):
5✔
572
    """Render a PEP8 expected 1 blank line before a nested definition message."""
573
    line = msg.line - 1
10✔
574
    yield from render_context(line - 1, line + 1, source_lines)
10✔
575
    body = source_lines[line]
10✔
576
    indentation = len(body) - len(body.lstrip())
10✔
577
    yield (
10✔
578
        None,
5✔
579
        slice(None, None),
5✔
580
        LineType.ERROR,
5✔
581
        body[:indentation] + NEW_BLANK_LINE_MESSAGE,
5✔
582
    )
583
    yield from render_context(msg.line, msg.line + 2, source_lines)
10✔
584

585

586
CUSTOM_MESSAGES = {
10✔
587
    "missing-module-docstring": render_missing_docstring,
5✔
588
    "missing-class-docstring": render_missing_docstring,
5✔
589
    "missing-function-docstring": render_missing_docstring,
5✔
590
    "trailing-newlines": render_trailing_newlines,
5✔
591
    "trailing-whitespace": render_trailing_whitespace,
5✔
592
    "missing-return-type": render_missing_return_type,
5✔
593
    "too-many-arguments": render_too_many_arguments,
5✔
594
    "missing-space-in-doctest": render_missing_space_in_doctest,
5✔
595
    "pep8-errors": render_pep8_errors,
5✔
596
}
597

598

599
class LineType(Enum):
10✔
600
    """An enumeration for _add_line method line types."""
5✔
601

602
    ERROR = 1  # line with error
5✔
603
    CONTEXT = 2  # non-error/other line added for context
5✔
604
    OTHER = 3  # line included in source but not error
5✔
605
    ELLIPSIS = 5  # code replaced with ellipsis
5✔
606
    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