Sitelet https://github.com/python/cpython/issues/91317
Skip to content

pathlib method relative_to doesnt work with // in paths #91317

Description

@John15321
mannequin
BPO 47161
Nosy @eryksun, @barneygale, @arhadthedev, @John15321
PRs
  • gh-91317: Document that Path does not collapse initial // #32193
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2022-03-30.11:01:52.097>
    labels = ['3.8', 'type-bug', 'library']
    title = 'pathlib method relative_to doesnt work with // in paths'
    updated_at = <Date 2022-04-01.15:38:13.214>
    user = 'https://github.com/John15321'

    bugs.python.org fields:

    activity = <Date 2022-04-01.15:38:13.214>
    actor = 'barneygale'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2022-03-30.11:01:52.097>
    creator = 'John15321'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 47161
    keywords = ['patch']
    message_count = 8.0
    messages = ['416336', '416342', '416348', '416351', '416352', '416355', '416356', '416489']
    nosy_count = 4.0
    nosy_names = ['eryksun', 'barneygale', 'arhadthedev', 'John15321']
    pr_nums = ['32193']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue47161'
    versions = ['Python 3.8']

    Activity

    1. John15321 commented on Mar 30, 2022

      John15321mannequin
      MannequinAuthor

      The // path should be equivalent to /, and in some ways, it does behave like that in pathlib. But in the relative_to method on a Path object, it does not work
      This is causing our CI pipeline to fail. In the documentation here you can see // being properly processed:

      https://docs.python.org/3/library/pathlib.html

      In [10]: x=Path("/Library/Video") ; x.relative_to(Path("/"))
      Out[10]: PosixPath('Library/Video')
      
      In [11]: x=Path("//Library/Video") ; x.relative_to(Path("/"))
      ---------------------------------------------------------------------------
      ValueError                                Traceback (most recent call last)
      Input In [11], in <cell line: 1>()
      ----> 1 x=Path("//Library/Video") ; x.relative_to(Path("/"))
      
      File ~/.pyenv/versions/3.8.13/lib/python3.8/pathlib.py:908, in PurePath.relative_to(self, *other)
          906 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
          907     formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
      --> 908     raise ValueError("{!r} does not start with {!r}"
          909                      .format(str(self), str(formatted)))
          910 return self._from_parsed_parts('', root if n == 1 else '',
          911                                abs_parts[n:])
      
      ValueError: '//Library/Video' does not start with '/'
    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or error
      on Mar 30, 2022
    3. arhadthedev commented on Mar 30, 2022

      @arhadthedev
      Member

      I started to investigate and found that a double slash in the beginning cancels any parsing of a path:

          >>> Path("//Library/Video")._parts
          ['\\\\Library\\Video\\']

      vs

          >>> Path("/Library/Video")._parts
          ['\\', 'Library', 'Video']

      Investigating further.

    4. arhadthedev commented on Mar 30, 2022

      @arhadthedev
      Member

      As I found out, any path starting with two double slashes is treated as an UNC (network) path:

      vvvvvvvvvvvvvvvvvvvv root
      \\machine\mountpoint\directory\etc\...
                 directory ^^^^^^^^^^^^^^^^^
      

      So "/Library/Video" and "/" are directories mounted on your machine while "//Library/Video" is a computer named "Library" with a share named "Video".

      However, an error message in Python 3.8 (as a file path suggests) is misleading. In 3.11 it was changed to "'\\Library\Video\' is not in the subpath of '\' OR one path is relative and the other is absolute."

    5. arhadthedev commented on Mar 30, 2022

      @arhadthedev
      Member

      Also, the error message cannot be fixed because for 3.8 only security fixes are accepted since May 2021. For 3.9 and later, the message is already corrected.

    6. John15321 commented on Mar 30, 2022

      John15321mannequin
      MannequinAuthor

      But shouldn't it just work with // as a /? It seems like this is the behavior elsewhere. Sure I get that it cannot be done for 3.8. But the new error message implies that either // is not a subpath of / which it is, or that one is relative and the other is absolute, which is also false because both are absolutes

    7. arhadthedev commented on Mar 30, 2022

      @arhadthedev
      Member

      But shouldn't it just work with // as a /? It seems like this is the behavior elsewhere.

      It works elsewhere because empty directory names are impossible so can be dropped. But if // is placed in the beginning, it gets a special meaning that totally changes the whole path so its plain replacement would give a totally wrong one.

      Roughly speaking, "//Library/Video" is /Video on a computer named Library.

    8. John15321 commented on Mar 30, 2022

      John15321mannequin
      MannequinAuthor

      Hmm..., I get it, but Im not gonna lie it's pretty confusing given that in other places // works as a substitute for /. Maybe it should be mentioned in the documentation?

    9. eryksun commented on Apr 1, 2022

      @eryksun
      Contributor

      Hmm..., I get it, but Im not gonna lie it's pretty confusing given
      that in other places // works as a substitute for /. Maybe it
      should be mentioned in the documentation?

      In Linux, the system resolves "//" as just "/". In other POSIX systems, such as Cygwin or MSYS2 (running in Windows), "//" is a UNC path of the form "//server/share/filepath". I would expect resolve() to handle this. For example:

      Linux:

          >>> p = pathlib.Path('//tmp')
          >>> p
          PosixPath('//tmp')
          >>> p.resolve()
          PosixPath('/tmp')

      However, resolve() is broken for a UNC path in 3.9 under MSYS2:

          >>> p = pathlib.Path('//localhost/C$/temp')
          >>> p.exists()
          True
          >>> p.resolve()
          PosixPath('/localhost/C$/temp')
          >>> p.resolve().exists()
          False

      realpath() is also broken for this case in 3.9 under MSYS2:

          >>> os.path.realpath(p)
          '/localhost/C$/temp'
    10. transferred this issue fromon Apr 10, 2022
    11. added 5 commits that reference this issue on Jun 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.8 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions