Skip to content

Commit

Permalink
Added asynchronous Path class (#327)
Browse files Browse the repository at this point in the history
Fixes #296.
  • Loading branch information
agronholm authored Jul 3, 2021
1 parent 321c6e2 commit 5591099
Show file tree
Hide file tree
Showing 7 changed files with 846 additions and 37 deletions.
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Async file I/O
.. autofunction:: anyio.open_file

.. autoclass:: anyio.AsyncFile

.. autoclass:: anyio.Path

Streams and stream wrappers
---------------------------
Expand Down
43 changes: 43 additions & 0 deletions docs/fileio.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Asynchronous file I/O support
=============================

.. py:currentmodule:: anyio
AnyIO provides asynchronous wrappers for blocking file operations. These wrappers run blocking
operations in worker threads.

Expand Down Expand Up @@ -30,3 +32,44 @@ file objects support synchronous iteration::
run(main)

.. seealso:: :ref:`FileStreams`

Asynchronous path operations
----------------------------

AnyIO provides an asynchronous version of the :class:`pathlib.Path` class. It differs with the
original in a number of ways:

* Operations that perform disk I/O (like :meth:`~pathlib.Path.read_bytes``) are run in a worker
thread and thus require an ``await``
* Methods like :meth:`~pathlib.Path.glob` return an asynchronous iterator that yields asynchronous
:class:`~.Path` objects
* Properties and methods that normally return :class:`pathlib.Path` objects return :class:`~.Path`
objects instead
* Methods and properties from the Python 3.10 API are available on all versions
* Use as a context manager is not supported, as it is deprecated in pathlib

For example, to create a file with binary content::

from anyio import Path, run


async def main():
path = Path('/foo/bar')
await path.write_bytes(b'hello, world')

run(main)

Asynchronously iterating a directory contents can be done as follows::

from anyio import Path, run


async def main():
# Print the contents of every file (assumed to be text) in the directory /foo/bar
dir_path = Path('/foo/bar')
async for path in dir_path.iterdir():
if await path.is_file():
print(await path.read_text())
print('---------------------')

run(main)
4 changes: 4 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ This library adheres to `Semantic Versioning 2.0 <http://semver.org/>`_.

**UNRELEASED**

- Added asynchronous ``Path`` class
- Relaxed the type of the ``path`` initializer argument to ``FileReadStream`` and
``FileWriteStream``so they accept any path-like object (including the new asynchronous ``Path``
class)
- Dropped unnecessary dependency on the ``async_generator`` library

**3.2.1**
Expand Down
3 changes: 2 additions & 1 deletion src/anyio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
'TypedAttributeLookupError',
'WouldBlock',
'AsyncFile',
'Path',
'open_file',
'aclose_forcefully',
'open_signal_receiver',
Expand Down Expand Up @@ -80,7 +81,7 @@
BrokenResourceError, BrokenWorkerProcess, BusyResourceError, ClosedResourceError,
DelimiterNotFound, EndOfStream, ExceptionGroup, IncompleteRead, TypedAttributeLookupError,
WouldBlock)
from ._core._fileio import AsyncFile, open_file
from ._core._fileio import AsyncFile, Path, open_file
from ._core._resources import aclose_forcefully
from ._core._signals import open_signal_receiver
from ._core._sockets import (
Expand Down
Loading

0 comments on commit 5591099

Please sign in to comment.