GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — OSError when try convert URI to pathlib.Path · Issue #91504 · python/cpython

OSError when try convert URI to pathlib.Path #91504

New issue
Copy link
New issue
Copy link
Closed
Closed
OSError when try convert URI to pathlib.Path#91504
Copy link

Description

@bw7715
bw7715
opened on Apr 13, 2022
Issue body actions

When try to convert URI to pathlib.Path I get an error:

E:\Work\Venvs\p39\Scripts\python.exe "E:/uri_test/pathlib_test.py"
D:\Tests\samples\images\3.bmp exists=True
file:///D:/Tests/samples/images/3.bmp
Traceback (most recent call last):
  File "E:\uri_test\pathlib_test.py", line 9, in <module>
    from_uri_path = Path(uri_path).resolve()
  File "C:\pythons\Python39\lib\pathlib.py", line 1215, in resolve
    s = self._flavour.resolve(self, strict=strict)
  File "C:\pythons\Python39\lib\pathlib.py", line 215, in resolve
    s = self._ext_to_normal(_getfinalpathname(s))
OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect: 'file:\\D:\\Tests\\samples\\images\\3.bmp'

Code to reproduce:

from pathlib import Path

img_path = Path(r"D:\Tests\samples\images\3.bmp").resolve()
print(f"{img_path} exists={img_path.exists()}")

uri_path = img_path.as_uri()
print(uri_path)

from_uri_path = Path(uri_path).resolve()
print(f"{from_uri_path} exists={from_uri_path.exists()}")

Python version: 3.9

With 3.10 works.

