Skip to content

Commit 5bb0095

Browse files
Add typing to LintModuleOutputUpdate._check_output_text
Better typing for test options and fix existing issue
1 parent bb3c446 commit 5bb0095

13 files changed

+76
-28
lines changed

ChangeLog

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Release date: TBA
1111
..
1212
Put new features here and also in 'doc/whatsnew/2.13.rst'
1313

14-
* Some file in ``pylint.testutils`` were deprecated, imports should be done from the
15-
``pylint.testutils`` API directly
14+
* Some files in ``pylint.testutils`` were deprecated. In the future imports should be done from the
15+
``pylint.testutils.functional`` namespace directly.
1616

1717
..
1818
Insert your changelog randomly, it will reduce merge conflicts

pylint/testutils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
from pylint.testutils.checker_test_case import CheckerTestCase
5050
from pylint.testutils.constants import UPDATE_FILE, UPDATE_OPTION
5151
from pylint.testutils.decorator import set_config
52-
from pylint.testutils.functional.test_file import FunctionalTestFile
52+
from pylint.testutils.functional import FunctionalTestFile
5353
from pylint.testutils.get_test_info import _get_tests_info
5454
from pylint.testutils.global_test_linter import linter
5555
from pylint.testutils.lint_module_test import LintModuleTest

pylint/testutils/functional/find_functional_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
def get_functional_test_files_from_directory(
1616
input_dir: Union[Path, str]
1717
) -> List[FunctionalTestFile]:
18+
"""Get all functional tests in the input_dir."""
1819
suite = []
1920
for dirpath, _, filenames in os.walk(input_dir):
2021
if dirpath.endswith("__pycache__"):

pylint/testutils/functional/lint_module_output_update.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@
33

44
import csv
55
import os
6-
from typing import Optional
6+
from typing import List, Optional
77

88
from _pytest.config import Config
99

1010
from pylint.constants import PY38_PLUS
1111
from pylint.testutils.functional.test_file import FunctionalTestFile
12-
from pylint.testutils.lint_module_test import LintModuleTest
12+
from pylint.testutils.lint_module_test import LintModuleTest, MessageCounter
13+
from pylint.testutils.output_line import OutputLine
1314

1415

1516
class LintModuleOutputUpdate(LintModuleTest):
16-
"""If message files should be updated instead of checked."""
17+
"""Class to be used if expected output files should be updated instead of checked."""
1718

1819
class TestDialect(csv.excel):
20+
"""Dialect used by the csv writer."""
21+
1922
delimiter = ":"
2023
lineterminator = "\n"
2124

@@ -32,11 +35,19 @@ def __init__(
3235
)
3336
super().__init__(test_file, config)
3437

35-
def _check_output_text(self, _, expected_output, actual_output):
38+
def _check_output_text(
39+
self,
40+
_: MessageCounter,
41+
expected_output: List[OutputLine],
42+
actual_output: List[OutputLine],
43+
) -> None:
44+
"""Overwrite or remove the expected output file based on actual output."""
45+
# Remove the file if no output is actually expected and a file exists
3646
if not expected_output and not actual_output:
3747
if os.path.exists(self._test_file.expected_output):
3848
os.remove(self._test_file.expected_output)
3949
return
50+
# Write file with expected output
4051
with open(self._test_file.expected_output, "w", encoding="utf-8") as f:
4152
writer = csv.writer(f, dialect="test")
4253
for line in actual_output:

pylint/testutils/functional/test_file.py

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,47 @@
22
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
33

44
import configparser
5+
import sys
56
from os.path import basename, exists, join
7+
from typing import List, Tuple
68

79

8-
def parse_python_version(ver_str):
10+
def parse_python_version(ver_str: str) -> Tuple[int, ...]:
11+
"""Convert python version to a tuple of integers for easy comparison."""
912
return tuple(int(digit) for digit in ver_str.split("."))
1013

1114

1215
class NoFileError(Exception):
1316
pass
1417

1518

19+
if sys.version_info >= (3, 8):
20+
from typing import TypedDict
21+
else:
22+
from typing_extensions import TypedDict
23+
24+
25+
class TestFileOptions(TypedDict):
26+
min_pyver: Tuple[int, ...]
27+
max_pyver: Tuple[int, ...]
28+
min_pyver_end_position: Tuple[int, ...]
29+
requires: List[str]
30+
except_implementations: str # Type is actually comma separated list of string
31+
exclude_platforms: str # Type is actually comma separated list of string
32+
33+
34+
# mypy need something literal, we can't create this dynamically from TestFileOptions
35+
POSSIBLE_TEST_OPTIONS = {
36+
"min_pyver",
37+
"max_pyver",
38+
"min_pyver_end_position",
39+
"requires",
40+
"except_implementations",
41+
"exclude_platforms",
42+
"exclude_platforms",
43+
}
44+
45+
1646
class FunctionalTestFile:
1747
"""A single functional test case file with options."""
1848

@@ -23,23 +53,23 @@ class FunctionalTestFile:
2353
"requires": lambda s: s.split(","),
2454
}
2555

26-
def __init__(self, directory, filename):
56+
def __init__(self, directory: str, filename: str) -> None:
2757
self._directory = directory
2858
self.base = filename.replace(".py", "")
29-
self.options = {
59+
self.options: TestFileOptions = {
3060
"min_pyver": (2, 5),
3161
"max_pyver": (4, 0),
3262
"min_pyver_end_position": (3, 8),
3363
"requires": [],
34-
"except_implementations": [],
35-
"exclude_platforms": [],
64+
"except_implementations": "",
65+
"exclude_platforms": "",
3666
}
3767
self._parse_options()
3868

39-
def __repr__(self):
69+
def __repr__(self) -> str:
4070
return f"FunctionalTest:{self.base}"
4171

42-
def _parse_options(self):
72+
def _parse_options(self) -> None:
4373
cp = configparser.ConfigParser()
4474
cp.add_section("testoptions")
4575
try:
@@ -49,26 +79,30 @@ def _parse_options(self):
4979

5080
for name, value in cp.items("testoptions"):
5181
conv = self._CONVERTERS.get(name, lambda v: v)
52-
self.options[name] = conv(value)
82+
83+
assert (
84+
name in POSSIBLE_TEST_OPTIONS
85+
), f"[testoptions]' can only contains one of {POSSIBLE_TEST_OPTIONS}"
86+
self.options[name] = conv(value) # type: ignore[misc]
5387

5488
@property
55-
def option_file(self):
89+
def option_file(self) -> str:
5690
return self._file_type(".rc")
5791

5892
@property
59-
def module(self):
93+
def module(self) -> str:
6094
package = basename(self._directory)
6195
return ".".join([package, self.base])
6296

6397
@property
64-
def expected_output(self):
98+
def expected_output(self) -> str:
6599
return self._file_type(".txt", check_exists=False)
66100

67101
@property
68-
def source(self):
102+
def source(self) -> str:
69103
return self._file_type(".py")
70104

71-
def _file_type(self, ext, check_exists=True):
105+
def _file_type(self, ext: str, check_exists: bool = True) -> str:
72106
name = join(self._directory, self.base + ext)
73107
if not check_exists or exists(name):
74108
return name

pylint/testutils/lint_module_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from pylint.lint import PyLinter
1818
from pylint.message.message import Message
1919
from pylint.testutils.constants import _EXPECTED_RE, _OPERATORS, UPDATE_OPTION
20-
from pylint.testutils.functional.test_file import (
20+
from pylint.testutils.functional.test_file import ( # need to import from functional.test_file to avoid cyclic import
2121
FunctionalTestFile,
2222
NoFileError,
2323
parse_python_version,

tests/functional/f/fixme.rc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
[testoptions]
1+
[MISCELLANEOUS]
2+
# List of note tags to take in consideration, separated by a comma.
23
notes=XXX,TODO,./TODO
4+
# Regular expression of note tags to take in consideration.
35
notes-rgx=FIXME(?!.*ISSUE-\d+)|TO.*DO
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
[testoptions]
1+
[MASTER]
22
limit-inference-results=0
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[testoptions]
1+
[DESIGN]
22
min-public-methods=10 # to combat inherited methods
33

44
exclude-too-few-public-methods=json.*,^.*Control$
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
[testoptions]
1+
[DESIGN]
22
max-parents=2
33
ignored-parents=functional.t.too.too_many_ancestors_ignored_parents.E

0 commit comments

Comments
 (0)