From 0faf5b5f036dac53dd041ed1f9eec54d953c9a61 Mon Sep 17 00:00:00 2001 From: Stanley Kudrow Date: Tue, 12 Nov 2024 12:00:32 +0300 Subject: [PATCH 1/7] rebase --- src/aiofiles/base.py | 12 +++++++++++ src/aiofiles/os.py | 45 ++++++++++++++++++++++++------------------ src/aiofiles/ospath.py | 25 +++++++++-------------- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/aiofiles/base.py b/src/aiofiles/base.py index 35b26d2..26951f0 100644 --- a/src/aiofiles/base.py +++ b/src/aiofiles/base.py @@ -1,6 +1,7 @@ from asyncio import get_running_loop from collections.abc import Awaitable from contextlib import AbstractAsyncContextManager +from functools import partial, wraps class AsyncBase: @@ -65,3 +66,14 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): None, self._obj._file.__exit__, exc_type, exc_val, exc_tb ) self._obj = None + + +def wrap(func): + @wraps(func) + async def run(*args, loop=None, executor=None, **kwargs): + if loop is None: + loop = get_running_loop() + pfunc = partial(func, *args, **kwargs) + return await loop.run_in_executor(executor, pfunc) + + return run diff --git a/src/aiofiles/os.py b/src/aiofiles/os.py index 92243fa..ceeff14 100644 --- a/src/aiofiles/os.py +++ b/src/aiofiles/os.py @@ -2,8 +2,9 @@ import os -from . import ospath as path -from .ospath import wrap +from aiofiles.base import wrap +from aiofiles import ospath as path + __all__ = [ "path", @@ -25,34 +26,40 @@ "wrap", "getcwd", ] -if hasattr(os, "link"): - __all__ += ["link"] -if hasattr(os, "sendfile"): - __all__ += ["sendfile"] -if hasattr(os, "statvfs"): - __all__ += ["statvfs"] -stat = wrap(os.stat) +access = wrap(os.access) + +getcwd = wrap(os.getcwd) + +listdir = wrap(os.listdir) + +makedirs = wrap(os.makedirs) +mkdir = wrap(os.mkdir) + +readlink = wrap(os.readlink) +remove = wrap(os.remove) +removedirs = wrap(os.removedirs) rename = wrap(os.rename) renames = wrap(os.renames) replace = wrap(os.replace) -remove = wrap(os.remove) -unlink = wrap(os.unlink) -mkdir = wrap(os.mkdir) -makedirs = wrap(os.makedirs) rmdir = wrap(os.rmdir) -removedirs = wrap(os.removedirs) -symlink = wrap(os.symlink) -readlink = wrap(os.readlink) -listdir = wrap(os.listdir) + scandir = wrap(os.scandir) -access = wrap(os.access) -getcwd = wrap(os.getcwd) +stat = wrap(os.stat) +symlink = wrap(os.symlink) + +unlink = wrap(os.unlink) + +walk = wrap(os.walk) + if hasattr(os, "link"): + __all__ += ["link"] link = wrap(os.link) if hasattr(os, "sendfile"): + __all__ += ["sendfile"] sendfile = wrap(os.sendfile) if hasattr(os, "statvfs"): + __all__ += ["statvfs"] statvfs = wrap(os.statvfs) diff --git a/src/aiofiles/ospath.py b/src/aiofiles/ospath.py index 387d68d..35ff218 100644 --- a/src/aiofiles/ospath.py +++ b/src/aiofiles/ospath.py @@ -1,30 +1,23 @@ """Async executor versions of file functions from the os.path module.""" -import asyncio -from functools import partial, wraps from os import path +from aiofiles.base import wrap -def wrap(func): - @wraps(func) - async def run(*args, loop=None, executor=None, **kwargs): - if loop is None: - loop = asyncio.get_running_loop() - pfunc = partial(func, *args, **kwargs) - return await loop.run_in_executor(executor, pfunc) - - return run +abspath = wrap(path.abspath) exists = wrap(path.exists) -isfile = wrap(path.isfile) -isdir = wrap(path.isdir) -islink = wrap(path.islink) -ismount = wrap(path.ismount) + getsize = wrap(path.getsize) getmtime = wrap(path.getmtime) getatime = wrap(path.getatime) getctime = wrap(path.getctime) + +isfile = wrap(path.isfile) +isdir = wrap(path.isdir) +islink = wrap(path.islink) +ismount = wrap(path.ismount) + samefile = wrap(path.samefile) sameopenfile = wrap(path.sameopenfile) -abspath = wrap(path.abspath) From f750be1611384867caec1b97d3192071270171a3 Mon Sep 17 00:00:00 2001 From: Stanley Kudrow Date: Tue, 12 Nov 2024 12:14:42 +0300 Subject: [PATCH 2/7] remove docstring from the AsyncBase.__aiter__ --- src/aiofiles/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aiofiles/base.py b/src/aiofiles/base.py index 26951f0..795b365 100644 --- a/src/aiofiles/base.py +++ b/src/aiofiles/base.py @@ -15,7 +15,6 @@ def _loop(self): return self._ref_loop or get_running_loop() def __aiter__(self): - """We are our own iterator.""" return self def __repr__(self): From cddf0a993ebc8db0b8130aa5e27a1fe9abcf2132 Mon Sep 17 00:00:00 2001 From: Stanley Kudrow Date: Tue, 12 Nov 2024 12:36:49 +0300 Subject: [PATCH 3/7] add a test on os.walk wrappee --- tests/test_os.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_os.py b/tests/test_os.py index 76159d7..a361d97 100644 --- a/tests/test_os.py +++ b/tests/test_os.py @@ -3,7 +3,7 @@ import asyncio import os import platform -from os import stat +from os import stat, walk from os.path import dirname, exists, isdir, join from pathlib import Path @@ -498,3 +498,13 @@ async def test_abspath(): abs_filename = join(dirname(__file__), "resources", "test_file1.txt") result = await aiofiles.os.path.abspath(relative_filename) assert result == abs_filename + + +@pytest.mark.parametrize(("path",), [(".",), ("..",)]) +async def test_walk(path: str): + """Testing the `os.walk` wrapped function.""" + + os_walk_res = list(walk(top=path)) + aio_walk_res = list(await aiofiles.os.walk(top=path)) + + assert aio_walk_res == os_walk_res From 830cf426f9e13e57b1e9b2ec11489cc298cbeeb4 Mon Sep 17 00:00:00 2001 From: stankudrow Date: Sun, 8 Dec 2024 09:40:40 +0300 Subject: [PATCH 4/7] update the .gitignore with the latest Python template --- .gitignore | 116 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 4ecf26f..e46c23a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,13 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +*$py.class # C extensions *.so # Distribution / packaging .Python -env/ -pyvenv/ build/ develop-eggs/ dist/ @@ -20,10 +19,12 @@ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg -.venv* +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -38,12 +39,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml -*,cover +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo @@ -51,30 +57,118 @@ coverage.xml # Django stuff: *.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ # PyBuilder +.pybuilder/ target/ -# IDEA IDE files -.idea/ -*.iml -.pytest_cache +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +.python-version -# VScode -.vscode/ +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. -# https://pdm.fming.dev/#use-with-ide +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control .pdm.toml .pdm-python .pdm-build/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Visual Studio Code +.vscode + +# ruff stuff +.ruff_cache From b071b007a45e7a828a12eaa0c4c69f4907110497 Mon Sep 17 00:00:00 2001 From: stankudrow Date: Sun, 8 Dec 2024 10:01:22 +0300 Subject: [PATCH 5/7] add os.walk async generator implementation --- src/aiofiles/os.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/aiofiles/os.py b/src/aiofiles/os.py index ceeff14..19ea497 100644 --- a/src/aiofiles/os.py +++ b/src/aiofiles/os.py @@ -51,8 +51,6 @@ unlink = wrap(os.unlink) -walk = wrap(os.walk) - if hasattr(os, "link"): __all__ += ["link"] @@ -63,3 +61,15 @@ if hasattr(os, "statvfs"): __all__ += ["statvfs"] statvfs = wrap(os.statvfs) + + +async def walk(top, topdown=True, onerror=None, followlinks=False): + """Asynchronous directory tree generator. + + Wraps the `os.walk` function. + """ + + for content in os.walk( + top, topdown=topdown, onerror=onerror, followlinks=followlinks + ): + yield content From cda621e1f03c42bec8cd6f7d33915bf19618b56b Mon Sep 17 00:00:00 2001 From: stankudrow Date: Sun, 8 Dec 2024 10:03:07 +0300 Subject: [PATCH 6/7] fix os.walk test: from coro to asyncgen --- tests/test_os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_os.py b/tests/test_os.py index a361d97..59187ab 100644 --- a/tests/test_os.py +++ b/tests/test_os.py @@ -505,6 +505,6 @@ async def test_walk(path: str): """Testing the `os.walk` wrapped function.""" os_walk_res = list(walk(top=path)) - aio_walk_res = list(await aiofiles.os.walk(top=path)) + aio_walk_res = [r async for r in aiofiles.os.walk(top=path)] assert aio_walk_res == os_walk_res From 9d9c06c6321dde7369156a6345303412ead00d71 Mon Sep 17 00:00:00 2001 From: stankudrow Date: Sat, 28 Dec 2024 12:02:19 +0300 Subject: [PATCH 7/7] stick to relative imports --- src/aiofiles/ospath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiofiles/ospath.py b/src/aiofiles/ospath.py index 35ff218..92107c6 100644 --- a/src/aiofiles/ospath.py +++ b/src/aiofiles/ospath.py @@ -2,7 +2,7 @@ from os import path -from aiofiles.base import wrap +from .base import wrap abspath = wrap(path.abspath)