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

pyta-uoft / pyta / 9277280214

28 May 2024 10:39PM UTC coverage: 90.016% (-1.6%) from 91.615%
9277280214

Pull #1042

github

web-flow
Merge d04e53df7 into 921a9ea7f
Pull Request #1042: test: Refactor `test_check_on_dir` method

2759 of 3065 relevant lines covered (90.02%)

8.87 hits per line

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

74.28
/python_ta/reporters/node_printers.py
1
"""Specify how errors should be rendered."""
5✔
2

3
import re
10✔
4
from enum import Enum
10✔
5

6
from astroid import nodes
10✔
7

8
NEW_BLANK_LINE_MESSAGE = "# INSERT NEW BLANK LINE HERE"
10✔
9

10

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

16

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

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

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

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

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

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

54

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

75

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

85

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

94

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

103

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

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

118

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

124

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

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

134

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

196

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

201

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

211

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

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

225

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

239

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

257

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

267

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

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

283

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

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

294

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

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

309

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

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

321

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

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

333

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

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

347

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

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

361

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

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

373

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

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

390

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

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

406

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

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

420

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

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

436

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

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

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

450

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

463

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

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

482

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

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

494

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

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

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

514

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

529

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

551

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

566

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

579

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

601

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

616

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

629

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

633
    ERROR = 1  # line with error
10✔
634
    CONTEXT = 2  # non-error/other line added for context
10✔
635
    OTHER = 3  # line included in source but not error
10✔
636
    ELLIPSIS = 5  # code replaced with ellipsis
10✔
637
    DOCSTRING = 6  # docstring needed warning
10✔
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