Skip to content

Pathlib join pure windows path and pure posix path changes it behaviour on 3.13 #125069

Closed

Description

@pohmelie

Bug report

Bug description:

3.12.7

Python 3.12.7 (main, Oct  8 2024, 00:39:14) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pathlib; pathlib.PureWindowsPath("C:\\A") / pathlib.PurePosixPath("/foo/C:\\B").relative_to("/")
PureWindowsPath('C:/B')

3.13.0rc3

Python 3.13.0rc3 (main, Oct  7 2024, 23:18:57) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pathlib; pathlib.PureWindowsPath("C:\\A") / pathlib.PurePosixPath("/foo/C:\\B").relative_to("/")
PureWindowsPath('C:/A/foo/C:/B')

Not sure if it is a bug, or a fixed behaviour

CPython versions tested on:

3.12, 3.13

Operating systems tested on:

Linux

Linked PRs

Activity

  1. AA-Turner commented on Oct 7, 2024

    @AA-Turner
    Member

    cc @barneygale, though the reproducer does look a bit off, with multiple drive roots.

    @pohmelie can you reproduce this using only os.path?

    A

  2. pohmelie commented on Oct 7, 2024

    @pohmelie
    Author

    @AA-Turner
    If you provide a code with os.path module, then I can test it. But, AFAIK, os.path have no ability to manage windows and posix paths.

  3. terryjreedy commented on Oct 7, 2024

    @terryjreedy
    Member

    Checking 3.13 What's New or the linked log file might reveal if intentional change.

  4. imfeh2 commented on Oct 8, 2024

    @imfeh2
    Contributor

    It happens after this change.
    #111431

  5. ZeroIntensity commented on Oct 8, 2024

    @ZeroIntensity
    Member

    cc @barneygale, apparently that PR introduced a breaking change.

    Edit: oh wait, Adam already pinged, sorry!

  6. barneygale commented on Oct 8, 2024

    @barneygale
    Contributor

    Thanks for reporting this! There's a few things going on here. I think it comes down to pathlib's special handling of PurePath(PurePath(...)) and our efforts to delay joining of *pathsegments. I believe the 3.13 behaviour is correct, and that we should fix 3.12. PR coming up...

  7. barneygale commented on Oct 8, 2024

    @barneygale
    Contributor

    Another repro, this one works in 3.13 and 3.14:

    >>> a = PureWindowsPath('a')
    >>> b1 = PurePosixPath('b', 'c:\\d')
    >>> b2 = PurePosixPath('b/c:\\d')
    >>> b1 == b2
    True
    >>> a / b1
    PureWindowsPath('c:/d')
    >>> a / b2
    PureWindowsPath('a/b/c:/d')
  8. added 2 commits that reference this issue on Oct 8, 2024
  9. barneygale commented on Oct 13, 2024

    @barneygale
    Contributor

    PR available, if anyone has a mo to review: #125156

  10. added 2 commits that reference this issue on Oct 13, 2024
  11. added a commit that references this issue on Oct 13, 2024
  12. added 3 commits that reference this issue on Oct 13, 2024
  13. barneygale commented on Oct 13, 2024

    @barneygale
    Contributor

    I've pushed a fix to 3.12, 3.13 and 3.14 (main).

    In the original repro case, the 3.12 behaviour was buggy and the 3.13 behaviour was correct. The optimisation in #111431 (3.13+) happened to fix the issue for paths generated by relative_to(). The change I've applied here should fix the problem at its source.

    Thanks for reporting :)

  14. pohmelie commented on Oct 13, 2024

    @pohmelie
    Author

    @barneygale
    So, what should I do in my library code? Should I dispatch behaviour by python version? Will the fix lands on 3.12.8 and buggy behaviour still presents in 3.12.0 to 3.12.7 inclusive? 🤔 I want equal behaviour of my function on all supported python versions from 3.8 to 3.13.

  15. barneygale commented on Oct 13, 2024

    @barneygale
    Contributor

    @barneygale So, what should I do in my library code? Should I dispatch behaviour by python version? Will the fix lands on 3.12.8 and buggy behaviour still presents in 3.12.0 to 3.12.7 inclusive? 🤔 I want equal behaviour of my function on all supported python versions from 3.8 to 3.13.

    Untested, but I think this will work in all version:

    diff --git a/src/aioftp/server.py b/src/aioftp/server.py
    index 1830d78..d31f40a 100644
    --- a/src/aioftp/server.py
    +++ b/src/aioftp/server.py
    @@ -1031,7 +1031,7 @@ class Server:
                 else:
                     resolved_virtual_path /= part
             base_path = connection.user.base_path
    -        real_path = base_path / resolved_virtual_path.relative_to("/")
    +        real_path = base_path / str(resolved_virtual_path.relative_to("/"))
             # replace with `is_relative_to` check after 3.9+ requirements lands
             try:
                 real_path.relative_to(base_path)
  16. barneygale commented on Oct 14, 2024

    @barneygale
    Contributor

    Please use https://discuss.python.org/ for general discussion. If you're looking to convince other people to do work on your behalf (e.g. improving the pathlib codebase) then I suggest you strike a more constructive note. If you're planning to put in your own issues and PRs I'd be happy to review.

  17. pohmelie commented on Oct 14, 2024

    @pohmelie
    Author

    @barneygale yep, looks like this str(...) trick works, thank you!

  18. barneygale commented on Oct 14, 2024

    @barneygale
    Contributor

    Wow what valuable feedback, thanks buddy! :P

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.13only security fixestopic-pathlibtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions