Fix handling of Windows junctions in normalize_path_maybe_ignore (#2904)
Fixes #2569
This commit is contained in:
parent
fd6e92aa46
commit
24ffc54a53
@ -53,6 +53,8 @@
|
|||||||
- Black can now parse starred expressions in the target of `for` and `async for`
|
- 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).
|
statements, e.g `for item in *items_1, *items_2: pass` (#2879).
|
||||||
|
|
||||||
|
- Fix handling of directory junctions on Windows (#2904)
|
||||||
|
|
||||||
### Performance
|
### Performance
|
||||||
|
|
||||||
<!-- Changes that improve Black's performance. -->
|
<!-- Changes that improve Black's performance. -->
|
||||||
|
@ -151,23 +151,22 @@ def normalize_path_maybe_ignore(
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
abspath = path if path.is_absolute() else Path.cwd() / path
|
abspath = path if path.is_absolute() else Path.cwd() / path
|
||||||
normalized_path = abspath.resolve().relative_to(root).as_posix()
|
normalized_path = abspath.resolve()
|
||||||
except OSError as e:
|
try:
|
||||||
if report:
|
root_relative_path = normalized_path.relative_to(root).as_posix()
|
||||||
report.path_ignored(path, f"cannot be read because {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if path.is_symlink():
|
|
||||||
if report:
|
if report:
|
||||||
report.path_ignored(
|
report.path_ignored(
|
||||||
path, f"is a symbolic link that points outside {root}"
|
path, f"is a symbolic link that points outside {root}"
|
||||||
)
|
)
|
||||||
return None
|
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(
|
def path_is_excluded(
|
||||||
|
@ -1418,6 +1418,25 @@ def test_bpo_33660_workaround(self) -> None:
|
|||||||
normalized_path = black.normalize_path_maybe_ignore(path, root, report)
|
normalized_path = black.normalize_path_maybe_ignore(path, root, report)
|
||||||
self.assertEqual(normalized_path, "workspace/project")
|
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:
|
def test_newline_comment_interaction(self) -> None:
|
||||||
source = "class A:\\\r\n# type: ignore\n pass\n"
|
source = "class A:\\\r\n# type: ignore\n pass\n"
|
||||||
output = black.format_str(source, mode=DEFAULT_MODE)
|
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]
|
path.iterdir.return_value = [child]
|
||||||
child.resolve.return_value = Path("/a/b/c")
|
child.resolve.return_value = Path("/a/b/c")
|
||||||
child.as_posix.return_value = "/a/b/c"
|
child.as_posix.return_value = "/a/b/c"
|
||||||
child.is_symlink.return_value = True
|
|
||||||
try:
|
try:
|
||||||
list(
|
list(
|
||||||
black.gen_python_files(
|
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}")
|
pytest.fail(f"`get_python_files_in_dir()` failed: {ve}")
|
||||||
path.iterdir.assert_called_once()
|
path.iterdir.assert_called_once()
|
||||||
child.resolve.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))
|
@patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None))
|
||||||
def test_get_sources_with_stdin(self) -> None:
|
def test_get_sources_with_stdin(self) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user