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 os
8
+ import os .path
5
9
from pathlib import Path
6
10
11
+ import pytest
12
+ from pytest import CaptureFixture
13
+
14
+ from pylint .lint import Run as LintRun
7
15
from pylint .testutils ._run import _Run as Run
8
16
9
17
@@ -21,3 +29,136 @@ def test_fall_back_on_base_config(tmp_path: Path) -> None:
21
29
f .write ("1" )
22
30
Run ([str (test_file )], exit = False )
23
31
assert id (runner .linter .config ) == id (runner .linter ._base_config )
32
+
33
+
34
+ @pytest .fixture
35
+ def _create_subconfig_test_fs (tmp_path : Path ) -> tuple [Path , Path , Path , Path ]:
36
+ level1_dir = tmp_path / "level1_dir"
37
+ level1_init = level1_dir / "__init__.py"
38
+ conf_file1 = level1_dir / "pylintrc"
39
+ test_file1 = level1_dir / "a.py"
40
+ test_file3 = level1_dir / "z.py"
41
+ subdir = level1_dir / "sub"
42
+ level2_init = subdir / "__init__.py"
43
+ conf_file2 = subdir / "pylintrc"
44
+ test_file2 = subdir / "b.py"
45
+ os .makedirs (subdir )
46
+ level1_init .touch ()
47
+ level2_init .touch ()
48
+ test_file_text = "#LEVEL1\n #LEVEL2\n #ALL_LEVELS\n #TODO\n "
49
+ test_file1 .write_text (test_file_text )
50
+ test_file2 .write_text (test_file_text )
51
+ test_file3 .write_text (test_file_text )
52
+ conf1 = "[MISCELLANEOUS]\n notes=LEVEL1,ALL_LEVELS"
53
+ conf2 = "[MISCELLANEOUS]\n notes=LEVEL2,ALL_LEVELS"
54
+ conf_file1 .write_text (conf1 )
55
+ conf_file2 .write_text (conf2 )
56
+ return level1_dir , test_file1 , test_file2 , test_file3
57
+
58
+
59
+ # check that use-parent-configs doesn't break anything
60
+ @pytest .mark .parametrize (
61
+ "local_config_args" ,
62
+ [["--use-local-configs=y" ], ["--use-local-configs=y" , "--use-parent-configs=y" ]],
63
+ )
64
+ # check files and configs from top-level package or subpackage
65
+ @pytest .mark .parametrize ("test_file_index" , [0 , 1 , 2 ])
66
+ # check cases when cwd contains pylintrc or not
67
+ @pytest .mark .parametrize ("start_dir_modificator" , ["." , ".." ])
68
+ def test_subconfig_vs_root_config (
69
+ _create_subconfig_test_fs : tuple [Path , ...],
70
+ capsys : CaptureFixture ,
71
+ test_file_index : int ,
72
+ local_config_args : list [str ],
73
+ start_dir_modificator : str ,
74
+ ) -> None :
75
+ """Test that each checked file or module uses config
76
+ from its own directory.
77
+ """
78
+ level1_dir , * tmp_files = _create_subconfig_test_fs
79
+ test_file = tmp_files [test_file_index ]
80
+ start_dir = (level1_dir / start_dir_modificator ).resolve ()
81
+
82
+ orig_cwd = os .getcwd ()
83
+ output = [f"{ start_dir = } , { test_file = } " ]
84
+ os .chdir (start_dir )
85
+ for _ in range (2 ):
86
+ # _Run adds --rcfile, which overrides config from cwd, so we need original Run here
87
+ LintRun ([* local_config_args , str (test_file )], exit = False )
88
+ output .append (capsys .readouterr ().out .replace ("\\ n" , "\n " ))
89
+
90
+ test_file = test_file .parent
91
+ os .chdir (orig_cwd )
92
+
93
+ expected_note = f"LEVEL{ (test_file_index % 2 )+ 1 } "
94
+ assert (
95
+ expected_note in output [1 ]
96
+ ), f"readable debug output:\n { output [0 ]} \n { output [1 ]} "
97
+ assert (
98
+ expected_note in output [2 ]
99
+ ), f"readable debug output:\n { output [0 ]} \n { output [2 ]} "
100
+
101
+ if test_file_index == 0 :
102
+ # 'pylint level1_dir/' should use config from subpackage when checking level1_dir/sub/b.py
103
+ assert (
104
+ "LEVEL2" in output [2 ]
105
+ ), f"readable debug output:\n { output [0 ]} \n { output [2 ]} "
106
+ if test_file_index == 1 :
107
+ # 'pylint level1_dir/sub/b.py' and 'pylint level1_dir/sub/' should use
108
+ # level1_dir/sub/pylintrc, not level1_dir/pylintrc
109
+ assert (
110
+ "LEVEL1" not in output [1 ]
111
+ ), f"readable debug output:\n { output [0 ]} \n { output [1 ]} "
112
+ assert (
113
+ "LEVEL1" not in output [2 ]
114
+ ), f"readable debug output:\n { output [0 ]} \n { output [2 ]} "
115
+
116
+
117
+ @pytest .mark .parametrize ("test_file_index" , [0 , 1 ])
118
+ def test_subconfig_vs_cli_arg (
119
+ _create_subconfig_test_fs : tuple [Path , ...],
120
+ capsys : CaptureFixture ,
121
+ test_file_index : int ,
122
+ ) -> None :
123
+ """Test that cli args have priority over subconfigs."""
124
+ test_root , * tmp_files = _create_subconfig_test_fs
125
+ test_file = tmp_files [test_file_index ]
126
+ orig_cwd = os .getcwd ()
127
+ os .chdir (test_root )
128
+ LintRun (["--notes=FIXME" , "--use-local-configs=y" , str (test_file )], exit = False )
129
+ output = capsys .readouterr ().out .replace ("\\ n" , "\n " )
130
+ os .chdir (orig_cwd )
131
+
132
+ # check that cli arg overrides default value
133
+ assert "TODO" not in output
134
+ # notes=FIXME in cli should override all pylintrc configs
135
+ assert "ALL_LEVELS" not in output
136
+
137
+
138
+ def _create_parent_subconfig_fs (tmp_path : Path ) -> Path :
139
+ level1_dir = tmp_path / "package"
140
+ conf_file = level1_dir / "pylintrc"
141
+ subdir = level1_dir / "sub"
142
+ test_file = subdir / "b.py"
143
+ os .makedirs (subdir )
144
+ test_file_text = "#LEVEL1\n #LEVEL2\n #TODO\n "
145
+ test_file .write_text (test_file_text )
146
+ conf = "[MISCELLANEOUS]\n notes=LEVEL1,LEVEL2"
147
+ conf_file .write_text (conf )
148
+ return test_file
149
+
150
+
151
+ def test_subconfig_in_parent (tmp_path : Path , capsys : CaptureFixture ) -> None :
152
+ """Test that searching local configs in parent directories works."""
153
+ test_file = _create_parent_subconfig_fs (tmp_path )
154
+ orig_cwd = os .getcwd ()
155
+ os .chdir (tmp_path )
156
+ LintRun (
157
+ ["--use-parent-configs=y" , "--use-local-configs=y" , str (test_file )], exit = False
158
+ )
159
+ output1 = capsys .readouterr ().out .replace ("\\ n" , "\n " )
160
+ os .chdir (orig_cwd )
161
+
162
+ # check that file is linted with config from ../, which is not a cwd
163
+ assert "TODO" not in output1
164
+ assert "LEVEL1" in output1
0 commit comments