diff options
author | Tor Colvin <torcolvin@gmail.com> | 2020-06-02 07:56:33 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-02 08:56:33 -0300 |
commit | fe640934111b52b0461a3d35994730667525b783 (patch) | |
tree | 57fc221ed6ffdbc562193fdf55509decac1c1c13 /src/_pytest/pathlib.py | |
parent | 9214e63af38ad76c6d4f02f0db4a4cdb736ab032 (diff) | |
download | pytest-fe640934111b52b0461a3d35994730667525b783.tar.gz |
Fix removal of very long paths on Windows (#6755)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
Diffstat (limited to 'src/_pytest/pathlib.py')
-rw-r--r-- | src/_pytest/pathlib.py | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 21ec61e2c..90a7460b0 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -100,10 +100,41 @@ def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool: return True +def ensure_extended_length_path(path: Path) -> Path: + """Get the extended-length version of a path (Windows). + + On Windows, by default, the maximum length of a path (MAX_PATH) is 260 + characters, and operations on paths longer than that fail. But it is possible + to overcome this by converting the path to "extended-length" form before + performing the operation: + https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + + On Windows, this function returns the extended-length absolute version of path. + On other platforms it returns path unchanged. + """ + if sys.platform.startswith("win32"): + path = path.resolve() + path = Path(get_extended_length_path_str(str(path))) + return path + + +def get_extended_length_path_str(path: str) -> str: + """Converts to extended length path as a str""" + long_path_prefix = "\\\\?\\" + unc_long_path_prefix = "\\\\?\\UNC\\" + if path.startswith((long_path_prefix, unc_long_path_prefix)): + return path + # UNC + if path.startswith("\\\\"): + return unc_long_path_prefix + path[2:] + return long_path_prefix + path + + def rm_rf(path: Path) -> None: """Remove the path contents recursively, even if some elements are read-only. """ + path = ensure_extended_length_path(path) onerror = partial(on_rm_rf_error, start_path=path) shutil.rmtree(str(path), onerror=onerror) @@ -220,6 +251,7 @@ def register_cleanup_lock_removal(lock_path: Path, register=atexit.register): def maybe_delete_a_numbered_dir(path: Path) -> None: """removes a numbered directory if its lock can be obtained and it does not seem to be in use""" + path = ensure_extended_length_path(path) lock_path = None try: lock_path = create_cleanup_lock(path) |