pax_global_header00006660000000000000000000000064147373142320014520gustar00rootroot0000000000000052 comment=40996b578546e18a0dff313dda8e9a7d6b586525 python-aiormq-6.8.1/000077500000000000000000000000001473731423200143435ustar00rootroot00000000000000python-aiormq-6.8.1/.coveragerc000066400000000000000000000001001473731423200164530ustar00rootroot00000000000000[run] branch = True omit = */env/* */tests/* */.*/* python-aiormq-6.8.1/.editorconfig000066400000000000000000000004351473731423200170220ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true [*.{py,yml}] indent_style = space [*.py] indent_size = 4 [docs/**.py] max_line_length = 80 [*.rst] indent_size = 4 [Makefile] indent_style = tab [*.yml] indent_size = 2 python-aiormq-6.8.1/.github/000077500000000000000000000000001473731423200157035ustar00rootroot00000000000000python-aiormq-6.8.1/.github/workflows/000077500000000000000000000000001473731423200177405ustar00rootroot00000000000000python-aiormq-6.8.1/.github/workflows/tests.yml000066400000000000000000000064711473731423200216350ustar00rootroot00000000000000name: tests on: push: branches: [ master ] pull_request: branches: [ master ] jobs: pylama: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup python3.10 uses: actions/setup-python@v2 with: python-version: "3.10" - name: Cache virtualenv id: venv-cache uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ github.job }}-${{ github.ref }} - run: python -m pip install poetry - run: poetry install - run: poetry run pylama env: FORCE_COLOR: 1 mypy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup python3.10 uses: actions/setup-python@v2 with: python-version: "3.10" - name: Cache virtualenv id: venv-cache uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ github.job }}-${{ github.ref }} - run: python -m pip install poetry - run: poetry install - run: poetry run mypy env: FORCE_COLOR: 1 docs: runs-on: ubuntu-latest services: rabbitmq: image: docker://mosquito/aiormq-rabbitmq ports: - 5672:5672 - 5671:5671 steps: - uses: actions/checkout@v2 - name: Setup python3.10 uses: actions/setup-python@v2 with: python-version: "3.10" - name: Cache virtualenv id: venv-cache uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ github.job }}-${{ github.ref }} - run: python -m pip install poetry - run: poetry install - run: poetry run pytest -svv README.rst env: FORCE_COLOR: 1 tests: runs-on: ubuntu-latest services: rabbitmq: image: docker://mosquito/aiormq-rabbitmq ports: - 5672:5672 - 5671:5671 strategy: fail-fast: false matrix: python: - '3.8' - '3.9' - '3.10' - '3.11' - '3.12' steps: - uses: actions/checkout@v2 - name: Setup python${{ matrix.python }} uses: actions/setup-python@v2 with: python-version: "${{ matrix.python }}" - name: Cache virtualenv id: venv-cache uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ github.job }}-${{ github.ref }}-${{ matrix.python }} - run: python -m pip install poetry - run: poetry install --with=uvloop - name: pytest run: >- poetry run pytest \ -vv \ --cov=aiormq \ --cov-report=term-missing \ --doctest-modules \ --aiomisc-test-timeout=120 \ tests env: FORCE_COLOR: 1 - run: poetry run coveralls env: COVERALLS_PARALLEL: 'true' COVERALLS_SERVICE_NAME: github GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} finish: needs: - tests runs-on: ubuntu-latest steps: - name: Coveralls Finished uses: coverallsapp/github-action@master with: github-token: ${{ secrets.github_token }} parallel-finished: true python-aiormq-6.8.1/.gitignore000066400000000000000000000037371473731423200163450ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) ### VirtualEnv template # Virtualenv # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ .Python [Bb]in [Ii]nclude [Ll]ib [Ll]ib64 [Ll]ocal [Ss]cripts pyvenv.cfg .venv pip-selfcheck.json ### IPythonNotebook template # Temporary data .ipynb_checkpoints/ ### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ docs/source/apidoc # PyBuilder target/ # IPython Notebook .ipynb_checkpoints # pyenv .python-version # pytest .pytest_cache # celery beat schedule file celerybeat-schedule # dotenv .env # virtualenv venv/ ENV/ # Spyder project settings .spyderproject # Rope project settings .ropeproject ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff: .idea/ ## File-based project format: *.iws ## Plugin-specific files: # IntelliJ /out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties /htmlcov /temp .DS_Store .*cache python-aiormq-6.8.1/COPYING000066400000000000000000000243301473731423200154000ustar00rootroot00000000000000Apache 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 2023 Dmitry Orlov 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. python-aiormq-6.8.1/Makefile000066400000000000000000000011501473731423200160000ustar00rootroot00000000000000all: clean test NAME:=$(shell poetry version -n | awk '{print $1}') VERSION:=$(shell poetry version -s) RABBITMQ_CONTAINER_NAME:=aiormq_rabbitmq RABBITMQ_IMAGE:=mosquito/aiormq-rabbitmq rabbitmq: docker kill $(RABBITMQ_CONTAINER_NAME) || true docker run --pull=always --rm -d \ --name $(RABBITMQ_CONTAINER_NAME) \ -p 5671:5671 \ -p 5672:5672 \ -p 15671:15671 \ -p 15672:15672 \ $(RABBITMQ_IMAGE) upload: poetry publish --build --skip-existing test: poetry run pytest -vvx --cov=aiormq \ --cov-report=term-missing tests README.rst clean: rm -fr *.egg-info .tox develop: clean poetry install python-aiormq-6.8.1/README.rst000066400000000000000000000420221473731423200160320ustar00rootroot00000000000000====== AIORMQ ====== .. image:: https://coveralls.io/repos/github/mosquito/aiormq/badge.svg?branch=master :target: https://coveralls.io/github/mosquito/aiormq?branch=master :alt: Coveralls .. image:: https://img.shields.io/pypi/status/aiormq.svg :target: https://github.com/mosquito/aiormq :alt: Status .. image:: https://github.com/mosquito/aiormq/workflows/tests/badge.svg :target: https://github.com/mosquito/aiormq/actions?query=workflow%3Atests :alt: Build status .. image:: https://img.shields.io/pypi/v/aiormq.svg :target: https://pypi.python.org/pypi/aiormq/ :alt: Latest Version .. image:: https://img.shields.io/pypi/wheel/aiormq.svg :target: https://pypi.python.org/pypi/aiormq/ .. image:: https://img.shields.io/pypi/pyversions/aiormq.svg :target: https://pypi.python.org/pypi/aiormq/ .. image:: https://img.shields.io/pypi/l/aiormq.svg :target: https://github.com/mosquito/aiormq/blob/master/LICENSE.md aiormq is a pure python AMQP client library. .. contents:: Table of contents Status ====== * 3.x.x branch - Production/Stable * 4.x.x branch - Unstable (Experimental) * 5.x.x and greater is only Production/Stable releases. Features ======== * Connecting by URL * amqp example: **amqp://user:password@server.host/vhost** * secure amqp example: **amqps://user:password@server.host/vhost?cafile=ca.pem&keyfile=key.pem&certfile=cert.pem&no_verify_ssl=0** * Buffered queue for received frames * Only `PLAIN`_ auth mechanism support * `Publisher confirms`_ support * `Transactions`_ support * Channel based asynchronous locks .. note:: AMQP 0.9.1 requires serialize sending for some frame types on the channel. e.g. Content body must be following after content header. But frames might be sent asynchronously on another channels. * Tracking unroutable messages (Use **connection.channel(on_return_raises=False)** for disabling) * Full SSL/TLS support, using your choice of: * ``amqps://`` url query parameters: * ``cafile=`` - string contains path to ca certificate file * ``capath=`` - string contains path to ca certificates * ``cadata=`` - base64 encoded ca certificate data * ``keyfile=`` - string contains path to key file * ``certfile=`` - string contains path to certificate file * ``no_verify_ssl`` - boolean disables certificates validation * ``context=`` `SSLContext`_ keyword argument to ``connect()``. * Python `type hints`_ * Uses `pamqp`_ as an AMQP 0.9.1 frame encoder/decoder .. _Publisher confirms: https://www.rabbitmq.com/confirms.html .. _Transactions: https://www.rabbitmq.com/semantics.html .. _PLAIN: https://www.rabbitmq.com/authentication.html .. _type hints: https://docs.python.org/3/library/typing.html .. _pamqp: https://pypi.org/project/pamqp/ .. _SSLContext: https://docs.python.org/3/library/ssl.html#ssl.SSLContext Tutorial ======== Introduction ------------ Simple consumer *************** .. code-block:: python import asyncio import aiormq async def on_message(message): """ on_message doesn't necessarily have to be defined as async. Here it is to show that it's possible. """ print(f" [x] Received message {message!r}") print(f"Message body is: {message.body!r}") print("Before sleep!") await asyncio.sleep(5) # Represents async I/O operations print("After sleep!") async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() # Declaring queue declare_ok = await channel.queue_declare('helo') consume_ok = await channel.basic_consume( declare_ok.queue, on_message, no_ack=True ) loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.run_forever() Simple publisher **************** .. code-block:: python :name: test_simple_publisher import asyncio from typing import Optional import aiormq from aiormq.abc import DeliveredMessage MESSAGE: Optional[DeliveredMessage] = None async def main(): global MESSAGE body = b'Hello World!' # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost//") # Creating a channel channel = await connection.channel() declare_ok = await channel.queue_declare("hello", auto_delete=True) # Sending the message await channel.basic_publish(body, routing_key='hello') print(f" [x] Sent {body}") MESSAGE = await channel.basic_get(declare_ok.queue) print(f" [x] Received message from {declare_ok.queue!r}") loop = asyncio.get_event_loop() loop.run_until_complete(main()) assert MESSAGE is not None assert MESSAGE.routing_key == "hello" assert MESSAGE.body == b'Hello World!' Work Queues ----------- Create new task *************** .. code-block:: python import sys import asyncio import aiormq async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() body = b' '.join(sys.argv[1:]) or b"Hello World!" # Sending the message await channel.basic_publish( body, routing_key='task_queue', properties=aiormq.spec.Basic.Properties( delivery_mode=1, ) ) print(f" [x] Sent {body!r}") await connection.close() loop = asyncio.get_event_loop() loop.run_until_complete(main()) Simple worker ************* .. code-block:: python import asyncio import aiormq import aiormq.abc async def on_message(message: aiormq.abc.DeliveredMessage): print(f" [x] Received message {message!r}") print(f" Message body is: {message.body!r}") async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() await channel.basic_qos(prefetch_count=1) # Declaring queue declare_ok = await channel.queue_declare('task_queue', durable=True) # Start listening the queue with name 'task_queue' await channel.basic_consume(declare_ok.queue, on_message, no_ack=True) loop = asyncio.get_event_loop() loop.run_until_complete(main()) # we enter a never-ending loop that waits for data and runs # callbacks whenever necessary. print(" [*] Waiting for messages. To exit press CTRL+C") loop.run_forever() Publish Subscribe ----------------- Publisher ********* .. code-block:: python import sys import asyncio import aiormq async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() await channel.exchange_declare( exchange='logs', exchange_type='fanout' ) body = b' '.join(sys.argv[1:]) or b"Hello World!" # Sending the message await channel.basic_publish( body, routing_key='info', exchange='logs' ) print(f" [x] Sent {body!r}") await connection.close() loop = asyncio.get_event_loop() loop.run_until_complete(main()) Subscriber ********** .. code-block:: python import asyncio import aiormq import aiormq.abc async def on_message(message: aiormq.abc.DeliveredMessage): print(f"[x] {message.body!r}") await message.channel.basic_ack( message.delivery.delivery_tag ) async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() await channel.basic_qos(prefetch_count=1) await channel.exchange_declare( exchange='logs', exchange_type='fanout' ) # Declaring queue declare_ok = await channel.queue_declare(exclusive=True) # Binding the queue to the exchange await channel.queue_bind(declare_ok.queue, 'logs') # Start listening the queue with name 'task_queue' await channel.basic_consume(declare_ok.queue, on_message) loop = asyncio.get_event_loop() loop.create_task(main()) # we enter a never-ending loop that waits for data # and runs callbacks whenever necessary. print(' [*] Waiting for logs. To exit press CTRL+C') loop.run_forever() Routing ------- Direct consumer *************** .. code-block:: python import sys import asyncio import aiormq import aiormq.abc async def on_message(message: aiormq.abc.DeliveredMessage): print(f" [x] {message.delivery.routing_key!r}:{message.body!r}" await message.channel.basic_ack( message.delivery.delivery_tag ) async def main(): # Perform connection connection = aiormq.Connection("amqp://guest:guest@localhost/") await connection.connect() # Creating a channel channel = await connection.channel() await channel.basic_qos(prefetch_count=1) severities = sys.argv[1:] if not severities: sys.stderr.write(f"Usage: {sys.argv[0]} [info] [warning] [error]\n") sys.exit(1) # Declare an exchange await channel.exchange_declare( exchange='logs', exchange_type='direct' ) # Declaring random queue declare_ok = await channel.queue_declare(durable=True, auto_delete=True) for severity in severities: await channel.queue_bind( declare_ok.queue, 'logs', routing_key=severity ) # Start listening the random queue await channel.basic_consume(declare_ok.queue, on_message) loop = asyncio.get_event_loop() loop.run_until_complete(main()) # we enter a never-ending loop that waits for data # and runs callbacks whenever necessary. print(" [*] Waiting for messages. To exit press CTRL+C") loop.run_forever() Emitter ******* .. code-block:: python import sys import asyncio import aiormq async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() await channel.exchange_declare( exchange='logs', exchange_type='direct' ) body = ( b' '.join(arg.encode() for arg in sys.argv[2:]) or b"Hello World!" ) # Sending the message routing_key = sys.argv[1] if len(sys.argv) > 2 else 'info' await channel.basic_publish( body, exchange='logs', routing_key=routing_key, properties=aiormq.spec.Basic.Properties( delivery_mode=1 ) ) print(f" [x] Sent {body!r}") await connection.close() loop = asyncio.get_event_loop() loop.run_until_complete(main()) Topics ------ Publisher ********* .. code-block:: python import sys import asyncio import aiormq async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() await channel.exchange_declare('topic_logs', exchange_type='topic') routing_key = ( sys.argv[1] if len(sys.argv) > 2 else 'anonymous.info' ) body = ( b' '.join(arg.encode() for arg in sys.argv[2:]) or b"Hello World!" ) # Sending the message await channel.basic_publish( body, exchange='topic_logs', routing_key=routing_key, properties=aiormq.spec.Basic.Properties( delivery_mode=1 ) ) print(f" [x] Sent {body!r}") await connection.close() loop = asyncio.get_event_loop() loop.run_until_complete(main()) Consumer ******** .. code-block:: python import asyncio import sys import aiormq import aiormq.abc async def on_message(message: aiormq.abc.DeliveredMessage): print(f" [x] {message.delivery.routing_key!r}:{message.body!r}") await message.channel.basic_ack( message.delivery.delivery_tag ) async def main(): # Perform connection connection = await aiormq.connect( "amqp://guest:guest@localhost/", loop=loop ) # Creating a channel channel = await connection.channel() await channel.basic_qos(prefetch_count=1) # Declare an exchange await channel.exchange_declare('topic_logs', exchange_type='topic') # Declaring queue declare_ok = await channel.queue_declare('task_queue', durable=True) binding_keys = sys.argv[1:] if not binding_keys: sys.stderr.write( f"Usage: {sys.argv[0]} [binding_key]...\n" ) sys.exit(1) for binding_key in binding_keys: await channel.queue_bind( declare_ok.queue, 'topic_logs', routing_key=binding_key ) # Start listening the queue with name 'task_queue' await channel.basic_consume(declare_ok.queue, on_message) loop = asyncio.get_event_loop() loop.create_task(main()) # we enter a never-ending loop that waits for # data and runs callbacks whenever necessary. print(" [*] Waiting for messages. To exit press CTRL+C") loop.run_forever() Remote procedure call (RPC) --------------------------- RPC server ********** .. code-block:: python import asyncio import aiormq import aiormq.abc def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n-1) + fib(n-2) async def on_message(message:aiormq.abc.DeliveredMessage): n = int(message.body.decode()) print(f" [.] fib({n})") response = str(fib(n)).encode() await message.channel.basic_publish( response, routing_key=message.header.properties.reply_to, properties=aiormq.spec.Basic.Properties( correlation_id=message.header.properties.correlation_id ), ) await message.channel.basic_ack(message.delivery.delivery_tag) print('Request complete') async def main(): # Perform connection connection = await aiormq.connect("amqp://guest:guest@localhost/") # Creating a channel channel = await connection.channel() # Declaring queue declare_ok = await channel.queue_declare('rpc_queue') # Start listening the queue with name 'hello' await channel.basic_consume(declare_ok.queue, on_message) loop = asyncio.get_event_loop() loop.create_task(main()) # we enter a never-ending loop that waits for data # and runs callbacks whenever necessary. print(" [x] Awaiting RPC requests") loop.run_forever() RPC client ********** .. code-block:: python import asyncio import uuid import aiormq import aiormq.abc class FibonacciRpcClient: def __init__(self): self.connection = None # type: aiormq.Connection self.channel = None # type: aiormq.Channel self.callback_queue = '' self.futures = {} self.loop = loop async def connect(self): self.connection = await aiormq.connect("amqp://guest:guest@localhost/") self.channel = await self.connection.channel() declare_ok = await self.channel.queue_declare( exclusive=True, auto_delete=True ) await self.channel.basic_consume(declare_ok.queue, self.on_response) self.callback_queue = declare_ok.queue return self async def on_response(self, message: aiormq.abc.DeliveredMessage): future = self.futures.pop(message.header.properties.correlation_id) future.set_result(message.body) async def call(self, n): correlation_id = str(uuid.uuid4()) future = loop.create_future() self.futures[correlation_id] = future await self.channel.basic_publish( str(n).encode(), routing_key='rpc_queue', properties=aiormq.spec.Basic.Properties( content_type='text/plain', correlation_id=correlation_id, reply_to=self.callback_queue, ) ) return int(await future) async def main(): fibonacci_rpc = await FibonacciRpcClient().connect() print(" [x] Requesting fib(30)") response = await fibonacci_rpc.call(30) print(r" [.] Got {response!r}") loop = asyncio.get_event_loop() loop.run_until_complete(main()) python-aiormq-6.8.1/aiormq/000077500000000000000000000000001473731423200156335ustar00rootroot00000000000000python-aiormq-6.8.1/aiormq/__init__.py000066400000000000000000000032621473731423200177470ustar00rootroot00000000000000from pamqp import commands as spec from . import abc from .channel import Channel from .connection import Connection, connect from .exceptions import ( AMQPChannelError, AMQPConnectionError, AMQPError, AMQPException, AuthenticationError, ChannelAccessRefused, ChannelClosed, ChannelInvalidStateError, ChannelLockedResource, ChannelNotFoundEntity, ChannelPreconditionFailed, ConnectionChannelError, ConnectionClosed, ConnectionCommandInvalid, ConnectionFrameError, ConnectionInternalError, ConnectionNotAllowed, ConnectionNotImplemented, ConnectionResourceError, ConnectionSyntaxError, ConnectionUnexpectedFrame, DeliveryError, DuplicateConsumerTag, IncompatibleProtocolError, InvalidFrameError, MethodNotImplemented, ProbableAuthenticationError, ProtocolSyntaxError, PublishError, ) __all__ = ( "AMQPChannelError", "AMQPConnectionError", "AMQPError", "AMQPException", "AuthenticationError", "Channel", "ChannelAccessRefused", "ChannelClosed", "ChannelInvalidStateError", "ChannelLockedResource", "ChannelNotFoundEntity", "ChannelPreconditionFailed", "Connection", "ConnectionChannelError", "ConnectionClosed", "ConnectionCommandInvalid", "ConnectionFrameError", "ConnectionInternalError", "ConnectionNotAllowed", "ConnectionNotImplemented", "ConnectionResourceError", "ConnectionSyntaxError", "ConnectionUnexpectedFrame", "DeliveryError", "DuplicateConsumerTag", "IncompatibleProtocolError", "InvalidFrameError", "MethodNotImplemented", "ProbableAuthenticationError", "ProtocolSyntaxError", "PublishError", "abc", "connect", "spec", ) python-aiormq-6.8.1/aiormq/abc.py000066400000000000000000000416611473731423200167420ustar00rootroot00000000000000import asyncio import dataclasses import io import logging from abc import ABC, abstractmethod, abstractproperty from types import TracebackType from typing import ( Any, Awaitable, Callable, Coroutine, Dict, Iterable, Optional, Set, Tuple, Type, Union, ) import pamqp from pamqp import commands as spec from pamqp.base import Frame from pamqp.body import ContentBody from pamqp.commands import Basic, Channel, Confirm, Exchange, Queue, Tx from pamqp.common import FieldArray, FieldTable, FieldValue from pamqp.constants import REPLY_SUCCESS from pamqp.header import ContentHeader from pamqp.heartbeat import Heartbeat from yarl import URL ExceptionType = Union[BaseException, Type[BaseException]] # noinspection PyShadowingNames class TaskWrapper: __slots__ = "_exception", "task" _exception: Union[BaseException, Type[BaseException]] task: asyncio.Task def __init__(self, task: asyncio.Task): self.task = task self._exception = asyncio.CancelledError def throw(self, exception: ExceptionType) -> None: self._exception = exception self.task.cancel() async def __inner(self) -> Any: try: return await self.task except asyncio.CancelledError as e: raise self._exception from e def __await__(self, *args: Any, **kwargs: Any) -> Any: return self.__inner().__await__() def cancel(self) -> None: return self.throw(asyncio.CancelledError()) def __getattr__(self, item: str) -> Any: return getattr(self.task, item) def __repr__(self) -> str: return "<%s: %s>" % (self.__class__.__name__, repr(self.task)) TaskType = Union[asyncio.Task, TaskWrapper] CoroutineType = Coroutine[Any, None, Any] GetResultType = Union[Basic.GetEmpty, Basic.GetOk] @dataclasses.dataclass(frozen=True) class DeliveredMessage: delivery: Union[spec.Basic.Deliver, spec.Basic.Return, GetResultType] header: ContentHeader body: bytes channel: "AbstractChannel" @property def routing_key(self) -> Optional[str]: if isinstance( self.delivery, ( spec.Basic.Return, spec.Basic.GetOk, spec.Basic.Deliver, ), ): return self.delivery.routing_key return None @property def exchange(self) -> Optional[str]: if isinstance( self.delivery, ( spec.Basic.Return, spec.Basic.GetOk, spec.Basic.Deliver, ), ): return self.delivery.exchange return None @property def delivery_tag(self) -> Optional[int]: if isinstance( self.delivery, ( spec.Basic.GetOk, spec.Basic.Deliver, ), ): return self.delivery.delivery_tag return None @property def redelivered(self) -> Optional[bool]: if isinstance( self.delivery, ( spec.Basic.GetOk, spec.Basic.Deliver, ), ): return self.delivery.redelivered return None @property def consumer_tag(self) -> Optional[str]: if isinstance(self.delivery, spec.Basic.Deliver): return self.delivery.consumer_tag return None @property def message_count(self) -> Optional[int]: if isinstance(self.delivery, spec.Basic.GetOk): return self.delivery.message_count return None ChannelRType = Tuple[int, Channel.OpenOk] CallbackCoro = Coroutine[Any, Any, Any] ConsumerCallback = Callable[[DeliveredMessage], CallbackCoro] ReturnCallback = Callable[[DeliveredMessage], Any] ArgumentsType = FieldTable ConfirmationFrameType = Union[ Basic.Ack, Basic.Nack, Basic.Reject, ] @dataclasses.dataclass(frozen=True) class SSLCerts: cert: Optional[str] key: Optional[str] capath: Optional[str] cafile: Optional[str] cadata: Optional[bytes] verify: bool @dataclasses.dataclass(frozen=True) class FrameReceived: channel: int frame: str URLorStr = Union[URL, str] DrainResult = Awaitable[None] TimeoutType = Optional[Union[float, int]] FrameType = Union[Frame, ContentHeader, ContentBody] RpcReturnType = Optional[ Union[ Basic.CancelOk, Basic.ConsumeOk, Basic.GetOk, Basic.QosOk, Basic.RecoverOk, Channel.CloseOk, Channel.FlowOk, Channel.OpenOk, Confirm.SelectOk, Exchange.BindOk, Exchange.DeclareOk, Exchange.DeleteOk, Exchange.UnbindOk, Queue.BindOk, Queue.DeleteOk, Queue.DeleteOk, Queue.PurgeOk, Queue.UnbindOk, Tx.CommitOk, Tx.RollbackOk, Tx.SelectOk, ] ] @dataclasses.dataclass(frozen=True) class ChannelFrame: payload: bytes should_close: bool drain_future: Optional[asyncio.Future] = None def drain(self) -> None: if not self.should_drain: return if self.drain_future is not None and not self.drain_future.done(): self.drain_future.set_result(None) @property def should_drain(self) -> bool: return self.drain_future is not None and not self.drain_future.done() @classmethod def marshall( cls, channel_number: int, frames: Iterable[Union[FrameType, Heartbeat, ContentBody]], drain_future: Optional[asyncio.Future] = None, ) -> "ChannelFrame": should_close = False with io.BytesIO() as fp: for frame in frames: if should_close: logger = logging.getLogger( "aiormq.connection", ).getChild( "marshall", ) logger.warning( "It looks like you are going to send a frame %r after " "the connection is closed, it's pointless, " "the frame is dropped.", frame, ) continue if isinstance(frame, spec.Connection.CloseOk): should_close = True fp.write(pamqp.frame.marshal(frame, channel_number)) return cls( payload=fp.getvalue(), drain_future=drain_future, should_close=should_close, ) class AbstractFutureStore: futures: Set[Union[asyncio.Future, TaskType]] loop: asyncio.AbstractEventLoop @abstractmethod def add(self, future: Union[asyncio.Future, TaskWrapper]) -> None: raise NotImplementedError @abstractmethod def reject_all(self, exception: Optional[ExceptionType]) -> Any: raise NotImplementedError @abstractmethod def create_task(self, coro: CoroutineType) -> TaskType: raise NotImplementedError @abstractmethod def create_future(self) -> asyncio.Future: raise NotImplementedError @abstractmethod def get_child(self) -> "AbstractFutureStore": raise NotImplementedError class AbstractBase(ABC): loop: asyncio.AbstractEventLoop @abstractmethod def _future_store_child(self) -> AbstractFutureStore: raise NotImplementedError @abstractmethod def create_task(self, coro: CoroutineType) -> TaskType: raise NotImplementedError def create_future(self) -> asyncio.Future: raise NotImplementedError @abstractmethod async def _on_close(self, exc: Optional[Exception] = None) -> None: raise NotImplementedError @abstractmethod async def close( self, exc: Optional[ExceptionType] = asyncio.CancelledError(), ) -> None: raise NotImplementedError @abstractmethod def __str__(self) -> str: raise NotImplementedError @abstractproperty def is_closed(self) -> bool: raise NotImplementedError class AbstractChannel(AbstractBase): frames: asyncio.Queue connection: "AbstractConnection" number: int on_return_callbacks: Set[ReturnCallback] closing: asyncio.Future @abstractmethod async def open(self) -> spec.Channel.OpenOk: pass @abstractmethod async def basic_get( self, queue: str = "", no_ack: bool = False, timeout: TimeoutType = None, ) -> DeliveredMessage: raise NotImplementedError @abstractmethod async def basic_cancel( self, consumer_tag: str, *, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.CancelOk: raise NotImplementedError @abstractmethod async def basic_consume( self, queue: str, consumer_callback: ConsumerCallback, *, no_ack: bool = False, exclusive: bool = False, arguments: Optional[ArgumentsType] = None, consumer_tag: Optional[str] = None, timeout: TimeoutType = None, ) -> spec.Basic.ConsumeOk: raise NotImplementedError @abstractmethod def basic_ack( self, delivery_tag: int, multiple: bool = False, wait: bool = True, ) -> DrainResult: raise NotImplementedError @abstractmethod def basic_nack( self, delivery_tag: int, multiple: bool = False, requeue: bool = True, wait: bool = True, ) -> DrainResult: raise NotImplementedError @abstractmethod def basic_reject( self, delivery_tag: int, *, requeue: bool = True, wait: bool = True, ) -> DrainResult: raise NotImplementedError @abstractmethod async def basic_publish( self, body: bytes, *, exchange: str = "", routing_key: str = "", properties: Optional[spec.Basic.Properties] = None, mandatory: bool = False, immediate: bool = False, timeout: TimeoutType = None, ) -> Optional[ConfirmationFrameType]: raise NotImplementedError @abstractmethod async def basic_qos( self, *, prefetch_size: Optional[int] = None, prefetch_count: Optional[int] = None, global_: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.QosOk: raise NotImplementedError @abstractmethod async def basic_recover( self, *, nowait: bool = False, requeue: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.RecoverOk: raise NotImplementedError @abstractmethod async def exchange_declare( self, exchange: str = "", *, exchange_type: str = "direct", passive: bool = False, durable: bool = False, auto_delete: bool = False, internal: bool = False, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Exchange.DeclareOk: raise NotImplementedError @abstractmethod async def exchange_delete( self, exchange: str = "", *, if_unused: bool = False, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Exchange.DeleteOk: raise NotImplementedError @abstractmethod async def exchange_bind( self, destination: str = "", source: str = "", routing_key: str = "", *, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Exchange.BindOk: raise NotImplementedError @abstractmethod async def exchange_unbind( self, destination: str = "", source: str = "", routing_key: str = "", *, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Exchange.UnbindOk: raise NotImplementedError @abstractmethod async def flow( self, active: bool, timeout: TimeoutType = None, ) -> spec.Channel.FlowOk: raise NotImplementedError @abstractmethod async def queue_bind( self, queue: str, exchange: str, routing_key: str = "", nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.BindOk: raise NotImplementedError @abstractmethod async def queue_declare( self, queue: str = "", *, passive: bool = False, durable: bool = False, exclusive: bool = False, auto_delete: bool = False, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.DeclareOk: raise NotImplementedError @abstractmethod async def queue_delete( self, queue: str = "", if_unused: bool = False, if_empty: bool = False, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Queue.DeleteOk: raise NotImplementedError @abstractmethod async def queue_purge( self, queue: str = "", nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Queue.PurgeOk: raise NotImplementedError @abstractmethod async def queue_unbind( self, queue: str = "", exchange: str = "", routing_key: str = "", arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.UnbindOk: raise NotImplementedError @abstractmethod async def tx_commit( self, timeout: TimeoutType = None, ) -> spec.Tx.CommitOk: raise NotImplementedError @abstractmethod async def tx_rollback( self, timeout: TimeoutType = None, ) -> spec.Tx.RollbackOk: raise NotImplementedError @abstractmethod async def tx_select(self, timeout: TimeoutType = None) -> spec.Tx.SelectOk: raise NotImplementedError @abstractmethod async def confirm_delivery( self, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Confirm.SelectOk: raise NotImplementedError class AbstractConnection(AbstractBase): FRAME_BUFFER_SIZE: int = 10 # Interval between sending heartbeats based on the heartbeat(timeout) HEARTBEAT_INTERVAL_MULTIPLIER: TimeoutType # Allow three missed heartbeats (based on heartbeat(timeout) HEARTBEAT_GRACE_MULTIPLIER: int server_properties: ArgumentsType connection_tune: spec.Connection.Tune channels: Dict[int, Optional[AbstractChannel]] write_queue: asyncio.Queue url: URL closing: asyncio.Future @abstractmethod def set_close_reason( self, reply_code: int = REPLY_SUCCESS, reply_text: str = "normally closed", class_id: int = 0, method_id: int = 0, ) -> None: raise NotImplementedError @abstractproperty def is_opened(self) -> bool: raise NotImplementedError @abstractmethod def __str__(self) -> str: raise NotImplementedError @abstractmethod async def connect( self, client_properties: Optional[FieldTable] = None, ) -> bool: raise NotImplementedError @abstractproperty def server_capabilities(self) -> ArgumentsType: raise NotImplementedError @abstractproperty def basic_nack(self) -> bool: raise NotImplementedError @abstractproperty def consumer_cancel_notify(self) -> bool: raise NotImplementedError @abstractproperty def exchange_exchange_bindings(self) -> bool: raise NotImplementedError @abstractproperty def publisher_confirms(self) -> Optional[bool]: raise NotImplementedError async def channel( self, channel_number: Optional[int] = None, publisher_confirms: bool = True, frame_buffer_size: int = FRAME_BUFFER_SIZE, timeout: TimeoutType = None, **kwargs: Any, ) -> AbstractChannel: raise NotImplementedError @abstractmethod async def __aenter__(self) -> "AbstractConnection": raise NotImplementedError @abstractmethod async def __aexit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> Optional[bool]: raise NotImplementedError @abstractmethod async def ready(self) -> None: raise NotImplementedError @abstractmethod async def update_secret( self, new_secret: str, *, reason: str = "", timeout: TimeoutType = None, ) -> spec.Connection.UpdateSecretOk: raise NotImplementedError __all__ = ( "AbstractBase", "AbstractChannel", "AbstractConnection", "AbstractFutureStore", "ArgumentsType", "CallbackCoro", "ChannelFrame", "ChannelRType", "ConfirmationFrameType", "ConsumerCallback", "CoroutineType", "DeliveredMessage", "DrainResult", "ExceptionType", "FieldArray", "FieldTable", "FieldValue", "FrameReceived", "FrameType", "GetResultType", "ReturnCallback", "RpcReturnType", "SSLCerts", "TaskType", "TaskWrapper", "TimeoutType", "URLorStr", ) python-aiormq-6.8.1/aiormq/auth.py000066400000000000000000000015361473731423200171530ustar00rootroot00000000000000import abc from enum import Enum from typing import Optional from .abc import AbstractConnection class AuthBase: value: Optional[str] def __init__(self, connector: AbstractConnection): self.connector = connector self.value = None @abc.abstractmethod def encode(self) -> str: raise NotImplementedError def marshal(self) -> str: if self.value is None: self.value = self.encode() return self.value class PlainAuth(AuthBase): def encode(self) -> str: return ( "\x00" + (self.connector.url.user or "guest") + "\x00" + (self.connector.url.password or "guest") ) class ExternalAuth(AuthBase): def encode(self) -> str: return "" class AuthMechanism(Enum): PLAIN = PlainAuth EXTERNAL = ExternalAuth python-aiormq-6.8.1/aiormq/base.py000066400000000000000000000113001473731423200171120ustar00rootroot00000000000000import abc import asyncio from contextlib import suppress from functools import wraps from typing import Any, Callable, Coroutine, Optional, Set, TypeVar, Union from weakref import WeakSet from .abc import ( AbstractBase, AbstractFutureStore, CoroutineType, ExceptionType, TaskType, TaskWrapper, TimeoutType, ) from .tools import Countdown, shield T = TypeVar("T") class FutureStore(AbstractFutureStore): __slots__ = "futures", "loop", "parent" futures: Set[Union[asyncio.Future, TaskType]] weak_futures: WeakSet loop: asyncio.AbstractEventLoop def __init__(self, loop: asyncio.AbstractEventLoop): self.futures = set() self.loop = loop self.parent: Optional[FutureStore] = None def __on_task_done( self, future: Union[asyncio.Future, TaskWrapper], ) -> Callable[..., Any]: def remover(*_: Any) -> None: nonlocal future if future in self.futures: self.futures.remove(future) return remover def add(self, future: Union[asyncio.Future, TaskWrapper]) -> None: self.futures.add(future) future.add_done_callback(self.__on_task_done(future)) if self.parent: self.parent.add(future) @shield async def reject_all(self, exception: Optional[ExceptionType]) -> None: tasks = [] while self.futures: future: Union[TaskType, asyncio.Future] = self.futures.pop() if future.done(): continue if isinstance(future, TaskWrapper): future.throw(exception or Exception) tasks.append(future) elif isinstance(future, asyncio.Future): future.set_exception(exception or Exception) if tasks: await asyncio.gather(*tasks, return_exceptions=True) def create_task(self, coro: CoroutineType) -> TaskType: task: TaskWrapper = TaskWrapper(self.loop.create_task(coro)) self.add(task) return task def create_future(self, weak: bool = False) -> asyncio.Future: future = self.loop.create_future() self.add(future) return future def get_child(self) -> "FutureStore": store = FutureStore(self.loop) store.parent = self return store class Base(AbstractBase): __slots__ = "loop", "__future_store", "closing" def __init__( self, *, loop: asyncio.AbstractEventLoop, parent: Optional[AbstractBase] = None, ): self.loop: asyncio.AbstractEventLoop = loop if parent: self.__future_store = parent._future_store_child() else: self.__future_store = FutureStore(loop=self.loop) self.closing = self._create_closing_future() def _create_closing_future(self) -> asyncio.Future: future = self.__future_store.create_future() future.add_done_callback(lambda x: x.exception()) return future def _cancel_tasks( self, exc: Optional[ExceptionType] = None, ) -> Coroutine[Any, Any, None]: return self.__future_store.reject_all(exc) def _future_store_child(self) -> AbstractFutureStore: return self.__future_store.get_child() def create_task(self, coro: CoroutineType) -> TaskType: return self.__future_store.create_task(coro) def create_future(self) -> asyncio.Future: return self.__future_store.create_future() @abc.abstractmethod async def _on_close( self, exc: Optional[ExceptionType] = None, ) -> None: # pragma: no cover return async def __closer(self, exc: Optional[ExceptionType]) -> None: if self.is_closed: # pragma: no cover return with suppress(Exception): await self._on_close(exc) with suppress(Exception): await self._cancel_tasks(exc) async def close( self, exc: Optional[ExceptionType] = asyncio.CancelledError, timeout: TimeoutType = None, ) -> None: if self.is_closed: return None countdown = Countdown(timeout) await countdown(self.__closer(exc)) def __repr__(self) -> str: cls_name = self.__class__.__name__ return '<{0}: "{1}" at 0x{2:02x}>'.format( cls_name, str(self), id(self), ) @abc.abstractmethod def __str__(self) -> str: # pragma: no cover raise NotImplementedError @property def is_closed(self) -> bool: return self.closing.done() TaskFunctionType = Callable[..., T] def task(func: TaskFunctionType) -> TaskFunctionType: @wraps(func) async def wrap(self: Base, *args: Any, **kwargs: Any) -> Any: return await self.create_task(func(self, *args, **kwargs)) return wrap python-aiormq-6.8.1/aiormq/channel.py000066400000000000000000000715051473731423200176250ustar00rootroot00000000000000import asyncio import io import logging from collections import OrderedDict from contextlib import suppress from functools import partial from io import BytesIO from random import getrandbits from types import MappingProxyType from typing import ( Any, Awaitable, Callable, Dict, List, Mapping, Optional, Set, Tuple, Type, Union, ) from uuid import UUID import pamqp.frame from pamqp import commands as spec from pamqp.base import Frame from pamqp.body import ContentBody from pamqp.constants import REPLY_SUCCESS from pamqp.exceptions import AMQPFrameError from pamqp.header import ContentHeader from aiormq.tools import Countdown, awaitable from .abc import ( AbstractChannel, AbstractConnection, ArgumentsType, ChannelFrame, ConfirmationFrameType, ConsumerCallback, DeliveredMessage, ExceptionType, FrameType, GetResultType, ReturnCallback, RpcReturnType, TimeoutType, ) from .base import Base, task from .exceptions import ( AMQPChannelError, AMQPError, ChannelAccessRefused, ChannelClosed, ChannelInvalidStateError, ChannelLockedResource, ChannelNotFoundEntity, ChannelPreconditionFailed, DeliveryError, DuplicateConsumerTag, InvalidFrameError, MethodNotImplemented, PublishError, ) log = logging.getLogger(__name__) EXCEPTION_MAPPING: Mapping[int, Type[AMQPChannelError]] = MappingProxyType({ 403: ChannelAccessRefused, 404: ChannelNotFoundEntity, 405: ChannelLockedResource, 406: ChannelPreconditionFailed, }) def exception_by_code(frame: spec.Channel.Close) -> AMQPError: if frame.reply_code is None: return ChannelClosed(frame.reply_code, frame.reply_text) exception_class = EXCEPTION_MAPPING.get(frame.reply_code) if exception_class is None: return ChannelClosed(frame.reply_code, frame.reply_text) return exception_class(frame.reply_text) def _check_routing_key(key: str) -> None: if len(key) > 255: raise ValueError("Routing key too long (max 255 bytes)") class Returning(asyncio.Future): pass ConfirmationType = Union[asyncio.Future, Returning] class Channel(Base, AbstractChannel): # noinspection PyTypeChecker CONTENT_FRAME_SIZE = len(pamqp.frame.marshal(ContentBody(b""), 0)) CHANNEL_CLOSE_TIMEOUT = 10 confirmations: Dict[int, ConfirmationType] def __init__( self, connector: AbstractConnection, number: int, publisher_confirms: bool = True, frame_buffer: Optional[int] = None, on_return_raises: bool = True, ): super().__init__(loop=connector.loop, parent=connector) self.connection = connector if ( publisher_confirms and not connector.publisher_confirms ): # pragma: no cover raise ValueError("Server doesn't support publisher confirms") self.consumers: Dict[str, ConsumerCallback] = {} self.confirmations = OrderedDict() self.message_id_delivery_tag: Dict[str, int] = dict() self.delivery_tag = 0 self.getter: Optional[asyncio.Future] = None self.getter_lock = asyncio.Lock() self.frames: asyncio.Queue = asyncio.Queue(maxsize=frame_buffer or 0) self.max_content_size = ( connector.connection_tune.frame_max - self.CONTENT_FRAME_SIZE ) self.__lock = asyncio.Lock() self.number: int = number self.publisher_confirms = publisher_confirms self.rpc_frames: asyncio.Queue = asyncio.Queue( maxsize=frame_buffer or 0, ) self.write_queue = connector.write_queue self.on_return_raises = on_return_raises self.on_return_callbacks: Set[ReturnCallback] = set() self._close_exception = None self.create_task(self._reader()) self.__close_reply_code: int = REPLY_SUCCESS self.__close_reply_text: str = "" self.__close_class_id: int = 0 self.__close_method_id: int = 0 self.__close_event: asyncio.Event = asyncio.Event() def set_close_reason( self, reply_code: int = REPLY_SUCCESS, reply_text: str = "", class_id: int = 0, method_id: int = 0, ) -> None: self.__close_reply_code = reply_code self.__close_reply_text = reply_text self.__close_class_id = class_id self.__close_method_id = method_id @property def lock(self) -> asyncio.Lock: if self.is_closed: raise ChannelInvalidStateError("%r closed" % self) return self.__lock async def _get_frame(self) -> FrameType: weight, frame = await self.frames.get() self.frames.task_done() return frame def __str__(self) -> str: return str(self.number) @task async def rpc( self, frame: Frame, timeout: TimeoutType = None, ) -> RpcReturnType: if self.__close_event.is_set(): raise ChannelInvalidStateError("Channel closed by RPC timeout") countdown = Countdown(timeout) lock = self.lock async with countdown.enter_context(lock): try: await countdown( self.write_queue.put( ChannelFrame.marshall( channel_number=self.number, frames=[frame], ), ), ) if not (frame.synchronous or getattr(frame, "nowait", False)): return None result = await countdown(self.rpc_frames.get()) self.rpc_frames.task_done() if result.name not in frame.valid_responses: # pragma: no cover raise InvalidFrameError(frame) return result except (asyncio.CancelledError, asyncio.TimeoutError): if self.is_closed: raise log.warning( "Closing channel %r because RPC call %s cancelled", self, frame, ) self.__close_event.set() await self.write_queue.put( ChannelFrame.marshall( channel_number=self.number, frames=[ spec.Channel.Close( class_id=0, method_id=0, reply_code=504, reply_text=( "RPC timeout on frame {!s}".format(frame) ), ), ], ), ) raise async def open(self, timeout: TimeoutType = None) -> spec.Channel.OpenOk: frame: spec.Channel.OpenOk = await self.rpc( spec.Channel.Open(), timeout=timeout, ) if self.publisher_confirms: await self.rpc(spec.Confirm.Select()) if frame is None: # pragma: no cover raise AMQPFrameError(frame) return frame async def __get_content_frame(self) -> ContentBody: content_frame = await self._get_frame() if not isinstance(content_frame, ContentBody): raise ValueError( "Unexpected frame {!r}".format(content_frame), content_frame, ) return content_frame async def _read_content( self, frame: Union[spec.Basic.Deliver, spec.Basic.Return, GetResultType], header: ContentHeader, ) -> DeliveredMessage: with BytesIO() as body: content: Optional[ContentBody] = None if header.body_size: content = await self.__get_content_frame() while content and body.tell() < header.body_size: body.write(content.value) if body.tell() < header.body_size: content = await self.__get_content_frame() return DeliveredMessage( delivery=frame, header=header, body=body.getvalue(), channel=self, ) async def __get_content_header(self) -> ContentHeader: frame: FrameType = await self._get_frame() if not isinstance(frame, ContentHeader): raise ValueError( "Unexpected frame {!r} instead of ContentHeader".format(frame), frame, ) return frame async def _on_deliver_frame(self, frame: spec.Basic.Deliver) -> None: header: ContentHeader = await self.__get_content_header() message = await self._read_content(frame, header) if frame.consumer_tag is None: log.warning("Frame %r has no consumer tag", frame) return consumer = self.consumers.get(frame.consumer_tag) if consumer is not None: # noinspection PyAsyncCall self.create_task(consumer(message)) async def _on_get_frame( self, frame: Union[spec.Basic.GetOk, spec.Basic.GetEmpty], ) -> None: message = None if isinstance(frame, spec.Basic.GetOk): header: ContentHeader = await self.__get_content_header() message = await self._read_content(frame, header) if isinstance(frame, spec.Basic.GetEmpty): message = DeliveredMessage( delivery=frame, header=ContentHeader(), body=b"", channel=self, ) getter = getattr(self, "getter", None) if getter is None: raise RuntimeError("Getter is None") if getter.done(): log.error("Got message but no active getter") return getter.set_result((frame, message)) return async def _on_return_frame(self, frame: spec.Basic.Return) -> None: header: ContentHeader = await self.__get_content_header() message = await self._read_content(frame, header) message_id = message.header.properties.message_id if message_id is None: log.error("message_id if None on returned message %r", message) return delivery_tag = self.message_id_delivery_tag.get(message_id) if delivery_tag is None: # pragma: nocover log.error("Unhandled message %r returning", message) return confirmation = self.confirmations.pop(delivery_tag, None) if confirmation is None: # pragma: nocover return self.confirmations[delivery_tag] = Returning() if self.on_return_raises: confirmation.set_exception(PublishError(message, frame)) return for cb in self.on_return_callbacks: # noinspection PyBroadException try: cb(message) except Exception: log.exception("Unhandled return callback exception") confirmation.set_result(message) def _confirm_delivery( self, delivery_tag: Optional[int], frame: ConfirmationFrameType, ) -> None: if delivery_tag not in self.confirmations: return confirmation = self.confirmations.pop(delivery_tag) if isinstance(confirmation, Returning): return elif confirmation.done(): # pragma: nocover log.warning( "Delivery tag %r confirmed %r was ignored", delivery_tag, frame, ) return elif isinstance(frame, spec.Basic.Ack): confirmation.set_result(frame) return confirmation.set_exception( DeliveryError(None, frame), ) # pragma: nocover async def _on_confirm_frame(self, frame: ConfirmationFrameType) -> None: if not self.publisher_confirms: # pragma: nocover return if frame.delivery_tag not in self.confirmations: log.error("Unexpected confirmation frame %r from broker", frame) return multiple = getattr(frame, "multiple", False) if multiple: for delivery_tag in self.confirmations.keys(): if frame.delivery_tag >= delivery_tag: # Should be called later to avoid keys copying self.loop.call_soon( self._confirm_delivery, delivery_tag, frame, ) else: self._confirm_delivery(frame.delivery_tag, frame) async def _on_cancel_frame( self, frame: Union[spec.Basic.CancelOk, spec.Basic.Cancel], ) -> None: if frame.consumer_tag is not None: self.consumers.pop(frame.consumer_tag, None) async def _on_close_frame(self, frame: spec.Channel.Close) -> None: exc: BaseException = exception_by_code(frame) with suppress(asyncio.QueueFull): self.write_queue.put_nowait( ChannelFrame.marshall( channel_number=self.number, frames=[spec.Channel.CloseOk()], ), ) self.connection.channels.pop(self.number, None) self.__close_event.set() raise exc async def _on_close_ok_frame(self, _: spec.Channel.CloseOk) -> None: self.connection.channels.pop(self.number, None) self.__close_event.set() raise ChannelClosed(None, None) async def _reader(self) -> None: hooks: Mapping[Any, Tuple[bool, Callable[[Any], Awaitable[None]]]] hooks = { spec.Basic.Deliver: (False, self._on_deliver_frame), spec.Basic.GetOk: (True, self._on_get_frame), spec.Basic.GetEmpty: (True, self._on_get_frame), spec.Basic.Return: (False, self._on_return_frame), spec.Basic.Cancel: (False, self._on_cancel_frame), spec.Basic.CancelOk: (True, self._on_cancel_frame), spec.Channel.Close: (False, self._on_close_frame), spec.Channel.CloseOk: (False, self._on_close_ok_frame), spec.Basic.Ack: (False, self._on_confirm_frame), spec.Basic.Nack: (False, self._on_confirm_frame), } last_exception: Optional[BaseException] = None try: while True: frame = await self._get_frame() should_add_to_rpc, hook = hooks.get(type(frame), (True, None)) if hook is not None: await hook(frame) if should_add_to_rpc: await self.rpc_frames.put(frame) except asyncio.CancelledError as e: self.__close_event.set() last_exception = e return except Exception as e: last_exception = e raise finally: await self.close( last_exception, timeout=self.CHANNEL_CLOSE_TIMEOUT, ) @task async def _on_close(self, exc: Optional[ExceptionType] = None) -> None: if not self.connection.is_opened or self.__close_event.is_set(): return await self.rpc( spec.Channel.Close( reply_code=self.__close_reply_code, class_id=self.__close_class_id, method_id=self.__close_method_id, ), timeout=self.connection.connection_tune.heartbeat or None, ) await self.__close_event.wait() async def basic_get( self, queue: str = "", no_ack: bool = False, timeout: TimeoutType = None, ) -> DeliveredMessage: countdown = Countdown(timeout) async with countdown.enter_context(self.getter_lock): self.getter = self.create_future() await self.rpc( spec.Basic.Get(queue=queue, no_ack=no_ack), timeout=countdown.get_timeout(), ) frame: Union[spec.Basic.GetEmpty, spec.Basic.GetOk] message: DeliveredMessage frame, message = await countdown(self.getter) del self.getter return message async def basic_cancel( self, consumer_tag: str, *, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.CancelOk: return await self.rpc( spec.Basic.Cancel(consumer_tag=consumer_tag, nowait=nowait), timeout=timeout, ) async def basic_consume( self, queue: str, consumer_callback: ConsumerCallback, *, no_ack: bool = False, exclusive: bool = False, arguments: Optional[ArgumentsType] = None, consumer_tag: Optional[str] = None, timeout: TimeoutType = None, ) -> spec.Basic.ConsumeOk: consumer_tag = consumer_tag or "ctag%i.%s" % ( self.number, UUID(int=getrandbits(128), version=4).hex, ) if consumer_tag in self.consumers: raise DuplicateConsumerTag(self.number) self.consumers[consumer_tag] = awaitable(consumer_callback) return await self.rpc( spec.Basic.Consume( queue=queue, no_ack=no_ack, exclusive=exclusive, consumer_tag=consumer_tag, arguments=arguments, ), timeout=timeout, ) async def basic_ack( self, delivery_tag: int, multiple: bool = False, wait: bool = True, ) -> None: drain_future = self.create_future() if wait else None await self.write_queue.put( ChannelFrame.marshall( frames=[ spec.Basic.Ack( delivery_tag=delivery_tag, multiple=multiple, ), ], channel_number=self.number, drain_future=drain_future, ), ) if drain_future is not None: await drain_future async def basic_nack( self, delivery_tag: int, multiple: bool = False, requeue: bool = True, wait: bool = True, ) -> None: if not self.connection.basic_nack: raise MethodNotImplemented drain_future = self.create_future() if wait else None await self.write_queue.put( ChannelFrame.marshall( frames=[ spec.Basic.Nack( delivery_tag=delivery_tag, multiple=multiple, requeue=requeue, ), ], channel_number=self.number, drain_future=drain_future, ), ) if drain_future is not None: await drain_future async def basic_reject( self, delivery_tag: int, *, requeue: bool = True, wait: bool = True, ) -> None: drain_future = self.create_future() await self.write_queue.put( ChannelFrame.marshall( channel_number=self.number, frames=[ spec.Basic.Reject( delivery_tag=delivery_tag, requeue=requeue, ), ], drain_future=drain_future, ), ) if drain_future is not None: await drain_future def _split_body(self, body: bytes) -> List[ContentBody]: if not body: return [] if len(body) < self.max_content_size: return [ContentBody(body)] with io.BytesIO(body) as fp: reader = partial(fp.read, self.max_content_size) return list(map(ContentBody, iter(reader, b""))) async def basic_publish( self, body: bytes, *, exchange: str = "", routing_key: str = "", properties: Optional[spec.Basic.Properties] = None, mandatory: bool = False, immediate: bool = False, timeout: TimeoutType = None, wait: bool = True, ) -> Optional[ConfirmationFrameType]: _check_routing_key(routing_key) countdown = Countdown(timeout=timeout) publish_frame = spec.Basic.Publish( exchange=exchange, routing_key=routing_key, mandatory=mandatory, immediate=immediate, ) content_header = ContentHeader( properties=properties or spec.Basic.Properties(delivery_mode=1), body_size=len(body), ) if not content_header.properties.message_id: # UUID compatible random bytes rnd_uuid = UUID(int=getrandbits(128), version=4) content_header.properties.message_id = rnd_uuid.hex confirmation: Optional[ConfirmationType] = None async with countdown.enter_context(self.lock): self.delivery_tag += 1 if self.publisher_confirms: message_id = content_header.properties.message_id if self.delivery_tag not in self.confirmations: self.confirmations[ self.delivery_tag ] = self.create_future() confirmation = self.confirmations[self.delivery_tag] self.message_id_delivery_tag[message_id] = self.delivery_tag if confirmation is None: return confirmation.add_done_callback( lambda _: self.message_id_delivery_tag.pop( message_id, None, ), ) body_frames: List[Union[FrameType, ContentBody]] body_frames = [publish_frame, content_header] body_frames += self._split_body(body) drain_future = self.create_future() if wait else None await countdown( self.write_queue.put( ChannelFrame.marshall( frames=body_frames, channel_number=self.number, drain_future=drain_future, ), ), ) if drain_future: await drain_future if not self.publisher_confirms: return None if confirmation is None: return None return await countdown(confirmation) async def basic_qos( self, *, prefetch_size: Optional[int] = None, prefetch_count: Optional[int] = None, global_: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.QosOk: return await self.rpc( spec.Basic.Qos( prefetch_size=prefetch_size or 0, prefetch_count=prefetch_count or 0, global_=global_, ), timeout=timeout, ) async def basic_recover( self, *, nowait: bool = False, requeue: bool = False, timeout: TimeoutType = None, ) -> spec.Basic.RecoverOk: frame: Union[spec.Basic.RecoverAsync, spec.Basic.Recover] if nowait: frame = spec.Basic.RecoverAsync(requeue=requeue) else: frame = spec.Basic.Recover(requeue=requeue) return await self.rpc(frame, timeout=timeout) async def exchange_declare( self, exchange: str = "", *, exchange_type: str = "direct", passive: bool = False, durable: bool = False, auto_delete: bool = False, internal: bool = False, nowait: bool = False, arguments: Optional[Dict[str, Any]] = None, timeout: TimeoutType = None, ) -> spec.Exchange.DeclareOk: return await self.rpc( spec.Exchange.Declare( exchange=str(exchange), exchange_type=str(exchange_type), passive=bool(passive), durable=bool(durable), auto_delete=bool(auto_delete), internal=bool(internal), nowait=bool(nowait), arguments=arguments, ), timeout=timeout, ) async def exchange_delete( self, exchange: str = "", *, if_unused: bool = False, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Exchange.DeleteOk: return await self.rpc( spec.Exchange.Delete( exchange=exchange, nowait=nowait, if_unused=if_unused, ), timeout=timeout, ) async def exchange_bind( self, destination: str = "", source: str = "", routing_key: str = "", *, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Exchange.BindOk: _check_routing_key(routing_key) return await self.rpc( spec.Exchange.Bind( destination=destination, source=source, routing_key=routing_key, nowait=nowait, arguments=arguments, ), timeout=timeout, ) async def exchange_unbind( self, destination: str = "", source: str = "", routing_key: str = "", *, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Exchange.UnbindOk: _check_routing_key(routing_key) return await self.rpc( spec.Exchange.Unbind( destination=destination, source=source, routing_key=routing_key, nowait=nowait, arguments=arguments, ), timeout=timeout, ) async def flow( self, active: bool, timeout: TimeoutType = None, ) -> spec.Channel.FlowOk: return await self.rpc( spec.Channel.Flow(active=active), timeout=timeout, ) async def queue_bind( self, queue: str, exchange: str, routing_key: str = "", nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.BindOk: _check_routing_key(routing_key) return await self.rpc( spec.Queue.Bind( queue=queue, exchange=exchange, routing_key=routing_key, nowait=nowait, arguments=arguments, ), timeout=timeout, ) async def queue_declare( self, queue: str = "", *, passive: bool = False, durable: bool = False, exclusive: bool = False, auto_delete: bool = False, nowait: bool = False, arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.DeclareOk: return await self.rpc( spec.Queue.Declare( queue=queue, passive=bool(passive), durable=bool(durable), exclusive=bool(exclusive), auto_delete=bool(auto_delete), nowait=bool(nowait), arguments=arguments, ), timeout=timeout, ) async def queue_delete( self, queue: str = "", if_unused: bool = False, if_empty: bool = False, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Queue.DeleteOk: return await self.rpc( spec.Queue.Delete( queue=queue, if_unused=if_unused, if_empty=if_empty, nowait=nowait, ), timeout=timeout, ) async def queue_purge( self, queue: str = "", nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Queue.PurgeOk: return await self.rpc( spec.Queue.Purge(queue=queue, nowait=nowait), timeout=timeout, ) async def queue_unbind( self, queue: str = "", exchange: str = "", routing_key: str = "", arguments: Optional[ArgumentsType] = None, timeout: TimeoutType = None, ) -> spec.Queue.UnbindOk: _check_routing_key(routing_key) return await self.rpc( spec.Queue.Unbind( routing_key=routing_key, arguments=arguments, queue=queue, exchange=exchange, ), timeout=timeout, ) async def tx_commit( self, timeout: TimeoutType = None, ) -> spec.Tx.CommitOk: return await self.rpc(spec.Tx.Commit(), timeout=timeout) async def tx_rollback( self, timeout: TimeoutType = None, ) -> spec.Tx.RollbackOk: return await self.rpc(spec.Tx.Rollback(), timeout=timeout) async def tx_select(self, timeout: TimeoutType = None) -> spec.Tx.SelectOk: return await self.rpc(spec.Tx.Select(), timeout=timeout) async def confirm_delivery( self, nowait: bool = False, timeout: TimeoutType = None, ) -> spec.Confirm.SelectOk: return await self.rpc( spec.Confirm.Select(nowait=nowait), timeout=timeout, ) python-aiormq-6.8.1/aiormq/connection.py000066400000000000000000000730651473731423200203570ustar00rootroot00000000000000import asyncio import logging import platform import ssl import sys from base64 import b64decode from collections.abc import AsyncIterable from contextlib import suppress from io import BytesIO from types import MappingProxyType, TracebackType from typing import ( Any, Awaitable, Callable, Dict, Mapping, Optional, Tuple, Type, Union, ) import pamqp.frame from pamqp import commands as spec from pamqp.base import Frame from pamqp.common import FieldTable from pamqp.constants import REPLY_SUCCESS from pamqp.exceptions import AMQPFrameError, AMQPInternalError, AMQPSyntaxError from pamqp.frame import FrameTypes from pamqp.header import ProtocolHeader from pamqp.heartbeat import Heartbeat from yarl import URL from .abc import ( AbstractChannel, AbstractConnection, ArgumentsType, ChannelFrame, ExceptionType, SSLCerts, TaskType, URLorStr, ) from .auth import AuthMechanism from .base import Base, task from .channel import Channel from .exceptions import ( AMQPConnectionError, AMQPError, AuthenticationError, ConnectionChannelError, ConnectionClosed, ConnectionCommandInvalid, ConnectionFrameError, ConnectionInternalError, ConnectionNotAllowed, ConnectionNotImplemented, ConnectionResourceError, ConnectionSyntaxError, ConnectionUnexpectedFrame, IncompatibleProtocolError, ProbableAuthenticationError, ) from .tools import Countdown, censor_url # noinspection PyUnresolvedReferences try: from importlib.metadata import Distribution __version__ = Distribution.from_name("aiormq").version except ImportError: import pkg_resources __version__ = pkg_resources.get_distribution("aiormq").version log = logging.getLogger(__name__) CHANNEL_CLOSE_RESPONSES = (spec.Channel.Close, spec.Channel.CloseOk) DEFAULT_PORTS = { "amqp": 5672, "amqps": 5671, } PRODUCT = "aiormq" PLATFORM = "{} {} ({} build {})".format( platform.python_implementation(), platform.python_version(), *platform.python_build(), ) TimeType = Union[float, int] TimeoutType = Optional[TimeType] ReceivedFrame = Tuple[int, int, FrameTypes] EXCEPTION_MAPPING = MappingProxyType({ 501: ConnectionFrameError, 502: ConnectionSyntaxError, 503: ConnectionCommandInvalid, 504: ConnectionChannelError, 505: ConnectionUnexpectedFrame, 506: ConnectionResourceError, 530: ConnectionNotAllowed, 540: ConnectionNotImplemented, 541: ConnectionInternalError, }) def exception_by_code(frame: spec.Connection.Close) -> AMQPError: if frame.reply_code is None: return ConnectionClosed(frame.reply_code, frame.reply_text) exc_class = EXCEPTION_MAPPING.get(frame.reply_code) if exc_class is None: return ConnectionClosed(frame.reply_code, frame.reply_text) return exc_class(frame.reply_text) def parse_bool(v: Any) -> bool: if isinstance(v, bool): return v v = str(v) return v.lower() in ( "true", "yes", "y", "enable", "on", "enabled", "1" ) def parse_int(v: Any) -> int: if isinstance(v, int): return v v = str(v) try: return int(v) except ValueError: return 0 def parse_timeout(v: Any) -> TimeoutType: if isinstance(v, float): if v.is_integer(): return int(v) return v if isinstance(v, int): return v v = str(v) try: if "." in v: result = float(v) if result.is_integer(): return int(result) return result return int(v) except ValueError: return 0 def parse_heartbeat(v: str) -> int: result = parse_int(v) return result if 0 <= result < 65535 else 0 def parse_connection_name(connection_name: Optional[str]) -> Dict[str, str]: if not connection_name or not isinstance(connection_name, str): return {} return dict(connection_name=connection_name) class FrameReceiver(AsyncIterable): _loop: asyncio.AbstractEventLoop def __init__( self, reader: asyncio.StreamReader, ): self.reader: asyncio.StreamReader = reader self.started: bool = False self.lock = asyncio.Lock() @property def loop(self) -> asyncio.AbstractEventLoop: if not hasattr(self, "_loop"): self._loop = asyncio.get_event_loop() return self._loop def __aiter__(self) -> "FrameReceiver": return self async def get_frame(self) -> ReceivedFrame: if self.reader.at_eof(): del self.reader raise StopAsyncIteration with BytesIO() as fp: async with self.lock: try: fp.write(await self.reader.readexactly(1)) if fp.getvalue() == b"\0x00": fp.write(await self.reader.read()) raise AMQPFrameError(fp.getvalue()) if self.reader is None: raise AMQPConnectionError() fp.write(await self.reader.readexactly(6)) if not self.started and fp.getvalue().startswith(b"AMQP"): raise AMQPSyntaxError else: self.started = True frame_type, _, frame_length = pamqp.frame.frame_parts( fp.getvalue(), ) if frame_length is None: raise AMQPInternalError("No frame length", None) fp.write(await self.reader.readexactly(frame_length + 1)) except asyncio.IncompleteReadError as e: raise AMQPConnectionError( "Server connection unexpectedly closed. " f"Read {len(e.partial)} bytes but {e.expected} " "bytes expected", ) from e except ConnectionRefusedError as e: raise AMQPConnectionError( f"Server connection refused: {e!r}", ) from e except ConnectionResetError as e: raise AMQPConnectionError( f"Server connection reset: {e!r}", ) from e except ConnectionError as e: raise AMQPConnectionError( f"Server connection error: {e!r}", ) from e except OSError as e: raise AMQPConnectionError( f"Server communication error: {e!r}", ) from e return pamqp.frame.unmarshal(fp.getvalue()) async def __anext__(self) -> ReceivedFrame: return await self.get_frame() class FrameGenerator(AsyncIterable): def __init__(self, queue: asyncio.Queue): self.queue: asyncio.Queue = queue self.close_event: asyncio.Event = asyncio.Event() def __aiter__(self) -> "FrameGenerator": return self async def __anext__(self) -> ChannelFrame: if self.close_event.is_set(): raise StopAsyncIteration frame: ChannelFrame = await self.queue.get() self.queue.task_done() return frame class Connection(Base, AbstractConnection): FRAME_BUFFER_SIZE = 10 # Interval between sending heartbeats based on the heartbeat(timeout) HEARTBEAT_INTERVAL_MULTIPLIER = 0.5 # Allow three missed heartbeats (based on heartbeat(timeout) HEARTBEAT_GRACE_MULTIPLIER = 3 READER_CLOSE_TIMEOUT = 2 _reader_task: TaskType _writer_task: TaskType __create_connection_kwargs: Mapping[str, Any] write_queue: asyncio.Queue server_properties: ArgumentsType connection_tune: spec.Connection.Tune channels: Dict[int, Optional[AbstractChannel]] @staticmethod def _parse_ca_data(data: Optional[str]) -> Optional[bytes]: return b64decode(data) if data else None def __init__( self, url: URLorStr, *, loop: Optional[asyncio.AbstractEventLoop] = None, context: Optional[ssl.SSLContext] = None, **create_connection_kwargs: Any, ): super().__init__(loop=loop or asyncio.get_event_loop(), parent=None) self.url = URL(url) if self.url.is_absolute() and not self.url.port: self.url = self.url.with_port(DEFAULT_PORTS[self.url.scheme]) if self.url.path == "/" or not self.url.path: self.vhost = "/" else: quoted_vhost = self.url.path[1:] # yarl>=1.9.5 skips unquoting backslashes in path self.vhost = quoted_vhost.replace("%2F", "/") self.ssl_context = context self.ssl_certs = SSLCerts( cafile=self.url.query.get("cafile"), capath=self.url.query.get("capath"), cadata=self._parse_ca_data(self.url.query.get("cadata")), key=self.url.query.get("keyfile"), cert=self.url.query.get("certfile"), verify=self.url.query.get("no_verify_ssl", "0") == "0", ) self.started = False self.channels = {} self.write_queue = asyncio.Queue( maxsize=self.FRAME_BUFFER_SIZE, ) self.last_channel = 1 self.timeout = parse_int(self.url.query.get("timeout", "60")) self.heartbeat_timeout = parse_heartbeat( self.url.query.get("heartbeat", "60"), ) self.last_channel_lock = asyncio.Lock() self.connected = asyncio.Event() self.connection_name = self.url.query.get("name") self.__close_reply_code: int = REPLY_SUCCESS self.__close_reply_text: str = "normally closed" self.__close_class_id: int = 0 self.__close_method_id: int = 0 self.__update_secret_lock: asyncio.Lock = asyncio.Lock() self.__update_secret_future: Optional[asyncio.Future] = None self.__connection_unblocked: asyncio.Event = asyncio.Event() self.__heartbeat_grace_timeout = ( (self.heartbeat_timeout + 1) * self.HEARTBEAT_GRACE_MULTIPLIER ) self.__last_frame_time: float = self.loop.time() self.__create_connection_kwargs = create_connection_kwargs async def ready(self) -> None: await self.connected.wait() await self.__connection_unblocked.wait() def set_close_reason( self, reply_code: int = REPLY_SUCCESS, reply_text: str = "normally closed", class_id: int = 0, method_id: int = 0, ) -> None: self.__close_reply_code = reply_code self.__close_reply_text = reply_text self.__close_class_id = class_id self.__close_method_id = method_id @property def is_opened(self) -> bool: is_reader_running = ( hasattr(self, "_reader_task") and not self._reader_task.done() ) is_writer_running = ( hasattr(self, "_writer_task") and not self._writer_task.done() ) return ( is_reader_running and is_writer_running and not self.is_closed ) def __str__(self) -> str: return str(censor_url(self.url)) def _get_ssl_context(self) -> ssl.SSLContext: context = ssl.create_default_context( ssl.Purpose.SERVER_AUTH, capath=self.ssl_certs.capath, cafile=self.ssl_certs.cafile, cadata=self.ssl_certs.cadata, ) if self.ssl_certs.cert: context.load_cert_chain(self.ssl_certs.cert, self.ssl_certs.key) if not self.ssl_certs.verify: context.check_hostname = False context.verify_mode = ssl.CERT_NONE return context def _client_properties(self, **kwargs: Any) -> Dict[str, Any]: properties = { "platform": PLATFORM, "version": __version__, "product": PRODUCT, "capabilities": { "authentication_failure_close": True, "basic.nack": True, "connection.blocked": True, "consumer_cancel_notify": True, "publisher_confirms": True, }, "information": "See https://github.com/mosquito/aiormq/", } properties.update( parse_connection_name(self.connection_name), ) properties.update(kwargs) return properties def _credentials_class( self, start_frame: spec.Connection.Start, ) -> AuthMechanism: auth_requested = self.url.query.get("auth", "plain").upper() auth_available = start_frame.mechanisms.split() if auth_requested in auth_available: with suppress(KeyError): return AuthMechanism[auth_requested] raise AuthenticationError( start_frame.mechanisms, [m.name for m in AuthMechanism], ) @staticmethod async def _rpc( request: Frame, writer: asyncio.StreamWriter, frame_receiver: FrameReceiver, wait_response: bool = True, ) -> Optional[FrameTypes]: writer.write(pamqp.frame.marshal(request, 0)) await writer.drain() if not wait_response: return None _, _, frame = await frame_receiver.get_frame() if request.synchronous and frame.name not in request.valid_responses: raise AMQPInternalError( "one of {!r}".format(request.valid_responses), frame, ) elif isinstance(frame, spec.Connection.Close): if frame.reply_code == 403: raise ProbableAuthenticationError(frame.reply_text) raise ConnectionClosed(frame.reply_code, frame.reply_text) return frame @task async def connect( self, client_properties: Optional[FieldTable] = None, ) -> bool: if self.is_opened: raise RuntimeError("Connection already opened") ssl_context = self.ssl_context if ssl_context is None and self.url.scheme == "amqps": ssl_context = await self.loop.run_in_executor( None, self._get_ssl_context, ) self.ssl_context = ssl_context log.debug("Connecting to: %s", self) try: reader, writer = await asyncio.open_connection( self.url.host, self.url.port, ssl=ssl_context, **self.__create_connection_kwargs, ) frame_receiver = FrameReceiver(reader) except OSError as e: raise AMQPConnectionError(*e.args) from e frame: Optional[FrameTypes] try: protocol_header = ProtocolHeader() writer.write(protocol_header.marshal()) _, _, frame = await frame_receiver.get_frame() except EOFError as e: raise IncompatibleProtocolError(*e.args) from e if not isinstance(frame, spec.Connection.Start): raise AMQPInternalError("Connection.StartOk", frame) credentials = self._credentials_class(frame) server_properties: ArgumentsType = frame.server_properties try: frame = await self._rpc( spec.Connection.StartOk( client_properties=self._client_properties( **(client_properties or {}), ), mechanism=credentials.name, response=credentials.value(self).marshal(), ), writer=writer, frame_receiver=frame_receiver, ) if not isinstance(frame, spec.Connection.Tune): raise AMQPInternalError("Connection.Tune", frame) connection_tune: spec.Connection.Tune = frame connection_tune.heartbeat = self.heartbeat_timeout await self._rpc( spec.Connection.TuneOk( channel_max=connection_tune.channel_max, frame_max=connection_tune.frame_max, heartbeat=connection_tune.heartbeat, ), writer=writer, frame_receiver=frame_receiver, wait_response=False, ) frame = await self._rpc( spec.Connection.Open(virtual_host=self.vhost), writer=writer, frame_receiver=frame_receiver, ) if not isinstance(frame, spec.Connection.OpenOk): raise AMQPInternalError("Connection.OpenOk", frame) except BaseException as e: await self.__close_writer(writer) await self.close(e) raise # noinspection PyAsyncCall self._reader_task = self.create_task(self.__reader(frame_receiver)) self._reader_task.add_done_callback(self._on_reader_done) # noinspection PyAsyncCall self._writer_task = self.create_task(self.__writer(writer)) self.connection_tune = connection_tune self.server_properties = server_properties return True def _on_reader_done(self, task: asyncio.Task) -> None: log.debug("Reader exited for %r", self) if not task.cancelled() and task.exception() is not None: log.debug("Cancelling cause reader exited abnormally") self.set_close_reason( reply_code=500, reply_text="reader unexpected closed", ) async def close_writer_task() -> None: if not self._writer_task.done(): self._writer_task.cancel() await asyncio.gather(self._writer_task, return_exceptions=True) try: exc = task.exception() except asyncio.CancelledError as e: exc = e await self.close(exc) self.loop.create_task(close_writer_task()) async def __handle_close_ok(self, _: spec.Connection.CloseOk) -> None: return async def __handle_heartbeat(self, _: Heartbeat) -> None: return async def __handle_close(self, frame: spec.Connection.Close) -> None: log.exception( "Unexpected connection close from remote \"%s\", " "Connection.Close(reply_code=%r, reply_text=%r)", self, frame.reply_code, frame.reply_text, ) with suppress(asyncio.QueueFull): self.write_queue.put_nowait( ChannelFrame.marshall( channel_number=0, frames=[spec.Connection.CloseOk()], ), ) exception = exception_by_code(frame) if ( self.__update_secret_future is not None and not self.__update_secret_future.done() ): self.__update_secret_future.set_exception(exception) raise exception async def __handle_channel_close_ok( self, _: spec.Channel.CloseOk, ) -> None: self.channels.pop(0, None) async def __handle_channel_update_secret_ok( self, frame: spec.Connection.UpdateSecretOk, ) -> None: if ( self.__update_secret_future is not None and not self.__update_secret_future.done() ): self.__update_secret_future.set_result(frame) return log.warning("Got unexpected UpdateSecretOk frame") async def __handle_connection_blocked( self, frame: spec.Connection.Blocked, ) -> None: log.warning("Connection %r was blocked by: %r", self, frame.reason) self.__connection_unblocked.clear() async def __handle_connection_unblocked( self, _: spec.Connection.Unblocked, ) -> None: log.warning("Connection %r was unblocked", self) self.__connection_unblocked.set() async def __reader(self, frame_receiver: FrameReceiver) -> None: self.__connection_unblocked.set() self.connected.set() # Not very optimal, but avoid creating a task for each frame sending # noinspection PyAsyncCall if self.heartbeat_timeout > 0: self.create_task(self.__heartbeat()) channel_frame_handlers: Mapping[Any, Callable[[Any], Awaitable[None]]] channel_frame_handlers = { spec.Connection.CloseOk: self.__handle_close_ok, spec.Connection.Close: self.__handle_close, Heartbeat: self.__handle_heartbeat, spec.Channel.CloseOk: self.__handle_channel_close_ok, spec.Connection.UpdateSecretOk: ( self.__handle_channel_update_secret_ok ), spec.Connection.Blocked: self.__handle_connection_blocked, spec.Connection.Unblocked: self.__handle_connection_unblocked, } try: async for weight, channel, frame in frame_receiver: self.__last_frame_time = self.loop.time() log.debug( "Received frame %r in channel #%d weight=%s on %r", frame, channel, weight, self, ) if channel == 0: handler = channel_frame_handlers.get(type(frame)) if handler is None: log.error("Unexpected frame %r", frame) continue await handler(frame) continue ch: Optional[AbstractChannel] = self.channels.get(channel) if ch is None: log.error( "Got frame for closed channel %d: %r", channel, frame, ) continue if isinstance(frame, CHANNEL_CLOSE_RESPONSES): self.channels[channel] = None await ch.frames.put((weight, frame)) except asyncio.CancelledError: if self.is_connection_was_stuck: log.warning( "Server connection %r was stuck. No frames were received " "in %d seconds.", self, self.__heartbeat_grace_timeout, ) self._writer_task.cancel() raise @property def is_connection_was_stuck(self) -> bool: delay = self.loop.time() - self.__last_frame_time return delay > self.__heartbeat_grace_timeout async def __heartbeat(self) -> None: heartbeat_timeout = max(1, self.heartbeat_timeout // 2) heartbeat = ChannelFrame.marshall( frames=[Heartbeat()], channel_number=0, ) while not self.closing.done(): if self.is_connection_was_stuck: self._reader_task.cancel() return await asyncio.sleep(heartbeat_timeout) try: await asyncio.wait_for( self.write_queue.put(heartbeat), timeout=self.__heartbeat_grace_timeout, ) except asyncio.TimeoutError: self._reader_task.cancel() return async def __writer(self, writer: asyncio.StreamWriter) -> None: channel_frame: ChannelFrame try: frame_iterator = FrameGenerator(self.write_queue) self.closing.add_done_callback( lambda _: frame_iterator.close_event.set(), ) if not self.__connection_unblocked.is_set(): await self.__connection_unblocked.wait() async for channel_frame in frame_iterator: log.debug("Prepare to send %r", channel_frame) writer.write(channel_frame.payload) if channel_frame.should_close: await writer.drain() channel_frame.drain() return if channel_frame.should_drain: await writer.drain() channel_frame.drain() except asyncio.CancelledError: if not self.__check_writer(writer) or self.is_connection_was_stuck: raise frame = spec.Connection.Close( reply_code=self.__close_reply_code, reply_text=self.__close_reply_text, class_id=self.__close_class_id, method_id=self.__close_method_id, ) writer.write(ChannelFrame.marshall(0, [frame]).payload) log.debug("Sending %r to %r", frame, self) try: await asyncio.wait_for( writer.drain(), timeout=self.__heartbeat_grace_timeout, ) finally: await self.__close_writer(writer) raise finally: log.debug("Writer exited for %r", self) if sys.version_info < (3, 7): async def __close_writer(self, writer: asyncio.StreamWriter) -> None: log.debug("Writer on connection %s closed", self) writer.close() else: async def __close_writer(self, writer: asyncio.StreamWriter) -> None: log.debug("Writer on connection %s closed", self) with suppress(OSError, RuntimeError): if writer.can_write_eof(): writer.write_eof() writer.close() await writer.wait_closed() @staticmethod def __check_writer(writer: asyncio.StreamWriter) -> bool: if writer is None: return False if hasattr(writer, "is_closing"): return not writer.is_closing() if writer.transport: return not writer.transport.is_closing() return writer.can_write_eof() async def _on_close( self, ex: Optional[ExceptionType] = ConnectionClosed(0, "normal closed"), ) -> None: log.debug("Closing connection %r cause: %r", self, ex) if not self._reader_task.done(): self._reader_task.cancel() if not self._writer_task.done(): self._writer_task.cancel() await asyncio.gather( self._reader_task, self._writer_task, return_exceptions=True, ) @property def server_capabilities(self) -> ArgumentsType: return self.server_properties["capabilities"] # type: ignore @property def basic_nack(self) -> bool: return bool(self.server_capabilities.get("basic.nack")) @property def consumer_cancel_notify(self) -> bool: return bool(self.server_capabilities.get("consumer_cancel_notify")) @property def exchange_exchange_bindings(self) -> bool: return bool(self.server_capabilities.get("exchange_exchange_bindings")) @property def publisher_confirms(self) -> Optional[bool]: publisher_confirms = self.server_capabilities.get("publisher_confirms") if publisher_confirms is None: return None return bool(publisher_confirms) async def channel( self, channel_number: Optional[int] = None, publisher_confirms: bool = True, frame_buffer_size: int = FRAME_BUFFER_SIZE, timeout: TimeoutType = None, **kwargs: Any, ) -> AbstractChannel: await self.connected.wait() if self.is_closed: raise RuntimeError("%r closed" % self) if not self.publisher_confirms and publisher_confirms: raise ValueError("Server doesn't support publisher_confirms") if channel_number is None: async with self.last_channel_lock: if self.channels: self.last_channel = max(self.channels.keys()) while self.last_channel in self.channels.keys(): self.last_channel += 1 if self.last_channel > 65535: log.warning("Resetting channel number for %r", self) self.last_channel = 1 # switching context for prevent blocking event-loop await asyncio.sleep(0) channel_number = self.last_channel elif channel_number in self.channels: raise ValueError("Channel %d already used" % channel_number) if channel_number < 0 or channel_number > 65535: raise ValueError("Channel number too large") channel = Channel( self, channel_number, frame_buffer=frame_buffer_size, publisher_confirms=publisher_confirms, **kwargs, ) self.channels[channel_number] = channel try: await channel.open(timeout=timeout) except Exception: self.channels[channel_number] = None raise return channel async def update_secret( self, new_secret: str, *, reason: str = "", timeout: TimeoutType = None, ) -> spec.Connection.UpdateSecretOk: channel_frame = ChannelFrame.marshall( channel_number=0, frames=[ spec.Connection.UpdateSecret( new_secret=new_secret, reason=reason, ), ], ) countdown = Countdown(timeout) async with countdown.enter_context(self.__update_secret_lock): self.__update_secret_future = self.loop.create_future() await self.write_queue.put(channel_frame) try: response: spec.Connection.UpdateSecretOk = ( await countdown(self.__update_secret_future) ) finally: self.__update_secret_future = None return response async def __aenter__(self) -> AbstractConnection: if not self.is_opened: await self.connect() return self async def __aexit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: await self.close(exc_val) async def connect( url: URLorStr, *args: Any, client_properties: Optional[FieldTable] = None, **kwargs: Any, ) -> AbstractConnection: connection = Connection(url, *args, **kwargs) await connection.connect(client_properties or {}) return connection python-aiormq-6.8.1/aiormq/exceptions.py000066400000000000000000000133071473731423200203720ustar00rootroot00000000000000from typing import Any, Optional from pamqp.base import Frame from pamqp.commands import Basic from .abc import DeliveredMessage class AMQPError(Exception): reason = "An unspecified AMQP error has occurred: %s" def __repr__(self) -> str: try: return "<%s: %s>" % ( self.__class__.__name__, self.reason % self.args, ) except TypeError: # FIXME: if you are here file an issue return f"<{self.__class__.__name__}: {self.args!r}>" # Backward compatibility AMQPException = AMQPError class AMQPConnectionError(AMQPError, ConnectionError): reason = "Unexpected connection problem" def __repr__(self) -> str: if self.args: return f"<{self.__class__.__name__}: {self.args!r}>" return AMQPError.__repr__(self) class IncompatibleProtocolError(AMQPConnectionError): reason = "The protocol returned by the server is not supported" class AuthenticationError(AMQPConnectionError): reason = ( "Server and client could not negotiate use of the " "authentication mechanisms. Server supports only %r, " "but client supports only %r." ) class ProbableAuthenticationError(AMQPConnectionError): reason = ( "Client was disconnected at a connection stage indicating a " "probable authentication error: %s" ) class ConnectionClosed(AMQPConnectionError): reason = "The AMQP connection was closed (%s) %s" class ConnectionSyntaxError(ConnectionClosed): reason = ( "The sender sent a frame that contained illegal values for " "one or more fields. This strongly implies a programming error " "in the sending peer: %r" ) class ConnectionFrameError(ConnectionClosed): reason = ( "The sender sent a malformed frame that the recipient could " "not decode. This strongly implies a programming error " "in the sending peer: %r" ) class ConnectionCommandInvalid(ConnectionClosed): reason = ( "The client sent an invalid sequence of frames, attempting to " "perform an operation that was considered invalid by the server." " This usually implies a programming error in the client: %r" ) class ConnectionChannelError(ConnectionClosed): reason = ( "The client attempted to work with a channel that had not been " "correctly opened. This most likely indicates a fault in the " "client layer: %r" ) class ConnectionUnexpectedFrame(ConnectionClosed): reason = ( "The peer sent a frame that was not expected, usually in the " "context of a content header and body. This strongly indicates " "a fault in the peer's content processing: %r" ) class ConnectionResourceError(ConnectionClosed): reason = ( "The server could not complete the method because it lacked " "sufficient resources. This may be due to the client creating " "too many of some type of entity: %r" ) class ConnectionNotAllowed(ConnectionClosed): reason = ( "The client tried to work with some entity in a manner that is " "prohibited by the server, due to security settings or by " "some other criteria: %r" ) class ConnectionNotImplemented(ConnectionClosed): reason = ( "The client tried to use functionality that is " "not implemented in the server: %r" ) class ConnectionInternalError(ConnectionClosed): reason = ( " The server could not complete the method because of an " "internal error. The server may require intervention by an " "operator in order to resume normal operations: %r" ) class AMQPChannelError(AMQPError): reason = "An unspecified AMQP channel error has occurred" class ChannelClosed(AMQPChannelError): reason = "The channel was closed (%s) %s" class ChannelAccessRefused(ChannelClosed): reason = ( "The client attempted to work with a server entity to " "which it has no access due to security settings: %r" ) class ChannelNotFoundEntity(ChannelClosed): reason = ( "The client attempted to work with a server " "entity that does not exist: %r" ) class ChannelLockedResource(ChannelClosed): reason = ( "The client attempted to work with a server entity to " "which it has no access because another client is working " "with it: %r" ) class ChannelPreconditionFailed(ChannelClosed): reason = ( "The client requested a method that was not allowed because " "some precondition failed: %r" ) class DuplicateConsumerTag(ChannelClosed): reason = "The consumer tag specified already exists for this channel: %s" class ProtocolSyntaxError(AMQPError): reason = "An unspecified protocol syntax error occurred" class InvalidFrameError(ProtocolSyntaxError): reason = "Invalid frame received: %r" class MethodNotImplemented(AMQPError): pass class DeliveryError(AMQPError): __slots__ = "message", "frame" reason = "Error when delivery message %r, frame %r" def __init__( self, message: Optional[DeliveredMessage], frame: Frame, *args: Any, ): self.message = message self.frame = frame super().__init__(self.message, self.frame) class PublishError(DeliveryError): reason = "%r for routing key %r" def __init__(self, message: DeliveredMessage, frame: Frame, *args: Any): assert isinstance(message.delivery, Basic.Return) self.message = message self.frame = frame super(DeliveryError, self).__init__( message.delivery.reply_text, message.delivery.routing_key, *args, ) class ChannelInvalidStateError(RuntimeError): pass python-aiormq-6.8.1/aiormq/py.typed000066400000000000000000000000001473731423200173200ustar00rootroot00000000000000python-aiormq-6.8.1/aiormq/tools.py000066400000000000000000000063241473731423200173520ustar00rootroot00000000000000import asyncio import platform import time from functools import wraps from types import TracebackType from typing import ( Any, AsyncContextManager, Awaitable, Callable, Coroutine, Optional, Type, TypeVar, Union, ) from yarl import URL from aiormq.abc import TimeoutType T = TypeVar("T") def censor_url(url: URL) -> URL: if url.password is not None: return url.with_password("******") return url def shield(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]: @wraps(func) def wrap(*args: Any, **kwargs: Any) -> Awaitable[T]: return asyncio.shield(func(*args, **kwargs)) return wrap def awaitable( func: Callable[..., Union[T, Awaitable[T]]], ) -> Callable[..., Coroutine[Any, Any, T]]: # Avoid python 3.8+ warning if asyncio.iscoroutinefunction(func): return func # type: ignore @wraps(func) async def wrap(*args: Any, **kwargs: Any) -> T: result = func(*args, **kwargs) if hasattr(result, "__await__"): return await result # type: ignore if asyncio.iscoroutine(result) or asyncio.isfuture(result): return await result return result # type: ignore return wrap class Countdown: __slots__ = "loop", "deadline" if platform.system() == "Windows": @staticmethod def _now() -> float: # windows monotonic timer resolution is not enough. # Have to use time.time() return time.time() else: @staticmethod def _now() -> float: return time.monotonic() def __init__(self, timeout: TimeoutType = None): self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() self.deadline: TimeoutType = None if timeout is not None: self.deadline = self._now() + timeout def get_timeout(self) -> TimeoutType: if self.deadline is None: return None current = self._now() if current >= self.deadline: raise asyncio.TimeoutError return self.deadline - current async def __call__(self, coro: Awaitable[T]) -> T: try: timeout = self.get_timeout() except asyncio.TimeoutError: fut = asyncio.ensure_future(coro) fut.cancel() await asyncio.gather(fut, return_exceptions=True) raise if self.deadline is None and not timeout: return await coro return await asyncio.wait_for(coro, timeout=timeout) def enter_context( self, ctx: AsyncContextManager[T], ) -> AsyncContextManager[T]: return CountdownContext(self, ctx) class CountdownContext(AsyncContextManager): def __init__(self, countdown: Countdown, ctx: AsyncContextManager): self.countdown: Countdown = countdown self.ctx: AsyncContextManager = ctx async def __aenter__(self) -> T: return await self.countdown(self.ctx.__aenter__()) async def __aexit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> Any: return await self.countdown( self.ctx.__aexit__(exc_type, exc_val, exc_tb), ) python-aiormq-6.8.1/aiormq/types.py000066400000000000000000000003471473731423200173550ustar00rootroot00000000000000import warnings from .abc import * # noqa warnings.warn( "aiormq.types was deprecated and will be removed in " "one of next major releases. Use aiormq.abc instead.", category=DeprecationWarning, stacklevel=2, ) python-aiormq-6.8.1/gray.conf000066400000000000000000000001271473731423200161540ustar00rootroot00000000000000formatters = add-trailing-comma,isort,unify min-python-version = 3.7 log-level = error python-aiormq-6.8.1/poetry.lock000066400000000000000000002322601473731423200165440ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiomisc" version = "17.3.25" description = "aiomisc - miscellaneous utils for asyncio" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "aiomisc-17.3.25-py3-none-any.whl", hash = "sha256:40c10d60149769ff6d2d36790986e2c5296414eb49c6107f361e77234aaa6fbc"}, {file = "aiomisc-17.3.25.tar.gz", hash = "sha256:10f11f04aff59dbc1c6a3ab2bf99455fed3658cbdbc859e28f6c3927dd7e50af"}, ] [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 (>=1.3.8,<2.0.0)"] grpc = ["grpcio (>=1.56.0,<2.0.0)", "grpcio-tools (>=1.56.0,<2.0.0)"] raven = ["aiohttp (>3)", "raven"] rich = ["rich"] uvloop = ["uvloop (>=0.14,<1)"] [[package]] name = "aiomisc-pytest" version = "1.1.1" description = "pytest integration for aiomisc" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "aiomisc_pytest-1.1.1-py3-none-any.whl", hash = "sha256:c07251f79c936c85c7589429f43c728cf1a34b80c0089b268f2cfa6186e77020"}, {file = "aiomisc_pytest-1.1.1.tar.gz", hash = "sha256:2c378c41b078c0576027de6bf7fbc537a7e69285d23eaf4d45738d5d0de56dd3"}, ] [package.dependencies] aiomisc = ">=17" pytest = ">=7.2.1,<8.0.0" [[package]] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[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 = "collective-checkdocs" version = "0.2" description = "Distutils command to view and validate restructured text in package's long_description" optional = false python-versions = "*" files = [ {file = "collective.checkdocs-0.2.zip", hash = "sha256:3a5328257c5224bc72753820c182910d7fb336bc1dba5e09113d48566655e46e"}, ] [package.dependencies] docutils = "*" [[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.0" description = "Add colours to the output of Python's logging module." optional = false python-versions = ">=3.6" files = [ {file = "colorlog-6.8.0-py3-none-any.whl", hash = "sha256:4ed23b05a1154294ac99f511fabe8c1d6d4364ec1f7fc989c7fb515ccc29d375"}, {file = "colorlog-6.8.0.tar.gz", hash = "sha256:fbb6fdf9d5685f2517f388fb29bb27d54e8654dd31f58bc2a3b217e967a95ca6"}, ] [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 = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[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.7" description = "Pure python logging handler for writing logs to the journald using native protocol" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "logging_journald-0.6.7-py3-none-any.whl", hash = "sha256:ef9333a84fd64fbe1e18ca6f22624af4fc5d92d519a2e2652aa43358548898eb"}, {file = "logging_journald-0.6.7.tar.gz", hash = "sha256:5fdb576fff2ff82e98be1c7b4f0cbd87f061de5dbed38030f388dd4ba2d52e7d"}, ] [[package]] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] name = "multidict" version = "6.0.4" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] [[package]] name = "mypy" version = "0.991" description = "Optional static typing for Python" optional = false python-versions = ">=3.7" files = [ {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, ] [package.dependencies] mypy-extensions = ">=0.4.3" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pamqp" version = "3.3.0" description = "RabbitMQ Focused AMQP low-level library" optional = false python-versions = ">=3.7" files = [ {file = "pamqp-3.3.0-py2.py3-none-any.whl", hash = "sha256:c901a684794157ae39b52cbf700db8c9aae7a470f13528b9d7b4e5f7202f8eb0"}, {file = "pamqp-3.3.0.tar.gz", hash = "sha256:40b8795bd4efcf2b0f8821c1de83d12ca16d5760f4507836267fd7a02b06763b"}, ] [package.extras] codegen = ["lxml", "requests", "yapf"] testing = ["coverage", "flake8", "flake8-comprehensions", "flake8-deprecated", "flake8-import-order", "flake8-print", "flake8-quotes", "flake8-rst-docstrings", "flake8-tuple", "yapf"] [[package]] name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.7" files = [ {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] [[package]] name = "pycodestyle" version = "2.10.0" description = "Python style guide checker" optional = false python-versions = ">=3.6" files = [ {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, ] [[package]] name = "pydocstyle" version = "6.3.0" description = "Python docstring style checker" optional = false python-versions = ">=3.6" files = [ {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, ] [package.dependencies] snowballstemmer = ">=2.2.0" [package.extras] toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" version = "3.0.1" description = "passive checker of Python programs" optional = false python-versions = ">=3.6" files = [ {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, ] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylama" version = "8.4.1" description = "Code audit tool for python" optional = false python-versions = ">=3.7" files = [ {file = "pylama-8.4.1-py3-none-any.whl", hash = "sha256:5bbdbf5b620aba7206d688ed9fc917ecd3d73e15ec1a89647037a09fa3a86e60"}, {file = "pylama-8.4.1.tar.gz", hash = "sha256:2d4f7aecfb5b7466216d48610c7d6bad1c3990c29cdd392ad08259b161e486f6"}, ] [package.dependencies] mccabe = ">=0.7.0" pycodestyle = ">=2.9.1" pydocstyle = ">=6.1.1" pyflakes = ">=2.5.0" [package.extras] all = ["eradicate", "mypy", "pylint", "radon", "vulture"] eradicate = ["eradicate"] mypy = ["mypy"] pylint = ["pylint"] radon = ["radon"] tests = ["eradicate (>=2.0.0)", "mypy", "pylama-quotes", "pylint (>=2.11.1)", "pytest (>=7.1.2)", "pytest-mypy", "radon (>=5.1.0)", "toml", "types-setuptools", "types-toml", "vulture"] toml = ["toml (>=0.10.2)"] vulture = ["vulture"] [[package]] name = "pytest" version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytest-rst" version = "0.0.7" description = "Test code from RST documents with pytest" optional = false python-versions = "*" files = [ {file = "pytest-rst-0.0.7.tar.gz", hash = "sha256:2b34aa9d41ce8dce3e685e6f40dff432804962ffec49d3d37565f2dbad4014d5"}, ] [package.dependencies] docutils = "*" py = "*" pygments = "*" pytest = "*" [[package]] name = "requests" version = "2.32.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, ] [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 = "setuptools" version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "types-docutils" version = "0.20.0.3" description = "Typing stubs for docutils" optional = false python-versions = "*" files = [ {file = "types-docutils-0.20.0.3.tar.gz", hash = "sha256:4928e790f42b99d5833990f99c8dd9fa9f16825f6ed30380ca981846d36870cd"}, {file = "types_docutils-0.20.0.3-py3-none-any.whl", hash = "sha256:a930150d8e01a9170f9bca489f46808ddebccdd8bc1e47c07968a77e49fb9321"}, ] [[package]] name = "types-setuptools" version = "65.7.0.4" description = "Typing stubs for setuptools" optional = false python-versions = "*" files = [ {file = "types-setuptools-65.7.0.4.tar.gz", hash = "sha256:147809433301fe7e0f4ef5c0782f9a0453788960575e1efb6da5fe8cb2493c9f"}, {file = "types_setuptools-65.7.0.4-py3-none-any.whl", hash = "sha256:522067dfd8e1771f8d7e047e451de2740dc4e0c9f48a22302a6cc96e6c964a13"}, ] [package.dependencies] types-docutils = "*" [[package]] name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvloop" version = "0.18.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = false python-versions = ">=3.7.0" files = [ {file = "uvloop-0.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f354d669586fca96a9a688c585b6257706d216177ac457c92e15709acaece10"}, {file = "uvloop-0.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:280904236a5b333a273292b3bcdcbfe173690f69901365b973fa35be302d7781"}, {file = "uvloop-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad79cd30c7e7484bdf6e315f3296f564b3ee2f453134a23ffc80d00e63b3b59e"}, {file = "uvloop-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99deae0504547d04990cc5acf631d9f490108c3709479d90c1dcd14d6e7af24d"}, {file = "uvloop-0.18.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:edbb4de38535f42f020da1e3ae7c60f2f65402d027a08a8c60dc8569464873a6"}, {file = "uvloop-0.18.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:54b211c46facb466726b227f350792770fc96593c4ecdfaafe20dc00f3209aef"}, {file = "uvloop-0.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25b714f07c68dcdaad6994414f6ec0f2a3b9565524fba181dcbfd7d9598a3e73"}, {file = "uvloop-0.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1121087dfeb46e9e65920b20d1f46322ba299b8d93f7cb61d76c94b5a1adc20c"}, {file = "uvloop-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74020ef8061678e01a40c49f1716b4f4d1cc71190d40633f08a5ef8a7448a5c6"}, {file = "uvloop-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f4a549cd747e6f4f8446f4b4c8cb79504a8372d5d3a9b4fc20e25daf8e76c05"}, {file = "uvloop-0.18.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6132318e1ab84a626639b252137aa8d031a6c0550250460644c32ed997604088"}, {file = "uvloop-0.18.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:585b7281f9ea25c4a5fa993b1acca4ad3d8bc3f3fe2e393f0ef51b6c1bcd2fe6"}, {file = "uvloop-0.18.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:61151cc207cf5fc88863e50de3d04f64ee0fdbb979d0b97caf21cae29130ed78"}, {file = "uvloop-0.18.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c65585ae03571b73907b8089473419d8c0aff1e3826b3bce153776de56cbc687"}, {file = "uvloop-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3d301e23984dcbc92d0e42253e0e0571915f0763f1eeaf68631348745f2dccc"}, {file = "uvloop-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:680da98f12a7587f76f6f639a8aa7708936a5d17c5e7db0bf9c9d9cbcb616593"}, {file = "uvloop-0.18.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:75baba0bfdd385c886804970ae03f0172e0d51e51ebd191e4df09b929771b71e"}, {file = "uvloop-0.18.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ed3c28337d2fefc0bac5705b9c66b2702dc392f2e9a69badb1d606e7e7f773bb"}, {file = "uvloop-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8849b8ef861431543c07112ad8436903e243cdfa783290cbee3df4ce86d8dd48"}, {file = "uvloop-0.18.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:211ce38d84118ae282a91408f61b85cf28e2e65a0a8966b9a97e0e9d67c48722"}, {file = "uvloop-0.18.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a8f706b943c198dcedf1f2fb84899002c195c24745e47eeb8f2fb340f7dfc3"}, {file = "uvloop-0.18.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:58e44650cbc8607a218caeece5a689f0a2d10be084a69fc32f7db2e8f364927c"}, {file = "uvloop-0.18.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b8b7cf7806bdc745917f84d833f2144fabcc38e9cd854e6bc49755e3af2b53e"}, {file = "uvloop-0.18.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:56c1026a6b0d12b378425e16250acb7d453abaefe7a2f5977143898db6cfe5bd"}, {file = "uvloop-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:12af0d2e1b16780051d27c12de7e419b9daeb3516c503ab3e98d364cc55303bb"}, {file = "uvloop-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b028776faf9b7a6d0a325664f899e4c670b2ae430265189eb8d76bd4a57d8a6e"}, {file = "uvloop-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53aca21735eee3859e8c11265445925911ffe410974f13304edb0447f9f58420"}, {file = "uvloop-0.18.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:847f2ed0887047c63da9ad788d54755579fa23f0784db7e752c7cf14cf2e7506"}, {file = "uvloop-0.18.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6e20bb765fcac07879cd6767b6dca58127ba5a456149717e0e3b1f00d8eab51c"}, {file = "uvloop-0.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e14de8800765b9916d051707f62e18a304cde661fa2b98a58816ca38d2b94029"}, {file = "uvloop-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f3b18663efe0012bc4c315f1b64020e44596f5fabc281f5b0d9bc9465288559c"}, {file = "uvloop-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6d341bc109fb8ea69025b3ec281fcb155d6824a8ebf5486c989ff7748351a37"}, {file = "uvloop-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:895a1e3aca2504638a802d0bec2759acc2f43a0291a1dff886d69f8b7baff399"}, {file = "uvloop-0.18.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d90858f32a852988d33987d608bcfba92a1874eb9f183995def59a34229f30d"}, {file = "uvloop-0.18.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db1fcbad5deb9551e011ca589c5e7258b5afa78598174ac37a5f15ddcfb4ac7b"}, {file = "uvloop-0.18.0.tar.gz", hash = "sha256:d5d1135beffe9cd95d0350f19e2716bc38be47d5df296d7cc46e3b7557c0d1ff"}, ] [package.extras] docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] [[package]] name = "yarl" version = "1.9.4" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.8" content-hash = "a6ddd156362ba6dedf5f83ded8b3a4fb566c06bdf0a7fbf83e8bc122ed71a45f" python-aiormq-6.8.1/poetry.toml000066400000000000000000000001051473731423200165560ustar00rootroot00000000000000cache-dir = ".cache" [virtualenvs] path = ".venv" in-project = true python-aiormq-6.8.1/pylama.ini000066400000000000000000000002701473731423200163260ustar00rootroot00000000000000[pylama] linters = mccabe,pycodestyle,pyflakes skip = *env*,.tox*,*build*,.*,env/*,.venv/* ignore = C901 [pylama:pycodestyle] max_line_length = 80 show-pep8 = True show-source = True python-aiormq-6.8.1/pyproject.toml000066400000000000000000000046511473731423200172650ustar00rootroot00000000000000[tool.poetry] name = "aiormq" version = "6.8.1" description = "Pure python AMQP asynchronous client library" authors = ["Dmitry Orlov "] readme = "README.rst" license = "Apache-2.0" keywords=["rabbitmq", "asyncio", "amqp", "amqp 0.9.1", "driver", "pamqp"] homepage = "https://github.com/mosquito/aiormq" classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Topic :: Internet", "Topic :: Software Development", "Topic :: Software Development :: Libraries", "Topic :: System :: Clustering", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: MacOS", "Operating System :: POSIX", "Operating System :: Microsoft", "Programming Language :: Python", "Programming Language :: Python :: 3", "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 :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: CPython", ] packages = [{ include = "aiormq" }] [tool.poetry.urls] "Source" = "https://github.com/mosquito/aiormq" "Tracker" = "https://github.com/mosquito/aiormq/issues" "Documentation" = "https://github.com/mosquito/aiormq/blob/master/README.rst" [tool.poetry.dependencies] python = "^3.8" pamqp = "3.3.0" setuptools = [{ version = '*', python = "< 3.8" }] yarl = [{ version = '*'}] [tool.poetry.group.dev.dependencies] pytest = "^7.4.4" coverage = "^6.5.0" coveralls = "^3.3.1" pylama = "^8.4.1" pytest-cov = "^4.0.0" collective-checkdocs = "^0.2" mypy = "^0.991" pytest-rst = "^0.0.7" types-setuptools = "^65.6.0.2" aiomisc-pytest = "^1.1.1" setuptools = "^69.0.3" [tool.poetry.group.uvloop.dependencies] uvloop = ["^0.18"] [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 = false files = "aiormq" python-aiormq-6.8.1/tests/000077500000000000000000000000001473731423200155055ustar00rootroot00000000000000python-aiormq-6.8.1/tests/__init__.py000066400000000000000000000000001473731423200176040ustar00rootroot00000000000000python-aiormq-6.8.1/tests/certs/000077500000000000000000000000001473731423200166255ustar00rootroot00000000000000python-aiormq-6.8.1/tests/certs/Dockerfile000066400000000000000000000006221473731423200206170ustar00rootroot00000000000000FROM rabbitmq:3.8-management-alpine RUN mkdir -p /certs/ COPY tests/certs/ca.pem /certs/ COPY tests/certs/server.key /certs/ COPY tests/certs/server.pem /certs/ ENV RABBITMQ_SSL_CERTFILE=/certs/server.pem ENV RABBITMQ_SSL_KEYFILE=/certs/server.key ENV RABBITMQ_SSL_CACERTFILE=/certs/ca.pem ENV RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT=false ENV RABBITMQ_DEFAULT_USER=guest ENV RABBITMQ_DEFAULT_PASS=guest python-aiormq-6.8.1/tests/certs/ca.pem000066400000000000000000000041221473731423200177120ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- python-aiormq-6.8.1/tests/certs/client.key000066400000000000000000000062471473731423200206260ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- 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 -----END RSA PRIVATE KEY----- python-aiormq-6.8.1/tests/certs/client.pem000066400000000000000000000102241473731423200206050ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- python-aiormq-6.8.1/tests/certs/server.key000066400000000000000000000062531473731423200206530ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- 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 -----END RSA PRIVATE KEY----- python-aiormq-6.8.1/tests/certs/server.pem000066400000000000000000000103011473731423200206310ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- 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 MvekAEBEOasl6ZnGM/QQBN1o -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- python-aiormq-6.8.1/tests/conftest.py000066400000000000000000000071131473731423200177060ustar00rootroot00000000000000import asyncio import gc import logging import os import tracemalloc import pamqp import pytest from aiomisc_pytest import TCPProxy from yarl import URL from aiormq import Connection def cert_path(*args): return os.path.join( os.path.abspath(os.path.dirname(__file__)), "certs", *args, ) AMQP_URL = URL(os.getenv("AMQP_URL", "amqp://guest:guest@localhost/")) amqp_urls = { "amqp": AMQP_URL, "amqp-named": AMQP_URL.update_query(name="pytest"), "amqps": AMQP_URL.with_scheme("amqps").with_query( {"cafile": cert_path("ca.pem"), "no_verify_ssl": 1}, ), "amqps-client": AMQP_URL.with_scheme("amqps").with_query( { "cafile": cert_path("ca.pem"), "keyfile": cert_path("client.key"), "certfile": cert_path("client.pem"), "no_verify_ssl": 1, }, ), } amqp_url_list, amqp_url_ids = [], [] for name, url in amqp_urls.items(): amqp_url_list.append(url) amqp_url_ids.append(name) @pytest.fixture(params=amqp_url_list, ids=amqp_url_ids) async def amqp_url(request): return request.param @pytest.fixture async def amqp_connection(amqp_url, loop): connection = Connection(amqp_url, loop=loop) async with connection: yield connection channel_params = [ dict(channel_number=None, frame_buffer_size=10, publisher_confirms=True), dict(channel_number=None, frame_buffer_size=1, publisher_confirms=True), dict(channel_number=None, frame_buffer_size=10, publisher_confirms=False), dict(channel_number=None, frame_buffer_size=1, publisher_confirms=False), ] @pytest.fixture(params=channel_params) async def amqp_channel(request, amqp_connection): try: yield await amqp_connection.channel(**request.param) finally: await amqp_connection.close() skip_when_quick_test = pytest.mark.skipif( os.getenv("TEST_QUICK") is not None, reason="quick test", ) @pytest.fixture(autouse=True) def memory_tracer(): tracemalloc.start() tracemalloc.clear_traces() filters = ( tracemalloc.Filter(True, pamqp.__file__), tracemalloc.Filter(True, asyncio.__file__), ) snapshot_before = tracemalloc.take_snapshot().filter_traces(filters) def format_stat(stats): items = [ "TOP STATS:", "%-90s %6s %6s %6s" % ("Traceback", "line", "size", "count"), ] for stat in stats: fname = stat.traceback[0].filename lineno = stat.traceback[0].lineno items.append( "%-90s %6s %6s %6s" % (fname, lineno, stat.size_diff, stat.count_diff), ) return "\n".join(items) try: yield gc.collect() snapshot_after = tracemalloc.take_snapshot().filter_traces(filters) top_stats = snapshot_after.compare_to( snapshot_before, "lineno", cumulative=True, ) if top_stats: logging.error(format_stat(top_stats)) raise AssertionError("Possible memory leak") finally: tracemalloc.stop() @pytest.fixture() async def proxy(tcp_proxy, localhost, amqp_url: URL): port = amqp_url.port or 5672 if amqp_url.scheme == "amqp" else 5671 async with tcp_proxy(amqp_url.host, port) as proxy: yield proxy @pytest.fixture async def proxy_connection(proxy: TCPProxy, amqp_url: URL, loop): url = amqp_url.with_host( "localhost", ).with_port( proxy.proxy_port, ) connection = Connection(url, loop=loop) await connection.connect() try: yield connection finally: await connection.close() python-aiormq-6.8.1/tests/test_channel.py000066400000000000000000000204611473731423200205310ustar00rootroot00000000000000import asyncio import uuid from os import urandom import pytest from aiomisc_pytest import TCPProxy import aiormq from aiormq.abc import DeliveredMessage async def test_simple(amqp_channel: aiormq.Channel): await amqp_channel.basic_qos(prefetch_count=1) assert amqp_channel.number queue = asyncio.Queue() deaclare_ok = await amqp_channel.queue_declare(auto_delete=True) consume_ok = await amqp_channel.basic_consume(deaclare_ok.queue, queue.put) await amqp_channel.basic_publish( b"foo", routing_key=deaclare_ok.queue, properties=aiormq.spec.Basic.Properties(message_id="123"), ) message: DeliveredMessage = await queue.get() assert message.body == b"foo" cancel_ok = await amqp_channel.basic_cancel(consume_ok.consumer_tag) assert cancel_ok.consumer_tag == consume_ok.consumer_tag assert cancel_ok.consumer_tag not in amqp_channel.consumers await amqp_channel.queue_delete(deaclare_ok.queue) deaclare_ok = await amqp_channel.queue_declare(auto_delete=True) await amqp_channel.basic_publish(b"foo bar", routing_key=deaclare_ok.queue) message = await amqp_channel.basic_get(deaclare_ok.queue, no_ack=True) assert message.body == b"foo bar" async def test_blank_body(amqp_channel: aiormq.Channel): await amqp_channel.basic_qos(prefetch_count=1) assert amqp_channel.number queue = asyncio.Queue() deaclare_ok = await amqp_channel.queue_declare(auto_delete=True) consume_ok = await amqp_channel.basic_consume(deaclare_ok.queue, queue.put) await amqp_channel.basic_publish( b"", routing_key=deaclare_ok.queue, properties=aiormq.spec.Basic.Properties(message_id="123"), ) message: DeliveredMessage = await queue.get() assert message.body == b"" cancel_ok = await amqp_channel.basic_cancel(consume_ok.consumer_tag) assert cancel_ok.consumer_tag == consume_ok.consumer_tag assert cancel_ok.consumer_tag not in amqp_channel.consumers await amqp_channel.queue_delete(deaclare_ok.queue) deaclare_ok = await amqp_channel.queue_declare(auto_delete=True) await amqp_channel.basic_publish(b"foo bar", routing_key=deaclare_ok.queue) message = await amqp_channel.basic_get(deaclare_ok.queue, no_ack=True) assert message.body == b"foo bar" async def test_bad_consumer(amqp_channel: aiormq.Channel, loop): channel: aiormq.Channel = amqp_channel await channel.basic_qos(prefetch_count=1) declare_ok = await channel.queue_declare() future = loop.create_future() await channel.basic_publish(b"urgent", routing_key=declare_ok.queue) consumer_tag = loop.create_future() async def bad_consumer(message): await channel.basic_cancel(await consumer_tag) future.set_result(message) raise Exception consume_ok = await channel.basic_consume( declare_ok.queue, bad_consumer, no_ack=False, ) consumer_tag.set_result(consume_ok.consumer_tag) message = await future await channel.basic_reject(message.delivery.delivery_tag, requeue=True) assert message.body == b"urgent" future = loop.create_future() await channel.basic_consume( declare_ok.queue, future.set_result, no_ack=True, ) message = await future assert message.body == b"urgent" assert message.delivery_tag is not None assert message.exchange is not None assert message.redelivered async def test_ack_nack_reject(amqp_channel: aiormq.Channel): channel: aiormq.Channel = amqp_channel await channel.basic_qos(prefetch_count=1) declare_ok = await channel.queue_declare(auto_delete=True) queue = asyncio.Queue() await channel.basic_consume(declare_ok.queue, queue.put, no_ack=False) await channel.basic_publish(b"rejected", routing_key=declare_ok.queue) message: DeliveredMessage = await queue.get() assert message.body == b"rejected" assert message.delivery_tag is not None assert message.exchange is not None assert not message.redelivered await channel.basic_reject(message.delivery.delivery_tag, requeue=False) await channel.basic_publish(b"nacked", routing_key=declare_ok.queue) message = await queue.get() assert message.body == b"nacked" await channel.basic_nack(message.delivery.delivery_tag, requeue=False) await channel.basic_publish(b"acked", routing_key=declare_ok.queue) message = await queue.get() assert message.body == b"acked" await channel.basic_ack(message.delivery.delivery_tag) async def test_confirm_multiple(amqp_channel: aiormq.Channel): """ RabbitMQ has been observed to send confirmations in a strange pattern when publishing simultaneously where only some messages are delivered to a queue. It sends acks like this 1 2 4 5(multiple, confirming also 3). This test is probably inconsequential without publisher_confirms This is a regression for https://github.com/mosquito/aiormq/issues/10 """ channel: aiormq.Channel = amqp_channel exchange = uuid.uuid4().hex await channel.exchange_declare(exchange, exchange_type="topic") try: declare_ok = await channel.queue_declare(exclusive=True) await channel.queue_bind( declare_ok.queue, exchange, routing_key="test.5", ) for i in range(10): messages = [ asyncio.ensure_future( channel.basic_publish( b"test", exchange=exchange, routing_key="test.{}".format(i), ), ) for i in range(10) ] _, pending = await asyncio.wait(messages, timeout=0.2) assert not pending, "not all publishes were completed (confirmed)" await asyncio.sleep(0.05) finally: await channel.exchange_delete(exchange) async def test_exclusive_queue_locked(amqp_connection): channel0 = await amqp_connection.channel() channel1 = await amqp_connection.channel() qname = str(uuid.uuid4()) await channel0.queue_declare(qname, exclusive=True) try: await channel0.basic_consume(qname, print, exclusive=True) with pytest.raises(aiormq.exceptions.ChannelLockedResource): await channel1.queue_declare(qname) await channel1.basic_consume(qname, print, exclusive=True) finally: await channel0.queue_delete(qname) async def test_remove_writer_when_closed(amqp_channel: aiormq.Channel): with pytest.raises(aiormq.exceptions.ChannelClosed): await amqp_channel.queue_declare( "amq.forbidden_queue_name", auto_delete=True, ) with pytest.raises(aiormq.exceptions.ChannelInvalidStateError): await amqp_channel.queue_delete("amq.forbidden_queue_name") async def test_proxy_connection(proxy_connection, proxy: TCPProxy): channel: aiormq.Channel = await proxy_connection.channel() await channel.queue_declare(auto_delete=True) async def test_declare_queue_timeout(proxy_connection, proxy: TCPProxy): for _ in range(3): channel: aiormq.Channel = await proxy_connection.channel() qname = str(uuid.uuid4()) with proxy.slowdown(read_delay=5, write_delay=0): with pytest.raises(asyncio.TimeoutError): await channel.queue_declare( qname, auto_delete=True, timeout=0.5, ) async def test_big_message(amqp_channel: aiormq.Channel): size = 20 * 1024 * 1024 message = urandom(size) await amqp_channel.basic_publish(message) async def test_routing_key_too_large(amqp_channel: aiormq.Channel): routing_key = "x" * 256 with pytest.raises(ValueError): await amqp_channel.basic_publish(b"foo bar", routing_key=routing_key) exchange = uuid.uuid4().hex await amqp_channel.exchange_declare(exchange, exchange_type="topic") with pytest.raises(ValueError): await amqp_channel.exchange_bind(exchange, exchange, routing_key) with pytest.raises(ValueError): await amqp_channel.exchange_unbind(exchange, exchange, routing_key) queue = await amqp_channel.queue_declare(exclusive=True) with pytest.raises(ValueError): await amqp_channel.queue_bind(queue.queue, exchange, routing_key) with pytest.raises(ValueError): await amqp_channel.queue_unbind(queue.queue, exchange, routing_key) await amqp_channel.exchange_delete(exchange) python-aiormq-6.8.1/tests/test_connection.py000066400000000000000000000370011473731423200212560ustar00rootroot00000000000000import asyncio import itertools import os import ssl import uuid from binascii import hexlify from typing import Optional import aiomisc import pytest from pamqp.commands import Basic from yarl import URL import aiormq from aiormq.abc import DeliveredMessage from aiormq.auth import AuthBase, ExternalAuth, PlainAuth from aiormq.connection import parse_int, parse_timeout, parse_bool from .conftest import AMQP_URL, cert_path, skip_when_quick_test CERT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "certs")) async def test_simple(amqp_connection: aiormq.Connection): channel1 = await amqp_connection.channel() await channel1.basic_qos(prefetch_count=1) assert channel1.number channel2 = await amqp_connection.channel(11) assert channel2.number await channel1.close() await channel2.close() channel = await amqp_connection.channel() queue = asyncio.Queue() deaclare_ok = await channel.queue_declare(auto_delete=True) consume_ok = await channel.basic_consume(deaclare_ok.queue, queue.put) await channel.basic_publish(b"foo", routing_key=deaclare_ok.queue) message: DeliveredMessage = await queue.get() assert message.body == b"foo" with pytest.raises(aiormq.exceptions.PublishError) as e: await channel.basic_publish( b"bar", routing_key=deaclare_ok.queue + "foo", mandatory=True, ) message = e.value.message assert message.delivery.routing_key == deaclare_ok.queue + "foo" assert message.body == b"bar" assert "'NO_ROUTE' for routing key" in repr(e.value) cancel_ok = await channel.basic_cancel(consume_ok.consumer_tag) assert cancel_ok.consumer_tag == consume_ok.consumer_tag await channel.queue_delete(deaclare_ok.queue) deaclare_ok = await channel.queue_declare(auto_delete=True) await channel.basic_publish(b"foo bar", routing_key=deaclare_ok.queue) message = await channel.basic_get(deaclare_ok.queue, no_ack=True) assert message.body == b"foo bar" await amqp_connection.close() with pytest.raises(RuntimeError): await channel.basic_get(deaclare_ok.queue) with pytest.raises(RuntimeError): await amqp_connection.channel() async def test_channel_reuse(amqp_connection: aiormq.Connection): for _ in range(10): channel = await amqp_connection.channel(channel_number=1) await channel.basic_qos(prefetch_count=1) await channel.close() del channel async def test_channel_closed_reuse(amqp_connection: aiormq.Connection): for _ in range(10): channel = await amqp_connection.channel(channel_number=1) with pytest.raises(aiormq.ChannelAccessRefused): await channel.exchange_declare("", passive=True, auto_delete=True) async def test_bad_channel(amqp_connection: aiormq.Connection): with pytest.raises(ValueError): await amqp_connection.channel(65536) with pytest.raises(ValueError): await amqp_connection.channel(-1) channel = await amqp_connection.channel(65535) with pytest.raises(aiormq.exceptions.ChannelNotFoundEntity): await channel.queue_bind(uuid.uuid4().hex, uuid.uuid4().hex) async def test_properties(amqp_connection): assert amqp_connection.connection_tune.channel_max > 0 assert amqp_connection.connection_tune.heartbeat > 1 assert amqp_connection.connection_tune.frame_max > 1024 await amqp_connection.close() async def test_open(amqp_connection): with pytest.raises(RuntimeError): await amqp_connection.connect() channel = await amqp_connection.channel() await channel.close() await amqp_connection.close() async def test_channel_close(amqp_connection): channel = await amqp_connection.channel() assert channel.number in amqp_connection.channels await channel.close() assert channel.number not in amqp_connection.channels async def test_conncetion_reject(loop): with pytest.raises(ConnectionError): await aiormq.connect( "amqp://guest:guest@127.0.0.1:59999/", loop=loop, ) connection = aiormq.Connection( "amqp://guest:guest@127.0.0.1:59999/", loop=loop, ) with pytest.raises(ConnectionError): await loop.create_task(connection.connect()) async def test_auth_base(amqp_connection): with pytest.raises(NotImplementedError): AuthBase(amqp_connection).marshal() async def test_auth_plain(amqp_connection, loop): auth = PlainAuth(amqp_connection).marshal() assert auth == PlainAuth(amqp_connection).marshal() auth_parts = auth.split("\x00") assert auth_parts == ["", "guest", "guest"] connection = aiormq.Connection( amqp_connection.url.with_user("foo").with_password("bar"), loop=loop, ) auth = PlainAuth(connection).marshal() auth_parts = auth.split("\x00") assert auth_parts == ["", "foo", "bar"] auth = PlainAuth(connection) auth.value = "boo" assert auth.marshal() == "boo" async def test_auth_external(loop): url = AMQP_URL.with_scheme("amqps") url.update_query(auth="external") connection = aiormq.Connection auth = ExternalAuth(connection).marshal() auth = ExternalAuth(connection) auth.value = "" assert auth.marshal() == "" async def test_channel_closed(amqp_connection): for i in range(10): channel = await amqp_connection.channel() with pytest.raises(aiormq.exceptions.ChannelNotFoundEntity): await channel.basic_consume("foo", lambda x: None) channel = await amqp_connection.channel() with pytest.raises(aiormq.exceptions.ChannelNotFoundEntity): await channel.queue_declare( "foo_%s" % i, auto_delete=True, passive=True, ) await amqp_connection.close() async def test_timeout_default(loop): connection = aiormq.Connection(AMQP_URL, loop=loop) await connection.connect() assert connection.timeout == 60 await connection.close() async def test_timeout_1000(loop): url = AMQP_URL.update_query(timeout=1000) connection = aiormq.Connection(url, loop=loop) await connection.connect() assert connection.timeout await connection.close() async def test_heartbeat_0(loop): url = AMQP_URL.update_query(heartbeat=0) connection = aiormq.Connection(url, loop=loop) await connection.connect() assert connection.connection_tune.heartbeat == 0 await connection.close() async def test_heartbeat_default(loop): connection = aiormq.Connection(AMQP_URL, loop=loop) await connection.connect() assert connection.connection_tune.heartbeat == 60 await connection.close() async def test_heartbeat_above_range(loop): url = AMQP_URL.update_query(heartbeat=70000) connection = aiormq.Connection(url, loop=loop) await connection.connect() assert connection.connection_tune.heartbeat == 0 await connection.close() async def test_heartbeat_under_range(loop): url = AMQP_URL.update_query(heartbeat=-1) connection = aiormq.Connection(url, loop=loop) await connection.connect() assert connection.connection_tune.heartbeat == 0 await connection.close() async def test_heartbeat_not_int(loop): url = AMQP_URL.update_query(heartbeat="None") connection = aiormq.Connection(url, loop=loop) await connection.connect() assert connection.connection_tune.heartbeat == 0 await connection.close() async def test_bad_credentials(amqp_url: URL): with pytest.raises(aiormq.exceptions.ProbableAuthenticationError): await aiormq.connect(amqp_url.with_password(uuid.uuid4().hex)) async def test_non_publisher_confirms(amqp_connection): amqp_connection.server_capabilities["publisher_confirms"] = False with pytest.raises(ValueError): await amqp_connection.channel(publisher_confirms=True) await amqp_connection.channel(publisher_confirms=False) @skip_when_quick_test async def test_no_free_channels(amqp_connection: aiormq.Connection): await asyncio.wait_for( asyncio.gather( *[ amqp_connection.channel(n + 1) for n in range(amqp_connection.connection_tune.channel_max) ], ), timeout=120, ) with pytest.raises(aiormq.exceptions.ConnectionNotAllowed): await asyncio.wait_for(amqp_connection.channel(), timeout=5) async def test_huge_message(amqp_connection: aiormq.Connection): conn: aiormq.Connection = amqp_connection body = os.urandom(int(conn.connection_tune.frame_max * 2.5)) channel: aiormq.Channel = await conn.channel() queue = asyncio.Queue() deaclare_ok = await channel.queue_declare(auto_delete=True) await channel.basic_consume(deaclare_ok.queue, queue.put) await channel.basic_publish(body, routing_key=deaclare_ok.queue) message: DeliveredMessage = await queue.get() assert message.body == body async def test_return_message(amqp_connection: aiormq.Connection): conn: aiormq.Connection = amqp_connection body = os.urandom(512) routing_key = hexlify(os.urandom(16)).decode() channel: aiormq.Channel = await conn.channel( on_return_raises=False, ) result: Optional[Basic.Return] = await channel.basic_publish( body, routing_key=routing_key, mandatory=True, ) assert result is not None assert result.delivery.name == "Basic.Return" assert result.delivery.routing_key == routing_key async def test_cancel_on_queue_deleted(amqp_connection, loop): conn: aiormq.Connection = amqp_connection channel: aiormq.Channel = await conn.channel() deaclare_ok = await channel.queue_declare(auto_delete=True) consume_ok = await channel.basic_consume(deaclare_ok.queue, print) assert consume_ok.consumer_tag in channel.consumers with pytest.raises(aiormq.DuplicateConsumerTag): await channel.basic_consume( deaclare_ok.queue, print, consumer_tag=consume_ok.consumer_tag, ) await channel.queue_delete(deaclare_ok.queue) await asyncio.sleep(0.1) assert consume_ok.consumer_tag not in channel.consumers URL_VHOSTS = [ ("amqp:///", "/"), ("amqp:////", "/"), ("amqp:///test", "test"), ("amqp:////test", "/test"), ("amqp://localhost/test", "test"), ("amqp://localhost//test", "/test"), ("amqps://localhost:5678//test", "/test"), ("amqps://localhost:5678/-test", "-test"), ("amqp://guest:guest@localhost/@test", "@test"), ("amqp://guest:guest@localhost//@test", "/@test"), ] async def test_ssl_verification_fails_without_trusted_ca(): url = AMQP_URL.with_scheme("amqps") with pytest.raises(ConnectionError, match=".*CERTIFICATE_VERIFY_FAILED.*"): connection = aiormq.Connection(url) await connection.connect() async def test_ssl_context(): url = AMQP_URL.with_scheme("amqps") context = ssl.create_default_context( purpose=ssl.Purpose.SERVER_AUTH, cafile=cert_path("ca.pem"), ) context.load_cert_chain(cert_path("client.pem"), cert_path("client.key")) context.check_hostname = False connection = aiormq.Connection(url, context=context) await connection.connect() await connection.close() @pytest.mark.parametrize("url,vhost", URL_VHOSTS) async def test_connection_urls_vhosts(url, vhost, loop): assert aiormq.Connection(url, loop=loop).vhost == vhost async def test_update_secret(amqp_connection, amqp_url: URL): respone = await amqp_connection.update_secret( amqp_url.password, timeout=1, ) assert isinstance(respone, aiormq.spec.Connection.UpdateSecretOk) @aiomisc.timeout(20) async def test_connection_stuck(proxy, amqp_url: URL): url = amqp_url.with_host( proxy.proxy_host, ).with_port( proxy.proxy_port, ).update_query(heartbeat="1") connection = await aiormq.connect(url) async with connection: # delay the delivery of each packet by 5 seconds, which # is more than the heartbeat with proxy.slowdown(50, 50): while connection.is_opened: await asyncio.sleep(1) writer_task: asyncio.Task = connection._writer_task # type: ignore assert writer_task.done() with pytest.raises(asyncio.CancelledError): assert writer_task.result() reader_task: asyncio.Task = connection._reader_task # type: ignore assert reader_task.done() with pytest.raises(asyncio.CancelledError): assert reader_task.result() class BadNetwork: def __init__(self, proxy, stair: int, disconnect_time: float): self.proxy = proxy self.stair = stair self.disconnect_time = disconnect_time self.num_bytes = 0 self.loop = asyncio.get_event_loop() self.lock = asyncio.Lock() proxy.set_content_processors( self.client_to_server, self.server_to_client, ) async def disconnect(self): async with self.lock: await asyncio.sleep(self.disconnect_time) await self.proxy.disconnect_all() self.stair *= 2 self.num_bytes = 0 async def server_to_client(self, chunk: bytes) -> bytes: async with self.lock: self.num_bytes += len(chunk) if self.num_bytes < self.stair: return chunk self.loop.create_task(self.disconnect()) return chunk @staticmethod def client_to_server(chunk: bytes) -> bytes: return chunk DISCONNECT_OFFSETS = [2 << i for i in range(1, 10)] STAIR_STEPS = list( itertools.product([0.0, 0.005, 0.05, 0.1], DISCONNECT_OFFSETS), ) STAIR_STEPS_IDS = [ f"[{i // len(DISCONNECT_OFFSETS)}] {t}-{s}" for i, (t, s) in enumerate(STAIR_STEPS) ] @aiomisc.timeout(30) @pytest.mark.parametrize( "disconnect_time,stair", STAIR_STEPS, ids=STAIR_STEPS_IDS, ) async def test_connection_close_stairway( disconnect_time: float, stair: int, proxy, amqp_url: URL, ): url = amqp_url.with_host( proxy.proxy_host, ).with_port( proxy.proxy_port, ).update_query(heartbeat="1") BadNetwork(proxy, stair, disconnect_time) async def run(): connection = await aiormq.connect(url) queue = asyncio.Queue() channel = await connection.channel() declare_ok = await channel.queue_declare(auto_delete=True) await channel.basic_consume( declare_ok.queue, queue.put, no_ack=True, ) while True: await channel.basic_publish( b"test", routing_key=declare_ok.queue, ) message: DeliveredMessage = await queue.get() assert message.body == b"test" for _ in range(5): with pytest.raises(aiormq.AMQPError): await run() PARSE_INT_PARAMS = ( (1, 1), ("1", 1), ("0.1", 0), ("-1", -1), ) @pytest.mark.parametrize("value,expected", PARSE_INT_PARAMS) def test_parse_int(value, expected): assert parse_int(value) == expected PARSE_TIMEOUT_PARAMS = ( (1, 1), (1.0, 1), ("0", 0), ("0.0", 0), ("0.111", 0.111), ) @pytest.mark.parametrize("value,expected", PARSE_TIMEOUT_PARAMS) def test_parse_timeout(value, expected): assert parse_timeout(value) == expected PARSE_BOOL_PARAMS = ( ("no", False), ("nope", False), ("do not do it bro", False), ("YES", True), ("yes", True), ("yeS", True), ("yEs", True), ("True", True), ("true", True), ("TRUE", True), ("1", True), ("ENABLE", True), ("ENAbled", True), ("y", True), ("Y", True), ) @pytest.mark.parametrize("value,expected", PARSE_BOOL_PARAMS) def test_parse_bool(value, expected): assert parse_bool(value) == expected python-aiormq-6.8.1/tests/test_future_store.py000066400000000000000000000045361473731423200216540ustar00rootroot00000000000000import asyncio import pytest from aiormq.abc import TaskWrapper from aiormq.base import FutureStore @pytest.fixture def root_store(loop): store = FutureStore(loop=loop) try: yield store finally: loop.run_until_complete( store.reject_all(Exception("Cancelling")), ) @pytest.fixture def child_store(loop, root_store): store = root_store.get_child() try: yield store finally: loop.run_until_complete( store.reject_all(Exception("Cancelling")), ) async def test_reject_all( loop, root_store: FutureStore, child_store: FutureStore, ): future1 = root_store.create_future() future2 = child_store.create_future() assert root_store.futures assert child_store.futures await root_store.reject_all(RuntimeError) await asyncio.sleep(0.1) assert isinstance(future1.exception(), RuntimeError) assert isinstance(future2.exception(), RuntimeError) assert not root_store.futures assert not child_store.futures async def test_result( loop, root_store: FutureStore, child_store: FutureStore, ): async def result(): await asyncio.sleep(0.1) return "result" assert await child_store.create_task(result()) == "result" async def test_siblings( loop, root_store: FutureStore, child_store: FutureStore, ): async def coro(store): await asyncio.sleep(0.1) await store.reject_all(RuntimeError) task1 = child_store.create_task(coro(child_store)) assert root_store.futures assert child_store.futures with pytest.raises(RuntimeError): await task1 await asyncio.sleep(0.1) assert not root_store.futures assert not child_store.futures child = child_store.get_child().get_child().get_child() task = child.create_task(coro(child)) assert root_store.futures assert child_store.futures assert child.futures with pytest.raises(RuntimeError): await task await asyncio.sleep(0.1) assert not root_store.futures assert not child_store.futures assert not child.futures async def test_task_wrapper(loop): future = loop.create_future() wrapped = TaskWrapper(future) wrapped.throw(RuntimeError()) with pytest.raises(asyncio.CancelledError): await future with pytest.raises(RuntimeError): await wrapped python-aiormq-6.8.1/tests/test_tools.py000066400000000000000000000021741473731423200202620ustar00rootroot00000000000000import asyncio import pytest from aiormq.tools import Countdown, awaitable def simple_func(): return 1 def await_result_func(): return asyncio.sleep(0) async def await_func(): await asyncio.sleep(0) return 2 def return_future(): loop = asyncio.get_event_loop() f = loop.create_future() loop.call_soon(f.set_result, 5) return f async def await_future(): return (await return_future()) + 1 def return_coroutine(): return await_future() AWAITABLE_FUNCS = [ (simple_func, 1), (await_result_func, None), (await_func, 2), (return_future, 5), (await_future, 6), (return_coroutine, 6), ] @pytest.mark.parametrize("func,result", AWAITABLE_FUNCS) async def test_awaitable(func, result, loop): assert await awaitable(func)() == result async def test_countdown(loop): countdown = Countdown(timeout=0.1) await countdown(asyncio.sleep(0)) # waiting for the countdown exceeded await asyncio.sleep(0.2) task = asyncio.create_task(asyncio.sleep(0)) with pytest.raises(asyncio.TimeoutError): await countdown(task) assert task.cancelled()