Skip to content

Commit 7c3533c

Browse files
DanielNoordcdce8pPierre-Sassoulas
authored
Update reporters to (allow) use of end_line and end_column (#5372)
* Update reporters to (allow) use end_line and end_column Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
1 parent 5e9d20d commit 7c3533c

File tree

5 files changed

+111
-2
lines changed

5 files changed

+111
-2
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Release date: TBA
1515

1616
Closes #4982
1717

18+
* Add ability to add ``end_line`` and ``end_column`` to the ``--msg-template`` option.
19+
With the standard ``TextReporter`` this will add the line and column number of the
20+
end of a node to the output of Pylint. If these numbers are unknown, they are represented
21+
by an empty string.
22+
1823
* Introduced primer tests and a configuration tests framework. The helper classes available in
1924
``pylint/testutil/`` are still unstable and might be modified in the near future.
2025

doc/user_guide/output.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ line
5858
line number
5959
column
6060
column number
61+
end_line
62+
line number of the end of the node
63+
end_column
64+
column number of the end of the node
6165
module
6266
module name
6367
obj
@@ -94,6 +98,9 @@ A few other examples:
9498
The ``--msg-template`` option can only be combined with text-based reporters (``--output-format`` either unspecified or one of: parseable, colorized or msvs).
9599
If both ``--output-format`` and ``--msg-template`` are specified, the ``--msg-template`` option will take precedence over the default line format defined by the reporter class.
96100

101+
If ``end_line`` or ``end_column`` are ``None``, they will be represented as an empty string
102+
by the default ``TextReporter``.
103+
97104
.. _Python new format syntax: https://docs.python.org/2/library/string.html#formatstrings
98105

99106
Source code analysis section

doc/whatsnew/2.12.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ Other Changes
205205

206206
Partially closes #5321
207207

208+
* Add ability to add ``end_line`` and ``end_column`` to the ``--msg-template`` option.
209+
With the standard ``TextReporter`` this will add the line and column number of the
210+
end of a node to the output of Pylint. If these numbers are unknown, they are represented
211+
by an empty string.
212+
208213
* Introduced primer tests and a configuration tests framework. The helper classes available in
209214
``pylint/testutil/`` are still unstable and might be modified in the near future.
210215

pylint/reporters/text.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
:colorized: an ANSI colorized text reporter
2525
"""
2626
import os
27+
import re
2728
import sys
2829
import warnings
2930
from typing import (
@@ -183,13 +184,38 @@ def __init__(self, output: Optional[TextIO] = None) -> None:
183184
super().__init__(output)
184185
self._modules: Set[str] = set()
185186
self._template = self.line_format
187+
self._fixed_template = self.line_format
188+
"""The output format template with any unrecognized arguments removed"""
186189

187190
def on_set_current_module(self, module: str, filepath: Optional[str]) -> None:
188-
self._template = str(self.linter.config.msg_template or self._template)
191+
"""Set the format template to be used and check for unrecognized arguments."""
192+
template = str(self.linter.config.msg_template or self._template)
193+
194+
# Return early if the template is the same as the previous one
195+
if template == self._template:
196+
return
197+
198+
# Set template to the currently selected template
199+
self._template = template
200+
201+
# Check to see if all parameters in the template are attributes of the Message
202+
arguments = re.findall(r"\{(.+?)(:.*)?\}", template)
203+
for argument in arguments:
204+
if argument[0] not in Message._fields:
205+
warnings.warn(
206+
f"Don't recognize the argument '{argument[0]}' in the --msg-template. "
207+
"Are you sure it is supported on the current version of pylint?"
208+
)
209+
template = re.sub(r"\{" + argument[0] + r"(:.*?)?\}", "", template)
210+
self._fixed_template = template
189211

190212
def write_message(self, msg: Message) -> None:
191213
"""Convenience method to write a formatted message with class default template"""
192-
self.writeln(msg.format(self._template))
214+
self_dict = msg._asdict()
215+
for key in ("end_line", "end_column"):
216+
self_dict[key] = self_dict[key] or ""
217+
218+
self.writeln(self._fixed_template.format(**self_dict))
193219

194220
def handle_message(self, msg: Message) -> None:
195221
"""manage message of different type and in the context of path"""

tests/unittest_reporting.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,72 @@ def test_template_option(linter):
5757
assert output.getvalue() == "************* Module 0123\nC0301:001\nC0301:002\n"
5858

5959

60+
def test_template_option_default(linter) -> None:
61+
"""Test the default msg-template setting"""
62+
output = StringIO()
63+
linter.reporter.out = output
64+
linter.open()
65+
linter.set_current_module("my_module")
66+
linter.add_message("C0301", line=1, args=(1, 2))
67+
linter.add_message("line-too-long", line=2, args=(3, 4))
68+
69+
out_lines = output.getvalue().split("\n")
70+
assert out_lines[1] == "my_module:1:0: C0301: Line too long (1/2) (line-too-long)"
71+
assert out_lines[2] == "my_module:2:0: C0301: Line too long (3/4) (line-too-long)"
72+
73+
74+
def test_template_option_end_line(linter) -> None:
75+
"""Test the msg-template option with end_line and end_column"""
76+
output = StringIO()
77+
linter.reporter.out = output
78+
linter.set_option(
79+
"msg-template",
80+
"{path}:{line}:{column}:{end_line}:{end_column}: {msg_id}: {msg} ({symbol})",
81+
)
82+
linter.open()
83+
linter.set_current_module("my_mod")
84+
linter.add_message("C0301", line=1, args=(1, 2))
85+
linter.add_message(
86+
"line-too-long", line=2, end_lineno=2, end_col_offset=4, args=(3, 4)
87+
)
88+
89+
out_lines = output.getvalue().split("\n")
90+
assert out_lines[1] == "my_mod:1:0::: C0301: Line too long (1/2) (line-too-long)"
91+
assert out_lines[2] == "my_mod:2:0:2:4: C0301: Line too long (3/4) (line-too-long)"
92+
93+
94+
def test_template_option_non_exisiting(linter) -> None:
95+
"""Test the msg-template option with a non exisiting options.
96+
This makes sure that this option remains backwards compatible as new
97+
parameters do not break on previous versions"""
98+
output = StringIO()
99+
linter.reporter.out = output
100+
linter.set_option(
101+
"msg-template",
102+
"{path}:{line}:{a_new_option}:({a_second_new_option:03d})",
103+
)
104+
linter.open()
105+
with pytest.warns(UserWarning) as records:
106+
linter.set_current_module("my_mod")
107+
assert len(records) == 2
108+
assert (
109+
"Don't recognize the argument 'a_new_option'" in records[0].message.args[0]
110+
)
111+
assert (
112+
"Don't recognize the argument 'a_second_new_option'"
113+
in records[1].message.args[0]
114+
)
115+
116+
linter.add_message("C0301", line=1, args=(1, 2))
117+
linter.add_message(
118+
"line-too-long", line=2, end_lineno=2, end_col_offset=4, args=(3, 4)
119+
)
120+
121+
out_lines = output.getvalue().split("\n")
122+
assert out_lines[1] == "my_mod:1::()"
123+
assert out_lines[2] == "my_mod:2::()"
124+
125+
60126
def test_deprecation_set_output(recwarn):
61127
"""TODO remove in 3.0"""
62128
reporter = BaseReporter()

0 commit comments

Comments
 (0)