diff --git a/blackdoc/__main__.py b/blackdoc/__main__.py index 9f80284..1e2d3ed 100644 --- a/blackdoc/__main__.py +++ b/blackdoc/__main__.py @@ -5,7 +5,7 @@ import black from . import __version__, format_lines, formats -from .blackcompat import gen_python_files +from .blackcompat import find_project_root, gen_python_files, read_pyproject_toml def check_format_names(string): @@ -22,7 +22,7 @@ def check_format_names(string): def collect_files(src, include, exclude): - root = black.find_project_root(tuple(src)) + root = find_project_root(tuple(src)) gitignore = black.get_gitignore(root) report = black.Report() @@ -302,6 +302,14 @@ def main(): help="Show the version and exit.", version=f"{program} {__version__}", ) + parser.add_argument( + "--config", + action="store", + nargs=1, + type=pathlib.Path, + default=None, + help="Read configuration from FILE path.", + ) parser.add_argument( "src", action="store", @@ -310,6 +318,10 @@ def main(): default=None, help="one or more paths to work on", ) + args = parser.parse_args() + if args.config or args.src: + file_defaults = read_pyproject_toml(tuple(args.src), args.config) + parser.set_defaults(**file_defaults) args = parser.parse_args() sys.exit(process(args)) diff --git a/blackdoc/blackcompat.py b/blackdoc/blackcompat.py index 2030036..3b35c51 100644 --- a/blackdoc/blackcompat.py +++ b/blackdoc/blackcompat.py @@ -3,8 +3,84 @@ For the license, see /licenses/black """ +from functools import lru_cache from pathlib import Path +import toml + + +@lru_cache() +def find_project_root(srcs): + """Return a directory containing .git, .hg, or pyproject.toml. + + That directory will be a common parent of all files and directories + passed in `srcs`. + + If no directory in the tree contains a marker that would specify it's the + project root, the root of the file system is returned. + """ + if not srcs: + return Path("/").resolve() + + path_srcs = [Path(Path.cwd(), src).resolve() for src in srcs] + + # A list of lists of parents for each 'src'. 'src' is included as a + # "parent" of itself if it is a directory + src_parents = [ + list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs + ] + + common_base = max( + set.intersection(*(set(parents) for parents in src_parents)), + key=lambda path: path.parts, + ) + + for directory in (common_base, *common_base.parents): + if (directory / ".git").exists(): + return directory + + if (directory / ".hg").is_dir(): + return directory + + if (directory / "pyproject.toml").is_file(): + return directory + + return directory + + +def find_pyproject_toml(path_search_start): + """Find the absolute filepath to a pyproject.toml if it exists""" + path_project_root = find_project_root(path_search_start) + path_pyproject_toml = path_project_root / "pyproject.toml" + return str(path_pyproject_toml) if path_pyproject_toml.is_file() else None + + +def parse_pyproject_toml(path_config): + """Parse a pyproject toml file, pulling out relevant parts for Black + + If parsing fails, will raise a toml.TomlDecodeError + """ + pyproject_toml = toml.load(path_config) + config = pyproject_toml.get("tool", {}).get("black", {}) + return {k.replace("--", "").replace("-", "_"): v for k, v in config.items()} + + +def read_pyproject_toml(source, config_path): + if not config_path: + config_path = find_pyproject_toml(source) + if config_path is None: + return {} + + try: + config = parse_pyproject_toml(config_path) + except (toml.TomlDecodeError, OSError) as e: + raise IOError(f"Error reading configuration file ({config_path}): {e}") + + if not config: + return {} + + return config + def normalize_path_maybe_ignore(path, root, report): """Normalize `path`. May return `None` if `path` was ignored. diff --git a/doc/changelog.rst b/doc/changelog.rst index b1099b9..9ec9334 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -7,6 +7,7 @@ v0.2 (*unreleased*) :rst:dir:`testcleanup` directives (:pull:`39`). - fix working with lines containing only the prompt and avoid changing the quotes of nested docstrings (:issue:`41`, :pull:`43`) +- allow configuring the use of ``black`` using ``pyproject.toml`` (:issue:`40`, :pull:`45`) v0.1.2 (31 August 2020) diff --git a/pyproject.toml b/pyproject.toml index 1cd983f..8259dee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,3 +3,6 @@ requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] + +[tool.isort] +profile = "black" \ No newline at end of file