Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 85 additions & 10 deletions trio/_path.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# type: ignore

from functools import wraps, partial
import os
import types
import pathlib
import sys
import types
from functools import partial, wraps
from typing import TYPE_CHECKING, Awaitable, Callable, TypeVar, Any

import trio
from trio._util import async_wraps, Final
from trio._util import Final, async_wraps


# re-wrap return value from methods that return new instances of pathlib.Path
Expand Down Expand Up @@ -156,11 +156,16 @@ class Path(metaclass=AsyncAutoWrapperType):
def __init__(self, *args):
self._wrapped = pathlib.Path(*args)

def __getattr__(self, name):
if name in self._forward:
value = getattr(self._wrapped, name)
return rewrap_path(value)
raise AttributeError(name)
# type checkers allow accessing any attributes on class instances with `__getattr__`
# so we hide it behind a type guard forcing it to rely on the hardcoded attribute
# list below.
if not TYPE_CHECKING:

def __getattr__(self, name):
if name in self._forward:
value = getattr(self._wrapped, name)
return rewrap_path(value)
raise AttributeError(name)

def __dir__(self):
return super().__dir__() + self._forward
Expand All @@ -182,6 +187,74 @@ async def open(self, *args, **kwargs):
value = await trio.to_thread.run_sync(func)
return trio.wrap_file(value)

if TYPE_CHECKING:
# the dunders listed in _forward_magic that aren't seen otherwise
__bytes__ = pathlib.Path.__bytes__
__truediv__ = pathlib.Path.__truediv__
__rtruediv__ = pathlib.Path.__rtruediv__

# These should be fully typed, either manually or with some magic wrapper
# function that copies the type of pathlib.Path except sticking an async in
# front of all of them. The latter is unfortunately not trivial, see attempts in
# https://github.com/python-trio/trio/issues/2630

# wrapped methods handled by __getattr__
absolute: Any
as_posix: Any
as_uri: Any
chmod: Any
cwd: Any
exists: Any
expanduser: Any
glob: Any
home: Any
is_absolute: Any
is_block_device: Any
is_char_device: Any
is_dir: Any
is_fifo: Any
is_file: Any
is_reserved: Any
is_socket: Any
is_symlink: Any
iterdir: Any
joinpath: Any
lchmod: Any
lstat: Any
match: Any
mkdir: Any
read_bytes: Any
read_text: Any
relative_to: Any
rename: Any
replace: Any
resolve: Any
rglob: Any
rmdir: Any
samefile: Any
stat: Any
symlink_to: Any
touch: Any
unlink: Any
with_name: Any
with_suffix: Any
write_bytes: Any
write_text: Any

if sys.platform != "win32":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to be able to auto-generate this (read from relevant file in https://github.com/python/typeshed and change the types... like just stick an async in the right places and call it a day). I suppose that's all part of "how will we do this" that is the followup, so ignore this :^)

group: Any
is_mount: Any
owner: Any

if sys.version_info >= (3, 8):
link_to: Any
if sys.version_info >= (3, 9):
is_relative_to: Any
with_stem: Any
readlink: Any
if sys.version_info >= (3, 10):
hardlink_to: Any

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line isn't the one I wanted to comment on, but I can't comment on it cause it's out of the diff github shows :(

Namely, the line that is os.PathLike.register(Path). Presumably we could subclass os.PathLike and that would help some typing stuff? I don't know though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, register isn't going to be fully supported everywhere. It'd also be better to inherit to indicate that we provide string paths, not bytes. A little tricky though since it's only as of 3.9 that you could subscript PathLike:

if TYPE_CHECKING:
    _StrPathLike = os.PathLike[str]
else:
    _StrPathLike = os.PathLike
...
class Path(_StrPathLike):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's leave that to a different PR that fully types trio.Path, can either continue with that in #2630 or open a different issue


Path.iterdir.__doc__ = """
Like :meth:`pathlib.Path.iterdir`, but async.
Expand All @@ -203,4 +276,6 @@ async def open(self, *args, **kwargs):
# sense than inventing our own special docstring for this.
del Path.absolute.__doc__

# TODO: This is likely not supported by all the static tools out there, see discussion in
# https://github.com/python-trio/trio/pull/2631#discussion_r1185612528
os.PathLike.register(Path)
1 change: 0 additions & 1 deletion trio/_path.pyi

This file was deleted.

Loading