Currently opening a UPath via open fails with a FileNotFoundError because the URI returned via __fspath__() is just interpreted as a string path.
>>> import upath
>>> pth = upath.UPath("memory:///abc.txt")
>>> pth.write_text("hello world")
11
>>> with open(pth, "rt") as f:
... print(f.read())
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'memory:/abc.txt'
There's two possible ways of improving this error:
- We raise an exception in non-local UPath subclasses in
__fspath__.
- Or we could keep the basic functionality that fspath returns a string and raise an exception if we detect that the result was used in
open
import os
import sys
from textwrap import dedent
from typing import Self
class _upath_str(str):
"""helper str subclass for better error messages
This helper class allows us to catch when a user tries
to provide a UPath instance of a non-local filesystem
to Python's `open()` function.
"""
def __str__(self) -> Self:
return self
def _upath_audit_hook(name: str, args: tuple) -> None:
"""audit hook matching the `open` event
Raises a better error message if we detect that a
user tries to use UPath instances with `open()`.
"""
if name == "open" and isinstance(args[0], _upath_str):
msg = dedent(
f"""\
builtins.open does not support non-local UPath instances.
Use the pathlib interface instead:
>>> pth = UPath({args[0]!r})
>>> with pth.open() as f:
>>> ...
"""
)
raise OSError(msg)
sys.addaudithook(_upath_audit_hook)
class A:
def __fspath__(self):
return _upath_str("abc")
def __str__(self):
return _upath_str("abc")
a = A()
b = _upath_str("b")
print(type(b))
c = str(a)
print(type(c))
print(type(str(c)))
os.fspath(a)
with open(a) as f:
pass
(1) would be preferable, but (2) might be useful, since there's some code out there that uses __fspath__ to convert os.PathLike to strings.
Need to do a survey of code on github, and if we choose (1) there needs to be a deprecation period.
Currently opening a
UPathviaopenfails with a FileNotFoundError because the URI returned via__fspath__()is just interpreted as a string path.There's two possible ways of improving this error:
__fspath__.open(1) would be preferable, but (2) might be useful, since there's some code out there that uses
__fspath__to convert os.PathLike to strings.Need to do a survey of code on github, and if we choose (1) there needs to be a deprecation period.