diff --git a/.gitignore b/.gitignore index 249499b135..0acfdf80c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,8 @@ .venv -.coverage -.coverage.* _build .DS_Store .vscode docs/_static/pypi.svg -.tox __pycache__ # Packaging artifacts @@ -21,6 +18,13 @@ src/_black_version.py .dmypy.json *.swp -.hypothesis/ venv/ .ipynb_checkpoints/ + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.hypothesis/ +.pytest_cache/ diff --git a/CHANGES.md b/CHANGES.md index b2e8f7439b..e78afb9ca2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ - Add partial support for the match statement. As it's experimental, it's only enabled when `--target-version py310` is explicitly specified (#2586) - Add support for parenthesized with (#2586) +- Allow specifying `config` in config files (#2525) ## 21.10b0 @@ -16,6 +17,7 @@ - Document stability policy, that will apply for non-beta releases (#2529) - Add new `--workers` parameter (#2514) +- Bumped typed-ast version minimum to 1.4.3 for 3.10 compatibility (#2519) - Fixed feature detection for positional-only arguments in lambdas (#2532) - Bumped typed-ast version minimum to 1.4.3 for 3.10 compatibility (#2519) - Fixed a Python 3.10 compatibility issue where the loop argument was still being passed diff --git a/src/black/__init__.py b/src/black/__init__.py index ad4ee1a0d1..997cb6ed5f 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -120,28 +120,30 @@ def read_pyproject_toml( if not config: return None - else: - # Sanitize the values to be Click friendly. For more information please see: - # https://github.com/psf/black/issues/1458 - # https://github.com/pallets/click/issues/1567 - config = { - k: str(v) if not isinstance(v, (list, dict)) else v - for k, v in config.items() - } - - target_version = config.get("target_version") + + if ctx.default_map: + config.update(ctx.default_map) + + black_config = config.get("config") + if black_config: + black_config_path = Path(Path(value).parent, black_config).resolve() + return read_pyproject_toml(ctx, param, str(black_config_path)) + + # Sanitize the values to be Click friendly. For more information please see: + # https://github.com/psf/black/issues/1458 + # https://github.com/pallets/click/issues/1567 + default_map: Dict[str, Any] = { + k: str(v) if not isinstance(v, (list, dict)) else v for k, v in config.items() + } + + target_version = default_map.get("target_version") if target_version is not None and not isinstance(target_version, list): raise click.BadOptionUsage( "target-version", "Config key target-version must be a list" ) - default_map: Dict[str, Any] = {} - if ctx.default_map: - default_map.update(ctx.default_map) - default_map.update(config) - ctx.default_map = default_map - return value + return str(value) def target_version_option_callback( diff --git a/tests/_black_config.toml b/tests/_black_config.toml new file mode 100644 index 0000000000..175fb41770 --- /dev/null +++ b/tests/_black_config.toml @@ -0,0 +1,9 @@ +[tool.black] +verbose = 1 +--check = "no" +diff = "y" +color = true +line-length = 88 +target-version = ["py36", "py37", "py38"] +exclude='\.pyi?$' +include='\.py?$' diff --git a/tests/invalid_test.toml b/tests/invalid_test.toml new file mode 100644 index 0000000000..8c92ada4f6 --- /dev/null +++ b/tests/invalid_test.toml @@ -0,0 +1,2 @@ +[tool.black] +config = "tests/bazqux.toml" diff --git a/tests/test_black.py b/tests/test_black.py index 7dbc3809d2..01175e868f 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1296,6 +1296,28 @@ def test_read_pyproject_toml(self) -> None: self.assertEqual(config["exclude"], r"\.pyi?$") self.assertEqual(config["include"], r"\.py?$") + def test_black_replace_config(self) -> None: + test_toml_file = THIS_DIR / "test_replace.toml" + fake_ctx = FakeContext() + black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file)) + config = fake_ctx.default_map + # `0` in `test_replace.toml` and `1` in `_black_config.toml`. Should be `1` as + # the one linked should overwrite the config + self.assertEqual(config["verbose"], "1") + self.assertEqual(config["check"], "no") + self.assertEqual(config["diff"], "y") + self.assertEqual(config["color"], "True") + self.assertEqual(config["line_length"], "88") + self.assertEqual(config["target_version"], ["py36", "py37", "py38"]) + self.assertEqual(config["exclude"], r"\.pyi?$") + self.assertEqual(config["include"], r"\.py?$") + + def test_invalid_black_config(self) -> None: + test_toml_file = THIS_DIR / "invalid_test.toml" + fake_ctx = FakeContext() + with self.assertRaises(click.exceptions.FileError): + black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file)) + def test_find_project_root(self) -> None: with TemporaryDirectory() as workspace: root = Path(workspace) diff --git a/tests/test_replace.toml b/tests/test_replace.toml new file mode 100644 index 0000000000..0541876a9d --- /dev/null +++ b/tests/test_replace.toml @@ -0,0 +1,4 @@ +[tool.black] +config = "_black_config.toml" +verbose = 0 +color = false