Skip to content

Commit 807904a

Browse files
committedOct 11, 2021
Drop support for Home and XDG config files
This has been a huge support burden for us. I seriously considered doing this in 3.0 but caved to a vocal minority and the desire to keep as much backwards compatibility as possible. At this point, however, I'm done witnessing the abuse Anthony has to suffer over this and I'm done with the undue hostility that people who don't bother to read the docs display. Hopefully, this eases that a bit.
1 parent 0b1c790 commit 807904a

File tree

6 files changed

+49
-157
lines changed

6 files changed

+49
-157
lines changed
 

‎docs/source/internal/option_handling.rst

+11-20
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,8 @@ In |Flake8| 2, configuration file discovery and management was handled by
129129
pep8. In pep8's 1.6 release series, it drastically broke how discovery and
130130
merging worked (as a result of trying to improve it). To avoid a dependency
131131
breaking |Flake8| again in the future, we have created our own discovery and
132-
management.
133-
As part of managing this ourselves, we decided to change management/discovery
134-
for 3.0.0. We have done the following:
135-
136-
- User files (files stored in a user's home directory or in the XDG directory
137-
inside their home directory) are the first files read. For example, if the
138-
user has a ``~/.flake8`` file, we will read that first.
132+
management in 3.0.0. In 4.0.0 we have once again changed how this works and we
133+
removed support for user-level config files.
139134

