Simplify check for symlinks that resolve outside root (#4221) · psf/black@a201003 (original) (raw)

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -50,9 +50,9 @@
50 50 gen_python_files,
51 51 get_gitignore,
52 52 get_root_relative_path,
53 -normalize_path_maybe_ignore,
54 53 parse_pyproject_toml,
55 54 path_is_excluded,
55 +resolves_outside_root_or_cannot_stat,
56 56 wrap_stream_for_windows,
57 57 )
58 58 from black.handle_ipynb_magics import (
@@ -763,12 +763,9 @@ def get_sources(
763 763 )
764 764 continue
765 765
766 -normalized_path: Optional[str] = normalize_path_maybe_ignore(
767 -path, root, report
768 - )
769 -if normalized_path is None:
766 +if resolves_outside_root_or_cannot_stat(path, root, report):
770 767 if verbose:
771 -out(f'Skipping invalid source: "{normalized_path}"', fg="red")
768 +out(f'Skipping invalid source: "{path}"', fg="red")
772 769 continue
773 770
774 771 if is_stdin:
@@ -780,7 +777,7 @@ def get_sources(
780 777 continue
781 778
782 779 if verbose:
783 -out(f'Found input source: "{normalized_path}"', fg="blue")
780 +out(f'Found input source: "{path}"', fg="blue")
784 781 sources.add(path)
785 782 elif path.is_dir():
786 783 path = root / (path.resolve().relative_to(root))
Original file line number Diff line number Diff line change
@@ -254,26 +254,24 @@ def get_gitignore(root: Path) -> PathSpec:
254 254 raise
255 255
256 256
257 -def normalize_path_maybe_ignore(
257 +def resolves_outside_root_or_cannot_stat(
258 258 path: Path,
259 259 root: Path,
260 260 report: Optional[Report] = None,
261 -) -> Optional[str]:
262 -"""Normalize `path`. May return `None` if `path` was ignored.
263 -
264 - `report` is where "path ignored" output goes.
261 +) -> bool:
262 +"""
263 + Returns whether the path is a symbolic link that points outside the
264 + root directory. Also returns True if we failed to resolve the path.
265 265 """
266 266 try:
267 -abspath = path if path.is_absolute() else Path.cwd() / path
268 -normalized_path = abspath.resolve()
269 -root_relative_path = get_root_relative_path(normalized_path, root, report)
270 -
267 +if sys.version_info < (3, 8, 6):
268 + path = path.absolute() # https://bugs.python.org/issue33660
269 +resolved_path = path.resolve()
270 + return get_root_relative_path(resolved_path, root, report) is None
271 271 except OSError as e:
272 272 if report:
273 273 report.path_ignored(path, f"cannot be read because {e}")
274 -return None
275 -
276 -return root_relative_path
274 +return True
277 275
278 276
279 277 def get_root_relative_path(
@@ -369,8 +367,7 @@ def gen_python_files(
369 367 report.path_ignored(child, "matches the --force-exclude regular expression")
370 368 continue
371 369
372 -normalized_path = normalize_path_maybe_ignore(child, root, report)
373 -if normalized_path is None:
370 +if resolves_outside_root_or_cannot_stat(child, root, report):
374 371 continue
375 372
376 373 if child.is_dir():
Original file line number Diff line number Diff line change
@@ -1760,12 +1760,15 @@ def test_bpo_33660_workaround(self) -> None:
1760 1760 return
1761 1761
1762 1762 # https://bugs.python.org/issue33660
1763 +# Can be removed when we drop support for Python 3.8.5
1763 1764 root = Path("/")
1764 1765 with change_directory(root):
1765 1766 path = Path("workspace") / "project"
1766 1767 report = black.Report(verbose=True)
1767 -normalized_path = black.normalize_path_maybe_ignore(path, root, report)
1768 -self.assertEqual(normalized_path, "workspace/project")
1768 +resolves_outside = black.resolves_outside_root_or_cannot_stat(
1769 +path, root, report
1770 + )
1771 +self.assertIs(resolves_outside, False)
1769 1772
1770 1773 def test_normalize_path_ignore_windows_junctions_outside_of_root(self) -> None:
1771 1774 if system() != "Windows":
@@ -1778,13 +1781,13 @@ def test_normalize_path_ignore_windows_junctions_outside_of_root(self) -> None:
1778 1781 os.system(f"mklink /J {junction_dir} {junction_target_outside_of_root}")
1779 1782
1780 1783 report = black.Report(verbose=True)
1781 -normalized_path = black.normalize_path_maybe_ignore(
1784 +resolves_outside = black.resolves_outside_root_or_cannot_stat(
1782 1785 junction_dir, root, report
1783 1786 )
1784 1787 # Manually delete for Python < 3.8
1785 1788 os.system(f"rmdir {junction_dir}")
1786 1789
1787 -self.assertEqual(normalized_path, None)
1790 +self.assertIs(resolves_outside, True)
1788 1791
1789 1792 def test_newline_comment_interaction(self) -> None:
1790 1793 source = "class A:\\\r\n# type: ignore\n pass\n"