Activity

  1. ericvsmith commented on Apr 13, 2022

    @ericvsmith
    ericvsmith
    on Apr 13, 2022
    Member
    More actions

    Please show what the output in 3.10 is.

  2. bw7715 commented on Apr 13, 2022

    @bw7715
    bw7715
    on Apr 13, 2022
    Author
    More actions

    Python 3.10 output:

    E:\Work\Venvs\p310\Scripts\python.exe "E:/uri_test/pathlib_test.py"
    D:\Tests\samples\images\3.bmp exists=True
    file:///D:/Tests/samples/images/3.bmp
    D:Tests\samples\images\3.bmp exists=True
    
    Process finished with exit code 0

    Now I see that one '\' is missing in path after drive letter:
    D:Tests\samples\images\3.bmp exists=True

  3. eryksun commented on Apr 13, 2022

    @eryksun
    eryksun
    on Apr 13, 2022
    Contributor
    More actions

    A pathlib.Path cannot be a URI or be constructed from a URI. For example:

    >>> p = pathlib.Path('file:///C:/Temp/test.txt')
    >>> p.drive
    ''
    >>> p.root
    ''
    >>> os.fspath(p)
    'file:\\C:\\Temp\\test.txt

    The path was parsed as an invalid relative path. Perhaps there's a case for adding a from_uri() constructor.

    In 3.10+, resolve() 'succeeds' with an invalid path such as this because it defaults to calling realpath() in non-strict mode, which in this case is garbage-in garbage-out. For example:

    >>> p.resolve()
    WindowsPath('C:Temp/test.txt')
    >>> os.path.realpath(p)
    'C:Temp\\test.txt'

    The above result is a drive-relative path, i.e. it has no root path and thus depends on the working directory on drive "C:".

  4. bw7715 commented on Apr 13, 2022

    @bw7715
    bw7715
    on Apr 13, 2022
    Author
    More actions

    I think this method from_uri() is a good idea.

    >>> p = pathlib.Path().from_uri("file:///D:/Tests/samples/images/3.bmp")
    >>> p.resolve()
    'D:\Tests\samples\images\3.bmp'
  5. bw7715 commented on Apr 13, 2022

    @bw7715
    bw7715
    on Apr 13, 2022
    Author
    More actions

    There is a problem with URI -> pathlib.Path -> URI conversion:

    >>>p = Path(r"file:///D:/Tests/samples/images/3.bmp").resolve()
    >>>p
    WindowsPath('D:Tests/samples/images/3.bmp')
    >>>p.as_uri()
    Traceback (most recent call last):
      File "C:\pythons\Python310\lib\code.py", line 90, in runcode
        exec(code, self.locals)
      File "<input>", line 1, in <module>
      File "C:\pythons\Python310\lib\pathlib.py", line 649, in as_uri
        raise ValueError("relative path can't be expressed as a file URI")
    ValueError: relative path can't be expressed as a file URI
  6. ericvsmith commented on Apr 13, 2022

    @ericvsmith
    ericvsmith
    on Apr 13, 2022
    Member
    More actions

    As @eryksun said: "A pathlib.Path cannot be a URI or be constructed from a URI".

  7. barneygale commented on Apr 15, 2022

    @barneygale
    barneygale
    on Apr 15, 2022
    Contributor
    More actions

    A solution that works today:

    from urllib.request import url2pathname
    from pathlib import Path
    
    def path_from_uri(uri):
        return Path(url2pathname(uri.removeprefix('file:')))

    I'd support the addition of a Path.from_uri() classmethod, on the condition that we make from_uri() and as_uri() call through to url2pathname and pathname2url in urllib.request, rather than adding another duplicate implementation.

    Reacted by Simão Afonso @ Powertools Tech
  8. barneygale commented on Apr 15, 2022

    @barneygale
    barneygale
    on Apr 15, 2022
    Contributor
    More actions

    I suspect that the rules for parsing/emitting URLs will vary depending on whether the path uses Windows or POSIX semantics. If that's the case, I'd like to propose the following:

    • A new os.path.fileuri() function, implemented in posixpath and ntpath.
      • nturl2path.pathname2uri() calls ntpath.fileuri()
      • urllib.request.pathname2uri() calls os.path.fileuri()
      • pathlib.PurePosixPath.as_uri() calls posixpath.fileuri()
      • pathlib.PureWindowsPath.as_uri() calls ntpath.fileuri()
    • A new os.path.parsefileuri() function (unsure of naming). Similarly:
      • nturl2path.uri2pathname() calls ntpath.parsefileuri()
      • urllib.request.uri2pathname() calls os.path.parsefileuri()
      • pathlib.PurePosixPath.from_uri() calls posixpath.parsefileuri()
      • pathlib.PureWindowsPath.from_uri() calls ntpath.parsefileuri().

    From a pathlib perspective this makes sense to me, as I'm hoping to move towards wrapping os.path more directly - see #31691.

    Thoughts?

  9. eryksun commented on May 3, 2022

    @eryksun
    eryksun
    on May 3, 2022
    Contributor
    More actions

    I'm closing this issue because the pathlib.Path constructor isn't intended to support file URIs.

    @barneygale, I think the suggested changes to os.path should be discussed in a forum, even if just to bike shed the function names.

    Reacted by Barney Gale
  10. eryksun
    closed this as completedon May 3, 2022
  11. indigo-jay commented on Apr 6, 2023

    @indigo-jay
    indigo-jay
    on Apr 6, 2023
    More actions

    If this is true, then the Path constructor should raise an exception when it's given a file URI, so that it doesn't give the appearance of supporting file URIs when in fact it doesn't.

  12. barneygale commented on Apr 6, 2023

    @barneygale
    barneygale
    on Apr 6, 2023
    Contributor
    More actions

    It's a good idea, but file URIs are also valid POSIX paths! You can mkdir -p file:///foo/bar/baz from your terminal today.

  13. ZanderBrown commented on Apr 6, 2023

    @ZanderBrown
    ZanderBrown
    on Apr 6, 2023
    More actions

    Seems relatively unlikely someone would actually want that though?

  14. barneygale commented on Apr 7, 2023

    @barneygale
    barneygale
    on Apr 7, 2023
    Contributor
    More actions

    As a user, I want to be able to throw any string at pathlib.Path and have it efficiently parse it as a path. It doesn't currently perform any validation beyond checking for bytes. If we attempt to detect and reject file: URIs, it will slow things down and open the door to validating other things, which will further complicate and slow pathlib.

    Reacted by Alex Waygood
  15. indigo-jay commented on Apr 7, 2023

    @indigo-jay
    indigo-jay
    on Apr 7, 2023
    More actions

    The current implementation is, IMO, the worst option: leave file: as the first element in the path. That is utterly useless (is_file() will always return False). As I see it, there are two relatively simple options that would make it at least somewhat useful:

    1. Remove the file: part and just create a path based on the rest. This would be the most useful since it would generate a potentially valid/useful path. Thinking about it, this is probably my personal preference and further supports the "throw any string" at Path instances and have it parse usable paths.
    2. Raise an exception - since a Path instance that includes file: as the first element is utterly useless, this would at least notify users that it's not going to generate a valid Path.
  16. barneygale commented on Apr 7, 2023

    @barneygale
    barneygale
    on Apr 7, 2023
    Contributor
    More actions

    That is utterly useless (is_file() will always return False)

    It works fine for me on Linux:

    $ touch 'file:'
    $ ./python 
    >>> import pathlib
    >>> pathlib.Path('file:').is_file()
    True
    
  17. ericvsmith commented on Apr 7, 2023

    @ericvsmith
    ericvsmith
    on Apr 7, 2023
    Member
    More actions

    I agree with @barneygale : There's no reason to prohibit filenames starting with "file:". They're perfectly valid, at least on POSIX systems.

  18. ericvsmith commented on Apr 7, 2023

    @ericvsmith
    ericvsmith
    on Apr 7, 2023
    Member
    More actions

    Also, I don't think we'd want to implement any "strip of leading 'file:' characters" logic just on Windows.

  19. eryksun commented on Apr 7, 2023

    @eryksun
    eryksun
    on Apr 7, 2023
    Contributor
    More actions

    Raise an exception - since a Path instance that includes file: as the first element is utterly useless

    To clarify the case on Windows, the NTFS and ReFS filesystems support file streams of the form "filename:streamname:streamtype", "filename:streamname" ("$DATA" stream type), and "filename::streamtype" (anonymous or default stream name for the given stream type). But just "file:" without a stream name or stream type is invalid, and "file:///foo/bar/baz" is invalid because "///foo/bar/baz" is parsed as the stream name, and stream names reserve "/", ":", and NUL as invalid name characters.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.