140135
- Project files (files stored in the current directory) are read next and
141136
merged on top of the user file. In other words, configuration in project
@@ -157,7 +152,7 @@ To facilitate the configuration file management, we've taken a different
157152
approach to discovery and management of files than pep8. In pep8 1.5, 1.6, and
158153
1.7 configuration discovery and management was centralized in `66 lines of
159154
very terse python`_ which was confusing and not very explicit. The terseness
160-
of this function (|Flake8|'s authors believe) caused the confusion and
155+
of this function (|Flake8| 3.0.0's authors believe) caused the confusion and
161156
problems with pep8's 1.6 series. As such, |Flake8| has separated out
162157
discovery, management, and merging into a module to make reasoning about each
163158
of these pieces easier and more explicit (as well as easier to test).
@@ -176,23 +171,19 @@ to parse those configuration files.
176171
.. note:: ``local_config_files`` also filters out non-existent files.
177172

178173
Configuration file merging and managemnt is controlled by the
179-
:class:`~flake8.options.config.MergedConfigParser`. This requires the instance
174+
:class:`~flake8.options.config.ConfigParser`. This requires the instance
180175
of :class:`~flake8.options.manager.OptionManager` that the program is using,
181176
the list of appended config files, and the list of extra arguments. This
182177
object is currently the sole user of the
183178
:class:`~flake8.options.config.ConfigFileFinder` object. It appropriately
184179
initializes the object and uses it in each of
185180

186-
- :meth:`~flake8.options.config.MergedConfigParser.parse_cli_config`
187-
- :meth:`~flake8.options.config.MergedConfigParser.parse_local_config`
188-
- :meth:`~flake8.options.config.MergedConfigParser.parse_user_config`
181+
- :meth:`~flake8.options.config.ConfigParser.parse_cli_config`
182+
- :meth:`~flake8.options.config.ConfigParser.parse_local_config`
189183

190-
Finally,
191-
:meth:`~flake8.options.config.MergedConfigParser.merge_user_and_local_config`
192-
takes the user and local configuration files that are parsed by
193-
:meth:`~flake8.options.config.MergedConfigParser.parse_local_config` and
194-
:meth:`~flake8.options.config.MergedConfigParser.parse_user_config`. The
195-
main usage of the ``MergedConfigParser`` is in
184+
Finally, :meth:`~flake8.options.config.ConfigParser.parse` returns the
185+
appropriate configuration dictionary for this execution of |Flake8|. The
186+
main usage of the ``ConfigParser`` is in
196187
:func:`~flake8.options.aggregator.aggregate_options`.
197188

198189
Aggregating Configuration File and Command Line Arguments
@@ -201,7 +192,7 @@ Aggregating Configuration File and Command Line Arguments
201192
:func:`~flake8.options.aggregator.aggregate_options` accepts an instance of
202193
:class:`~flake8.options.manager.OptionManager` and does the work to parse the
203194
command-line arguments passed by the user necessary for creating an instance
204-
of :class:`~flake8.options.config.MergedConfigParser`.
195+
of :class:`~flake8.options.config.ConfigParser`.
205196

206197
After parsing the configuration file, we determine the default ignore list. We
207198
use the defaults from the OptionManager and update those with the parsed
@@ -229,6 +220,6 @@ API Documentation
229220
:members:
230221
:special-members:
231222

232-
.. autoclass:: flake8.options.config.MergedConfigParser
223+
.. autoclass:: flake8.options.config.ConfigParser
233224
:members:
234225
:special-members:

‎docs/source/release-notes/4.0.0.rst

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
4.0.0 -- 202x-mm-dd
2+
-------------------
3+
4+
You can view the `4.0.0 milestone`_ on GitHub for more details.
5+
6+
Backwards Incompatible Changes
7+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
9+
- Due to constant confusion by users, user-level |Flake8| configuration files
10+
are no longer supported. Files will not be searched for in the user's home
11+
directory (e.g., ``~/.flake8``) nor in the XDG config directory (e.g.,
12+
``~/.config/flake8``).
13+
14+
.. all links
15+
.. _4.0.0 milestone:
16+
https://github.com/PyCQA/flake8/milestone/39

‎docs/source/release-notes/index.rst

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
All of the release notes that have been recorded for Flake8 are organized here
66
with the newest releases first.
77

8+
4.x Release Series
9+
==================
10+
11+
.. toctree::
12+
4.0.0
13+
814
3.x Release Series
915
==================
1016

‎src/flake8/options/aggregator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def aggregate_options(
3838
default_values, _ = manager.parse_args([])
3939

4040
# Make our new configuration file mergerator
41-
config_parser = config.MergedConfigParser(
41+
config_parser = config.ConfigParser(
4242
option_manager=manager, config_finder=config_finder
4343
)
4444

‎src/flake8/options/config.py

+5-59
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
LOG = logging.getLogger(__name__)
1313

14-
__all__ = ("ConfigFileFinder", "MergedConfigParser")
14+
__all__ = ("ConfigFileFinder", "ConfigParser")
1515

1616

1717
class ConfigFileFinder:
@@ -48,26 +48,12 @@ def __init__(
4848

4949
# User configuration file.
5050
self.program_name = program_name
51-
self.user_config_file = self._user_config_file(program_name)
5251

5352
# List of filenames to find in the local/project directory
5453
self.project_filenames = ("setup.cfg", "tox.ini", f".{program_name}")
5554

5655
self.local_directory = os.path.abspath(os.curdir)
5756

58-
@staticmethod
59-
def _user_config_file(program_name: str) -> str:
60-
if utils.is_windows():
61-
home_dir = os.path.expanduser("~")
62-
config_file_basename = f".{program_name}"
63-
else:
64-
home_dir = os.environ.get(
65-
"XDG_CONFIG_HOME", os.path.expanduser("~/.config")
66-
)
67-
config_file_basename = program_name
68-
69-
return os.path.join(home_dir, config_file_basename)
70-
7157
@staticmethod
7258
def _read_config(
7359
*files: str,
@@ -146,15 +132,8 @@ def local_configs(self):
146132
"""Parse all local config files into one config object."""
147133
return self.local_configs_with_files()[0]
148134

149-
def user_config(self):
150-
"""Parse the user config file into a config object."""
151-
config, found_files = self._read_config(self.user_config_file)
152-
if found_files:
153-
LOG.debug("Found user configuration files: %s", found_files)
154-
return config
155-
156135

157-
class MergedConfigParser:
136+
class ConfigParser:
158137
"""Encapsulate merging different types of configuration files.
159138
160139
This parses out the options registered that were specified in the
@@ -167,7 +146,7 @@ class MergedConfigParser:
167146
GETBOOL_ACTIONS = {"store_true", "store_false"}
168147

169148
def __init__(self, option_manager, config_finder):
170-
"""Initialize the MergedConfigParser instance.
149+
"""Initialize the ConfigParser instance.
171150
172151
:param flake8.options.manager.OptionManager option_manager:
173152
Initialized OptionManager.
@@ -239,19 +218,6 @@ def parse_local_config(self):
239218
LOG.debug("Parsing local configuration files.")
240219
return self._parse_config(config)
241220

242-
def parse_user_config(self):
243-
"""Parse and return the user configuration files."""
244-
config = self.config_finder.user_config()
245-
if not self.is_configured_by(config):
246-
LOG.debug(
247-
"User configuration files have no %s section",
248-
self.program_name,
249-
)
250-
return {}
251-
252-
LOG.debug("Parsing user configuration files.")
253-
return self._parse_config(config)
254-
255221
def parse_cli_config(self, config_path):
256222
"""Parse and return the file specified by --config."""
257223
config = self.config_finder.cli_config(config_path)
@@ -265,28 +231,8 @@ def parse_cli_config(self, config_path):
265231
LOG.debug("Parsing CLI configuration files.")
266232
return self._parse_config(config, os.path.dirname(config_path))
267233

268-
def merge_user_and_local_config(self):
269-
"""Merge the parsed user and local configuration files.
270-
271-
:returns:
272-
Dictionary of the parsed and merged configuration options.
273-
:rtype:
274-
dict
275-
"""
276-
user_config = self.parse_user_config()
277-
config = self.parse_local_config()
278-
279-
for option, value in user_config.items():
280-
config.setdefault(option, value)
281-
282-
return config
283-
284234
def parse(self):
285-
"""Parse and return the local and user config files.
286-
287-
First this copies over the parsed local configuration and then
288-
iterates over the options in the user configuration and sets them if
289-
they were not set by the local configuration file.
235+
"""Parse and return the local config files.
290236
291237
:returns:
292238
Dictionary of parsed configuration options
@@ -309,7 +255,7 @@ def parse(self):
309255
)
310256
return self.parse_cli_config(self.config_finder.config_file)
311257

312-
return self.merge_user_and_local_config()
258+
return self.parse_local_config()
313259

314260

315261
def get_local_plugins(config_finder):

‎tests/unit/test_merged_config_parser.py ‎tests/unit/test_config_parser.py

+10-77
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Unit tests for flake8.options.config.MergedConfigParser."""
1+
"""Unit tests for flake8.options.config.ConfigParser."""
22
import os
33
from unittest import mock
44

@@ -32,7 +32,7 @@ def test_parse_cli_config(optmanager, config_finder):
3232
"--ignore", parse_from_config=True, comma_separated_list=True
3333
)
3434
optmanager.add_option("--quiet", parse_from_config=True, action="count")
35-
parser = config.MergedConfigParser(optmanager, config_finder)
35+
parser = config.ConfigParser(optmanager, config_finder)
3636

3737
config_file = "tests/fixtures/config_files/cli-specified.ini"
3838
parsed_config = parser.parse_cli_config(config_file)
@@ -61,41 +61,11 @@ def test_is_configured_by(
6161
):
6262
"""Verify the behaviour of the is_configured_by method."""
6363
parsed_config, _ = config.ConfigFileFinder._read_config(filename)
64-
parser = config.MergedConfigParser(optmanager, config_finder)
64+
parser = config.ConfigParser(optmanager, config_finder)
6565

6666
assert parser.is_configured_by(parsed_config) is is_configured_by
6767

6868

69-
def test_parse_user_config(optmanager, config_finder):
70-
"""Verify parsing of user config files."""
71-
optmanager.add_option(
72-
"--exclude",
73-
parse_from_config=True,
74-
comma_separated_list=True,
75-
normalize_paths=True,
76-
)
77-
optmanager.add_option(
78-
"--ignore", parse_from_config=True, comma_separated_list=True
79-
)
80-
optmanager.add_option("--quiet", parse_from_config=True, action="count")
81-
parser = config.MergedConfigParser(optmanager, config_finder)
82-
83-
config_finder.user_config_file = (
84-
"tests/fixtures/config_files/" "cli-specified.ini"
85-
)
86-
parsed_config = parser.parse_user_config()
87-
88-
assert parsed_config == {
89-
"ignore": ["E123", "W234", "E111"],
90-
"exclude": [
91-
os.path.abspath("foo/"),
92-
os.path.abspath("bar/"),
93-
os.path.abspath("bogus/"),
94-
],
95-
"quiet": 1,
96-
}
97-
98-
9969
def test_parse_local_config(optmanager, config_finder):
10070
"""Verify parsing of local config files."""
10171
optmanager.add_option(
@@ -108,7 +78,7 @@ def test_parse_local_config(optmanager, config_finder):
10878
"--ignore", parse_from_config=True, comma_separated_list=True
10979
)
11080
optmanager.add_option("--quiet", parse_from_config=True, action="count")
111-
parser = config.MergedConfigParser(optmanager, config_finder)
81+
parser = config.ConfigParser(optmanager, config_finder)
11282

11383
with mock.patch.object(config_finder, "local_config_files") as localcfs:
11484
localcfs.return_value = [
@@ -127,47 +97,14 @@ def test_parse_local_config(optmanager, config_finder):
12797
}
12898

12999

130-
def test_merge_user_and_local_config(optmanager, config_finder):
131-
"""Verify merging of parsed user and local config files."""
132-
optmanager.add_option(
133-
"--exclude",
134-
parse_from_config=True,
135-
comma_separated_list=True,
136-
normalize_paths=True,
137-
)
138-
optmanager.add_option(
139-
"--ignore", parse_from_config=True, comma_separated_list=True
140-
)
141-
optmanager.add_option(
142-
"--select", parse_from_config=True, comma_separated_list=True
143-
)
144-
parser = config.MergedConfigParser(optmanager, config_finder)
145-
146-
with mock.patch.object(config_finder, "local_config_files") as localcfs:
147-
localcfs.return_value = [
148-
"tests/fixtures/config_files/local-config.ini"
149-
]
150-
config_finder.user_config_file = (
151-
"tests/fixtures/config_files/" "user-config.ini"
152-
)
153-
parsed_config = parser.merge_user_and_local_config()
154-
155-
assert parsed_config == {
156-
"exclude": [os.path.abspath("docs/")],
157-
"ignore": ["D203"],
158-
"select": ["E", "W", "F"],
159-
}
160-
161-
162100
def test_parse_isolates_config(optmanager):
163101
"""Verify behaviour of the parse method with isolated=True."""
164102
config_finder = mock.MagicMock()
165103
config_finder.ignore_config_files = True
166-
parser = config.MergedConfigParser(optmanager, config_finder)
104+
parser = config.ConfigParser(optmanager, config_finder)
167105

168106
assert parser.parse() == {}
169107
assert config_finder.local_configs.called is False
170-
assert config_finder.user_config.called is False
171108

172109

173110
def test_parse_uses_cli_config(optmanager):
@@ -176,7 +113,7 @@ def test_parse_uses_cli_config(optmanager):
176113
config_finder = mock.MagicMock()
177114
config_finder.config_file = config_file_value
178115
config_finder.ignore_config_files = False
179-
parser = config.MergedConfigParser(optmanager, config_finder)
116+
parser = config.ConfigParser(optmanager, config_finder)
180117

181118
parser.parse()
182119
config_finder.cli_config.assert_called_once_with(config_file_value)
@@ -206,13 +143,11 @@ def test_parsed_configs_are_equivalent(
206143
optmanager.add_option(
207144
"--ignore", parse_from_config=True, comma_separated_list=True
208145
)
209-
parser = config.MergedConfigParser(optmanager, config_finder)
146+
parser = config.ConfigParser(optmanager, config_finder)
210147

211148
with mock.patch.object(config_finder, "local_config_files") as localcfs:
212149
localcfs.return_value = [config_fixture_path]
213-
with mock.patch.object(config_finder, "user_config_file") as usercf:
214-
usercf.return_value = ""
215-
parsed_config = parser.merge_user_and_local_config()
150+
parsed_config = parser.parse()
216151

217152
assert parsed_config["ignore"] == ["E123", "W234", "E111"]
218153
assert parsed_config["exclude"] == [
@@ -243,13 +178,11 @@ def test_parsed_hyphenated_and_underscored_names(
243178
parse_from_config=True,
244179
comma_separated_list=True,
245180
)
246-
parser = config.MergedConfigParser(optmanager, config_finder)
181+
parser = config.ConfigParser(optmanager, config_finder)
247182

248183
with mock.patch.object(config_finder, "local_config_files") as localcfs:
249184
localcfs.return_value = [config_file]
250-
with mock.patch.object(config_finder, "user_config_file") as usercf:
251-
usercf.return_value = ""
252-
parsed_config = parser.merge_user_and_local_config()
185+
parsed_config = parser.parse()
253186

254187
assert parsed_config["max_line_length"] == 110
255188
assert parsed_config["enable_extensions"] == ["H101", "H235"]

0 commit comments

Comments
 (0)
Please sign in to comment.