-
-
Notifications
You must be signed in to change notification settings - Fork 986
Fix Windows path handling on Python 3.13+ #2196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -315,6 +315,68 @@ def join_path_native(a: PathLike, *p: PathLike) -> PathLike: | |
| return to_native_path(join_path(a, *p)) | ||
|
|
||
|
|
||
| def _is_path_rooted(path: PathLike) -> bool: | ||
| r"""Whether ``path`` has a root, including one encoded in a UNC drive. | ||
|
|
||
| On Windows, ``\directory`` is rooted on the current drive without being | ||
| absolute, while ``C:\directory`` has both a drive and a root. In contrast, | ||
| ``directory`` and the drive-relative ``C:directory`` have no root. | ||
| UNC paths are rooted: ``\\server\share`` stores the share in the drive | ||
| returned by :func:`os.path.splitdrive`, while ``\\server\share\directory`` | ||
| additionally has a rooted tail. | ||
| On POSIX, which has no drive concept, this simply distinguishes absolute | ||
| paths such as ``/directory`` from relative paths such as ``directory``. | ||
| """ | ||
| drive, tail = osp.splitdrive(os.fspath(path)) | ||
| separators = (os.sep,) if os.altsep is None else (os.sep, os.altsep) | ||
| return tail.startswith(separators) or drive.startswith(separators) | ||
|
|
||
|
|
||
| def _to_relative_path(root: PathLike, path: PathLike) -> str: | ||
| r"""Return a normalized Git-style path confined to ``root``. | ||
|
|
||
| A Windows path such as ``\directory`` is rooted but not absolute. Resolve it | ||
| against the drive of ``root`` rather than treating it as relative to ``root``. | ||
| Drive-relative paths such as ``C:directory`` are rejected because their meaning | ||
| depends on process-global per-drive state. | ||
|
|
||
| For example, with ``root`` set to ``C:\repo`` on Windows: | ||
|
|
||
| * ``directory\file`` -> ``directory/file`` | ||
| * ``directory\`` -> ``directory/`` | ||
| * ``C:\repo\directory\file`` -> ``directory/file`` | ||
| * ``\repo\directory\file`` -> ``directory/file`` | ||
| * ``C:directory\file`` -> :exc:`ValueError` | ||
| * ``C:\other\file`` -> :exc:`ValueError` | ||
|
|
||
| On POSIX, ``/repo/directory/file`` under ``/repo`` similarly becomes | ||
| ``directory/file``. A trailing separator is preserved as a Git-style ``/``. | ||
| """ | ||
| path_str = os.fspath(path) | ||
| if not path_str: | ||
| return path_str | ||
|
|
||
| drive, _tail = osp.splitdrive(path_str) | ||
| rooted = _is_path_rooted(path_str) | ||
| if drive and not rooted: | ||
| raise ValueError("Drive-relative path %r is not supported" % path_str) | ||
|
|
||
| root_abs = osp.abspath(os.fspath(root)) | ||
| path_abs = osp.abspath(osp.join(root_abs, path_str)) | ||
|
Comment on lines
+364
to
+365
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Codex says: Because the joined result already contains The existing |
||
| try: | ||
| common_path = osp.commonpath([root_abs, path_abs]) | ||
| except ValueError as e: | ||
| raise ValueError("Path %r is not in repository at %r" % (path_str, root_abs)) from e | ||
| if common_path != root_abs: | ||
| raise ValueError("Path %r is not in repository at %r" % (path_str, root_abs)) | ||
|
|
||
| relative_path = to_native_path_linux(osp.relpath(path_abs, root_abs)) | ||
| separators = (os.sep,) if os.altsep is None else (os.sep, os.altsep) | ||
| if path_str.endswith(separators) and relative_path != "." and not relative_path.endswith("/"): | ||
| relative_path += "/" | ||
| return relative_path | ||
|
|
||
|
|
||
| def assure_directory_exists(path: PathLike, is_file: bool = False) -> bool: | ||
| """Make sure that the directory pointed to by path exists. | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something I removed based on the codex note, but maybe ultimately it's safer to do this, and it's something it has copied from
symbolic.py... even though on case-sensitive filesystems it most definitely is overly restrictive to fold then separate paths.