{"id":13800512,"url":"https://github.com/alexdelorenzo/aiopath","last_synced_at":"2025-05-16T02:02:22.908Z","repository":{"id":52835893,"uuid":"345789541","full_name":"alexdelorenzo/aiopath","owner":"alexdelorenzo","description":"📁 Asynchronous pathlib for Python","archived":false,"fork":false,"pushed_at":"2024-10-20T22:29:53.000Z","size":277,"stargazers_count":177,"open_issues_count":23,"forks_count":7,"subscribers_count":3,"default_branch":"support-3.12","last_synced_at":"2025-05-10T18:05:48.405Z","etag":null,"topics":["async","asyncio","path","pathlib","paths","python","python-asyncio","python3","python3-asyncio"],"latest_commit_sha":null,"homepage":"https://alexdelorenzo.dev","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexdelorenzo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["alexdelorenzo"]}},"created_at":"2021-03-08T20:43:57.000Z","updated_at":"2025-05-06T11:22:49.000Z","dependencies_parsed_at":"2023-10-17T04:43:37.036Z","dependency_job_id":"ab8797e4-1aba-4cdb-9c22-8f1a8fa302ca","html_url":"https://github.com/alexdelorenzo/aiopath","commit_stats":{"total_commits":252,"total_committers":3,"mean_commits":84.0,"dds":0.5952380952380952,"last_synced_commit":"f381224d6262c2523c0e38de4feb18657015efcb"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdelorenzo%2Faiopath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdelorenzo%2Faiopath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdelorenzo%2Faiopath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdelorenzo%2Faiopath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexdelorenzo","download_url":"https://codeload.github.com/alexdelorenzo/aiopath/tar.gz/refs/heads/support-3.12","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254453646,"owners_count":22073616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","asyncio","path","pathlib","paths","python","python-asyncio","python3","python3-asyncio"],"created_at":"2024-08-04T00:01:13.255Z","updated_at":"2025-05-16T02:02:22.858Z","avatar_url":"https://github.com/alexdelorenzo.png","language":"Python","funding_links":["https://github.com/sponsors/alexdelorenzo","https://www.buymeacoffee.com/alexdelorenzo"],"categories":["Misc"],"sub_categories":[],"readme":"# 📁 Async `pathlib` for Python\n`aiopath` is a complete implementation of Python's [`pathlib`](https://docs.python.org/3/library/pathlib.html) that's compatible with [`asyncio`](https://docs.python.org/3/library/asyncio.html), [`trio`](https://github.com/python-trio/trio), and the [`async/await` syntax](https://www.python.org/dev/peps/pep-0492/). \n\nAll I/O performed by `aiopath` is asynchronous and [awaitable](https://docs.python.org/3/library/asyncio-task.html#awaitables).\n\nCheck out [`📂 app_paths`](https://github.com/alexdelorenzo/app_paths) for an example of library that uses `aiopath`, as well as the [`pyclean` script here](https://alexdelorenzo.dev/notes/pyclean).\n\n## Use case\nIf you're writing asynchronous Python code and want to take advantage of `pathlib`'s conveniences, but don't want to mix blocking and [non-blocking I/O](https://en.wikipedia.org/wiki/Asynchronous_I/O), then you can reach for `aiopath`.\n\nFor example, if you're writing an async [web scraping](https://en.wikipedia.org/wiki/Web_scraping) script, you might want to make several concurrent requests to websites and save the responses to persistent storage:\n```python3\nfrom asyncio import run, gather\n\nfrom aiohttp import ClientSession\nfrom aiopath import AsyncPath\n\n\nasync def save_page(url: str, name: str):\n  path = AsyncPath(name)\n\n  if await path.exists():\n    return\n\n  async with ClientSession() as session, session.get(url) as response:\n    content: bytes = await response.read()\n\n  await path.write_bytes(content)\n\n\nasync def main():\n  urls = [\n    'https://example.com',\n    'https://github.com/alexdelorenzo/aiopath',\n    'https://alexdelorenzo.dev',\n    'https://dupebot.firstbyte.dev'\n  ]\n\n  tasks = (\n    save_page(url, f'{index}.html')\n    for index, url in enumerate(urls)\n  )\n\n  await gather(*tasks)\n\n\nrun(main())\n```\nIf you used `pathlib` instead of `aiopath`, tasks accessing the disk would block the event loop, and async tasks accessing the network would suspend until the event loop was unblocked.\n\nBy using `aiopath`, the script can access the network and disk concurrently.\n\n## Implementation \n`aiopath` is a direct reimplementation of [CPython's `pathlib.py`](https://github.com/python/cpython/blob/master/Lib/pathlib.py) and shares some of its code. `aiopath`'s class hierarchy [directly matches the one from `pathlib`](https://docs.python.org/3/library/pathlib.html), where `Path` inherits from `PurePath`, `AsyncPath` inherits from `AsyncPurePath`, and so on.\n\nWith `aiopath`, methods that perform I/O are asynchronous and awaitable, and methods that perform I/O and return iterators in `pathlib` now return [async generators](https://www.python.org/dev/peps/pep-0525/). `aiopath` goes one step further, and wraps [`os.scandir()`](https://docs.python.org/3/library/os.html#os.scandir) and [`DirEntry`](https://docs.python.org/3/library/os.html#os.DirEntry) to make [`AsyncPath.glob()`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob) completely async.\n\n`aiopath` is typed with Python [type annotations](https://docs.python.org/3/library/typing.html), and if using the `aiofile` back end, it takes advantage of [`libaio`](https://pagure.io/libaio) for async I/O on Linux.\n\n# Usage\n `aiopath`'s API directly matches [`pathlib`](https://docs.python.org/3/library/pathlib.html), so check out the standard library documentation for [`PurePath`](https://docs.python.org/3/library/pathlib.html#pure-paths) and [`Path`](https://docs.python.org/3/library/pathlib.html#methods).\n \n### Running examples\nTo run the following examples with top-level `await` expressions, [launch an asynchronous Python REPL](https://www.integralist.co.uk/posts/python-asyncio/#running-async-code-in-the-repl) using `python3 -m asyncio` or an [IPython shell](https://ipython.org/).\n\nYou'll also need to install `asynctempfile` via PyPI, like so `python3 -m pip install asynctempfile`.\n\n## Replacing `pathlib`\nAll of `pathlib.Path`'s methods that perform synchronous I/O are reimplemented as asynchronous methods. `PurePath` methods are not asynchronous because they don't perform I/O.\n\n```python3\nfrom pathlib import Path\n\nfrom asynctempfile import NamedTemporaryFile\nfrom aiopath import AsyncPath\n\n\nasync with NamedTemporaryFile() as temp:\n  path = Path(temp.name)\n  apath = AsyncPath(temp.name)\n\n  # check existence\n  ## sync\n  assert path.exists()\n  ## async\n  assert await apath.exists()\n\n  # check if file\n  ## sync\n  assert path.is_file()\n  ## async\n  assert await apath.is_file()\n\n  # touch\n  path.touch()\n  await apath.touch()\n\n  # PurePath methods are not async\n  assert path.is_absolute() == apath.is_absolute()\n  assert path.as_uri() == apath.as_uri()\n\n  # read and write text\n  text: str = 'example'\n  await apath.write_text(text)\n  assert await apath.read_text() == text\n\nassert not path.exists()\nassert not await apath.exists()\n```\n\nYou can convert `pathlib.Path` objects to `aiopath.AsyncPath` objects, and vice versa:\n```python3\nfrom pathlib import Path\nfrom aiopath import AsyncPath\n\n\nhome: Path = Path.home()\nahome: AsyncPath = AsyncPath(home)\npath: Path = Path(ahome)\n\nassert isinstance(home, Path)\nassert isinstance(ahome, AsyncPath)\nassert isinstance(path, Path)\n\n# AsyncPath and Path objects can point to the same file\nassert str(home) == str(ahome) == str(path)\n\n# AsyncPath and Path objects are equivalent\nassert home == ahome\n```\n\n`AsyncPath` is a subclass of `Path` and `PurePath`, and a subclass of `AsyncPurePath`:\n```python3\nfrom pathlib import Path, PurePath\nfrom aiopath import AsyncPath, AsyncPurePath\n\n\nassert issubclass(AsyncPath, Path)\nassert issubclass(AsyncPath, PurePath)\nassert issubclass(AsyncPath, AsyncPurePath)\nassert issubclass(AsyncPurePath, PurePath)\n\npath: AsyncPath = await AsyncPath.home()\n\nassert isinstance(path, Path)\nassert isinstance(path, PurePath)\nassert isinstance(path, AsyncPurePath) \n```\n\nCheck out the test files in the [`tests` directory](https://github.com/alexdelorenzo/aiopath/blob/main/tests) for more examples of how `aiopath` compares to `pathlib`.\n\n## Opening a file\nYou can get an asynchronous [file-like object handle](https://docs.python.org/3/glossary.html#term-file-object) by using [asynchronous context managers](https://docs.python.org/3/reference/datamodel.html#asynchronous-context-managers). \n\n`AsyncPath.open()`'s async context manager yields an [`anyio.AsyncFile`](https://anyio.readthedocs.io/en/stable/api.html#async-file-i-o) object.\n\n```python3\nfrom asynctempfile import NamedTemporaryFile\nfrom aiopath import AsyncPath\n\n\ntext: str = 'example'\n\n# you can access a file with async context managers\nasync with NamedTemporaryFile() as temp:\n  path = AsyncPath(temp.name)\n\n  async with path.open(mode='w') as file:\n    await file.write(text)\n\n  async with path.open(mode='r') as file:\n    result: str = await file.read()\n\n  assert result == text\n\n# or you can use the read/write convenience methods\nasync with NamedTemporaryFile() as temp:\n  path = AsyncPath(temp.name)\n\n  await path.write_text(text)\n  result: str = await path.read_text()\n  assert result == text\n\n  content: bytes = text.encode()\n\n  await path.write_bytes(content)\n  result: bytes = await path.read_bytes()\n  assert result == content\n```\n\n## [Globbing](https://en.wikipedia.org/wiki/Glob_(programming))\n`aiopath` implements [`pathlib` globbing](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob) using async I/O and async generators.\n\n```python3\nfrom aiopath import AsyncPath\n\n\nhome: AsyncPath = await AsyncPath.home()\n\nasync for path in home.glob('*'):\n  assert isinstance(path, AsyncPath)\n  print(path)\n\ndownloads: AsyncPath = home / 'Downloads'\n\nif await downloads.exists():\n  # this might take a while\n  paths: list[AsyncPath] = \\\n    [path async for path in downloads.glob('**/*')]\n```\n\n# Installation\n## Dependencies\n - A POSIX compliant OS, or Windows\n - Python 3.7+\n - `requirements.txt`\n - [Rye](https://rye.astral.sh) (for building)\n\n\u003c!--#### Linux dependencies\nIf you're using a 4.18 or newer kernel and have [`libaio`](https://pagure.io/libaio) installed, `aiopath` will use it via `aiofile`. You can install `libaio` on Debian/Ubuntu like so:\n```bash\n$ sudo apt install libaio1 libaio-dev\n```--\u003e\n\n## PyPI\n```bash\n$ python3 -m pip install aiopath\n```\n\n#### Python 3.9 and older\n`aiopath` for Python 3.9 and older is available on PyPI under versions `0.5.x` and lower.\n\n#### Python 3.10 and newer\n`aiopath` for Python 3.10 and newer is available on PyPI under versions `0.6.x` and higher.\n\n## GitHub\nDownload a release archive for your Python version from [the releases page](https://github.com/alexdelorenzo/aiopath/releases).\n\nThen to build, run:\n\n```bash\n$ rye sync\n$ rye build\n```\n\nA wheel will be compiled in `dist/`.\n\n#### Python 3.9 and older\n`aiopath` for Python 3.9 and older is developed on the [`Python-3.9` branch](https://github.com/alexdelorenzo/aiopath/tree/Python-3.9).\n\n#### Python 3.10 and newer\n`aiopath` for Python 3.10 and newer is developed on the [`Python-3.10` branch](https://github.com/alexdelorenzo/aiopath/tree/Python-3.10).\n\n# Support\nWant to support this project and [other open-source projects](https://github.com/alexdelorenzo) like it?\n\n\u003ca href=\"https://www.buymeacoffee.com/alexdelorenzo\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" height=\"60px\" style=\"height: 60px !important;width: 217px !important;max-width:25%\" \u003e\u003c/a\u003e\n\n# License\nSee `LICENSE`. If you'd like to use this project with a different license, please get in touch.\n\n\n# Credit\nSee [`CREDIT.md`](/CREDIT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexdelorenzo%2Faiopath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexdelorenzo%2Faiopath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexdelorenzo%2Faiopath/lists"}