Skip to content

pathlib: inconsistent handling of rootless Windows paths #94909

Closed

Description

@barneygale
>>> from pathlib import PureWindowsPath
>>> a = 'C:/a/b'
>>> b = 'C:x/y'
>>> PureWindowsPath(a, b)
PureWindowsPath('C:x/y')
>>> PureWindowsPath(a) / b
PureWindowsPath('C:/a/b/x/y')

Seems to be the only case where the PureWindowsPath constuctor and joinpath() disagree when given the same arguments. This has implications for future work, so I'd love to make them consistent.

Affects all supported versions of Python.

Activity

  1. barneygale commented on Jul 16, 2022

    @barneygale
    ContributorAuthor

    Related to #64107, which improved the joinpath() behaviour.

  2. barneygale commented on Jul 16, 2022

    @barneygale
    ContributorAuthor

    Here's a patch that will be applicable when #31691 lands. It uses ntpath.join() to join the path parts, and consequently scans left-to-right without an early exit from the loop. Optimizations are possible, I just went for minimal diff:

    diff --git a/Lib/pathlib.py b/Lib/pathlib.py
    index bd86403c8e..ad3c03f1bd 100644
    --- a/Lib/pathlib.py
    +++ b/Lib/pathlib.py
    @@ -266,10 +266,8 @@ def _parse_parts(cls, parts):
             sep = cls._flavour.sep
             altsep = cls._flavour.altsep
             drv = root = ''
    -        it = reversed(parts)
    -        for part in it:
    -            if not part:
    -                continue
    +        if parts:
    +            part = cls._flavour.join(*parts)
                 if altsep:
                     part = part.replace(altsep, sep)
                 drv, rel = cls._flavour.splitdrive(part)
    @@ -289,20 +287,6 @@ def _parse_parts(cls, parts):
                 else:
                     if rel and rel != '.':
                         parsed.append(sys.intern(rel))
    -            if drv or root:
    -                if not drv:
    -                    # If no drive is present, try to find one in the previous
    -                    # parts. This makes the result of parsing e.g.
    -                    # ("C:", "/", "a") reasonably intuitive.
    -                    for part in it:
    -                        if not part:
    -                            continue
    -                        if altsep:
    -                            part = part.replace(altsep, sep)
    -                        drv = cls._flavour.splitdrive(part)[0]
    -                        if drv:
    -                            break
    -                break
             if drv or root:
                 parsed.append(drv + root)
             parsed.reverse()
  3. ericvsmith commented on Jul 19, 2022

    @ericvsmith
    Member

    I definitely agree that this is sufficiently complex that pathlib should use ntpath wherever possible.

  4. added a commit that references this issue on Jul 29, 2022
  5. barneygale commented on Jul 29, 2022

    @barneygale
    ContributorAuthor

    In fact, this would be good to fix before #31691 lands, as it makes backporting easier. PR available: #95450.

  6. self-assigned this
    on Aug 5, 2022
  7. added a commit that references this issue on Aug 12, 2022
  8. brettcannon commented on Aug 12, 2022

    @brettcannon
    Member

    The fix only went into main to minimize risk of breaking and out of simplicity since this fixes an edge case only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

OS-windowsstdlibStandard Library Python modules in the Lib/ directorytopic-pathlibtype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions