From 81afead103dc84eee0ecf29d3cdd0d633ca141ab Mon Sep 17 00:00:00 2001 From: Dmitry Orlov Date: Tue, 8 Oct 2024 11:28:23 +0200 Subject: [PATCH] Migrate to poetry --- .github/workflows/tests.yml | 14 +- MANIFEST.in | 6 - Makefile | 4 - README.md | 455 +++++++++++++++++++++++++++++++ README.rst | 514 ----------------------------------- aiofile/__init__.py | 1 + aiofile/version.py | 27 +- apache-v2.0.md | 195 ------------- poetry.lock | 529 ++++++++++++++++++++++++++++++++++++ pylama.ini | 5 - pyproject.toml | 76 ++++++ setup.cfg | 17 -- setup.py | 63 ----- tox.ini | 2 +- 14 files changed, 1081 insertions(+), 827 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 Makefile create mode 100644 README.md delete mode 100644 README.rst delete mode 100644 apache-v2.0.md create mode 100644 poetry.lock delete mode 100644 pylama.ini create mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eb37549..b96f459 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -62,9 +62,6 @@ jobs: - python-versions: "3.13.0-rc.3" toxenv: py313 os: ubuntu-latest - - python-versions: "3.10" - toxenv: py310 - os: macos-latest - python-versions: "3.11" toxenv: py311 os: macos-latest @@ -106,13 +103,16 @@ jobs: architecture: x64 - name: Creating a virtualenv python ${{ matrix.python-versions }} - run: python -m pip install tox + run: python -m pip install poetry - - name: tox - run: tox + - name: poetry install + run: poetry install + + - name: pytest + run: poetry run pytest --cov=aiofile --color=yes --cov-report=term-missing -vv tests README.md - name: coveralls - run: coveralls || true + run: poetry run coveralls || true env: COVERALLS_PARALLEL: 'true' COVERALLS_SERVICE_NAME: github diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 2d92962..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -recursive-exclude tests * -recursive-exclude systemd *.pyc -recursive-exclude __pycache__ * -recursive-include aiofile *.pyx *.pxd *.pyi -exclude .* -include README.md aiofile/py.typed diff --git a/Makefile b/Makefile deleted file mode 100644 index e2873e0..0000000 --- a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -build: sdist - -sdist: - python3 setup.py sdist bdist_wheel diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1d87a1 --- /dev/null +++ b/README.md @@ -0,0 +1,455 @@ +# AIOFile + +[![Github Actions](https://github.com/mosquito/aiofile/workflows/tox/badge.svg)](https://github.com/mosquito/aiofile/actions?query=branch%3Amaster) [![Latest Version](https://img.shields.io/pypi/v/aiofile.svg)](https://pypi.python.org/pypi/aiofile/) [![Wheel](https://img.shields.io/pypi/wheel/aiofile.svg)](https://pypi.python.org/pypi/aiofile/) [![Python Versions](https://img.shields.io/pypi/pyversions/aiofile.svg)](https://pypi.python.org/pypi/aiofile/) [![License](https://img.shields.io/pypi/l/aiofile.svg)](https://pypi.python.org/pypi/aiofile/) [![Coverage Status](https://coveralls.io/repos/github/mosquito/aiofile/badge.svg?branch=master)](https://coveralls.io/github/mosquito/aiofile?branch=master) + +Real asynchronous file operations with asyncio support. + +## Status + +Development - Stable + +## Features + +* Since version 2.0.0 using [caio](https://pypi.org/project/caio), which contains linux `libaio` and two + thread-based implementations (c-based and pure-python). +* AIOFile has no internal pointer. You should pass `offset` and + `chunk_size` for each operation or use helpers (Reader or Writer). + The simplest way is to use `async_open` for creating object with + file-like interface. +* For Linux using implementation based on [libaio](https://pagure.io/libaio). +* For POSIX (MacOS X and optional Linux) using implementation + based on [threadpool](https://github.com/mbrossard/threadpool/). +* Otherwise using pure-python thread-based implementation. +* Implementation chooses automatically depending on system compatibility. + +## Limitations + +* Linux native AIO implementation is not able to open special files. + Asynchronous operations against special fs like `/proc/` `/sys/` are not + supported by the kernel. It's not a aiofile's or caio issue. + In these cases, you might switch to thread-based implementations + (see [Troubleshooting](#troubleshooting) section). + However, when used on supported file systems, the linux implementation has a + smaller overhead and is preferred but it's not a silver bullet. + +## Code examples + +All code examples require Python 3.6+. + +### High-level API + +#### `async_open` helper + +Helper mimics python file-like objects, it returns file-like +objects with similar but async methods. + +Supported methods: + +* `async def read(length = -1)` - reading chunk from file, when length is + `-1`, will be reading file to the end. +* `async def write(data)` - writing chunk to file +* `def seek(offset)` - setting file pointer position +* `def tell()` - returns current file pointer position +* `async def readline(size=-1, newline="\n")` - read chunks until + newline or EOF. Since version 3.7.0 `__aiter__` returns `LineReader`. + + This method is suboptimal for small lines because it doesn't reuse read buffer. + When you want to read file by lines please avoid using `async_open` + use `LineReader` instead. +* `def __aiter__() -> LineReader` - iterator over lines. +* `def iter_chunked(chunk_size: int = 32768) -> Reader` - iterator over + chunks. +* `.file` property contains AIOFile object + +Basic example: + + +```python +import asyncio +from pathlib import Path +from tempfile import gettempdir + +from aiofile import async_open + +tmp_filename = Path(gettempdir()) / "hello.txt" + +async def main(): + async with async_open(tmp_filename, 'w+') as afp: + await afp.write("Hello ") + await afp.write("world") + afp.seek(0) + + print(await afp.read()) + + await afp.write("Hello from\nasync world") + print(await afp.readline()) + print(await afp.readline()) + +asyncio.run(main()) +``` + +Example without context manager: + + +```python +import asyncio +import atexit +import os +from tempfile import mktemp + +from aiofile import async_open + + +TMP_NAME = mktemp() +atexit.register(os.unlink, TMP_NAME) + + +async def main(): + afp = await async_open(TMP_NAME, "w") + await afp.write("Hello") + await afp.close() + + +asyncio.run(main()) +assert open(TMP_NAME, "r").read() == "Hello" +``` + +Concatenate example program (`cat`): + +```python +import asyncio +import sys +from argparse import ArgumentParser +from pathlib import Path + +from aiofile import async_open + +parser = ArgumentParser( + description="Read files line by line using asynchronous io API" +) +parser.add_argument("file_name", nargs="+", type=Path) + +async def main(arguments): + for src in arguments.file_name: + async with async_open(src, "r") as afp: + async for line in afp: + sys.stdout.write(line) + + +asyncio.run(main(parser.parse_args())) +``` + +Copy file example program (`cp`): + +```python +import asyncio +from argparse import ArgumentParser +from pathlib import Path + +from aiofile import async_open + +parser = ArgumentParser( + description="Copying files using asynchronous io API" +) +parser.add_argument("source", type=Path) +parser.add_argument("dest", type=Path) +parser.add_argument("--chunk-size", type=int, default=65535) + + +async def main(arguments): + async with async_open(arguments.source, "rb") as src, \ + async_open(arguments.dest, "wb") as dest: + async for chunk in src.iter_chunked(arguments.chunk_size): + await dest.write(chunk) + + +asyncio.run(main(parser.parse_args())) +``` + +Example with opening already open file pointer: + +```python +import asyncio +from typing import IO, Any +from aiofile import async_open + + +async def main(fp: IO[Any]): + async with async_open(fp) as afp: + await afp.write("Hello from\nasync world") + print(await afp.readline()) + + +with open("test.txt", "w+") as fp: + asyncio.run(main(fp)) +``` + +Linux native aio doesn't support reading and writing special files +(e.g. procfs/sysfs/unix pipes/etc.), so you can perform operations with +these files using compatible context objects. + +```python +import asyncio +from aiofile import async_open +from caio import thread_aio_asyncio +from contextlib import AsyncExitStack + + +async def main(): + async with AsyncExitStack() as stack: + + # Custom context should be reused + ctx = await stack.enter_async_context( + thread_aio_asyncio.AsyncioContext() + ) + + # Open special file with custom context + src = await stack.enter_async_context( + async_open("/proc/cpuinfo", "r", context=ctx) + ) + + # Open regular file with default context + dest = await stack.enter_async_context( + async_open("/tmp/cpuinfo", "w") + ) + + # Copying file content line by line + async for line in src: + await dest.write(line) + + +asyncio.run(main()) +``` + +### Low-level API + +The `AIOFile` class is a low-level interface for asynchronous file operations, and the read and write methods accept +an `offset=0` in bytes at which the operation will be performed. + +This allows you to do many independent IO operations on a once open file without moving the virtual carriage. + +For example, you may make 10 concurrent HTTP requests by specifying the `Range` header, and asynchronously write +one opened file, while the offsets must either be calculated manually, or use 10 instances of `Writer` with +specified initial offsets. + +In order to provide sequential reading and writing, there is `Writer`, `Reader` and `LineReader`. Keep in mind +`async_open` is not the same as AIOFile, it provides a similar interface for file operations, it simulates methods +like read or write as it is implemented in the built-in open. + +```python +import asyncio +from aiofile import AIOFile + + +async def main(): + async with AIOFile("hello.txt", 'w+') as afp: + payload = "Hello world\n" + + await asyncio.gather( + *[afp.write(payload, offset=i * len(payload)) for i in range(10)] + ) + + await afp.fsync() + + assert await afp.read(len(payload) * 10) == payload * 10 + +asyncio.run(main()) +``` + +The Low-level API in fact is just little bit sugared `caio` API. + +```python +import asyncio +from aiofile import AIOFile + + +async def main(): + async with AIOFile("/tmp/hello.txt", 'w+') as afp: + await afp.write("Hello ") + await afp.write("world", offset=7) + await afp.fsync() + + print(await afp.read()) + + +asyncio.run(main()) +``` + +#### `Reader` and `Writer` + +When you want to read or write file linearly following example +might be helpful. + +```python +import asyncio +from aiofile import AIOFile, Reader, Writer + + +async def main(): + async with AIOFile("/tmp/hello.txt", 'w+') as afp: + writer = Writer(afp) + reader = Reader(afp, chunk_size=8) + + await writer("Hello") + await writer(" ") + await writer("World") + await afp.fsync() + + async for chunk in reader: + print(chunk) + + +asyncio.run(main()) +``` + +#### `LineReader` - read file line by line + +LineReader is a helper that is very effective when you want to read a file +linearly and line by line. + +It contains a buffer and will read the fragments of the file chunk by +chunk into the buffer, where it will try to find lines. + +The default chunk size is 4KB. + +```python +import asyncio +from aiofile import AIOFile, LineReader, Writer + + +async def main(): + async with AIOFile("/tmp/hello.txt", 'w+') as afp: + writer = Writer(afp) + + await writer("Hello") + await writer(" ") + await writer("World") + await writer("\n") + await writer("\n") + await writer("From async world") + await afp.fsync() + + async for line in LineReader(afp): + print(line) + + +asyncio.run(main()) +``` + +When you want to read file by lines please avoid to use `async_open` +use `LineReader` instead. + +## More examples + +Useful examples with `aiofile` + +### Async CSV Dict Reader + +```python +import asyncio +import io +from csv import DictReader + +from aiofile import AIOFile, LineReader + + +class AsyncDictReader: + def __init__(self, afp, **kwargs): + self.buffer = io.BytesIO() + self.file_reader = LineReader( + afp, line_sep=kwargs.pop('line_sep', '\n'), + chunk_size=kwargs.pop('chunk_size', 4096), + offset=kwargs.pop('offset', 0), + ) + self.reader = DictReader( + io.TextIOWrapper( + self.buffer, + encoding=kwargs.pop('encoding', 'utf-8'), + errors=kwargs.pop('errors', 'replace'), + ), **kwargs, + ) + self.line_num = 0 + + def __aiter__(self): + return self + + async def __anext__(self): + if self.line_num == 0: + header = await self.file_reader.readline() + self.buffer.write(header) + + line = await self.file_reader.readline() + + if not line: + raise StopAsyncIteration + + self.buffer.write(line) + self.buffer.seek(0) + + try: + result = next(self.reader) + except StopIteration as e: + raise StopAsyncIteration from e + + self.buffer.seek(0) + self.buffer.truncate(0) + self.line_num = self.reader.line_num + + return result + + +async def main(): + async with AIOFile('sample.csv', 'rb') as afp: + async for item in AsyncDictReader(afp): + print(item) + + +asyncio.run(main()) +``` + +## Troubleshooting + +The caio `linux` implementation works normal for modern linux kernel versions +and file systems. So you may have problems specific for your environment. +It's not a bug and might be resolved some ways: + +1. Upgrade the kernel +2. Use compatible file systems +3. Use threads based or pure python implementation. + +The caio since version 0.7.0 contains some ways to do this. + +1. In runtime use the environment variable `CAIO_IMPL` with +possible values: + + * `linux` - use native linux kernels aio mechanism + * `thread` - use thread based implementation written in C + * `python` - use pure python implementation + +2. File `default_implementation` located near `__init__.py` in caio +installation path. It's useful for distros package maintainers. This file +might contains comments (lines starts with `#` symbol) and the first line +should be one of `linux` `thread` or `python`. + +3. You might manually manage contexts: + +```python +import asyncio + +from aiofile import async_open +from caio import linux_aio_asyncio, thread_aio_asyncio + + +async def main(): + linux_ctx = linux_aio_asyncio.AsyncioContext() + threads_ctx = thread_aio_asyncio.AsyncioContext() + + async with async_open("/tmp/test.txt", "w", context=linux_ctx) as afp: + await afp.write("Hello") + + async with async_open("/tmp/test.txt", "r", context=threads_ctx) as afp: + print(await afp.read()) + + +asyncio.run(main()) +``` diff --git a/README.rst b/README.rst deleted file mode 100644 index 8412949..0000000 --- a/README.rst +++ /dev/null @@ -1,514 +0,0 @@ -AIOFile -======= - -.. image:: https://github.com/mosquito/aiofile/workflows/tox/badge.svg - :target: https://github.com/mosquito/aiofile/actions?query=branch%3Amaster - :alt: Github Actions - -.. image:: https://img.shields.io/pypi/v/aiofile.svg - :target: https://pypi.python.org/pypi/aiofile/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/wheel/aiofile.svg - :target: https://pypi.python.org/pypi/aiofile/ - -.. image:: https://img.shields.io/pypi/pyversions/aiofile.svg - :target: https://pypi.python.org/pypi/aiofile/ - -.. image:: https://img.shields.io/pypi/l/aiofile.svg - :target: https://pypi.python.org/pypi/aiofile/ - -.. image:: https://coveralls.io/repos/github/mosquito/aiofile/badge.svg?branch=master - :target: https://coveralls.io/github/mosquito/aiofile?branch=master - - - -Real asynchronous file operations with asyncio support. - - -Status ------- - -Development - Stable - - -Features --------- - -* Since version 2.0.0 using `caio`_, which contains linux ``libaio`` and two - thread-based implementations (c-based and pure-python). -* AIOFile has no internal pointer. You should pass ``offset`` and - ``chunk_size`` for each operation or use helpers (Reader or Writer). - The simples way is to use ``async_open`` for creating object with - file-like interface. -* For Linux using implementation based on `libaio`_. -* For POSIX (MacOS X and optional Linux) using implementation - based on `threadpool`_. -* Otherwise using pure-python thread-based implementation. -* Implementation chooses automatically depending on system compatibility. - -.. _caio: https://pypi.org/project/caio -.. _libaio: https://pagure.io/libaio -.. _threadpool: https://github.com/mbrossard/threadpool/ - - -Limitations ------------ - -* Linux native AIO implementation is not able to open special files. - Asynchronous operations against special fs like ``/proc/`` ``/sys/`` are not - supported by the kernel. It's not a `aiofile`s or `caio` issue. - In this cases, you might switch to thread-based implementations - (see troubleshooting_ section). - However, when used on supported file systems, the linux implementation has a - smaller overhead and is preferred but it's not a silver bullet. - -Code examples -------------- - -All code examples requires python 3.6+. - -High-level API -++++++++++++++ - -``async_open`` helper -~~~~~~~~~~~~~~~~~~~~~ - -Helper mimics python file-like objects, it returns file-like -objects with similar but async methods. - -Supported methods: - -* ``async def read(length = -1)`` - reading chunk from file, when length is - ``-1``, will be reading file to the end. -* ``async def write(data)`` - writing chunk to file -* ``def seek(offset)`` - setting file pointer position -* ``def tell()`` - returns current file pointer position -* ``async def readline(size=-1, newline="\n")`` - read chunks until - newline or EOF. Since version 3.7.0 ``__aiter__`` returns ``LineReader``. - - This method is suboptimal for small lines because it doesn't reuse read buffer. - When you want to read file by lines please avoid using ``async_open`` - use ``LineReader`` instead. -* ``def __aiter__() -> LineReader`` - iterator over lines. -* ``def iter_chunked(chunk_size: int = 32768) -> Reader`` - iterator over - chunks. -* ``.file`` property contains AIOFile object - - -Basic example: - -.. code-block:: python - :name: test_basic - - import asyncio - from pathlib import Path - from tempfile import gettempdir - - from aiofile import async_open - - tmp_filename = Path(gettempdir()) / "hello.txt" - - async def main(): - async with async_open(tmp_filename, 'w+') as afp: - await afp.write("Hello ") - await afp.write("world") - afp.seek(0) - - print(await afp.read()) - - await afp.write("Hello from\nasync world") - print(await afp.readline()) - print(await afp.readline()) - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - -Example without context manager: - -.. code-block:: python - :name: test_no_context_manager - - import asyncio - import atexit - import os - from tempfile import mktemp - - from aiofile import async_open - - - TMP_NAME = mktemp() - atexit.register(os.unlink, TMP_NAME) - - - async def main(): - afp = await async_open(TMP_NAME, "w") - await afp.write("Hello") - await afp.close() - - - asyncio.run(main()) - assert open(TMP_NAME, "r").read() == "Hello" - - -Concatenate example program (``cat``): - -.. code-block:: python - - import asyncio - import sys - from argparse import ArgumentParser - from pathlib import Path - - from aiofile import async_open - - parser = ArgumentParser( - description="Read files line by line using asynchronous io API" - ) - parser.add_argument("file_name", nargs="+", type=Path) - - async def main(arguments): - for src in arguments.file_name: - async with async_open(src, "r") as afp: - async for line in afp: - sys.stdout.write(line) - - - asyncio.run(main(parser.parse_args())) - - -Copy file example program (``cp``): - -.. code-block:: python - - import asyncio - from argparse import ArgumentParser - from pathlib import Path - - from aiofile import async_open - - parser = ArgumentParser( - description="Copying files using asynchronous io API" - ) - parser.add_argument("source", type=Path) - parser.add_argument("dest", type=Path) - parser.add_argument("--chunk-size", type=int, default=65535) - - - async def main(arguments): - async with async_open(arguments.source, "rb") as src, \ - async_open(arguments.dest, "wb") as dest: - async for chunk in src.iter_chunked(arguments.chunk_size): - await dest.write(chunk) - - - asyncio.run(main(parser.parse_args())) - - -Example with opening already open file pointer: - -.. code-block:: python - :name: test_opened - - import asyncio - from typing import IO, Any - from aiofile import async_open - - - async def main(fp: IO[Any]): - async with async_open(fp) as afp: - await afp.write("Hello from\nasync world") - print(await afp.readline()) - - - with open("test.txt", "w+") as fp: - asyncio.run(main(fp)) - - -Linux native aio doesn't support reading and writing special files -(e.g. procfs/sysfs/unix pipes/etc.), so you can perform operations with -these files using compatible context objects. - -.. code-block:: python - - import asyncio - from aiofile import async_open - from caio import thread_aio_asyncio - from contextlib import AsyncExitStack - - - async def main(): - async with AsyncExitStack() as stack: - - # Custom context should be reused - ctx = await stack.enter_async_context( - thread_aio_asyncio.AsyncioContext() - ) - - # Open special file with custom context - src = await stack.enter_async_context( - async_open("/proc/cpuinfo", "r", context=ctx) - ) - - # Open regular file with default context - dest = await stack.enter_async_context( - async_open("/tmp/cpuinfo", "w") - ) - - # Copying file content line by line - async for line in src: - await dest.write(line) - - - asyncio.run(main()) - -Low-level API -++++++++++++++ - -The `AIOFile` class is a low-level interface for asynchronous file operations, and the read and write methods accept -an `offset=0` in bytes at which the operation will be performed. - -This allows you to do many independent IO operations on an once open file without moving the virtual carriage. - -For example, you may make 10 concurrent HTTP requests by specifying the `Range` header, and asynchronously write -one opened file, while the offsets must either be calculated manually, or use 10 instances of `Writer` with -specified initial offsets. - -In order to provide sequential reading and writing, there is `Writer`, `Reader` and `LineReader`. Keep in mind -`async_open` is not the same as AIOFile, it provides a similar interface for file operations, it simulates methods -like read or write as it is implemented in the built-in open. - -.. code-block:: python - :name: test_low_level_api - - import asyncio - from aiofile import AIOFile - - - async def main(): - async with AIOFile("hello.txt", 'w+') as afp: - payload = "Hello world\n" - - await asyncio.gather( - *[afp.write(payload, offset=i * len(payload)) for i in range(10)] - ) - - await afp.fsync() - - assert await afp.read(len(payload) * 10) == payload * 10 - - asyncio.run(main()) - -The Low-level API in fact is just little bit sugared ``caio`` API. - -.. code-block:: python - - import asyncio - from aiofile import AIOFile - - - async def main(): - async with AIOFile("/tmp/hello.txt", 'w+') as afp: - await afp.write("Hello ") - await afp.write("world", offset=7) - await afp.fsync() - - print(await afp.read()) - - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - -``Reader`` and ``Writer`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you want to read or write file linearly following example -might be helpful. - -.. code-block:: python - - import asyncio - from aiofile import AIOFile, Reader, Writer - - - async def main(): - async with AIOFile("/tmp/hello.txt", 'w+') as afp: - writer = Writer(afp) - reader = Reader(afp, chunk_size=8) - - await writer("Hello") - await writer(" ") - await writer("World") - await afp.fsync() - - async for chunk in reader: - print(chunk) - - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - - -``LineReader`` - read file line by line -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -LineReader is a helper that is very effective when you want to read a file -linearly and line by line. - -It contains a buffer and will read the fragments of the file chunk by -chunk into the buffer, where it will try to find lines. - -The default chunk size is 4KB. - -.. code-block:: python - - import asyncio - from aiofile import AIOFile, LineReader, Writer - - - async def main(): - async with AIOFile("/tmp/hello.txt", 'w+') as afp: - writer = Writer(afp) - - await writer("Hello") - await writer(" ") - await writer("World") - await writer("\n") - await writer("\n") - await writer("From async world") - await afp.fsync() - - async for line in LineReader(afp): - print(line) - - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - -When you want to read file by lines please avoid to use ``async_open`` -use ``LineReader`` instead. - - -More examples -------------- - -Useful examples with ``aiofile`` - -Async CSV Dict Reader -+++++++++++++++++++++ - -.. code-block:: python - - import asyncio - import io - from csv import DictReader - - from aiofile import AIOFile, LineReader - - - class AsyncDictReader: - def __init__(self, afp, **kwargs): - self.buffer = io.BytesIO() - self.file_reader = LineReader( - afp, line_sep=kwargs.pop('line_sep', '\n'), - chunk_size=kwargs.pop('chunk_size', 4096), - offset=kwargs.pop('offset', 0), - ) - self.reader = DictReader( - io.TextIOWrapper( - self.buffer, - encoding=kwargs.pop('encoding', 'utf-8'), - errors=kwargs.pop('errors', 'replace'), - ), **kwargs, - ) - self.line_num = 0 - - def __aiter__(self): - return self - - async def __anext__(self): - if self.line_num == 0: - header = await self.file_reader.readline() - self.buffer.write(header) - - line = await self.file_reader.readline() - - if not line: - raise StopAsyncIteration - - self.buffer.write(line) - self.buffer.seek(0) - - try: - result = next(self.reader) - except StopIteration as e: - raise StopAsyncIteration from e - - self.buffer.seek(0) - self.buffer.truncate(0) - self.line_num = self.reader.line_num - - return result - - - async def main(): - async with AIOFile('sample.csv', 'rb') as afp: - async for item in AsyncDictReader(afp): - print(item) - - - asyncio.run(main()) - - -.. _troubleshooting: - -Troubleshooting ---------------- - -The caio ``linux`` implementation works normal for modern linux kernel versions -and file systems. So you may have problems specific for your environment. -It's not a bug and might be resolved some ways: - -1. Upgrade the kernel -2. Use compatible file systems -3. Use threads based or pure python implementation. - -The caio since version 0.7.0 contains some ways to do this. - -1. In runtime use the environment variable ``CAIO_IMPL`` with -possible values: - -* ``linux`` - use native linux kernels aio mechanism -* ``thread`` - use thread based implementation written in C -* ``python`` - use pure python implementation - -2. File ``default_implementation`` located near ``__init__.py`` in caio -installation path. It's useful for distros package maintainers. This file -might contains comments (lines starts with ``#`` symbol) and the first line -should be one of ``linux`` ``thread`` or ``python``. - -3. You might manually manage contexts: - -.. code-block:: python - - import asyncio - - from aiofile import async_open - from caio import linux_aio_asyncio, thread_aio_asyncio - - - async def main(): - linux_ctx = linux_aio_asyncio.AsyncioContext() - threads_ctx = thread_aio_asyncio.AsyncioContext() - - async with async_open("/tmp/test.txt", "w", context=linux_ctx) as afp: - await afp.write("Hello") - - async with async_open("/tmp/test.txt", "r", context=threads_ctx) as afp: - print(await afp.read()) - - - asyncio.run(main()) diff --git a/aiofile/__init__.py b/aiofile/__init__.py index 50c257d..444e91e 100644 --- a/aiofile/__init__.py +++ b/aiofile/__init__.py @@ -3,6 +3,7 @@ BinaryFileWrapper, FileIOWrapperBase, LineReader, Reader, TextFileWrapper, Writer, async_open, ) + from .version import ( __author__, __version__, author_info, package_info, package_license, project_home, team_email, version_info, diff --git a/aiofile/version.py b/aiofile/version.py index 7ec53dd..9f149f7 100644 --- a/aiofile/version.py +++ b/aiofile/version.py @@ -1,15 +1,12 @@ -author_info = ( - ("Dmitry Orlov", "me@mosquito.su"), -) - -project_home = "http://github.com/mosquito/aiofile" - -package_info = "Asynchronous file operations." -package_license = "Apache 2" - -team_email = "me@mosquito.su" - -version_info = (3, 8, 9) - -__author__ = ", ".join("{} <{}>".format(*info) for info in author_info) -__version__ = ".".join(map(str, version_info)) +import importlib.metadata + +package_metadata = importlib.metadata.metadata('aiofile') + +__author__ = package_metadata['Author'] +__version__ = package_metadata['Version'] +author_info = [(package_metadata['Author'], package_metadata['Author-email'])] +package_info = package_metadata['Summary'] +package_license = package_metadata['License'] +project_home = package_metadata['Home-page'] +team_email = package_metadata['Author-email'] +version_info = tuple(map(int, __version__.split('.'))) \ No newline at end of file diff --git a/apache-v2.0.md b/apache-v2.0.md deleted file mode 100644 index f5f4b8b..0000000 --- a/apache-v2.0.md +++ /dev/null @@ -1,195 +0,0 @@ -Apache License -============== - -_Version 2.0, January 2004_ -_<>_ - -### Terms and Conditions for use, reproduction, and distribution - -#### 1. Definitions - -“License” shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -“Licensor” shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -“Legal Entity” shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, “control” means **(i)** the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the -outstanding shares, or **(iii)** beneficial ownership of such entity. - -“You” (or “Your”) shall mean an individual or Legal Entity exercising -permissions granted by this License. - -“Source” form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -“Object” form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -“Work” shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -“Derivative Works” shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -“Contribution” shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -“submitted” means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as “Not a Contribution.” - -“Contributor” shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -#### 2. Grant of Copyright License - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -#### 3. Grant of Patent License - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -#### 4. Redistribution - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -* **(a)** You must give any other recipients of the Work or Derivative Works a copy of -this License; and -* **(b)** You must cause any modified files to carry prominent notices stating that You -changed the files; and -* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -#### 5. Submission of Contributions - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -#### 6. Trademarks - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -#### 7. Disclaimer of Warranty - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -#### 8. Limitation of Liability - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -#### 9. Accepting Warranty or Additional Liability - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -_END OF TERMS AND CONDITIONS_ - -### APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets `[]` replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same “printed page” as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..8fb1391 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,529 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "aiomisc" +version = "17.5.26" +description = "aiomisc - miscellaneous utils for asyncio" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "aiomisc-17.5.26-py3-none-any.whl", hash = "sha256:1608362ff510741c19eda79be187c525e7b01bc091d4aa304cee89e2eb38d579"}, + {file = "aiomisc-17.5.26.tar.gz", hash = "sha256:efc37c481ce0501d0b9db8fbf6bde37e1c7a67096c3fd12aee04c83e46523f33"}, +] + +[package.dependencies] +colorlog = ">=6.0,<7.0" +logging-journald = {version = "*", markers = "sys_platform == \"linux\""} +typing_extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +aiohttp = ["aiohttp (>3)"] +asgi = ["aiohttp-asgi (>=0.5.2,<0.6.0)"] +carbon = ["aiocarbon (>=0.15,<0.16)"] +cron = ["croniter (==2.0)"] +dns = ["dnslib (>=0.9,<0.10)"] +grpc = ["grpcio (>=1.56,<2.0)", "grpcio-reflection (>=1.56,<2.0)", "grpcio-tools (>=1.56,<2.0)"] +raven = ["aiohttp (>3)", "raven"] +rich = ["rich"] +uvicorn = ["asgiref (>=3.7,<4.0)", "uvicorn (>=0.27,<0.28)"] +uvloop = ["uvloop (>=0.19,<1)"] + +[[package]] +name = "aiomisc-pytest" +version = "1.2.1" +description = "pytest integration for aiomisc" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "aiomisc_pytest-1.2.1-py3-none-any.whl", hash = "sha256:24a3802f0b794625a6bce0032f840c5e643e6f075c8a448297f1e92789c38736"}, + {file = "aiomisc_pytest-1.2.1.tar.gz", hash = "sha256:e2658fefb4770a85fe5e1a13e816f142dc6920da5d99ea338d852c7a035f3325"}, +] + +[package.dependencies] +aiomisc = ">=17" +pytest = "8.2.0" + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "caio" +version = "0.9.17" +description = "Asynchronous file IO for Linux MacOS or Windows." +optional = false +python-versions = "<4,>=3.7" +files = [ + {file = "caio-0.9.17-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3f69395fdd45c115b2ef59732e3c8664722a2b51de2d6eedb3d354b2f5f3be3c"}, + {file = "caio-0.9.17-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3028b746e9ec7f6d6ebb386a7fd8caf0eebed5d6e6b4f18c8ef25861934b1673"}, + {file = "caio-0.9.17-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:079730a353bbde03796fab681e969472eace09ffbe5000e584868a7fe389ba6f"}, + {file = "caio-0.9.17-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:549caa51b475877fe32856a26fe937366ae7a1c23a9727005b441db9abb12bcc"}, + {file = "caio-0.9.17-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0ddb253b145a53ecca76381677ce465bc5efeaecb6aaf493fac43ae79659f0fb"}, + {file = "caio-0.9.17-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3e320b0ea371c810359934f8e8fe81777c493cc5fb4d41de44277cbe7336e74"}, + {file = "caio-0.9.17-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:a39a49e279f82aa022f0786339d45d9550b5aa3e46eec7d08e0f351c503df0a5"}, + {file = "caio-0.9.17-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3e96925b9f15f43e6ef1d42a83edfd937eb11a984cb6ef7c10527e963595497"}, + {file = "caio-0.9.17-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fca916240597005d2b734f1442fa3c3cfb612bf46e0978b5232e5492a371de38"}, + {file = "caio-0.9.17-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40bd0afbd3491d1e407bcf74e3a9e9cc67a7f290ed29518325194184d63cc2b6"}, + {file = "caio-0.9.17-py3-none-any.whl", hash = "sha256:c55d4dc6b3a36f93237ecd6360e1c131c3808bc47d4191a130148a99b80bb311"}, + {file = "caio-0.9.17.tar.gz", hash = "sha256:8f30511526814d961aeef389ea6885273abe6c655f1e08abbadb95d12fdd9b4f"}, +] + +[package.extras] +develop = ["aiomisc-pytest", "pytest", "pytest-cov"] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colorlog" +version = "6.8.2" +description = "Add colours to the output of Python's logging module." +optional = false +python-versions = ">=3.6" +files = [ + {file = "colorlog-6.8.2-py3-none-any.whl", hash = "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"}, + {file = "colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "coverage" +version = "6.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "coveralls" +version = "3.3.1" +description = "Show coverage stats online via coveralls.io" +optional = false +python-versions = ">= 3.5" +files = [ + {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, + {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, +] + +[package.dependencies] +coverage = ">=4.1,<6.0.dev0 || >6.1,<6.1.1 || >6.1.1,<7.0" +docopt = ">=0.6.1" +requests = ">=1.0.0" + +[package.extras] +yaml = ["PyYAML (>=3.10)"] + +[[package]] +name = "docopt" +version = "0.6.2" +description = "Pythonic argument parser, that will make you smile" +optional = false +python-versions = "*" +files = [ + {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "logging-journald" +version = "0.6.9" +description = "Pure python logging handler for writing logs to the journald using native protocol" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "logging_journald-0.6.9-py3-none-any.whl", hash = "sha256:c3078b5db5e3e94a75fbf624d3255b3dfbf0bcbde7f36bad77271a7422acd0b0"}, + {file = "logging_journald-0.6.9.tar.gz", hash = "sha256:f4b242de387e24509e8dd490eac7980f98e2f82b259581d2c5936e1b477ec126"}, +] + +[[package]] +name = "markdown-pytest" +version = "0.3.2" +description = "Pytest plugin for runs tests directly from Markdown files" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "markdown_pytest-0.3.2-py3-none-any.whl", hash = "sha256:2faabe7bfba232b2b2075d6b552580d370733c5ff77934f41bcfcb960b06d09f"}, + {file = "markdown_pytest-0.3.2.tar.gz", hash = "sha256:ddb1b746e18fbdaab97bc2058b90c0b3d71063c8265a88a8c755f433f94a5e12"}, +] + +[package.dependencies] +pytest-subtests = ">=0.12.0" + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pytest" +version = "8.2.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, + {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-subtests" +version = "0.13.1" +description = "unittest subTest() support and subtests fixture" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest_subtests-0.13.1-py3-none-any.whl", hash = "sha256:ab616a22f64cd17c1aee65f18af94dbc30c444f8683de2b30895c3778265e3bd"}, + {file = "pytest_subtests-0.13.1.tar.gz", hash = "sha256:989e38f0f1c01bc7c6b2e04db7d9fd859db35d77c2c1a430c831a70cbf3fde2d"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +pytest = ">=7.0" + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "tomli" +version = "2.0.2" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8,<4" +content-hash = "e5efdf7c742b61a7695d59138c4a0c143304841128ac63acb63380b0226e8268" diff --git a/pylama.ini b/pylama.ini deleted file mode 100644 index b00b99f..0000000 --- a/pylama.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pylama] -skip = env*,.tox*,*build* - -[pylava:pycodestyle] -max_line_length = 80 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3c37376 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +[tool.poetry] +name = "aiofile" +version = "3.9.0" +description = "Asynchronous file operations." +license = "Apache-2.0" +authors = ["Dmitry Orlov "] +readme = "README.md" +homepage = "http://github.com/mosquito/aiofile" +keywords = ["aio", "python", "asyncio", "fileio", "io"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Natural Language :: Russian", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Software Development :: Libraries", + "Topic :: System", + "Topic :: System :: Operating System", +] +packages = [ + { include = "aiofile" }, +] + +[tool.poetry.dependencies] +python = ">=3.8,<4" +caio = "~0.9.0" + +[tool.poetry.group.dev.dependencies] +markdown-pytest = "^0.3.2" +pytest = ">=8.2.0,<8.3.0" +aiomisc-pytest = "^1.2.1" +pytest-cov = "^5.0.0" +coveralls = "<4" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.mypy] +check_untyped_defs = true +disallow_any_generics = false +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +follow_imports = "silent" +no_implicit_reexport = true +strict_optional = true +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true + +[tool.mypy.tests] +ignore_errors = true + +[tool.pylama] +skip = ["env*", ".tox*", "*build*"] + +[tool.pylama.pycodestyle] +max_line_length = 80 + diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index e773e2f..0000000 --- a/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[mypy] -check_untyped_defs = True -disallow_any_generics = False -disallow_incomplete_defs = True -disallow_subclassing_any = True -disallow_untyped_calls = True -disallow_untyped_decorators = True -disallow_untyped_defs = True -follow_imports = silent -no_implicit_reexport = True -strict_optional = True -warn_redundant_casts = True -warn_unused_configs = True -warn_unused_ignores = True - -[mypy-tests.*] -ignore_errors = True diff --git a/setup.py b/setup.py deleted file mode 100644 index a93dcd4..0000000 --- a/setup.py +++ /dev/null @@ -1,63 +0,0 @@ -from importlib.machinery import SourceFileLoader -from os import path - -from setuptools import setup - - -module = SourceFileLoader( - "version", path.join("aiofile", "version.py"), -).load_module() - -libraries = [] - - -setup( - name="aiofile", - version=module.__version__, - packages=["aiofile"], - include_package_data=True, - license=module.package_license, - description=module.package_info, - long_description=open("README.rst").read(), - platforms=["POSIX"], - url=module.project_home, - author=module.__author__, - author_email=module.team_email, - provides=["aiofile"], - keywords=["aio", "python", "asyncio", "fileio", "io"], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "Intended Audience :: Education", - "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: Apache Software License", - "Natural Language :: English", - "Natural Language :: Russian", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS :: MacOS X", - "Programming Language :: Python", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Software Development :: Libraries", - "Topic :: System", - "Topic :: System :: Operating System", - ], - python_requires=">=3.7, <4", - extras_require={ - "develop": [ - "aiomisc-pytest", - "pytest", - "pytest-rst", - "pytest-cov", - "coveralls", - ], - }, - install_requires=["caio~=0.9.0"], -) diff --git a/tox.ini b/tox.ini index 55fb19b..497a974 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ extras = develop commands= - py.test --cov=aiofile --color=yes --cov-report=term-missing -vv tests README.rst + [testenv:lint] deps =