blob: d1d483a3592fd5f14196e64cdbece34be8fe3f64 [file] [log] [blame]
# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <[email protected]>
# Copyright (c) 2014-2018, 2020 Claudiu Popa <[email protected]>
# Copyright (c) 2014 Calin Don <[email protected]>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2014 Arun Persaud <[email protected]>
# Copyright (c) 2015 Ionel Cristian Maries <[email protected]>
# Copyright (c) 2016-2017 Derek Gustafson <[email protected]>
# Copyright (c) 2018 Sushobhit <[email protected]>
# Copyright (c) 2019-2021 Pierre Sassoulas <[email protected]>
# Copyright (c) 2019 Ashley Whetter <[email protected]>
# Copyright (c) 2020 hippo91 <[email protected]>
# Copyright (c) 2021 DaniĆ«l van Noord <[email protected]>
# Copyright (c) 2021 Marc Mueller <[email protected]>
# Copyright (c) 2021 ruro <[email protected]>
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
# pylint: disable=redefined-outer-name
import warnings
from contextlib import redirect_stdout
from io import StringIO
from json import dumps
import pytest
from pylint import checkers
from pylint.interfaces import IReporter
from pylint.lint import PyLinter
from pylint.reporters import BaseReporter
from pylint.reporters.text import ParseableTextReporter, TextReporter
@pytest.fixture(scope="module")
def reporter():
return TextReporter
@pytest.fixture(scope="module")
def disable():
return ["I"]
def test_template_option(linter):
output = StringIO()
linter.reporter.set_output(output)
linter.set_option("msg-template", "{msg_id}:{line:03d}")
linter.open()
linter.set_current_module("0123")
linter.add_message("C0301", line=1, args=(1, 2))
linter.add_message("line-too-long", line=2, args=(3, 4))
assert output.getvalue() == "************* Module 0123\nC0301:001\nC0301:002\n"
def test_parseable_output_deprecated():
with warnings.catch_warnings(record=True) as cm:
warnings.simplefilter("always")
ParseableTextReporter()
assert len(cm) == 1
assert isinstance(cm[0].message, DeprecationWarning)
def test_parseable_output_regression():
output = StringIO()
with warnings.catch_warnings(record=True):
linter = PyLinter(reporter=ParseableTextReporter())
checkers.initialize(linter)
linter.config.persistent = 0
linter.reporter.set_output(output)
linter.set_option("output-format", "parseable")
linter.open()
linter.set_current_module("0123")
linter.add_message("line-too-long", line=1, args=(1, 2))
assert (
output.getvalue() == "************* Module 0123\n"
"0123:1: [C0301(line-too-long), ] "
"Line too long (1/2)\n"
)
class NopReporter(BaseReporter):
__implements__ = IReporter
name = "nop-reporter"
extension = ""
def __init__(self, output=None):
super().__init__(output)
print("A NopReporter was initialized.", file=self.out)
def writeln(self, string=""):
pass
def _display(self, layout):
pass
def test_multi_format_output(tmp_path):
text = StringIO(newline=None)
json = tmp_path / "somefile.json"
source_file = tmp_path / "somemodule.py"
source_file.write_text('NOT_EMPTY = "This module is not empty"\n')
escaped_source_file = dumps(str(source_file))
nop_format = NopReporter.__module__ + "." + NopReporter.__name__
formats = ",".join(["json:" + str(json), "text", nop_format])
with redirect_stdout(text):
linter = PyLinter()
linter.set_option("persistent", False)
linter.set_option("output-format", formats)
linter.set_option("reports", True)
linter.set_option("score", True)
linter.load_default_plugins()
assert linter.reporter.linter is linter
with pytest.raises(NotImplementedError):
linter.reporter.set_output(text)
linter.open()
linter.check_single_file("somemodule", source_file, "somemodule")
linter.add_message("line-too-long", line=1, args=(1, 2))
linter.generate_reports()
linter.reporter.writeln("direct output")
# Ensure the output files are flushed and closed
linter.reporter.close_output_files()
del linter.reporter
with open(json, encoding="utf-8") as f:
assert (
f.read() == "[\n"
" {\n"
' "type": "convention",\n'
' "module": "somemodule",\n'
' "obj": "",\n'
' "line": 1,\n'
' "column": 0,\n'
f' "path": {escaped_source_file},\n'
' "symbol": "missing-module-docstring",\n'
' "message": "Missing module docstring",\n'
' "message-id": "C0114"\n'
" },\n"
" {\n"
' "type": "convention",\n'
' "module": "somemodule",\n'
' "obj": "",\n'
' "line": 1,\n'
' "column": 0,\n'
f' "path": {escaped_source_file},\n'
' "symbol": "line-too-long",\n'
' "message": "Line too long (1/2)",\n'
' "message-id": "C0301"\n'
" }\n"
"]\n"
"direct output\n"
)
assert (
text.getvalue() == "A NopReporter was initialized.\n"
"************* Module somemodule\n"
f"{source_file}:1:0: C0114: Missing module docstring (missing-module-docstring)\n"
f"{source_file}:1:0: C0301: Line too long (1/2) (line-too-long)\n"
"\n"
"\n"
"Report\n"
"======\n"
"1 statements analysed.\n"
"\n"
"Statistics by type\n"
"------------------\n"
"\n"
"+---------+-------+-----------+-----------+------------+---------+\n"
"|type |number |old number |difference |%documented |%badname |\n"
"+=========+=======+===========+===========+============+=========+\n"
"|module |1 |NC |NC |0.00 |0.00 |\n"
"+---------+-------+-----------+-----------+------------+---------+\n"
"|class |0 |NC |NC |0 |0 |\n"
"+---------+-------+-----------+-----------+------------+---------+\n"
"|method |0 |NC |NC |0 |0 |\n"
"+---------+-------+-----------+-----------+------------+---------+\n"
"|function |0 |NC |NC |0 |0 |\n"
"+---------+-------+-----------+-----------+------------+---------+\n"
"\n"
"\n"
"\n"
"Raw metrics\n"
"-----------\n"
"\n"
"+----------+-------+------+---------+-----------+\n"
"|type |number |% |previous |difference |\n"
"+==========+=======+======+=========+===========+\n"
"|code |2 |66.67 |NC |NC |\n"
"+----------+-------+------+---------+-----------+\n"
"|docstring |0 |0.00 |NC |NC |\n"
"+----------+-------+------+---------+-----------+\n"
"|comment |0 |0.00 |NC |NC |\n"
"+----------+-------+------+---------+-----------+\n"
"|empty |1 |33.33 |NC |NC |\n"
"+----------+-------+------+---------+-----------+\n"
"\n"
"\n"
"\n"
"Duplication\n"
"-----------\n"
"\n"
"+-------------------------+------+---------+-----------+\n"
"| |now |previous |difference |\n"
"+=========================+======+=========+===========+\n"
"|nb duplicated lines |0 |NC |NC |\n"
"+-------------------------+------+---------+-----------+\n"
"|percent duplicated lines |0.000 |NC |NC |\n"
"+-------------------------+------+---------+-----------+\n"
"\n"
"\n"
"\n"
"Messages by category\n"
"--------------------\n"
"\n"
"+-----------+-------+---------+-----------+\n"
"|type |number |previous |difference |\n"
"+===========+=======+=========+===========+\n"
"|convention |2 |NC |NC |\n"
"+-----------+-------+---------+-----------+\n"
"|refactor |0 |NC |NC |\n"
"+-----------+-------+---------+-----------+\n"
"|warning |0 |NC |NC |\n"
"+-----------+-------+---------+-----------+\n"
"|error |0 |NC |NC |\n"
"+-----------+-------+---------+-----------+\n"
"\n"
"\n"
"\n"
"Messages\n"
"--------\n"
"\n"
"+-------------------------+------------+\n"
"|message id |occurrences |\n"
"+=========================+============+\n"
"|missing-module-docstring |1 |\n"
"+-------------------------+------------+\n"
"|line-too-long |1 |\n"
"+-------------------------+------------+\n"
"\n"
"\n"
"\n"
"\n"
"-------------------------------------\n"
"Your code has been rated at -10.00/10\n"
"\n"
"direct output\n"
)
def test_display_results_is_renamed():
class CustomReporter(TextReporter):
def _display(self, layout):
return None
reporter = CustomReporter()
with pytest.raises(AttributeError) as exc:
# pylint: disable=no-member
reporter.display_results()
assert "no attribute 'display_results'" in str(exc)