|
2 | 2 | # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
|
3 | 3 | # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
|
4 | 4 |
|
| 5 | +from __future__ import annotations |
| 6 | + |
| 7 | +import io |
5 | 8 | import os
|
| 9 | +import sys |
| 10 | +import warnings |
6 | 11 | from pathlib import Path
|
7 | 12 | from typing import Any, NoReturn
|
8 | 13 | from unittest import mock
|
9 | 14 | from unittest.mock import patch
|
10 | 15 |
|
| 16 | +import pytest |
11 | 17 | from pytest import CaptureFixture
|
12 | 18 |
|
13 |
| -from pylint.lint.pylinter import PyLinter |
| 19 | +from pylint.lint.pylinter import ( |
| 20 | + FORCE_COLOR, |
| 21 | + NO_COLOR, |
| 22 | + PY_COLORS, |
| 23 | + WARN_BOTH_COLOR_SET, |
| 24 | + PyLinter, |
| 25 | + _handle_force_color_no_color, |
| 26 | +) |
| 27 | +from pylint.reporters.text import ColorizedTextReporter, TextReporter |
14 | 28 | from pylint.utils import FileState
|
15 | 29 |
|
| 30 | +COLORIZED_REPORTERS = "colorized_reporters" |
| 31 | +TEXT_REPORTERS = "text_reporters" |
| 32 | +STDOUT_TEXT = "stdout" |
| 33 | + |
16 | 34 |
|
17 | 35 | def raise_exception(*args: Any, **kwargs: Any) -> NoReturn:
|
18 | 36 | raise ValueError
|
@@ -48,3 +66,125 @@ def test_crash_during_linting(
|
48 | 66 | assert len(files) == 1
|
49 | 67 | assert "pylint-crash-20" in str(files[0])
|
50 | 68 | assert any(m.symbol == "astroid-error" for m in linter.reporter.messages)
|
| 69 | + |
| 70 | + |
| 71 | +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: |
| 72 | + if metafunc.function.__name__ != test_handle_force_color_no_color.__name__: |
| 73 | + return |
| 74 | + |
| 75 | + if ( |
| 76 | + TEXT_REPORTERS not in metafunc.fixturenames |
| 77 | + or COLORIZED_REPORTERS not in metafunc.fixturenames |
| 78 | + ): |
| 79 | + warnings.warn( |
| 80 | + f"Missing fixture {TEXT_REPORTERS} or {COLORIZED_REPORTERS} in" |
| 81 | + f" {test_handle_force_color_no_color.function.__name__}??", |
| 82 | + stacklevel=2, |
| 83 | + ) |
| 84 | + return |
| 85 | + |
| 86 | + parameters = [] |
| 87 | + |
| 88 | + reporter_combinations = [ |
| 89 | + ("file", STDOUT_TEXT), |
| 90 | + ("file",), |
| 91 | + (STDOUT_TEXT,), |
| 92 | + ("",), |
| 93 | + ] |
| 94 | + |
| 95 | + for tr in list(reporter_combinations): |
| 96 | + for cr in list(reporter_combinations): |
| 97 | + tr = tuple(t for t in tr if t) |
| 98 | + cr = tuple(t for t in cr if t) |
| 99 | + |
| 100 | + total_reporters = len(tr) + len(cr) |
| 101 | + unique_reporters = len(set(tr + cr)) |
| 102 | + |
| 103 | + if total_reporters == 0: |
| 104 | + continue |
| 105 | + |
| 106 | + if unique_reporters != total_reporters: |
| 107 | + continue |
| 108 | + |
| 109 | + parameters.append((tuple(tr), tuple(cr))) |
| 110 | + |
| 111 | + metafunc.parametrize( |
| 112 | + f"{TEXT_REPORTERS}, {COLORIZED_REPORTERS}", parameters, ids=repr |
| 113 | + ) |
| 114 | + |
| 115 | + |
| 116 | +@pytest.mark.parametrize("no_color", [True, False], ids=lambda no_color: f"{no_color=}") |
| 117 | +@pytest.mark.parametrize( |
| 118 | + "py_colors", [True, False], ids=lambda py_colors: f"{py_colors=}" |
| 119 | +) |
| 120 | +@pytest.mark.parametrize( |
| 121 | + "force_color", [True, False], ids=lambda force_color: f"{force_color=}" |
| 122 | +) |
| 123 | +def test_handle_force_color_no_color( |
| 124 | + monkeypatch: pytest.MonkeyPatch, |
| 125 | + recwarn: pytest.WarningsRecorder, |
| 126 | + no_color: bool, |
| 127 | + py_colors: bool, |
| 128 | + force_color: bool, |
| 129 | + text_reporters: tuple[str], |
| 130 | + colorized_reporters: tuple[str], |
| 131 | +) -> None: |
| 132 | + monkeypatch.setenv(NO_COLOR, "1" if no_color else "") |
| 133 | + monkeypatch.setenv(FORCE_COLOR, "1" if force_color else "") |
| 134 | + monkeypatch.setenv(PY_COLORS, "1" if py_colors else "") |
| 135 | + |
| 136 | + force_color = force_color or py_colors |
| 137 | + |
| 138 | + if STDOUT_TEXT in text_reporters or STDOUT_TEXT in colorized_reporters: |
| 139 | + monkeypatch.setattr(sys, STDOUT_TEXT, io.TextIOWrapper(io.BytesIO())) |
| 140 | + |
| 141 | + reporters = [] |
| 142 | + for reporter, group in ( |
| 143 | + (TextReporter, text_reporters), |
| 144 | + (ColorizedTextReporter, colorized_reporters), |
| 145 | + ): |
| 146 | + for name in group: |
| 147 | + if name == STDOUT_TEXT: |
| 148 | + reporters.append(reporter()) |
| 149 | + if name == "file": |
| 150 | + reporters.append(reporter(io.TextIOWrapper(io.BytesIO()))) |
| 151 | + |
| 152 | + _handle_force_color_no_color(reporters) |
| 153 | + |
| 154 | + if no_color and force_color: |
| 155 | + # Both NO_COLOR and FORCE_COLOR are set; expecting a warning. |
| 156 | + both_color_warning = [ |
| 157 | + idx |
| 158 | + for idx, w in enumerate(recwarn.list) |
| 159 | + if WARN_BOTH_COLOR_SET in str(w.message) |
| 160 | + ] |
| 161 | + assert len(both_color_warning) == 1 |
| 162 | + recwarn.list.pop(both_color_warning[0]) |
| 163 | + |
| 164 | + if no_color: |
| 165 | + # No ColorizedTextReporter expected to be connected to stdout. |
| 166 | + assert all( |
| 167 | + not isinstance(rep, ColorizedTextReporter) |
| 168 | + for rep in reporters |
| 169 | + if rep.out.buffer is sys.stdout.buffer |
| 170 | + ) |
| 171 | + |
| 172 | + if STDOUT_TEXT in colorized_reporters: |
| 173 | + assert len(recwarn.list) == 1 # expect a warning for overriding stdout |
| 174 | + else: |
| 175 | + assert len(recwarn.list) == 0 # no warning expected |
| 176 | + elif force_color: |
| 177 | + # No TextReporter expected to be connected to stdout. |
| 178 | + # pylint: disable=unidiomatic-typecheck # Want explicit type check. |
| 179 | + assert all( |
| 180 | + type(rep) is not TextReporter |
| 181 | + for rep in reporters |
| 182 | + if rep.out.buffer is sys.stdout.buffer |
| 183 | + ) |
| 184 | + |
| 185 | + if STDOUT_TEXT in text_reporters: |
| 186 | + assert len(recwarn.list) == 1 # expect a warning for overriding stdout |
| 187 | + else: |
| 188 | + assert len(recwarn.list) == 0 # no warning expected |
| 189 | + else: |
| 190 | + assert len(recwarn.list) == 0 # no warning expected |
0 commit comments