Skip to content

Commit 332ed9b

Browse files
authored
Merge pull request #359 from akaihola/config-option-for-darker-config
2 parents 557de82 + 7d10bcf commit 332ed9b

File tree

5 files changed

+338
-51
lines changed

5 files changed

+338
-51
lines changed

CHANGES.rst

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Fixed
2424
- Update ``cachix/install-nix-action`` to ``v17`` to fix macOS build error.
2525
- Downgrade Python from 3.10 to 3.9 in the macOS NixOS build on GitHub due to a build
2626
error with Python 3.10.
27+
- Darker now reads its own configuration from the file specified using
28+
``-c``/``--config``, or in case a directory is specified, from ``pyproject.toml``
29+
inside that directory.
2730

2831

2932
1.4.2_ - 2022-03-12

src/darker/command_line.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,10 @@ def parse_command_line(argv: List[str]) -> Tuple[Namespace, DarkerConfig, Darker
106106
parser_for_srcs = make_argument_parser(require_src=False)
107107
args = parser_for_srcs.parse_args(argv)
108108

109-
# 2. Locate `pyproject.toml` based on those paths, or in the current directory if no
110-
# paths were given. Load Darker configuration from it.
111-
pyproject_config = load_config(args.src)
109+
# 2. Locate `pyproject.toml` based on the `-c`/`--config` command line option, or
110+
# if it's not provided, based on the paths to process, or in the current
111+
# directory if no paths were given. Load Darker configuration from it.
112+
pyproject_config = load_config(args.config, args.src)
112113
config = override_color_with_environment(pyproject_config)
113114

114115
# 3. Use configuration as defaults for re-parsing command line arguments, and don't

src/darker/config.py

+33-15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sys
66
from argparse import ArgumentParser, Namespace
77
from pathlib import Path
8-
from typing import Iterable, List, cast
8+
from typing import Iterable, List, Optional, cast
99

1010
import toml
1111

@@ -117,23 +117,41 @@ def override_color_with_environment(pyproject_config: DarkerConfig) -> DarkerCon
117117
return config
118118

119119

120-
def load_config(srcs: Iterable[str]) -> DarkerConfig:
121-
"""Find and load Darker configuration from given path or pyproject.toml
120+
def load_config(path: Optional[str], srcs: Iterable[str]) -> DarkerConfig:
121+
"""Find and load Darker configuration from a TOML configuration file
122122
123-
:param srcs: File(s) and directory/directories which will be processed. Their paths
124-
are used to look for the ``pyproject.toml`` configuration file.
123+
Darker determines the location for the configuration file by trying the following:
124+
- the file path in the `path` argument, given using the ``-c``/``--config`` command
125+
line option
126+
- ``pyproject.toml`` inside the directory specified by the `path` argument
127+
- ``pyproject.toml`` from a common parent directory to all items in `srcs`
128+
- ``pyproject.toml`` in the current working directory if `srcs` is empty
129+
130+
:param path: The file or directory specified using the ``-c``/``--config`` command
131+
line option, or `None` if the option was omitted.
132+
:param srcs: File(s) and directory/directories to be processed by Darker.
125133
126134
"""
127-
path = find_project_root(tuple(srcs or ["."])) / "pyproject.toml"
128-
if path.is_file():
129-
pyproject_toml = toml.load(path)
130-
config = cast(
131-
DarkerConfig, pyproject_toml.get("tool", {}).get("darker", {}) or {}
132-
)
133-
replace_log_level_name(config)
134-
validate_config_output_mode(config)
135-
return config
136-
return {}
135+
if path:
136+
for candidate_path in [Path(path), Path(path, "pyproject.toml")]:
137+
if candidate_path.is_file():
138+
config_path = candidate_path
139+
break
140+
else:
141+
if Path(path).is_dir() or path.endswith(os.sep):
142+
raise ConfigurationError(
143+
f"Configuration file {Path(path, 'pyproject.toml')} not found"
144+
)
145+
raise ConfigurationError(f"Configuration file {path} not found")
146+
else:
147+
config_path = find_project_root(tuple(srcs or ["."])) / "pyproject.toml"
148+
if not config_path.is_file():
149+
return {}
150+
pyproject_toml = toml.load(config_path)
151+
config = cast(DarkerConfig, pyproject_toml.get("tool", {}).get("darker", {}) or {})
152+
replace_log_level_name(config)
153+
validate_config_output_mode(config)
154+
return config
137155

138156

139157
def get_effective_config(args: Namespace) -> DarkerConfig:

src/darker/tests/test_command_line.py

+45
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,36 @@ def test_parse_command_line_config_src(
9191
assert filter_dict(dict(modified_cfg), "src") == expect
9292

9393

94+
@pytest.mark.kwparametrize(
95+
dict(argv=["."], expect="pylint"),
96+
dict(argv=["./subdir/"], expect="flake8"),
97+
dict(argv=["--config", "./pyproject.toml", "."], expect="pylint"),
98+
dict(argv=["--config", "./subdir/pyproject.toml", "."], expect="flake8"),
99+
dict(argv=["--config", "./pyproject.toml", "subdir/"], expect="pylint"),
100+
dict(argv=["--config", "./subdir/pyproject.toml", "subdir/"], expect="flake8"),
101+
)
102+
def test_parse_command_line_config_location_specified(
103+
tmp_path,
104+
monkeypatch,
105+
argv,
106+
expect,
107+
):
108+
"""Darker configuration is read from file pointed to using ``-c``/``--config``"""
109+
monkeypatch.chdir(tmp_path)
110+
subdir = tmp_path / "subdir"
111+
subdir.mkdir()
112+
root_config = tmp_path / "pyproject.toml"
113+
subdir_config = subdir / "pyproject.toml"
114+
root_config.write_text(toml.dumps({"tool": {"darker": {"lint": "pylint"}}}))
115+
subdir_config.write_text(toml.dumps({"tool": {"darker": {"lint": "flake8"}}}))
116+
117+
args, effective_cfg, modified_cfg = parse_command_line(argv)
118+
119+
assert args.lint == expect
120+
assert effective_cfg["lint"] == expect
121+
assert modified_cfg["lint"] == expect
122+
123+
94124
@pytest.mark.kwparametrize(
95125
dict(
96126
argv=["."],
@@ -212,6 +242,18 @@ def test_parse_command_line_config_src(
212242
expect_config=("config", "my.cfg"),
213243
expect_modified=("config", "my.cfg"),
214244
),
245+
dict(
246+
argv=["-c", "subdir_with_config", "."],
247+
expect_value=("config", "subdir_with_config"),
248+
expect_config=("config", "subdir_with_config"),
249+
expect_modified=("config", "subdir_with_config"),
250+
),
251+
dict(
252+
argv=["--config=subdir_with_config", "."],
253+
expect_value=("config", "subdir_with_config"),
254+
expect_config=("config", "subdir_with_config"),
255+
expect_modified=("config", "subdir_with_config"),
256+
),
215257
dict(
216258
argv=["."],
217259
expect_value=("log_level", 30),
@@ -375,6 +417,9 @@ def test_parse_command_line(
375417
"""``parse_command_line()`` parses options correctly"""
376418
monkeypatch.chdir(tmp_path)
377419
(tmp_path / "dummy.py").touch()
420+
(tmp_path / "my.cfg").touch()
421+
(tmp_path / "subdir_with_config").mkdir()
422+
(tmp_path / "subdir_with_config" / "pyproject.toml").touch()
378423
with patch.dict(os.environ, environ, clear=True), raises_if_exception(
379424
expect_value
380425
) as expect_exception:

0 commit comments

Comments
 (0)