Fix handling of Windows junctions in normalize_path_maybe_ignore (#2904)

Fixes #2569
This commit is contained in:
yoerg 2022-03-08 16:28:13 +01:00 committed by GitHub
parent fd6e92aa46
commit 24ffc54a53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 36 deletions

View File

@ -53,6 +53,8 @@
- Black can now parse starred expressions in the target of `for` and `async for`
statements, e.g `for item in *items_1, *items_2: pass` (#2879).
- Fix handling of directory junctions on Windows (#2904)
### Performance
<!-- Changes that improve Black's performance. -->

View File

@ -151,23 +151,22 @@ def normalize_path_maybe_ignore(
"""
try:
abspath = path if path.is_absolute() else Path.cwd() / path
normalized_path = abspath.resolve().relative_to(root).as_posix()
except OSError as e:
if report:
report.path_ignored(path, f"cannot be read because {e}")
return None
except ValueError:
if path.is_symlink():
normalized_path = abspath.resolve()
try:
root_relative_path = normalized_path.relative_to(root).as_posix()
except ValueError:
if report:
report.path_ignored(
path, f"is a symbolic link that points outside {root}"
)
return None
raise
except OSError as e:
if report:
report.path_ignored(path, f"cannot be read because {e}")
return None
return normalized_path
return root_relative_path
def path_is_excluded(

View File

@ -1418,6 +1418,25 @@ def test_bpo_33660_workaround(self) -> None:
normalized_path = black.normalize_path_maybe_ignore(path, root, report)
self.assertEqual(normalized_path, "workspace/project")
def test_normalize_path_ignore_windows_junctions_outside_of_root(self) -> None:
if system() != "Windows":
return
with TemporaryDirectory() as workspace:
root = Path(workspace)
junction_dir = root / "junction"
junction_target_outside_of_root = root / ".."
os.system(f"mklink /J {junction_dir} {junction_target_outside_of_root}")
report = black.Report(verbose=True)
normalized_path = black.normalize_path_maybe_ignore(
junction_dir, root, report
)
# Manually delete for Python < 3.8
os.system(f"rmdir {junction_dir}")
self.assertEqual(normalized_path, None)
def test_newline_comment_interaction(self) -> None:
source = "class A:\\\r\n# type: ignore\n pass\n"
output = black.format_str(source, mode=DEFAULT_MODE)
@ -1994,7 +2013,6 @@ def test_symlink_out_of_root_directory(self) -> None:
path.iterdir.return_value = [child]
child.resolve.return_value = Path("/a/b/c")
child.as_posix.return_value = "/a/b/c"
child.is_symlink.return_value = True
try:
list(
black.gen_python_files(
@ -2014,31 +2032,6 @@ def test_symlink_out_of_root_directory(self) -> None:
pytest.fail(f"`get_python_files_in_dir()` failed: {ve}")
path.iterdir.assert_called_once()
child.resolve.assert_called_once()
child.is_symlink.assert_called_once()
# `child` should behave like a strange file which resolved path is clearly
# outside of the `root` directory.
child.is_symlink.return_value = False
with pytest.raises(ValueError):
list(
black.gen_python_files(
path.iterdir(),
root,
include,
exclude,
None,
None,
report,
gitignore,
verbose=False,
quiet=False,
)
)
path.iterdir.assert_called()
assert path.iterdir.call_count == 2
child.resolve.assert_called()
assert child.resolve.call_count == 2
child.is_symlink.assert_called()
assert child.is_symlink.call_count == 2
@patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None))
def test_get_sources_with_stdin(self) -> None: