]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Introduce "project root" as a concept
authorŁukasz Langa <lukasz@langa.pl>
Mon, 4 Jun 2018 17:59:36 +0000 (10:59 -0700)
committerŁukasz Langa <lukasz@langa.pl>
Mon, 4 Jun 2018 18:03:18 +0000 (11:03 -0700)
This is required for regular expressions in `--include=` and `--exclude=` not
to catch false positives from directories outside of the project.

black.py
tests/test_black.py

index da00525ca945e6443aa61ffe82a1f93ad0061e71..730c64de0b1883ff42e906d90d6985d4ddec9b51 100644 (file)
--- a/black.py
+++ b/black.py
@@ -253,10 +253,13 @@ def main(
     except re.error:
         err(f"Invalid regular expression for exclude given: {exclude!r}")
         ctx.exit(2)
+    root = find_project_root(src)
     for s in src:
         p = Path(s)
         if p.is_dir():
-            sources.extend(gen_python_files_in_dir(p, include_regex, exclude_regex))
+            sources.extend(
+                gen_python_files_in_dir(p, root, include_regex, exclude_regex)
+            )
         elif p.is_file():
             # if a file was explicitly given, we don't care about its extension
             sources.append(p)
@@ -2792,13 +2795,14 @@ def get_future_imports(node: Node) -> Set[str]:
 
 
 def gen_python_files_in_dir(
-    path: Path, include: Pattern[str], exclude: Pattern[str]
+    path: Path, root: Path, include: Pattern[str], exclude: Pattern[str]
 ) -> Iterator[Path]:
     """Generate all files under `path` whose paths are not excluded by the
     `exclude` regex, but are included by the `include` regex.
     """
+    assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
     for child in path.iterdir():
-        normalized_path = child.resolve().as_posix()
+        normalized_path = child.resolve().relative_to(root).as_posix()
         if child.is_dir():
             normalized_path += "/"
         exclude_match = exclude.search(normalized_path)
@@ -2806,7 +2810,7 @@ def gen_python_files_in_dir(
             continue
 
         if child.is_dir():
-            yield from gen_python_files_in_dir(child, include, exclude)
+            yield from gen_python_files_in_dir(child, root, include, exclude)
 
         elif child.is_file():
             include_match = include.search(normalized_path)
@@ -2814,6 +2818,35 @@ def gen_python_files_in_dir(
                 yield child
 
 
+def find_project_root(srcs: List[str]) -> Path:
+    """Return a directory containing .git, .hg, or pyproject.toml.
+
+    That directory can be one of the directories passed in `srcs` or their
+    common parent.
+
+    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()
+
+    common_base = min(Path(src).resolve() for src in srcs)
+    if common_base.is_dir():
+        # Append a fake file so `parents` below returns `common_base_dir`, too.
+        common_base /= "fake-file"
+    for directory in common_base.parents:
+        if (directory / ".git").is_dir():
+            return directory
+
+        if (directory / ".hg").is_dir():
+            return directory
+
+        if (directory / "pyproject.toml").is_file():
+            return directory
+
+    return directory
+
+
 @dataclass
 class Report:
     """Provides a reformatting counter. Can be rendered with `str(report)`."""
index 7e50c2fca74daa54578fa1d9c7a5f4c221de374d..7389da9417fbfb8a03df7d1dbf49c59a8678616c 100644 (file)
@@ -864,7 +864,8 @@ class BlackTestCase(unittest.TestCase):
             Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.py"),
             Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.pyi"),
         ]
-        sources.extend(black.gen_python_files_in_dir(path, include, exclude))
+        this_abs = THIS_DIR.resolve()
+        sources.extend(black.gen_python_files_in_dir(path, this_abs, include, exclude))
         self.assertEqual(sorted(expected), sorted(sources))
 
     def test_empty_include(self) -> None:
@@ -882,9 +883,10 @@ class BlackTestCase(unittest.TestCase):
             Path(path / "b/.definitely_exclude/a.py"),
             Path(path / "b/.definitely_exclude/a.pyi"),
         ]
+        this_abs = THIS_DIR.resolve()
         sources.extend(
             black.gen_python_files_in_dir(
-                path, empty, re.compile(black.DEFAULT_EXCLUDES)
+                path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES)
             )
         )
         self.assertEqual(sorted(expected), sorted(sources))
@@ -901,9 +903,10 @@ class BlackTestCase(unittest.TestCase):
             Path(path / "b/.definitely_exclude/a.py"),
             Path(path / "b/.definitely_exclude/a.pyi"),
         ]
+        this_abs = THIS_DIR.resolve()
         sources.extend(
             black.gen_python_files_in_dir(
-                path, re.compile(black.DEFAULT_INCLUDES), empty
+                path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty
             )
         )
         self.assertEqual(sorted(expected), sorted(sources))