pax_global_header00006660000000000000000000000064145503211730014513gustar00rootroot0000000000000052 comment=5e9a5b80d169bc69a37d04e6f402dcc366069e99 pamqp-3.3.0/000077500000000000000000000000001455032117300126345ustar00rootroot00000000000000pamqp-3.3.0/.codeclimate.yml000066400000000000000000000004661455032117300157140ustar00rootroot00000000000000languages: Python: true exclude_paths: - codegen/* - docs/* - tests/* - tools/* checks: argument-count: enabled: false file-lines: enabled: false similar-code: enabled: false method-complexity: config: threshold: 10 return-statements: config: threshold: 10 pamqp-3.3.0/.editorconfig000066400000000000000000000005131455032117300153100ustar00rootroot00000000000000# top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true # 4 space indentation [*.py] indent_style = space indent_size = 4 # 2 space indentation [*.yml] indent_style = space indent_size = 2 [bootstrap] indent_style = space indent_size = 2 pamqp-3.3.0/.github/000077500000000000000000000000001455032117300141745ustar00rootroot00000000000000pamqp-3.3.0/.github/workflows/000077500000000000000000000000001455032117300162315ustar00rootroot00000000000000pamqp-3.3.0/.github/workflows/deploy.yaml000066400000000000000000000012111455032117300204040ustar00rootroot00000000000000name: Deployment on: push: branches-ignore: ["*"] tags: ["*"] jobs: deploy: runs-on: ubuntu-latest if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'gmr/pamqp' container: python:3.10-alpine steps: - name: Checkout repository uses: actions/checkout@v1 - name: Install wheel run: pip3 install wheel - name: Build package run: python3 setup.py sdist bdist_wheel - name: Publish package uses: pypa/gh-action-pypi-publish@master with: user: __token__ password: ${{ secrets.PYPI_PASSWORD }} pamqp-3.3.0/.github/workflows/testing.yaml000066400000000000000000000023511455032117300205730ustar00rootroot00000000000000name: Testing on: push: branches: ["*"] paths-ignore: - 'docs/**' - '*.md' - '*.rst' tags-ignore: ["*"] jobs: test: runs-on: ubuntu-latest strategy: matrix: python: - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" container: image: python:${{ matrix.python }}-alpine steps: - name: Checkout repository uses: actions/checkout@v1 - name: Setup environment run: apk --update add gcc libpq make musl-dev linux-headers alpine-conf - name: Set the timezone run: setup-timezone -z America/New_York - name: Install testing dependencies run: pip3 install -e '.[testing]' - name: Create build directory run: mkdir build - name: Run flake8 tests run: flake8 --output build/flake8.txt --tee - name: Run tests run: coverage run && coverage report && coverage xml - name: Upload Coverage uses: codecov/codecov-action@v1.0.2 if: github.event_name == 'push' && github.repository == 'gmr/pamqp' with: token: ${{secrets.CODECOV_TOKEN}} file: build/coverage.xml flags: unittests pamqp-3.3.0/.gitignore000066400000000000000000000003321455032117300146220ustar00rootroot00000000000000.idea .mypy_cache .vscode codegen/amqp-rabbitmq-0.9.1.json codegen/amqp0-9-1.xml *.pyc build dist docs/_build atlassian-ide-plugin.xml .DS_Store pamqp.egg-info env env2.7 env3 .coverage coverage.xml xunit.xml coverage pamqp-3.3.0/CONTRIBUTING.md000066400000000000000000000007221455032117300150660ustar00rootroot00000000000000# Contributing To get setup in the environment and run the tests, take the following steps: ```sh python3 -m venv env source env/bin/activate pip install -e '.[testing]' flake8 coverage run && coverage report ``` Please format your code contributions with the ``yapf`` formatter: ```sh yapf -i --recursive --style=pep8 pamqp ``` ## Test Coverage Pull requests that make changes or additions that are not covered by tests will likely be closed without review. pamqp-3.3.0/LICENSE000066400000000000000000000027341455032117300136470ustar00rootroot00000000000000Copyright (c) 2011-2022 Gavin M. Roy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pamqp-3.3.0/MANIFEST.in000066400000000000000000000000721455032117300143710ustar00rootroot00000000000000include README.rst include LICENSE include pamqp/py.typed pamqp-3.3.0/README.rst000066400000000000000000000052431455032117300143270ustar00rootroot00000000000000pamqp ===== pamqp is a low level AMQP 0-9-1 frame encoding and decoding library for Python 3. pamqp is not a end-user client library for talking to RabbitMQ but rather is used by client libraries for marshaling and unmarshaling AMQP frames. |Version| |Status| |Coverage| |License| |Maintainability| |Downloads| Documentation ------------- https://pamqp.readthedocs.io Python Versions Supported ------------------------- 3.7+ License ------- Copyright (c) 2011-2024 Gavin M. Roy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .. |Version| image:: https://img.shields.io/pypi/v/pamqp.svg? :target: https://pypi.python.org/pypi/pamqp .. |Status| image:: https://github.com/gmr/pamqp/workflows/Testing/badge.svg? :target: https://github.com/gmr/pamqp/actions?workflow=Testing :alt: Build Status .. |Coverage| image:: https://img.shields.io/codecov/c/github/gmr/pamqp.svg? :target: https://codecov.io/github/gmr/pamqp?branch=master .. |License| image:: https://img.shields.io/pypi/l/pamqp.svg? :target: https://pamqp.readthedocs.org .. |Maintainability| image:: https://api.codeclimate.com/v1/badges/9efbb0957abb036254a1/maintainability :target: https://codeclimate.com/github/gmr/pamqp .. |Downloads| image:: https://img.shields.io/pypi/dm/pamqp :target: https://pypi.org/project/pamqp/ pamqp-3.3.0/codegen/000077500000000000000000000000001455032117300142405ustar00rootroot00000000000000pamqp-3.3.0/codegen/extensions.xml000066400000000000000000000076371455032117300171760ustar00rootroot00000000000000 Returned when RabbitMQ sends back with 'basic.return' when a 'mandatory' message cannot be delivered to any queue. The reason the connection was blocked. The queue name identifies the queue within the vhost. In methods where the queue name may be blank, and that has no specific significance, this refers to the 'current' queue for the channel, meaning the last queue that the client declared on the channel. If the client did not declare a queue, and the method needs a queue name, this will result in a 502 (syntax error) channel exception. The number of messages pending in the queue. If set, the server will not respond to the method. The client should not wait for a reply method. If the server could not complete the method it will raise a channel or connection exception. Deprecated value that must be zero. Deprecated value that must be empty. Deprecated value that must be False. Used when negotiating a connection on an out-of-band channel. Do not use, must be zero. Deprecated value that must be False. Deprecated value that must be empty. Do not use, must be zero. Deprecated value that must be empty. pamqp-3.3.0/docs/000077500000000000000000000000001455032117300135645ustar00rootroot00000000000000pamqp-3.3.0/docs/Makefile000066400000000000000000000126701455032117300152320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pamqp.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pamqp.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pamqp" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pamqp" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pamqp-3.3.0/docs/_static/000077500000000000000000000000001455032117300152125ustar00rootroot00000000000000pamqp-3.3.0/docs/_static/css/000077500000000000000000000000001455032117300160025ustar00rootroot00000000000000pamqp-3.3.0/docs/_static/css/custom.css000066400000000000000000000012251455032117300200260ustar00rootroot00000000000000.md-content { margin-right: 2em; } .md-main__inner { padding: 0; } .navheader { padding-left: 1em; } .md-sidebar--secondary { display: none; } .viewcode-link { font-size: .75em; margin-left: .5em; } .caption-text { font-style: oblique; } code.descname { background: transparent; box-shadow: none; font-size: 100% !important; } code.descname { background: transparent; box-shadow: none; font-size: 100% !important; font-weight: bold; } code.descclassname { background: transparent; box-shadow: none; font-size: 100% !important; font-weight: bold; } .rst-versions { font-size: 1.75em; } pamqp-3.3.0/docs/base.rst000066400000000000000000000002121455032117300152230ustar00rootroot00000000000000pamqp.base ========== .. automodule:: pamqp.base :members: :special-members: :inherited-members: :member-order: bysource pamqp-3.3.0/docs/body.rst000066400000000000000000000001611455032117300152510ustar00rootroot00000000000000pamqp.body ========== .. automodule:: pamqp.body :members: :special-members: :member-order: bysource pamqp-3.3.0/docs/changelog.rst000066400000000000000000000254221455032117300162520ustar00rootroot00000000000000Changelog ========= 3.3.0 (2024-01-12) ------------------ - Allow space character in exchange and queue names (#47) - Convert AMQP timestamp property to handle milliseconds (#48 - `david1155 `_) - Remove internal must be false check to support RabbitMQ Tracing (#50 - `hari01584 `_) - Remove usage of deprecated datetime.utcfromtimestamp (#52 - `decaz `_) 3.2.1 (2022-09-07) ------------------ - Add wheel to distribution format (#43) 3.2.0 (2022-06-27) ------------------ - Allow long-str to fall back to bytes in case of UnicodeDecodeError (AMQP 1.0 interop) (#40 - `dmaone `_) - DOMAIN_REGEX enhanced to fulfill tag uri scheme for exchange and queue names. (#42 - `deschmih `_) 3.1.0 (2022-01-10) ------------------ - Add implicit UTC timezone behavior to the AMQP Basic.Properties timestamp value. (#37 - `RemiCardona `_) - Add support for short-short-int and short-short-uint. (#33 - `michal800106 `_) 3.0.1 (2020-08-07) ------------------ - Fix an issue with `Basic.Reject` `requeue=False` always being set to `True` (#29 - `eandersson `_) 3.0.0 (2020-08-04) ------------------ - Fix unsigned short-int encoding and decoding to use the correct amqp field designation of ``B`` instad of ``b`` (#27) - Fix timestamp encoding/decoding tests to work when the timezone is set on the host machine (#26) 3.0.0a6 (2020-03-19) -------------------- - `pamqp.commands.Basic.QoS.globally` renamed back to :attr:`pamqp.commands.Basic.QoS.global_` - Refactored codegen to put params in the class level docstring not ``__init__`` - Added new :meth:`pamqp.frame.Frame.validate()` method - Changed validation to ignore attributes with a value of `None` - Changed default value behaviors to only use default values if one is specified, instead of by data type. - Overwrote AMQP spec for queue name max-length to match documented RabbitMQ value (#24) - Updated documentation in codegen output - Added strict validation of `pamqp.commands.Basic.Properties.delivery_mode` to ensure it's ``0`` or ``1`` - Fixed codegen with respect to applying extension data over base spec data 3.0.0a5 (2020-03-11) -------------------- - Rename `pamqp.frame._frame_parts` to :meth:`pamqp.frame.frame_parts` (#15 again) - `pamqp.commands.Basic.QoS.global_` renamed to :attr:`pamqp.commands.Basic.QoS.globally` - Removed mypy checking due to errors in mypy and recursive type aliases - Added `pamqp/py.typed` for PEP-561 compatibility (#21 - `michael-k `_) 3.0.0a4 (2020-01-01) -------------------- - Refactor codegen.py - Revert the behaviors added in 3.0.0a2 with regard to documented defaults and `None` - Use `amqp0-9-1.extended.xml` instead of `amqp-0-9-1.xml` to get the documentation for RabbitMQ added classes/methods - Add strict value checking for deprecated values - Remove empty ``__init__`` functions from method classes 3.0.0a3 (2019-12-31) -------------------- - Make comparison of Basic.Properties against other object types raise `NotImplementedError` - Return test coverage to 100% 3.0.0a2 (2019-12-31) -------------------- - Added mypy as part of the test pipeline and made updates based upon its findings. - Added length checking and regex checking for values specified in AMQP spec - Fixed some of the type annotations added in 3.0.0a0 - Fixed some of the documentation and label usage in `pamqp.commands` - Removed redundant inline documentation in `pamqp.commands` - Updated default values to only reflect defaults specified in the XML and JSON specs. If no default is specified, the value will now be `None`. 3.0.0a1 (2019-12-19) -------------------- - Revert the removal of `pamqp.body.ContentBody.name` 3.0.0a0 (2019-12-16) -------------------- - Update to support Python 3.6+ only - Add typing annotations to all modules, callables, and classes - Moved exceptions from `pamqp.specification` to `pamqp.exceptions` - Moved constants from `pamqp.specification` to `pamqp.constants` - Moved base classes out of `pamqp.specification` to `pamqp.base` - Changed the structure of nested classes for AMQP Commands (Classes & Methods) in `pamqp.specification` to functions in `pamqp.commands` - Renamed `pamqp.specification.ERRORS` to `pamqp.exceptions.CLASS_MAPPING` - Remove convenience exports of `pamqp.headers.ContentHeader` and `pamqp.header.ProtocolHeader` - pamqp.body.ContentBody.value now only supports `bytes` - Changed `pamqp.decode.timestamp` to return a `datetime.datetime` instance instead of `time.struct_time`. - Updated `pamqp.encode.support_deprecated_rabbitmq()` to allow for toggling support. - Changed `pamqp.encode.timestamp` to only support `datetime.datetime` and `time.struct_time` values, dropping epoch (`int`) support. - Removed `pamqp.frame.BasicProperties.to_dict()` in favor of behavior allowing for `dict(pamqp.frame.BasicProperties)` - Optimized `pamqp.heartbeat.Heartbeat` to marshal the static frame value as a predefined class attribute. - Add support for `Connection.UpdateSecret` and `Connection.UpdateSecretOk`. - Removed the ability to unset a `Basic.Property` by invoking `del properties[key]` - Removed the deprecated `pamqp.codec` sub-package 2.3.0 (2019-04-18) ------------------ - Add :py:func:`pamqp.encode.support_deprecated_rabbitmq` function to limit data types available when encoding field-tables for older RabbitMQ versions. 2.2.0 (2019-04-18) ------------------ - Change :py:meth:`pamqp.encode.timestamp` to allow for numeric/epoch timestamps (#14 - `mosquito `_) - Change :py:meth:`pamqp.frame.frame_parts` to a public method (#15 - `mosquito `_) - Cleanup of code to pass configured flake8 tests - Add support for 8-bit unsigned integer values in :py:meth:`pamqp.encode.table_integer` 2.1.0 (2018-12-28) ------------------ - Change raising a DeprecationWarning exception to using warnings.warn for deprecated AMQP methods (#13 - `dzen `_) 2.0.0 (2018-09-11) ------------------ - **Change Python versions supported to 2.7 and 3.4+** - **Always decode field table keys as strings (#6)** - This may be a breaking change means in Python3 keys will always be type str for short strings. This includes frame values and field table values. - In Python 2.7 if a short-string (key, frame field value, etc) has UTF-8 characters in it, it will be a `unicode` object. - Combine test coverage across all Python versions - Fix range for signed short integer (#7) - Fix guards for usage of unsigned short usage in `pamqp.encode` (#7) - Fix encoding and decoding of unsigned short (#7) - Add support for unsigned short integer and long integer in field tables (#10) - Address edge case of small value in long type (#8) - Address long string encoding inconsistency (#9) - Cleanup unicode object & conditionals in py3 (#9) - Add `pamqp.exceptions.PAMQPException` as a base class for pamqp specific exceptions (#4) - Fix decoding of void values in a field table or array 1.6.1 (2015-02-05) ------------------ - Fix the encoding guard for unsigned short integers to be 65535 [rabbitpy #62] 1.6.0 (2014-12-12) ------------------ - Remove UTF-8 encoding from byte_array (#2) - Fix AMQP Field Tables / `Basic.Properties` headers behavior: - Field names per spec should not exceed 128 bytes - long-strings should not be utf-8 encoded (only short-strings *boggle*) - Ensure that field table long strings are not coerced to UTF-8 as specified in AMQP 0-9-1 If a string is passed in as a long string in a field table and it contains UTF-8 characters it will be UTF-8 encoded - Move AMQP Methods in specification.py to slotted classes - Change `Basic.Properties` to a slotted class - Instead of class level attributes with the same name as obj attributes, prefix class attributes for data types with an underscore - Add new class method type() for `Basic.Properties` for accessing data type - Add new class method type() for AMQP methods for accessing data type - Change `Basic.Properties.attributes` to `Basic.Properties.attributes()`, returning the list of slotted attributes - Fix a typo for booleans in the method mapping for table decoding - `Frame.__getitem__` will now raise a KeyError instead of None for an invalid attribute - `PropertiesBase` no longer checks to see if an attribute is set for contains - Adds new specification tests - More efficiently handle the frame end character in Python 3 1.5.0 (2014-11-05) ------------------ - Cleanup how UTF-8 is handled in decoding strings - Ensure that field tables (headers property, etc) can use keys with utf-8 data - Address missing and mis-aligned AMQP-0-9-1 field table decoding with the field type indicators from the RabbitMQ protocol errata page - Fix a encoding by type bug introduced with 1.4 having to do with bytearrays - Be explicit about needing a class id in the ContentHeader - Update the tests to reflect the unicode changes - Clean up the tests 1.4.0 (2014-11-04) ------------------ - Fix a long standing bug for non-specified responses for RabbitMQ AMQP extensions - Refactor adding bytearrays and recoding complexity - Add bytearray support (#1 and gmr/rabbitpy#48) - Change encode/decode type errors from ValueError to TypeError exceptions - Remove separate codecs for Python 2 & 3 - Move codecs from `pamqp.codec.encode` and `pamqp.codec.decode` to `pamqp.encode` and `pamqp.decode` - Deprecate pamqp.codec - Remove weird imports from top level __init__.py, not sure what I was thinking there - Clean up codegen a bit to make it more PYTHON3 compatible - Update codegen/include for new codec and PYTHON2/PYTHON3 behavior - Update documentation - Distribution updates: - Let travis upload to pypi - Add wheel distribution - Update supported python versions - Update classifiers 1.3.1 (2014-02-14) ------------------ - Fix encoding of long-long-integers 1.3.0 (2014-01-17) ------------------ - Remove support for short strings in field tables 1.2.4 (2013-12-22) ------------------ - Add short-short-int support 1.2.3 (2013-12-22) ------------------ - Fix distribution requirements 1.2.2 (2013-12-22) ------------------ - Add decimal data type support 1.2.1 (2013-07-29) ------------------ - Fix Confirm.Select definition 1.2.0 (2013-07-08) ------------------ - Add support for Connection.Blocked, Connection.Unblocked - Add documentation to specification.py in the codegen process 1.1.3 (2013-03-27) ------------------ - Fix exception creation 1.1.2 (2013-03-27) ------------------ - Add Confirm.Select, Confirm.SelectOk 1.1.1 (2013-03-22) ------------------ - Remove debugging print statements (eek) 1.1.0 (2013-03-21) ------------------ - Add Python 3.3 support 1.0.1 (2012-10-02) ------------------ - Address Unicode issues - Add void support in table arrays 1.0.0 (2012-09-24) ------------------ - Initial version pamqp-3.3.0/docs/commands.rst000066400000000000000000000001761455032117300161230ustar00rootroot00000000000000pamqp.commands ============== .. automodule:: pamqp.commands :members: :special-members: :member-order: bysource pamqp-3.3.0/docs/common.rst000066400000000000000000000001311455032117300156010ustar00rootroot00000000000000pamqp.common ============ .. automodule:: pamqp.common :members: :undoc-members: pamqp-3.3.0/docs/conf.py000066400000000000000000000027341455032117300150710ustar00rootroot00000000000000import datetime import pkg_resources import sphinx_material html_theme = 'sphinx_material' html_theme_path = sphinx_material.html_theme_path() html_context = sphinx_material.get_html_context() html_sidebars = { "**": ["globaltoc.html", "searchbox.html"] } html_theme_options = { 'base_url': 'http://pamqp.readthedocs.io', 'repo_url': 'https://github.com/gmr/pamqp/', 'repo_name': 'pamqp', 'html_minify': True, 'css_minify': True, 'nav_title': 'pamqp', 'globaltoc_depth': 1, 'theme_color': 'fc6600', 'color_primary': 'grey', 'color_accent': 'orange', 'version_dropdown': False } html_static_path = ['_static'] html_css_files = [ 'css/custom.css' ] master_doc = 'index' project = 'pamqp' release = version = pkg_resources.get_distribution(project).version copyright = '2011-{}, Gavin M. Roy'.format(datetime.date.today().year) extensions = [ 'sphinx.ext.autodoc', 'sphinx_autodoc_typehints', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinx_material' ] set_type_checking_flag = True typehints_fully_qualified = True always_document_param_types = True typehints_document_rtype = True templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} autodoc_default_options = { 'autodoc_typehints': 'description', 'special-members': ('__contains__,__eq__,__getitem__,' '__iter__,__len__'), } pamqp-3.3.0/docs/decode.rst000066400000000000000000000001411455032117300155350ustar00rootroot00000000000000pamqp.decode ============ .. automodule:: pamqp.decode :members: :member-order: bysource pamqp-3.3.0/docs/encode.rst000066400000000000000000000001411455032117300155470ustar00rootroot00000000000000pamqp.encode ============ .. automodule:: pamqp.encode :members: :member-order: bysource pamqp-3.3.0/docs/exceptions.rst000066400000000000000000000006761455032117300165100ustar00rootroot00000000000000pamqp.exceptions ================ The :py:mod:`pamqp.exceptions` module is auto-generated, created by the ``tools/codegen.py`` application. :py:mod:`pamqp.exceptions` implements AMQP exceptions as Python exceptions so that client libraries can raise these exceptions as is appropriate without having to implement their own extensions for AMQP protocol related issues. .. automodule:: pamqp.exceptions :members: :member-order: bysource pamqp-3.3.0/docs/frame.rst000066400000000000000000000001361455032117300154100ustar00rootroot00000000000000pamqp.frame =========== .. automodule:: pamqp.frame :members: :member-order: bysource pamqp-3.3.0/docs/genindex.rst000066400000000000000000000000141455032117300161120ustar00rootroot00000000000000Index ===== pamqp-3.3.0/docs/header.rst000066400000000000000000000001421455032117300155430ustar00rootroot00000000000000pamqp.header ============ .. automodule:: pamqp.header :members: :member-order: bysource pamqp-3.3.0/docs/heartbeat.rst000066400000000000000000000001521455032117300162530ustar00rootroot00000000000000pamqp.heartbeat =============== .. automodule:: pamqp.heartbeat :members: :member-order: bysource pamqp-3.3.0/docs/index.rst000066400000000000000000000051421455032117300154270ustar00rootroot00000000000000pamqp ===== pamqp is a low level AMQP 0-9-1 frame encoding and decoding library for Python 3. pamqp is not a end-user client library for talking to RabbitMQ but rather is used by client libraries for marshaling and unmarshaling AMQP frames. |Version| |License| Issues ------ Please report any issues to the Github repo at `https://github.com/gmr/pamqp/issues `_ Source ------ pamqp source is available on Github at `https://github.com/gmr/pamqp `_ Installation ------------ pamqp is available from the `Python Package Index `_ but should generally be installed as a dependency from a client library. Documentation ------------- .. toctree:: :maxdepth: 1 base body commands common decode encode exceptions frame header heartbeat changelog genindex License ------- Copyright (c) 2011-2024 Gavin M. Roy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .. |Version| image:: https://img.shields.io/pypi/v/pamqp.svg? :target: https://pypi.python.org/pypi/pamqp :alt: Package Version .. |License| image:: https://img.shields.io/pypi/l/pamqp.svg? :target: https://github.com/gmr/pamqp/blob/master/LICENSE :alt: BSD pamqp-3.3.0/docs/requirements.txt000066400000000000000000000001111455032117300170410ustar00rootroot00000000000000Sphinx==2.4.4 sphinx-autodoc-typehints sphinx-material==0.0.23 typed_ast pamqp-3.3.0/pamqp/000077500000000000000000000000001455032117300137525ustar00rootroot00000000000000pamqp-3.3.0/pamqp/__init__.py000066400000000000000000000004601455032117300160630ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """AMQP Specifications and Classes""" __author__ = 'Gavin M. Roy' __email__ = 'gavinmroy@gmail.com' __since__ = '2011-09-23' __version__ = version = '3.3.0' __all__ = [ 'body', 'decode', 'commands', 'constants', 'encode', 'exceptions', 'frame', 'header', 'heartbeat' ] pamqp-3.3.0/pamqp/base.py000066400000000000000000000161671455032117300152510ustar00rootroot00000000000000""" Base classes for the representation of frames and data structures. """ import logging import struct import typing from pamqp import common, decode, encode LOGGER = logging.getLogger(__name__) class _AMQData: """Base class for AMQ methods and properties for encoding and decoding""" __annotations__: typing.Dict = {} __slots__: typing.List = [] name = '_AMQData' def __contains__(self, item: str) -> bool: """Return if the item is in the attribute list""" return item in self.__slots__ def __getitem__(self, item: str) -> common.FieldValue: """Return an attribute as if it were a dict :param item: The key to use to retrieve the value :rtype: :const:`pamqp.common.FieldValue` :raises: KeyError """ return getattr(self, item) def __iter__(self) \ -> typing.Generator[typing.Tuple[str, common.FieldValue], None, None]: """Iterate the attributes and values as key, value pairs :rtype: (:class:`str`, :const:`pamqp.common.FieldValue`) """ for attribute in self.__slots__: yield attribute, getattr(self, attribute) def __len__(self) -> int: """Return the length of the attribute list""" return len(self.__slots__) def __repr__(self) -> str: """Return the representation of the frame object""" return '<{} object at {}>'.format(self.name, hex(id(self))) @classmethod def amqp_type(cls, attr: str) -> str: """Return the AMQP data type for an attribute :param attr: The attribute name """ return getattr(cls, '_' + attr) @classmethod def attributes(cls) -> list: """Return the list of attributes""" return cls.__slots__ class Frame(_AMQData): """Base Class for AMQ Methods for encoding and decoding""" frame_id = 0 index = 0 synchronous = False valid_responses: typing.List = [] def marshal(self) -> bytes: """Dynamically encode the frame by taking the list of attributes and encode them item by item getting the value form the object attribute and the data type from the class attribute. """ self.validate() byte, offset, output, processing_bitset = -1, 0, [], False for argument in self.__slots__: data_type = self.amqp_type(argument) if not processing_bitset and data_type == 'bit': byte, offset, processing_bitset = 0, 0, True data_value = getattr(self, argument, 0) if processing_bitset: if data_type != 'bit': processing_bitset = False output.append(encode.octet(byte)) else: byte = encode.bit(data_value, byte, offset) offset += 1 if offset == 8: # pragma: nocover output.append(encode.octet(byte)) processing_bitset = False continue # pragma: nocover output.append(encode.by_type(data_value, data_type)) if processing_bitset: output.append(encode.octet(byte)) return b''.join(output) def unmarshal(self, data: bytes) -> None: """Dynamically decode the frame data applying the values to the method object by iterating through the attributes in order and decoding them. :param data: The raw AMQP frame data """ offset, processing_bitset = 0, False for argument in self.__slots__: data_type = self.amqp_type(argument) if offset == 7 and processing_bitset: # pragma: nocover data = data[1:] offset = 0 if processing_bitset and data_type != 'bit': offset = 0 processing_bitset = False data = data[1:] consumed, value = decode.by_type(data, data_type, offset) if data_type == 'bit': offset += 1 processing_bitset = True consumed = 0 setattr(self, argument, value) if consumed: data = data[consumed:] def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises: ValueError """ class BasicProperties(_AMQData): """Provide a base object that marshals and unmarshals the Basic.Properties object values. """ flags: typing.Dict[str, int] = {} name = 'BasicProperties' def __eq__(self, other: object) -> bool: if not isinstance(other, BasicProperties): raise NotImplementedError return all( getattr(self, k, None) == getattr(other, k, None) for k in self.__slots__) def encode_property(self, name: str, value: common.FieldValue) -> bytes: """Encode a single property value :param name: The name of the property to encode :param value: The property to encode :type value: :const:`pamqp.common.FieldValue` :raises: TypeError """ return encode.by_type(value, self.amqp_type(name)) def marshal(self) -> bytes: """Take the Basic.Properties data structure and marshal it into the data structure needed for the ContentHeader. """ flags = 0 parts = [] for property_name in self.__slots__: property_value = getattr(self, property_name) if property_value is not None and property_value != '': flags = flags | self.flags[property_name] parts.append( self.encode_property(property_name, property_value)) flag_pieces = [] while True: remainder = flags >> 16 partial_flags = flags & 0xFFFE if remainder != 0: # pragma: nocover partial_flags |= 1 flag_pieces.append(struct.pack('>H', partial_flags)) flags = remainder if not flags: # pragma: nocover break return b''.join(flag_pieces + parts) def unmarshal(self, flags: int, data: bytes) -> None: """Dynamically decode the frame data applying the values to the method object by iterating through the attributes in order and decoding them. """ for property_name in self.__slots__: if flags & self.flags[property_name]: data_type = getattr(self.__class__, '_' + property_name) consumed, value = decode.by_type(data, data_type) setattr(self, property_name, value) data = data[consumed:] def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises: ValueError """ if self.cluster_id != '': raise ValueError('cluster_id must be empty') if self.delivery_mode is not None and self.delivery_mode not in [1, 2]: raise ValueError('Invalid delivery_mode value: {}'.format( self.delivery_mode)) pamqp-3.3.0/pamqp/body.py000066400000000000000000000023701455032117300152630ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ The :py:mod:`pamqp.body` module contains the :py:class:`Body` class which is used when unmarshalling body frames. When dealing with content frames, the message body will be returned from the library as an instance of the body class. """ class ContentBody: """ContentBody carries the value for an AMQP message body frame :param value: The value for the ContentBody frame """ name = 'ContentBody' def __init__(self, value: bytes): """Create a new instance of a ContentBody object""" self.value = value def __len__(self) -> int: """Return the length of the content body value""" return len(self.value) if self.value else 0 def marshal(self) -> bytes: """Return the marshaled content body. This method is here for API compatibility, there is no special marshaling for the payload in a content frame. """ return self.value def unmarshal(self, data: bytes) -> None: """Apply the data to the object. This method is here for API compatibility, there is no special unmarshalling for the payload in a content frame. :param data: The content body data from the frame """ self.value = data pamqp-3.3.0/pamqp/commands.py000066400000000000000000003253071455032117300161370ustar00rootroot00000000000000""" The classes inside :mod:`pamqp.commands` allow for the automatic marshaling and unmarshaling of AMQP method frames and :class:`Basic.Properties `. In addition the command classes contain information that designates if they are synchronous commands and if so, what the expected responses are. Each commands arguments are detailed in the class and are listed in the attributes property. .. note:: All AMQ classes and methods extend :class:`pamqp.base.Frame`. """ # Auto-generated, do not edit this file. import datetime import typing import warnings from pamqp import base, common, constants class Connection: """Work with socket connections The connection class provides methods for a client to establish a network connection to a server, and for both peers to operate the connection thereafter. """ __slots__: typing.List[str] = [] frame_id = 10 # AMQP Frame ID index = 0x000A0000 # pamqp Mapping Index class Start(base.Frame): """Start connection negotiation This method starts the connection negotiation process by telling the client the protocol version that the server proposes, along with a list of security mechanisms which the client can use for authentication. :param version_major: Protocol major version - Default: ``0`` :param version_minor: Protocol minor version - Default: ``9`` :param server_properties: Server properties - Default: ``{}`` :type server_properties: :const:`~pamqp.common.FieldTable` :param mechanisms: Available security mechanisms - Default: ``PLAIN`` :param locales: Available message locales - Default: ``en_US`` """ __annotations__: typing.Dict[str, object] = { 'version_major': int, 'version_minor': int, 'server_properties': common.FieldTable, 'mechanisms': str, 'locales': str } __slots__: typing.List[str] = [ # AMQ Method Attributes 'version_major', 'version_minor', 'server_properties', 'mechanisms', 'locales' ] frame_id = 10 # AMQP Frame ID index = 0x000A000A # pamqp Mapping Index name = 'Connection.Start' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.StartOk'] # Class Attribute Types for unmarshaling _version_major = 'octet' _version_minor = 'octet' _server_properties = 'table' _mechanisms = 'longstr' _locales = 'longstr' def __init__(self, version_major: int = 0, version_minor: int = 9, server_properties: typing.Optional[ common.FieldTable] = None, mechanisms: str = 'PLAIN', locales: str = 'en_US') -> None: """Initialize the :class:`Connection.Start` class""" self.version_major = version_major self.version_minor = version_minor self.server_properties = server_properties or {} self.mechanisms = mechanisms self.locales = locales class StartOk(base.Frame): """Select security mechanism and locale This method selects a SASL security mechanism. :param client_properties: Client properties - Default: ``{}`` :type client_properties: :const:`~pamqp.common.FieldTable` :param mechanism: Selected security mechanism - Default: ``PLAIN`` :param response: Security response data - Default: ``''`` :param locale: Selected message locale - Default: ``en_US`` """ __annotations__: typing.Dict[str, object] = { 'client_properties': common.FieldTable, 'mechanism': str, 'response': str, 'locale': str } __slots__: typing.List[str] = [ # AMQ Method Attributes 'client_properties', 'mechanism', 'response', 'locale' ] frame_id = 11 # AMQP Frame ID index = 0x000A000B # pamqp Mapping Index name = 'Connection.StartOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _client_properties = 'table' _mechanism = 'shortstr' _response = 'longstr' _locale = 'shortstr' def __init__(self, client_properties: typing.Optional[ common.FieldTable] = None, mechanism: str = 'PLAIN', response: str = '', locale: str = 'en_US') -> None: """Initialize the :class:`Connection.StartOk` class""" self.client_properties = client_properties or {} self.mechanism = mechanism self.response = response self.locale = locale class Secure(base.Frame): """Security mechanism challenge The SASL protocol works by exchanging challenges and responses until both peers have received sufficient information to authenticate each other. This method challenges the client to provide more information. :param challenge: Security challenge data """ __annotations__: typing.Dict[str, object] = {'challenge': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'challenge' ] frame_id = 20 # AMQP Frame ID index = 0x000A0014 # pamqp Mapping Index name = 'Connection.Secure' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.SecureOk'] # Class Attribute Types for unmarshaling _challenge = 'longstr' def __init__(self, challenge: typing.Optional[str] = None) -> None: """Initialize the :class:`Connection.Secure` class""" self.challenge = challenge class SecureOk(base.Frame): """Security mechanism response This method attempts to authenticate, passing a block of SASL data for the security mechanism at the server side. :param response: Security response data """ __annotations__: typing.Dict[str, object] = {'response': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'response' ] frame_id = 21 # AMQP Frame ID index = 0x000A0015 # pamqp Mapping Index name = 'Connection.SecureOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _response = 'longstr' def __init__(self, response: typing.Optional[str] = None) -> None: """Initialize the :class:`Connection.SecureOk` class""" self.response = response class Tune(base.Frame): """Propose connection tuning parameters This method proposes a set of connection configuration values to the client. The client can accept and/or adjust these. :param channel_max: Proposed maximum channels - Default: ``0`` :param frame_max: Proposed maximum frame size - Default: ``0`` :param heartbeat: Desired heartbeat delay - Default: ``0`` """ __annotations__: typing.Dict[str, object] = { 'channel_max': int, 'frame_max': int, 'heartbeat': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'channel_max', 'frame_max', 'heartbeat' ] frame_id = 30 # AMQP Frame ID index = 0x000A001E # pamqp Mapping Index name = 'Connection.Tune' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.TuneOk'] # Class Attribute Types for unmarshaling _channel_max = 'short' _frame_max = 'long' _heartbeat = 'short' def __init__(self, channel_max: int = 0, frame_max: int = 0, heartbeat: int = 0) -> None: """Initialize the :class:`Connection.Tune` class""" self.channel_max = channel_max self.frame_max = frame_max self.heartbeat = heartbeat class TuneOk(base.Frame): """Negotiate connection tuning parameters This method sends the client's connection tuning parameters to the server. Certain fields are negotiated, others provide capability information. :param channel_max: Negotiated maximum channels - Default: ``0`` :param frame_max: Negotiated maximum frame size - Default: ``0`` :param heartbeat: Desired heartbeat delay - Default: ``0`` """ __annotations__: typing.Dict[str, object] = { 'channel_max': int, 'frame_max': int, 'heartbeat': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'channel_max', 'frame_max', 'heartbeat' ] frame_id = 31 # AMQP Frame ID index = 0x000A001F # pamqp Mapping Index name = 'Connection.TuneOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _channel_max = 'short' _frame_max = 'long' _heartbeat = 'short' def __init__(self, channel_max: int = 0, frame_max: int = 0, heartbeat: int = 0) -> None: """Initialize the :class:`Connection.TuneOk` class""" self.channel_max = channel_max self.frame_max = frame_max self.heartbeat = heartbeat class Open(base.Frame): """Open connection to virtual host This method opens a connection to a virtual host, which is a collection of resources, and acts to separate multiple application domains within a server. The server may apply arbitrary limits per virtual host, such as the number of each type of entity that may be used, per connection and/or in total. :param virtual_host: Virtual host name - Default: ``/`` :param capabilities: Deprecated, must be empty - Default: ``''`` :param insist: Deprecated, must be ``False`` - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'virtual_host': str, 'capabilities': str, 'insist': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'virtual_host', 'capabilities', 'insist' ] frame_id = 40 # AMQP Frame ID index = 0x000A0028 # pamqp Mapping Index name = 'Connection.Open' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.OpenOk'] # Class Attribute Types for unmarshaling _virtual_host = 'shortstr' _capabilities = 'shortstr' _insist = 'bit' def __init__(self, virtual_host: str = '/', capabilities: str = '', insist: bool = False) -> None: """Initialize the :class:`Connection.Open` class""" self.virtual_host = virtual_host self.capabilities = capabilities self.insist = insist self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.virtual_host is not None and len(self.virtual_host) > 127: raise ValueError('Max length exceeded for virtual_host') if self.capabilities is not None and self.capabilities != '': raise ValueError('capabilities must be empty') if self.insist is not None and self.insist is not False: raise ValueError('insist must be False') class OpenOk(base.Frame): """Signal that connection is ready This method signals to the client that the connection is ready for use. :param known_hosts: Deprecated, must be empty - Default: ``''`` """ __annotations__: typing.Dict[str, object] = {'known_hosts': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'known_hosts' ] frame_id = 41 # AMQP Frame ID index = 0x000A0029 # pamqp Mapping Index name = 'Connection.OpenOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _known_hosts = 'shortstr' def __init__(self, known_hosts: str = '') -> None: """Initialize the :class:`Connection.OpenOk` class""" self.known_hosts = known_hosts self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.known_hosts is not None and self.known_hosts != '': raise ValueError('known_hosts must be empty') class Close(base.Frame): """Request a connection close This method indicates that the sender wants to close the connection. This may be due to internal conditions (e.g. a forced shut-down) or due to an error handling a specific method, i.e. an exception. When a close is due to an exception, the sender provides the class and method id of the method which caused the exception. :param reply_code: Reply code from server :param reply_text: Localised reply text - Default: ``''`` :param class_id: Failing method class :param method_id: Failing method ID """ __annotations__: typing.Dict[str, object] = { 'reply_code': int, 'reply_text': str, 'class_id': int, 'method_id': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'reply_code', 'reply_text', 'class_id', 'method_id' ] frame_id = 50 # AMQP Frame ID index = 0x000A0032 # pamqp Mapping Index name = 'Connection.Close' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.CloseOk'] # Class Attribute Types for unmarshaling _reply_code = 'short' _reply_text = 'shortstr' _class_id = 'short' _method_id = 'short' def __init__(self, reply_code: typing.Optional[int] = None, reply_text: str = '', class_id: typing.Optional[int] = None, method_id: typing.Optional[int] = None) -> None: """Initialize the :class:`Connection.Close` class""" self.reply_code = reply_code self.reply_text = reply_text or '' self.class_id = class_id self.method_id = method_id class CloseOk(base.Frame): """Confirm a connection close This method confirms a :class:`Connection.Close` method and tells the recipient that it is safe to release resources for the connection and close the socket. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 51 # AMQP Frame ID index = 0x000A0033 # pamqp Mapping Index name = 'Connection.CloseOk' synchronous = False # Indicates if this is a synchronous AMQP method class Blocked(base.Frame): """Indicate that connection is blocked This method indicates that a connection has been blocked and does not accept new publishes. :param reason: Block reason - Default: ``''`` """ __annotations__: typing.Dict[str, object] = {'reason': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'reason' ] frame_id = 60 # AMQP Frame ID index = 0x000A003C # pamqp Mapping Index name = 'Connection.Blocked' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _reason = 'shortstr' def __init__(self, reason: str = '') -> None: """Initialize the :class:`Connection.Blocked` class""" self.reason = reason class Unblocked(base.Frame): """Indicate that connection is unblocked This method indicates that a connection has been unblocked and now accepts publishes. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 61 # AMQP Frame ID index = 0x000A003D # pamqp Mapping Index name = 'Connection.Unblocked' synchronous = False # Indicates if this is a synchronous AMQP method class UpdateSecret(base.Frame): """Update secret This method updates the secret used to authenticate this connection. It is used when secrets have an expiration date and need to be renewed, like OAuth 2 tokens. :param new_secret: New secret :param reason: Reason """ __annotations__: typing.Dict[str, object] = { 'new_secret': str, 'reason': str } __slots__: typing.List[str] = [ # AMQ Method Attributes 'new_secret', 'reason' ] frame_id = 70 # AMQP Frame ID index = 0x000A0046 # pamqp Mapping Index name = 'Connection.UpdateSecret' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Connection.UpdateSecretOk'] # Class Attribute Types for unmarshaling _new_secret = 'longstr' _reason = 'shortstr' def __init__(self, new_secret: typing.Optional[str] = None, reason: typing.Optional[str] = None) -> None: """Initialize the :class:`Connection.UpdateSecret` class""" self.new_secret = new_secret self.reason = reason class UpdateSecretOk(base.Frame): """Update secret response This method confirms the updated secret is valid. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 71 # AMQP Frame ID index = 0x000A0047 # pamqp Mapping Index name = 'Connection.UpdateSecretOk' synchronous = False # Indicates if this is a synchronous AMQP method class Channel: """Work with channels The channel class provides methods for a client to establish a channel to a server and for both peers to operate the channel thereafter. """ __slots__: typing.List[str] = [] frame_id = 20 # AMQP Frame ID index = 0x00140000 # pamqp Mapping Index class Open(base.Frame): """Open a channel for use This method opens a channel to the server. :param out_of_band: Protocol level field, do not use, must be ``0``. - Default: ``0`` """ __annotations__: typing.Dict[str, object] = {'out_of_band': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'out_of_band' ] frame_id = 10 # AMQP Frame ID index = 0x0014000A # pamqp Mapping Index name = 'Channel.Open' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Channel.OpenOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _out_of_band = 'shortstr' def __init__(self, out_of_band: str = '0') -> None: """Initialize the :class:`Channel.Open` class""" self.out_of_band = out_of_band self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.out_of_band is not None and self.out_of_band != '0': raise ValueError('out_of_band must be 0') class OpenOk(base.Frame): """Signal that the channel is ready This method signals to the client that the channel is ready for use. :param channel_id: Deprecated, must be ``0`` - Default: ``0`` """ __annotations__: typing.Dict[str, object] = {'channel_id': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'channel_id' ] frame_id = 11 # AMQP Frame ID index = 0x0014000B # pamqp Mapping Index name = 'Channel.OpenOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _channel_id = 'longstr' def __init__(self, channel_id: str = '0') -> None: """Initialize the :class:`Channel.OpenOk` class""" self.channel_id = channel_id self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.channel_id is not None and self.channel_id != '0': raise ValueError('channel_id must be 0') class Flow(base.Frame): """Enable/disable flow from peer This method asks the peer to pause or restart the flow of content data sent by a consumer. This is a simple flow-control mechanism that a peer can use to avoid overflowing its queues or otherwise finding itself receiving more messages than it can process. Note that this method is not intended for window control. It does not affect contents returned by :class:`Basic.GetOk` methods. :param active: Start/stop content frames """ __annotations__: typing.Dict[str, object] = {'active': bool} __slots__: typing.List[str] = [ # AMQ Method Attributes 'active' ] frame_id = 20 # AMQP Frame ID index = 0x00140014 # pamqp Mapping Index name = 'Channel.Flow' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Channel.FlowOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _active = 'bit' def __init__(self, active: typing.Optional[bool] = None) -> None: """Initialize the :class:`Channel.Flow` class""" self.active = active class FlowOk(base.Frame): """Confirm a flow method Confirms to the peer that a flow command was received and processed. :param active: Current flow setting """ __annotations__: typing.Dict[str, object] = {'active': bool} __slots__: typing.List[str] = [ # AMQ Method Attributes 'active' ] frame_id = 21 # AMQP Frame ID index = 0x00140015 # pamqp Mapping Index name = 'Channel.FlowOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _active = 'bit' def __init__(self, active: typing.Optional[bool] = None) -> None: """Initialize the :class:`Channel.FlowOk` class""" self.active = active class Close(base.Frame): """Request a channel close This method indicates that the sender wants to close the channel. This may be due to internal conditions (e.g. a forced shut-down) or due to an error handling a specific method, i.e. an exception. When a close is due to an exception, the sender provides the class and method id of the method which caused the exception. :param reply_code: Reply code from server :param reply_text: Localised reply text - Default: ``''`` :param class_id: Failing method class :param method_id: Failing method ID """ __annotations__: typing.Dict[str, object] = { 'reply_code': int, 'reply_text': str, 'class_id': int, 'method_id': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'reply_code', 'reply_text', 'class_id', 'method_id' ] frame_id = 40 # AMQP Frame ID index = 0x00140028 # pamqp Mapping Index name = 'Channel.Close' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Channel.CloseOk'] # Class Attribute Types for unmarshaling _reply_code = 'short' _reply_text = 'shortstr' _class_id = 'short' _method_id = 'short' def __init__(self, reply_code: typing.Optional[int] = None, reply_text: str = '', class_id: typing.Optional[int] = None, method_id: typing.Optional[int] = None) -> None: """Initialize the :class:`Channel.Close` class""" self.reply_code = reply_code self.reply_text = reply_text or '' self.class_id = class_id self.method_id = method_id class CloseOk(base.Frame): """Confirm a channel close This method confirms a :class:`Channel.Close` method and tells the recipient that it is safe to release resources for the channel. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 41 # AMQP Frame ID index = 0x00140029 # pamqp Mapping Index name = 'Channel.CloseOk' synchronous = False # Indicates if this is a synchronous AMQP method class Exchange: """Work with exchanges Exchanges match and distribute messages across queues. Exchanges can be configured in the server or declared at runtime. """ __slots__: typing.List[str] = [] frame_id = 40 # AMQP Frame ID index = 0x00280000 # pamqp Mapping Index class Declare(base.Frame): """Verify exchange exists, create if needed This method creates an exchange if it does not already exist, and if the exchange exists, verifies that it is of the correct and expected class. .. note:: The AMQP type argument is referred to as "exchange_type" to not conflict with the Python type keyword. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param exchange: exchange name - Default: ``''`` :param exchange_type: Exchange type - Default: ``direct`` :param passive: Do not create exchange - Default: ``False`` :param durable: Request a durable exchange - Default: ``False`` :param auto_delete: Auto-delete when unused - Default: ``False`` :param internal: Create internal exchange - Default: ``False`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments for declaration - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'exchange': str, 'exchange_type': str, 'passive': bool, 'durable': bool, 'auto_delete': bool, 'internal': bool, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'exchange', 'exchange_type', 'passive', 'durable', 'auto_delete', 'internal', 'nowait', 'arguments' ] frame_id = 10 # AMQP Frame ID index = 0x0028000A # pamqp Mapping Index name = 'Exchange.Declare' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Exchange.DeclareOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _exchange = 'shortstr' _exchange_type = 'shortstr' _passive = 'bit' _durable = 'bit' _auto_delete = 'bit' _internal = 'bit' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, exchange: str = '', exchange_type: str = 'direct', passive: bool = False, durable: bool = False, auto_delete: bool = False, internal: bool = False, nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Exchange.Declare` class""" self.ticket = ticket self.exchange = exchange self.exchange_type = exchange_type self.passive = passive self.durable = durable self.auto_delete = auto_delete self.internal = internal self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class DeclareOk(base.Frame): """Confirm exchange declaration This method confirms a Declare method and confirms the name of the exchange, essential for automatically-named exchanges. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 11 # AMQP Frame ID index = 0x0028000B # pamqp Mapping Index name = 'Exchange.DeclareOk' synchronous = False # Indicates if this is a synchronous AMQP method class Delete(base.Frame): """Delete an exchange This method deletes an exchange. When an exchange is deleted all queue bindings on the exchange are cancelled. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param exchange: exchange name - Default: ``''`` :param if_unused: Delete only if unused - Default: ``False`` :param nowait: Do not send a reply method - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'exchange': str, 'if_unused': bool, 'nowait': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'exchange', 'if_unused', 'nowait' ] frame_id = 20 # AMQP Frame ID index = 0x00280014 # pamqp Mapping Index name = 'Exchange.Delete' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Exchange.DeleteOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _exchange = 'shortstr' _if_unused = 'bit' _nowait = 'bit' def __init__(self, ticket: int = 0, exchange: str = '', if_unused: bool = False, nowait: bool = False) -> None: """Initialize the :class:`Exchange.Delete` class""" self.ticket = ticket self.exchange = exchange self.if_unused = if_unused self.nowait = nowait self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class DeleteOk(base.Frame): """Confirm deletion of an exchange This method confirms the deletion of an exchange. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 21 # AMQP Frame ID index = 0x00280015 # pamqp Mapping Index name = 'Exchange.DeleteOk' synchronous = False # Indicates if this is a synchronous AMQP method class Bind(base.Frame): """Bind exchange to an exchange This method binds an exchange to an exchange. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param destination: Name of the destination exchange to bind to - Default: ``''`` :param source: Name of the source exchange to bind to - Default: ``''`` :param routing_key: Message routing key - Default: ``''`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments for binding - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'destination': str, 'source': str, 'routing_key': str, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'destination', 'source', 'routing_key', 'nowait', 'arguments' ] frame_id = 30 # AMQP Frame ID index = 0x0028001E # pamqp Mapping Index name = 'Exchange.Bind' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Exchange.BindOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _destination = 'shortstr' _source = 'shortstr' _routing_key = 'shortstr' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, destination: str = '', source: str = '', routing_key: str = '', nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Exchange.Bind` class""" self.ticket = ticket self.destination = destination self.source = source self.routing_key = routing_key self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.destination is not None and len(self.destination) > 127: raise ValueError('Max length exceeded for destination') if self.destination is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.destination): raise ValueError('Invalid value for destination') if self.source is not None and len(self.source) > 127: raise ValueError('Max length exceeded for source') if self.source is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.source): raise ValueError('Invalid value for source') class BindOk(base.Frame): """Confirm bind successful This method confirms that the bind was successful. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 31 # AMQP Frame ID index = 0x0028001F # pamqp Mapping Index name = 'Exchange.BindOk' synchronous = False # Indicates if this is a synchronous AMQP method class Unbind(base.Frame): """Unbind an exchange from an exchange This method unbinds an exchange from an exchange. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param destination: Specifies the name of the destination exchange to unbind. - Default: ``''`` :param source: Specifies the name of the source exchange to unbind. - Default: ``''`` :param routing_key: Routing key of binding - Default: ``''`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments of binding - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'destination': str, 'source': str, 'routing_key': str, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'destination', 'source', 'routing_key', 'nowait', 'arguments' ] frame_id = 40 # AMQP Frame ID index = 0x00280028 # pamqp Mapping Index name = 'Exchange.Unbind' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Exchange.UnbindOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _destination = 'shortstr' _source = 'shortstr' _routing_key = 'shortstr' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, destination: str = '', source: str = '', routing_key: str = '', nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Exchange.Unbind` class""" self.ticket = ticket self.destination = destination self.source = source self.routing_key = routing_key self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.destination is not None and len(self.destination) > 127: raise ValueError('Max length exceeded for destination') if self.destination is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.destination): raise ValueError('Invalid value for destination') if self.source is not None and len(self.source) > 127: raise ValueError('Max length exceeded for source') if self.source is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.source): raise ValueError('Invalid value for source') class UnbindOk(base.Frame): """Confirm unbind successful This method confirms that the unbind was successful. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 51 # AMQP Frame ID index = 0x00280033 # pamqp Mapping Index name = 'Exchange.UnbindOk' synchronous = False # Indicates if this is a synchronous AMQP method class Queue: """Work with queues Queues store and forward messages. Queues can be configured in the server or created at runtime. Queues must be attached to at least one exchange in order to receive messages from publishers. """ __slots__: typing.List[str] = [] frame_id = 50 # AMQP Frame ID index = 0x00320000 # pamqp Mapping Index class Declare(base.Frame): """Declare queue, create if needed This method creates or checks a queue. When creating a new queue the client can specify various properties that control the durability of the queue and its contents, and the level of sharing for the queue. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: queue name - Default: ``''`` :param passive: Do not create queue - Default: ``False`` :param durable: Request a durable queue - Default: ``False`` :param exclusive: Request an exclusive queue - Default: ``False`` :param auto_delete: Auto-delete queue when unused - Default: ``False`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments for declaration - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'passive': bool, 'durable': bool, 'exclusive': bool, 'auto_delete': bool, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'passive', 'durable', 'exclusive', 'auto_delete', 'nowait', 'arguments' ] frame_id = 10 # AMQP Frame ID index = 0x0032000A # pamqp Mapping Index name = 'Queue.Declare' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Queue.DeclareOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _passive = 'bit' _durable = 'bit' _exclusive = 'bit' _auto_delete = 'bit' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, queue: str = '', passive: bool = False, durable: bool = False, exclusive: bool = False, auto_delete: bool = False, nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Queue.Declare` class""" self.ticket = ticket self.queue = queue self.passive = passive self.durable = durable self.exclusive = exclusive self.auto_delete = auto_delete self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class DeclareOk(base.Frame): """Confirms a queue definition This method confirms a Declare method and confirms the name of the queue, essential for automatically-named queues. :param queue: Reports the name of the queue. If the server generated a queue name, this field contains that name. :param message_count: Number of messages in the queue. :param consumer_count: Number of consumers :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'queue': str, 'message_count': int, 'consumer_count': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'queue', 'message_count', 'consumer_count' ] frame_id = 11 # AMQP Frame ID index = 0x0032000B # pamqp Mapping Index name = 'Queue.DeclareOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _queue = 'shortstr' _message_count = 'long' _consumer_count = 'long' def __init__(self, queue: typing.Optional[str] = None, message_count: typing.Optional[int] = None, consumer_count: typing.Optional[int] = None) -> None: """Initialize the :class:`Queue.DeclareOk` class""" self.queue = queue self.message_count = message_count self.consumer_count = consumer_count self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class Bind(base.Frame): """Bind queue to an exchange This method binds a queue to an exchange. Until a queue is bound it will not receive any messages. In a classic messaging model, store-and- forward queues are bound to a direct exchange and subscription queues are bound to a topic exchange. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to bind. - Default: ``''`` :param exchange: Name of the exchange to bind to - Default: ``''`` :param routing_key: Message routing key - Default: ``''`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments for binding - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'exchange': str, 'routing_key': str, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'exchange', 'routing_key', 'nowait', 'arguments' ] frame_id = 20 # AMQP Frame ID index = 0x00320014 # pamqp Mapping Index name = 'Queue.Bind' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Queue.BindOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _exchange = 'shortstr' _routing_key = 'shortstr' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, queue: str = '', exchange: str = '', routing_key: str = '', nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Queue.Bind` class""" self.ticket = ticket self.queue = queue self.exchange = exchange self.routing_key = routing_key self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class BindOk(base.Frame): """Confirm bind successful This method confirms that the bind was successful. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 21 # AMQP Frame ID index = 0x00320015 # pamqp Mapping Index name = 'Queue.BindOk' synchronous = False # Indicates if this is a synchronous AMQP method class Purge(base.Frame): """Purge a queue This method removes all messages from a queue which are not awaiting acknowledgment. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to purge. - Default: ``''`` :param nowait: Do not send a reply method - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'nowait': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'nowait' ] frame_id = 30 # AMQP Frame ID index = 0x0032001E # pamqp Mapping Index name = 'Queue.Purge' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Queue.PurgeOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _nowait = 'bit' def __init__(self, ticket: int = 0, queue: str = '', nowait: bool = False) -> None: """Initialize the :class:`Queue.Purge` class""" self.ticket = ticket self.queue = queue self.nowait = nowait self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class PurgeOk(base.Frame): """Confirms a queue purge This method confirms the purge of a queue. :param message_count: Reports the number of messages purged. """ __annotations__: typing.Dict[str, object] = {'message_count': int} __slots__: typing.List[str] = [ # AMQ Method Attributes 'message_count' ] frame_id = 31 # AMQP Frame ID index = 0x0032001F # pamqp Mapping Index name = 'Queue.PurgeOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _message_count = 'long' def __init__(self, message_count: typing.Optional[int] = None) -> None: """Initialize the :class:`Queue.PurgeOk` class""" self.message_count = message_count class Delete(base.Frame): """Delete a queue This method deletes a queue. When a queue is deleted any pending messages are sent to a dead-letter queue if this is defined in the server configuration, and all consumers on the queue are cancelled. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to delete. - Default: ``''`` :param if_unused: Delete only if unused - Default: ``False`` :param if_empty: Delete only if empty - Default: ``False`` :param nowait: Do not send a reply method - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'if_unused': bool, 'if_empty': bool, 'nowait': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'if_unused', 'if_empty', 'nowait' ] frame_id = 40 # AMQP Frame ID index = 0x00320028 # pamqp Mapping Index name = 'Queue.Delete' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Queue.DeleteOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _if_unused = 'bit' _if_empty = 'bit' _nowait = 'bit' def __init__(self, ticket: int = 0, queue: str = '', if_unused: bool = False, if_empty: bool = False, nowait: bool = False) -> None: """Initialize the :class:`Queue.Delete` class""" self.ticket = ticket self.queue = queue self.if_unused = if_unused self.if_empty = if_empty self.nowait = nowait self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class DeleteOk(base.Frame): """Confirm deletion of a queue This method confirms the deletion of a queue. :param message_count: Reports the number of messages deleted. """ __annotations__: typing.Dict[str, object] = {'message_count': int} __slots__: typing.List[str] = [ # AMQ Method Attributes 'message_count' ] frame_id = 41 # AMQP Frame ID index = 0x00320029 # pamqp Mapping Index name = 'Queue.DeleteOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _message_count = 'long' def __init__(self, message_count: typing.Optional[int] = None) -> None: """Initialize the :class:`Queue.DeleteOk` class""" self.message_count = message_count class Unbind(base.Frame): """Unbind a queue from an exchange This method unbinds a queue from an exchange. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to unbind. - Default: ``''`` :param exchange: The name of the exchange to unbind from. - Default: ``''`` :param routing_key: Routing key of binding - Default: ``''`` :param arguments: Arguments of binding - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'exchange': str, 'routing_key': str, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'exchange', 'routing_key', 'arguments' ] frame_id = 50 # AMQP Frame ID index = 0x00320032 # pamqp Mapping Index name = 'Queue.Unbind' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Queue.UnbindOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _exchange = 'shortstr' _routing_key = 'shortstr' _arguments = 'table' def __init__( self, ticket: int = 0, queue: str = '', exchange: str = '', routing_key: str = '', arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Queue.Unbind` class""" self.ticket = ticket self.queue = queue self.exchange = exchange self.routing_key = routing_key self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class UnbindOk(base.Frame): """Confirm unbind successful This method confirms that the unbind was successful. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 51 # AMQP Frame ID index = 0x00320033 # pamqp Mapping Index name = 'Queue.UnbindOk' synchronous = False # Indicates if this is a synchronous AMQP method class Basic: """Work with basic content The Basic class provides methods that support an industry-standard messaging model. """ __slots__: typing.List[str] = [] frame_id = 60 # AMQP Frame ID index = 0x003C0000 # pamqp Mapping Index class Qos(base.Frame): """Specify quality of service This method requests a specific quality of service. The QoS can be specified for the current channel or for all channels on the connection. The particular properties and semantics of a qos method always depend on the content class semantics. Though the qos method could in principle apply to both peers, it is currently meaningful only for the server. :param prefetch_size: Prefetch window in octets - Default: ``0`` :param prefetch_count: Prefetch window in messages - Default: ``0`` :param global_: Apply to entire connection - Default: ``False`` """ __annotations__: typing.Dict[str, object] = { 'prefetch_size': int, 'prefetch_count': int, 'global_': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'prefetch_size', 'prefetch_count', 'global_' ] frame_id = 10 # AMQP Frame ID index = 0x003C000A # pamqp Mapping Index name = 'Basic.Qos' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Basic.QosOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _prefetch_size = 'long' _prefetch_count = 'short' _global_ = 'bit' def __init__(self, prefetch_size: int = 0, prefetch_count: int = 0, global_: bool = False) -> None: """Initialize the :class:`Basic.Qos` class""" self.prefetch_size = prefetch_size self.prefetch_count = prefetch_count self.global_ = global_ class QosOk(base.Frame): """Confirm the requested qos This method tells the client that the requested QoS levels could be handled by the server. The requested QoS applies to all active consumers until a new QoS is defined. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 11 # AMQP Frame ID index = 0x003C000B # pamqp Mapping Index name = 'Basic.QosOk' synchronous = False # Indicates if this is a synchronous AMQP method class Consume(base.Frame): """Start a queue consumer This method asks the server to start a "consumer", which is a transient request for messages from a specific queue. Consumers last as long as the channel they were declared on, or until the client cancels them. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to consume from. - Default: ``''`` :param consumer_tag: Specifies the identifier for the consumer. The consumer tag is local to a channel, so two clients can use the same consumer tags. If this field is empty the server will generate a unique tag. - Default: ``''`` :param no_local: Do not deliver own messages - Default: ``False`` :param no_ack: No acknowledgement needed - Default: ``False`` :param exclusive: Request exclusive access - Default: ``False`` :param nowait: Do not send a reply method - Default: ``False`` :param arguments: Arguments for declaration - Default: ``{}`` :type arguments: :const:`~pamqp.common.Arguments` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'consumer_tag': str, 'no_local': bool, 'no_ack': bool, 'exclusive': bool, 'nowait': bool, 'arguments': common.Arguments } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'consumer_tag', 'no_local', 'no_ack', 'exclusive', 'nowait', 'arguments' ] frame_id = 20 # AMQP Frame ID index = 0x003C0014 # pamqp Mapping Index name = 'Basic.Consume' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Basic.ConsumeOk'] # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _consumer_tag = 'shortstr' _no_local = 'bit' _no_ack = 'bit' _exclusive = 'bit' _nowait = 'bit' _arguments = 'table' def __init__( self, ticket: int = 0, queue: str = '', consumer_tag: str = '', no_local: bool = False, no_ack: bool = False, exclusive: bool = False, nowait: bool = False, arguments: typing.Optional[common.Arguments] = None) -> None: """Initialize the :class:`Basic.Consume` class""" self.ticket = ticket self.queue = queue self.consumer_tag = consumer_tag self.no_local = no_local self.no_ack = no_ack self.exclusive = exclusive self.nowait = nowait self.arguments = arguments or {} self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class ConsumeOk(base.Frame): """Confirm a new consumer The server provides the client with a consumer tag, which is used by the client for methods called on the consumer at a later stage. :param consumer_tag: Holds the consumer tag specified by the client or provided by the server. """ __annotations__: typing.Dict[str, object] = {'consumer_tag': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'consumer_tag' ] frame_id = 21 # AMQP Frame ID index = 0x003C0015 # pamqp Mapping Index name = 'Basic.ConsumeOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _consumer_tag = 'shortstr' def __init__(self, consumer_tag: typing.Optional[str] = None) -> None: """Initialize the :class:`Basic.ConsumeOk` class""" self.consumer_tag = consumer_tag class Cancel(base.Frame): """End a queue consumer This method cancels a consumer. This does not affect already delivered messages, but it does mean the server will not send any more messages for that consumer. The client may receive an arbitrary number of messages in between sending the cancel method and receiving the cancel- ok reply. It may also be sent from the server to the client in the event of the consumer being unexpectedly cancelled (i.e. cancelled for any reason other than the server receiving the corresponding basic.cancel from the client). This allows clients to be notified of the loss of consumers due to events such as queue deletion. Note that as it is not a MUST for clients to accept this method from the server, it is advisable for the broker to be able to identify those clients that are capable of accepting the method, through some means of capability negotiation. :param consumer_tag: Consumer tag :param nowait: Do not send a reply method - Default: ``False`` """ __annotations__: typing.Dict[str, object] = { 'consumer_tag': str, 'nowait': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'consumer_tag', 'nowait' ] frame_id = 30 # AMQP Frame ID index = 0x003C001E # pamqp Mapping Index name = 'Basic.Cancel' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Basic.CancelOk'] # Valid responses to this method # Class Attribute Types for unmarshaling _consumer_tag = 'shortstr' _nowait = 'bit' def __init__(self, consumer_tag: typing.Optional[str] = None, nowait: bool = False) -> None: """Initialize the :class:`Basic.Cancel` class""" self.consumer_tag = consumer_tag self.nowait = nowait or False class CancelOk(base.Frame): """Confirm a cancelled consumer This method confirms that the cancellation was completed. :param consumer_tag: Consumer tag """ __annotations__: typing.Dict[str, object] = {'consumer_tag': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'consumer_tag' ] frame_id = 31 # AMQP Frame ID index = 0x003C001F # pamqp Mapping Index name = 'Basic.CancelOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _consumer_tag = 'shortstr' def __init__(self, consumer_tag: typing.Optional[str] = None) -> None: """Initialize the :class:`Basic.CancelOk` class""" self.consumer_tag = consumer_tag class Publish(base.Frame): """Publish a message This method publishes a message to a specific exchange. The message will be routed to queues as defined by the exchange configuration and distributed to any active consumers when the transaction, if any, is committed. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param exchange: Specifies the name of the exchange to publish to. The exchange name can be empty, meaning the default exchange. If the exchange name is specified, and that exchange does not exist, the server will raise a channel exception. - Default: ``''`` :param routing_key: Message routing key - Default: ``''`` :param mandatory: Indicate mandatory routing - Default: ``False`` :param immediate: Request immediate delivery - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'exchange': str, 'routing_key': str, 'mandatory': bool, 'immediate': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'exchange', 'routing_key', 'mandatory', 'immediate' ] frame_id = 40 # AMQP Frame ID index = 0x003C0028 # pamqp Mapping Index name = 'Basic.Publish' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _ticket = 'short' _exchange = 'shortstr' _routing_key = 'shortstr' _mandatory = 'bit' _immediate = 'bit' def __init__(self, ticket: int = 0, exchange: str = '', routing_key: str = '', mandatory: bool = False, immediate: bool = False) -> None: """Initialize the :class:`Basic.Publish` class""" self.ticket = ticket self.exchange = exchange self.routing_key = routing_key self.mandatory = mandatory self.immediate = immediate self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class Return(base.Frame): """Return a failed message This method returns an undeliverable message that was published with the "immediate" flag set, or an unroutable message published with the "mandatory" flag set. The reply code and text provide information about the reason that the message was undeliverable. :param reply_code: Reply code from server :param reply_text: Localised reply text - Default: ``''`` :param exchange: Specifies the name of the exchange that the message was originally published to. May be empty, meaning the default exchange. - Default: ``''`` :param routing_key: Message routing key :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'reply_code': int, 'reply_text': str, 'exchange': str, 'routing_key': str } __slots__: typing.List[str] = [ # AMQ Method Attributes 'reply_code', 'reply_text', 'exchange', 'routing_key' ] frame_id = 50 # AMQP Frame ID index = 0x003C0032 # pamqp Mapping Index name = 'Basic.Return' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _reply_code = 'short' _reply_text = 'shortstr' _exchange = 'shortstr' _routing_key = 'shortstr' def __init__(self, reply_code: typing.Optional[int] = None, reply_text: str = '', exchange: str = '', routing_key: typing.Optional[str] = None) -> None: """Initialize the :class:`Basic.Return` class""" self.reply_code = reply_code self.reply_text = reply_text or '' self.exchange = exchange or '' self.routing_key = routing_key self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class Deliver(base.Frame): """Notify the client of a consumer message This method delivers a message to the client, via a consumer. In the asynchronous message delivery model, the client starts a consumer using the Consume method, then the server responds with Deliver methods as and when messages arrive for that consumer. :param consumer_tag: Consumer tag :param delivery_tag: Server-assigned delivery tag :param redelivered: Message is being redelivered - Default: ``False`` :param exchange: Specifies the name of the exchange that the message was originally published to. May be empty, indicating the default exchange. - Default: ``''`` :param routing_key: Message routing key :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'consumer_tag': str, 'delivery_tag': int, 'redelivered': bool, 'exchange': str, 'routing_key': str } __slots__: typing.List[str] = [ # AMQ Method Attributes 'consumer_tag', 'delivery_tag', 'redelivered', 'exchange', 'routing_key' ] frame_id = 60 # AMQP Frame ID index = 0x003C003C # pamqp Mapping Index name = 'Basic.Deliver' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _consumer_tag = 'shortstr' _delivery_tag = 'longlong' _redelivered = 'bit' _exchange = 'shortstr' _routing_key = 'shortstr' def __init__(self, consumer_tag: typing.Optional[str] = None, delivery_tag: typing.Optional[int] = None, redelivered: bool = False, exchange: str = '', routing_key: typing.Optional[str] = None) -> None: """Initialize the :class:`Basic.Deliver` class""" self.consumer_tag = consumer_tag self.delivery_tag = delivery_tag self.redelivered = redelivered or False self.exchange = exchange or '' self.routing_key = routing_key self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class Get(base.Frame): """Direct access to a queue This method provides a direct access to the messages in a queue using a synchronous dialogue that is designed for specific types of application where synchronous functionality is more important than performance. :param ticket: Deprecated, must be ``0`` - Default: ``0`` :param queue: Specifies the name of the queue to get a message from. - Default: ``''`` :param no_ack: No acknowledgement needed - Default: ``False`` :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'ticket': int, 'queue': str, 'no_ack': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'ticket', 'queue', 'no_ack' ] frame_id = 70 # AMQP Frame ID index = 0x003C0046 # pamqp Mapping Index name = 'Basic.Get' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Basic.GetOk', 'Basic.GetEmpty'] # Class Attribute Types for unmarshaling _ticket = 'short' _queue = 'shortstr' _no_ack = 'bit' def __init__(self, ticket: int = 0, queue: str = '', no_ack: bool = False) -> None: """Initialize the :class:`Basic.Get` class""" self.ticket = ticket self.queue = queue self.no_ack = no_ack self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.ticket is not None and self.ticket != 0: raise ValueError('ticket must be 0') if self.queue is not None and len(self.queue) > 256: raise ValueError('Max length exceeded for queue') if self.queue is not None and not constants.DOMAIN_REGEX[ 'queue-name'].fullmatch(self.queue): raise ValueError('Invalid value for queue') class GetOk(base.Frame): """Provide client with a message This method delivers a message to the client following a get method. A message delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the get method. :param delivery_tag: Server-assigned delivery tag :param redelivered: Message is being redelivered - Default: ``False`` :param exchange: Specifies the name of the exchange that the message was originally published to. If empty, the message was published to the default exchange. - Default: ``''`` :param routing_key: Message routing key :param message_count: Number of messages in the queue. :raises ValueError: when an argument fails to validate """ __annotations__: typing.Dict[str, object] = { 'delivery_tag': int, 'redelivered': bool, 'exchange': str, 'routing_key': str, 'message_count': int } __slots__: typing.List[str] = [ # AMQ Method Attributes 'delivery_tag', 'redelivered', 'exchange', 'routing_key', 'message_count' ] frame_id = 71 # AMQP Frame ID index = 0x003C0047 # pamqp Mapping Index name = 'Basic.GetOk' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _delivery_tag = 'longlong' _redelivered = 'bit' _exchange = 'shortstr' _routing_key = 'shortstr' _message_count = 'long' def __init__(self, delivery_tag: typing.Optional[int] = None, redelivered: bool = False, exchange: str = '', routing_key: typing.Optional[str] = None, message_count: typing.Optional[int] = None) -> None: """Initialize the :class:`Basic.GetOk` class""" self.delivery_tag = delivery_tag self.redelivered = redelivered or False self.exchange = exchange or '' self.routing_key = routing_key self.message_count = message_count self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.exchange is not None and len(self.exchange) > 127: raise ValueError('Max length exceeded for exchange') if self.exchange is not None and not constants.DOMAIN_REGEX[ 'exchange-name'].fullmatch(self.exchange): raise ValueError('Invalid value for exchange') class GetEmpty(base.Frame): """Indicate no messages available This method tells the client that the queue has no messages available for the client. :param cluster_id: Deprecated, must be empty - Default: ``''`` """ __annotations__: typing.Dict[str, object] = {'cluster_id': str} __slots__: typing.List[str] = [ # AMQ Method Attributes 'cluster_id' ] frame_id = 72 # AMQP Frame ID index = 0x003C0048 # pamqp Mapping Index name = 'Basic.GetEmpty' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _cluster_id = 'shortstr' def __init__(self, cluster_id: str = '') -> None: """Initialize the :class:`Basic.GetEmpty` class""" self.cluster_id = cluster_id self.validate() def validate(self) -> None: """Validate the frame data ensuring all domains or attributes adhere to the protocol specification. :raises ValueError: on validation error """ if self.cluster_id is not None and self.cluster_id != '': raise ValueError('cluster_id must be empty') class Ack(base.Frame): """Acknowledge one or more messages When sent by the client, this method acknowledges one or more messages delivered via the Deliver or Get-Ok methods. When sent by server, this method acknowledges one or more messages published with the Publish method on a channel in confirm mode. The acknowledgement can be for a single message or a set of messages up to and including a specific message. :param delivery_tag: Server-assigned delivery tag - Default: ``0`` :param multiple: Acknowledge multiple messages - Default: ``False`` """ __annotations__: typing.Dict[str, object] = { 'delivery_tag': int, 'multiple': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'delivery_tag', 'multiple' ] frame_id = 80 # AMQP Frame ID index = 0x003C0050 # pamqp Mapping Index name = 'Basic.Ack' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _delivery_tag = 'longlong' _multiple = 'bit' def __init__(self, delivery_tag: int = 0, multiple: bool = False) -> None: """Initialize the :class:`Basic.Ack` class""" self.delivery_tag = delivery_tag self.multiple = multiple class Reject(base.Frame): """Reject an incoming message This method allows a client to reject a message. It can be used to interrupt and cancel large incoming messages, or return untreatable messages to their original queue. :param delivery_tag: Server-assigned delivery tag :param requeue: Requeue the message - Default: ``True`` """ __annotations__: typing.Dict[str, object] = { 'delivery_tag': int, 'requeue': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'delivery_tag', 'requeue' ] frame_id = 90 # AMQP Frame ID index = 0x003C005A # pamqp Mapping Index name = 'Basic.Reject' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _delivery_tag = 'longlong' _requeue = 'bit' def __init__(self, delivery_tag: typing.Optional[int] = None, requeue: bool = True) -> None: """Initialize the :class:`Basic.Reject` class""" self.delivery_tag = delivery_tag self.requeue = requeue class RecoverAsync(base.Frame): """Redeliver unacknowledged messages This method asks the server to redeliver all unacknowledged messages on a specified channel. Zero or more messages may be redelivered. This method is deprecated in favour of the synchronous Recover/Recover-Ok. .. deprecated:: This command is deprecated in AMQP 0-9-1 :param requeue: Requeue the message - Default: ``False`` """ __annotations__: typing.Dict[str, object] = {'requeue': bool} __slots__: typing.List[str] = [ # AMQ Method Attributes 'requeue' ] frame_id = 100 # AMQP Frame ID index = 0x003C0064 # pamqp Mapping Index name = 'Basic.RecoverAsync' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _requeue = 'bit' def __init__(self, requeue: bool = False) -> None: """Initialize the :class:`Basic.RecoverAsync` class""" self.requeue = requeue warnings.warn(constants.DEPRECATION_WARNING, category=DeprecationWarning) class Recover(base.Frame): """Redeliver unacknowledged messages This method asks the server to redeliver all unacknowledged messages on a specified channel. Zero or more messages may be redelivered. This method replaces the asynchronous Recover. :param requeue: Requeue the message - Default: ``False`` """ __annotations__: typing.Dict[str, object] = {'requeue': bool} __slots__: typing.List[str] = [ # AMQ Method Attributes 'requeue' ] frame_id = 110 # AMQP Frame ID index = 0x003C006E # pamqp Mapping Index name = 'Basic.Recover' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Basic.RecoverOk'] # Class Attribute Types for unmarshaling _requeue = 'bit' def __init__(self, requeue: bool = False) -> None: """Initialize the :class:`Basic.Recover` class""" self.requeue = requeue class RecoverOk(base.Frame): """Confirm recovery This method acknowledges a :class:`Basic.Recover` method. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 111 # AMQP Frame ID index = 0x003C006F # pamqp Mapping Index name = 'Basic.RecoverOk' synchronous = False # Indicates if this is a synchronous AMQP method class Nack(base.Frame): """Reject one or more incoming messages This method allows a client to reject one or more incoming messages. It can be used to interrupt and cancel large incoming messages, or return untreatable messages to their original queue. This method is also used by the server to inform publishers on channels in confirm mode of unhandled messages. If a publisher receives this method, it probably needs to republish the offending messages. :param delivery_tag: Server-assigned delivery tag - Default: ``0`` :param multiple: Reject multiple messages - Default: ``False`` :param requeue: Requeue the message - Default: ``True`` """ __annotations__: typing.Dict[str, object] = { 'delivery_tag': int, 'multiple': bool, 'requeue': bool } __slots__: typing.List[str] = [ # AMQ Method Attributes 'delivery_tag', 'multiple', 'requeue' ] frame_id = 120 # AMQP Frame ID index = 0x003C0078 # pamqp Mapping Index name = 'Basic.Nack' synchronous = False # Indicates if this is a synchronous AMQP method # Class Attribute Types for unmarshaling _delivery_tag = 'longlong' _multiple = 'bit' _requeue = 'bit' def __init__(self, delivery_tag: int = 0, multiple: bool = False, requeue: bool = True) -> None: """Initialize the :class:`Basic.Nack` class""" self.delivery_tag = delivery_tag self.multiple = multiple self.requeue = requeue class Properties(base.BasicProperties): """Content Properties .. Note:: The AMQP property type is named ``message_type`` as to not conflict with the Python ``type`` keyword :param content_type: MIME content type :param content_encoding: MIME content encoding :param headers: Message header field table :type headers: typing.Optional[:const:`~pamqp.common.FieldTable`] :param delivery_mode: Non-persistent (1) or persistent (2) :param priority: Message priority, 0 to 9 :param correlation_id: Application correlation identifier :param reply_to: Address to reply to :param expiration: Message expiration specification :param message_id: Application message identifier :param timestamp: Message timestamp :param message_type: Message type name :param user_id: Creating user id :param app_id: Creating application id :param cluster_id: Deprecated, must be empty :raises: ValueError """ __annotations__: typing.Dict[str, object] = { 'content_type': str, 'content_encoding': str, 'headers': common.FieldTable, 'delivery_mode': int, 'priority': int, 'correlation_id': str, 'reply_to': str, 'expiration': str, 'message_id': str, 'timestamp': datetime.datetime, 'message_type': str, 'user_id': str, 'app_id': str, 'cluster_id': str } __slots__: typing.List[str] = [ # AMQ Properties Attributes 'content_type', 'content_encoding', 'headers', 'delivery_mode', 'priority', 'correlation_id', 'reply_to', 'expiration', 'message_id', 'timestamp', 'message_type', 'user_id', 'app_id', 'cluster_id' ] # Flag values for marshaling / unmarshaling flags = { 'content_type': 32768, 'content_encoding': 16384, 'headers': 8192, 'delivery_mode': 4096, 'priority': 2048, 'correlation_id': 1024, 'reply_to': 512, 'expiration': 256, 'message_id': 128, 'timestamp': 64, 'message_type': 32, 'user_id': 16, 'app_id': 8, 'cluster_id': 4 } frame_id = 60 # AMQP Frame ID index = 0x003C # pamqp Mapping Index name = 'Basic.Properties' # Class Attribute Types for unmarshaling _content_type = 'shortstr' _content_encoding = 'shortstr' _headers = 'table' _delivery_mode = 'octet' _priority = 'octet' _correlation_id = 'shortstr' _reply_to = 'shortstr' _expiration = 'shortstr' _message_id = 'shortstr' _timestamp = 'timestamp' _message_type = 'shortstr' _user_id = 'shortstr' _app_id = 'shortstr' _cluster_id = 'shortstr' def __init__(self, content_type: typing.Optional[str] = None, content_encoding: typing.Optional[str] = None, headers: typing.Optional[common.FieldTable] = None, delivery_mode: typing.Optional[int] = None, priority: typing.Optional[int] = None, correlation_id: typing.Optional[str] = None, reply_to: typing.Optional[str] = None, expiration: typing.Optional[str] = None, message_id: typing.Optional[str] = None, timestamp: typing.Optional[datetime.datetime] = None, message_type: typing.Optional[str] = None, user_id: typing.Optional[str] = None, app_id: typing.Optional[str] = None, cluster_id: str = '') -> None: """Initialize the Basic.Properties class""" self.content_type = content_type self.content_encoding = content_encoding self.headers = headers self.delivery_mode = delivery_mode self.priority = priority self.correlation_id = correlation_id self.reply_to = reply_to self.expiration = expiration self.message_id = message_id self.timestamp = timestamp self.message_type = message_type self.user_id = user_id self.app_id = app_id self.cluster_id = cluster_id self.validate() class Tx: """Work with transactions The Tx class allows publish and ack operations to be batched into atomic units of work. The intention is that all publish and ack requests issued within a transaction will complete successfully or none of them will. Servers SHOULD implement atomic transactions at least where all publish or ack requests affect a single queue. Transactions that cover multiple queues may be non-atomic, given that queues can be created and destroyed asynchronously, and such events do not form part of any transaction. Further, the behaviour of transactions with respect to the immediate and mandatory flags on :class:`Basic.Publish` methods is not defined. """ __slots__: typing.List[str] = [] frame_id = 90 # AMQP Frame ID index = 0x005A0000 # pamqp Mapping Index class Select(base.Frame): """Select standard transaction mode This method sets the channel to use standard transactions. The client must use this method at least once on a channel before using the Commit or Rollback methods. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 10 # AMQP Frame ID index = 0x005A000A # pamqp Mapping Index name = 'Tx.Select' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Tx.SelectOk'] # Valid responses to this method class SelectOk(base.Frame): """Confirm transaction mode This method confirms to the client that the channel was successfully set to use standard transactions. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 11 # AMQP Frame ID index = 0x005A000B # pamqp Mapping Index name = 'Tx.SelectOk' synchronous = False # Indicates if this is a synchronous AMQP method class Commit(base.Frame): """Commit the current transaction This method commits all message publications and acknowledgments performed in the current transaction. A new transaction starts immediately after a commit. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 20 # AMQP Frame ID index = 0x005A0014 # pamqp Mapping Index name = 'Tx.Commit' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Tx.CommitOk'] # Valid responses to this method class CommitOk(base.Frame): """Confirm a successful commit This method confirms to the client that the commit succeeded. Note that if a commit fails, the server raises a channel exception. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 21 # AMQP Frame ID index = 0x005A0015 # pamqp Mapping Index name = 'Tx.CommitOk' synchronous = False # Indicates if this is a synchronous AMQP method class Rollback(base.Frame): """Abandon the current transaction This method abandons all message publications and acknowledgments performed in the current transaction. A new transaction starts immediately after a rollback. Note that unacked messages will not be automatically redelivered by rollback; if that is required an explicit recover call should be issued. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 30 # AMQP Frame ID index = 0x005A001E # pamqp Mapping Index name = 'Tx.Rollback' synchronous = True # Indicates if this is a synchronous AMQP method valid_responses = ['Tx.RollbackOk'] # Valid responses to this method class RollbackOk(base.Frame): """Confirm successful rollback This method confirms to the client that the rollback succeeded. Note that if an rollback fails, the server raises a channel exception. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 31 # AMQP Frame ID index = 0x005A001F # pamqp Mapping Index name = 'Tx.RollbackOk' synchronous = False # Indicates if this is a synchronous AMQP method class Confirm: """Work with confirms The Confirm class allows publishers to put the channel in confirm mode and subsequently be notified when messages have been handled by the broker. The intention is that all messages published on a channel in confirm mode will be acknowledged at some point. By acknowledging a message the broker assumes responsibility for it and indicates that it has done something it deems reasonable with it. Unroutable mandatory or immediate messages are acknowledged right after the :class:`Basic.Return` method. Messages are acknowledged when all queues to which the message has been routed have either delivered the message and received an acknowledgement (if required), or enqueued the message (and persisted it if required). Published messages are assigned ascending sequence numbers, starting at 1 with the first :class:`Confirm.Select` method. The server confirms messages by sending :class:`Basic.Ack` methods referring to these sequence numbers. """ __slots__: typing.List[str] = [] frame_id = 85 # AMQP Frame ID index = 0x00550000 # pamqp Mapping Index class Select(base.Frame): """Select confirm mode (i.e. enable publisher acknowledgements) This method sets the channel to use publisher acknowledgements. The client can only use this method on a non-transactional channel. :param nowait: Do not send a reply method - Default: ``False`` """ __annotations__: typing.Dict[str, object] = {'nowait': bool} __slots__: typing.List[str] = [ # AMQ Method Attributes 'nowait' ] frame_id = 10 # AMQP Frame ID index = 0x0055000A # pamqp Mapping Index name = 'Confirm.Select' synchronous = True # Indicates if this is a synchronous AMQP method # Valid responses to this method valid_responses = ['Confirm.SelectOk'] # Class Attribute Types for unmarshaling _nowait = 'bit' def __init__(self, nowait: bool = False) -> None: """Initialize the :class:`Confirm.Select` class""" self.nowait = nowait class SelectOk(base.Frame): """Acknowledge confirm mode This method confirms to the client that the channel was successfully set to use publisher acknowledgements. """ __annotations__: typing.Dict[str, object] = {} __slots__: typing.List[str] = [] # AMQ Method Attributes frame_id = 11 # AMQP Frame ID index = 0x0055000B # pamqp Mapping Index name = 'Confirm.SelectOk' synchronous = False # Indicates if this is a synchronous AMQP method # AMQP Class.Method Index Mapping INDEX_MAPPING = { 0x000A000A: Connection.Start, 0x000A000B: Connection.StartOk, 0x000A0014: Connection.Secure, 0x000A0015: Connection.SecureOk, 0x000A001E: Connection.Tune, 0x000A001F: Connection.TuneOk, 0x000A0028: Connection.Open, 0x000A0029: Connection.OpenOk, 0x000A0032: Connection.Close, 0x000A0033: Connection.CloseOk, 0x000A003C: Connection.Blocked, 0x000A003D: Connection.Unblocked, 0x000A0046: Connection.UpdateSecret, 0x000A0047: Connection.UpdateSecretOk, 0x0014000A: Channel.Open, 0x0014000B: Channel.OpenOk, 0x00140014: Channel.Flow, 0x00140015: Channel.FlowOk, 0x00140028: Channel.Close, 0x00140029: Channel.CloseOk, 0x0028000A: Exchange.Declare, 0x0028000B: Exchange.DeclareOk, 0x00280014: Exchange.Delete, 0x00280015: Exchange.DeleteOk, 0x0028001E: Exchange.Bind, 0x0028001F: Exchange.BindOk, 0x00280028: Exchange.Unbind, 0x00280033: Exchange.UnbindOk, 0x0032000A: Queue.Declare, 0x0032000B: Queue.DeclareOk, 0x00320014: Queue.Bind, 0x00320015: Queue.BindOk, 0x0032001E: Queue.Purge, 0x0032001F: Queue.PurgeOk, 0x00320028: Queue.Delete, 0x00320029: Queue.DeleteOk, 0x00320032: Queue.Unbind, 0x00320033: Queue.UnbindOk, 0x003C000A: Basic.Qos, 0x003C000B: Basic.QosOk, 0x003C0014: Basic.Consume, 0x003C0015: Basic.ConsumeOk, 0x003C001E: Basic.Cancel, 0x003C001F: Basic.CancelOk, 0x003C0028: Basic.Publish, 0x003C0032: Basic.Return, 0x003C003C: Basic.Deliver, 0x003C0046: Basic.Get, 0x003C0047: Basic.GetOk, 0x003C0048: Basic.GetEmpty, 0x003C0050: Basic.Ack, 0x003C005A: Basic.Reject, 0x003C0064: Basic.RecoverAsync, 0x003C006E: Basic.Recover, 0x003C006F: Basic.RecoverOk, 0x003C0078: Basic.Nack, 0x005A000A: Tx.Select, 0x005A000B: Tx.SelectOk, 0x005A0014: Tx.Commit, 0x005A0015: Tx.CommitOk, 0x005A001E: Tx.Rollback, 0x005A001F: Tx.RollbackOk, 0x0055000A: Confirm.Select, 0x0055000B: Confirm.SelectOk } pamqp-3.3.0/pamqp/common.py000066400000000000000000000044531455032117300156220ustar00rootroot00000000000000""" Common type aliases and classes. """ import datetime import decimal import struct import typing FieldArray = typing.List['FieldValue'] """A data structure for holding an array of field values.""" FieldTable = typing.Dict[str, 'FieldValue'] """Field tables are data structures that contain packed name-value pairs. The name-value pairs are encoded as short string defining the name, and octet defining the values type and then the value itself. The valid field types for tables are an extension of the native integer, bit, string, and timestamp types, and are shown in the grammar. Multi-octet integer fields are always held in network byte order. Guidelines for implementers: - Field names MUST start with a letter, '$' or '#' and may continue with letters, `$` or `#`, digits, or underlines, to a maximum length of 128 characters. - The server SHOULD validate field names and upon receiving an invalid field name, it SHOULD signal a connection exception with reply code 503 (syntax error). - Decimal values are not intended to support floating point values, but rather fixed-point business values such as currency rates and amounts. They are encoded as an octet representing the number of places followed by a long signed integer. The 'decimals' octet is not signed. - Duplicate fields are illegal. The behaviour of a peer with respect to a table containing duplicate fields is undefined. """ FieldValue = typing.Union[bool, bytes, bytearray, decimal.Decimal, FieldArray, FieldTable, float, int, None, str, datetime.datetime] """Defines valid field values for a :const:`FieldTable` and a :const:`FieldValue` """ Arguments = typing.Optional[FieldTable] """Defines an AMQP method arguments argument data type""" class Struct: """Simple object for getting to the struct objects for :mod:`pamqp.decode` / :mod:`pamqp.encode`. """ byte = struct.Struct('B') double = struct.Struct('>d') float = struct.Struct('>f') integer = struct.Struct('>I') uint = struct.Struct('>i') long_long_int = struct.Struct('>q') short_short_int = struct.Struct('>b') short_short_uint = struct.Struct('>B') timestamp = struct.Struct('>Q') long = struct.Struct('>l') ulong = struct.Struct('>L') short = struct.Struct('>h') ushort = struct.Struct('>H') pamqp-3.3.0/pamqp/constants.py000066400000000000000000000036431455032117300163460ustar00rootroot00000000000000# Auto-generated, do not edit this file. import re # AMQP Protocol Frame Prefix AMQP = b'AMQP' # AMQP Protocol Version VERSION = (0, 9, 1) # RabbitMQ Defaults DEFAULT_HOST = 'localhost' DEFAULT_PORT = 5672 DEFAULT_USER = 'guest' DEFAULT_PASS = 'guest' DEFAULT_VHOST = '/' # AMQP Constants FRAME_METHOD = 1 FRAME_HEADER = 2 FRAME_BODY = 3 FRAME_HEARTBEAT = 8 FRAME_MIN_SIZE = 4096 FRAME_END = 206 # Indicates that the method completed successfully. This reply code is reserved # for future use - the current protocol design does not use positive # confirmation and reply codes are sent only in case of an error. REPLY_SUCCESS = 200 # Not included in the spec XML or JSON files. FRAME_END_CHAR = b'\xce' FRAME_HEADER_SIZE = 7 FRAME_MAX_SIZE = 131072 # AMQP data types DATA_TYPES = [ 'bit', # single bit 'long', # 32-bit integer 'longlong', # 64-bit integer 'longstr', # long string 'octet', # single octet 'short', # 16-bit integer 'shortstr', # short string (max. 256 characters) 'table', # field table 'timestamp' # 64-bit timestamp ] # AMQP domains DOMAINS = { 'channel-id': 'longstr', 'class-id': 'short', 'consumer-tag': 'shortstr', 'delivery-tag': 'longlong', 'destination': 'shortstr', 'duration': 'longlong', 'exchange-name': 'shortstr', 'method-id': 'short', 'no-ack': 'bit', 'no-local': 'bit', 'offset': 'longlong', 'path': 'shortstr', 'peer-properties': 'table', 'queue-name': 'shortstr', 'redelivered': 'bit', 'reference': 'longstr', 'reject-code': 'short', 'reject-text': 'shortstr', 'reply-code': 'short', 'reply-text': 'shortstr', 'security-token': 'longstr' } # AMQP domain patterns DOMAIN_REGEX = { 'exchange-name': re.compile(r'^[a-zA-Z0-9-_.:@#,/ ]*$'), 'queue-name': re.compile(r'^[a-zA-Z0-9-_.:@#,/ ]*$') } # Other constants DEPRECATION_WARNING = 'This command is deprecated in AMQP 0-9-1' pamqp-3.3.0/pamqp/decode.py000066400000000000000000000321141455032117300155500ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ Functions for decoding data of various types including field tables and arrays """ import datetime import decimal as _decimal import typing from pamqp import common def by_type(value: bytes, data_type: str, offset: int = 0) -> typing.Tuple[int, common.FieldValue]: """Decodes values using the specified type :param value: The binary value to decode :param data_type: The data type name of the value :param offset: The starting position of the data in the byte stream :rtype: :class:`tuple` (:class:`int`, :const:`pamqp.common.FieldValue`) :raises ValueError: when the data type is unknown """ if data_type == 'bit': return bit(value, offset) decoder = METHODS.get(data_type) if decoder is None: raise ValueError('Unknown type: {}'.format(data_type)) return decoder(value) def bit(value: bytes, position: int) -> typing.Tuple[int, bool]: """Decode a bit value, returning bytes consumed and the value. :param value: The binary value to decode :param position: The position in the byte of the bit value :rtype: :class:`tuple` (:class:`int`, :class:`bool`) :raises ValueError: when the binary data can not be unpacked """ bit_buffer = common.Struct.byte.unpack_from(value)[0] try: return 0, (bit_buffer & (1 << position)) != 0 except TypeError: raise ValueError('Could not unpack bit value') def boolean(value: bytes) -> typing.Tuple[int, bool]: """Decode a boolean value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`bool`) :raises ValueError: when the binary data can not be unpacked """ try: return 1, bool(common.Struct.byte.unpack_from(value[0:1])[0]) except TypeError: raise ValueError('Could not unpack boolean value') def byte_array(value: bytes) -> typing.Tuple[int, bytearray]: """Decode a byte_array value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`bytearray`) :raises ValueError: when the binary data can not be unpacked """ try: length = common.Struct.integer.unpack(value[0:4])[0] return length + 4, bytearray(value[4:length + 4]) except TypeError: raise ValueError('Could not unpack byte array value') def decimal(value: bytes) -> typing.Tuple[int, _decimal.Decimal]: """Decode a decimal value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`decimal.Decimal`) :raises ValueError: when the binary data can not be unpacked """ try: decimals = common.Struct.byte.unpack(value[0:1])[0] raw = common.Struct.integer.unpack(value[1:5])[0] return 5, _decimal.Decimal(raw) * (_decimal.Decimal(10)**-decimals) except TypeError: raise ValueError('Could not unpack decimal value') def double(value: bytes) -> typing.Tuple[int, float]: """Decode a double value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`float`) :raises ValueError: when the binary data can not be unpacked """ try: return 8, common.Struct.double.unpack_from(value)[0] except TypeError: raise ValueError('Could not unpack double value') def floating_point(value: bytes) -> typing.Tuple[int, float]: """Decode a floating point value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`float`) :raises ValueError: when the binary data can not be unpacked """ try: return 4, common.Struct.float.unpack_from(value)[0] except TypeError: raise ValueError('Could not unpack floating point value') def long_int(value: bytes) -> typing.Tuple[int, int]: """Decode a long integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 4, common.Struct.long.unpack(value[0:4])[0] except TypeError: raise ValueError('Could not unpack long integer value') def long_uint(value: bytes) -> typing.Tuple[int, int]: """Decode an unsigned long integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 4, common.Struct.ulong.unpack(value[0:4])[0] except TypeError: raise ValueError('Could not unpack unsigned long integer value') def long_long_int(value: bytes) -> typing.Tuple[int, int]: """Decode a long-long integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 8, common.Struct.long_long_int.unpack(value[0:8])[0] except TypeError: raise ValueError('Could not unpack long-long integer value') def long_str(value: bytes) -> typing.Tuple[int, typing.Union[str, bytes]]: """Decode a string value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`str`) :raises ValueError: when the binary data can not be unpacked """ try: length = common.Struct.integer.unpack(value[0:4])[0] return length + 4, value[4:length + 4].decode('utf-8') except TypeError: raise ValueError('Could not unpack long string value') except UnicodeDecodeError: return length + 4, value[4:length + 4] def octet(value: bytes) -> typing.Tuple[int, int]: """Decode an octet value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 1, common.Struct.byte.unpack(value[0:1])[0] except TypeError: raise ValueError('Could not unpack octet value') def short_int(value: bytes) -> typing.Tuple[int, int]: """Decode a short integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 2, common.Struct.short.unpack_from(value[0:2])[0] except TypeError: raise ValueError('Could not unpack short integer value') def short_uint(value: bytes) -> typing.Tuple[int, int]: """Decode an unsigned short integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 2, common.Struct.ushort.unpack_from(value[0:2])[0] except TypeError: raise ValueError('Could not unpack unsigned short integer value') def short_short_int(value: bytes) -> typing.Tuple[int, int]: """Decode a short-short integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 1, common.Struct.short_short_int.unpack_from(value[0:1])[0] except TypeError: raise ValueError('Could not unpack short-short integer value') def short_short_uint(value: bytes) -> typing.Tuple[int, int]: """Decode a unsigned short-short integer value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`int`) :raises ValueError: when the binary data can not be unpacked """ try: return 1, common.Struct.short_short_uint.unpack_from(value[0:1])[0] except TypeError: raise ValueError('Could not unpack unsigned short-short integer value') def short_str(value: bytes) -> typing.Tuple[int, str]: """Decode a string value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`str`) :raises ValueError: when the binary data can not be unpacked """ try: length = common.Struct.byte.unpack(value[0:1])[0] return length + 1, value[1:length + 1].decode('utf-8') except TypeError: raise ValueError('Could not unpack short string value') def timestamp(value: bytes) -> typing.Tuple[int, datetime.datetime]: """Decode a timestamp value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :class:`datetime.datetime`) :raises ValueError: when the binary data can not be unpacked """ try: temp = common.Struct.timestamp.unpack(value[0:8]) ts_value = temp[0] # Anything above the year 2106 is likely milliseconds if ts_value > 0xFFFFFFFF: ts_value /= 1000.0 return 8, datetime.datetime.fromtimestamp(ts_value, tz=datetime.timezone.utc) except TypeError: raise ValueError('Could not unpack timestamp value') def embedded_value(value: bytes) -> typing.Tuple[int, common.FieldValue]: """Dynamically decode a value based upon the starting byte :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :const:`pamqp.common.FieldValue`) :raises ValueError: when the binary data can not be unpacked """ if not value: return 0, None try: bytes_consumed, temp = TABLE_MAPPING[value[0:1]](value[1:]) except KeyError: raise ValueError('Unknown type: {!r}'.format(value[:1])) return bytes_consumed + 1, temp def field_array(value: bytes) -> typing.Tuple[int, common.FieldArray]: """Decode a field array value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :const:`pamqp.common.FieldArray`) :raises ValueError: when the binary data can not be unpacked """ try: length = common.Struct.integer.unpack(value[0:4])[0] offset = 4 data = [] field_array_end = offset + length while offset < field_array_end: consumed, result = embedded_value(value[offset:]) offset += consumed data.append(result) return offset, data except TypeError: raise ValueError('Could not unpack data') def field_table(value: bytes) -> typing.Tuple[int, common.FieldTable]: """Decode a field array value, returning bytes consumed and the value. :param value: The binary value to decode :rtype: :class:`tuple` (:class:`int`, :const:`pamqp.common.FieldTable`) :raises ValueError: when the binary data can not be unpacked """ try: length = common.Struct.integer.unpack(value[0:4])[0] offset = 4 data = {} field_table_end = offset + length while offset < field_table_end: key_length = common.Struct.byte.unpack_from(value, offset)[0] offset += 1 key = value[offset:offset + key_length].decode('utf-8') offset += key_length consumed, result = embedded_value(value[offset:]) offset += consumed data[key] = result return field_table_end, data except TypeError: raise ValueError('Could not unpack data') def void(_: bytes) -> typing.Tuple[int, None]: """Return a void, no data to decode :param _: The empty bytes object to ignore :rtype: :class:`tuple` (:class:`int`, :const:`None`) """ return 0, None METHODS = { 'array': field_array, 'bit': bit, 'boolean': boolean, 'byte_array': byte_array, 'decimal': decimal, 'double': double, 'float': floating_point, 'long': long_uint, 'longlong': long_long_int, 'longstr': long_str, 'octet': octet, 'short': short_uint, 'shortstr': short_str, 'table': field_table, 'timestamp': timestamp, 'void': void, } # Define a data type mapping to methods for by_type() # See https://www.rabbitmq.com/amqp-0-9-1-errata.html TABLE_MAPPING = { b't': boolean, b'b': short_short_int, b'B': short_short_uint, b's': short_int, b'u': short_uint, b'I': long_int, b'i': long_uint, b'l': long_long_int, b'L': long_long_int, b'f': floating_point, b'd': double, b'D': decimal, b'S': long_str, b'A': field_array, b'T': timestamp, b'F': field_table, b'V': void, b'\x00': void, # While not documented, have seen this in the wild b'x': byte_array, } # Define a mapping for use in `field_array()` and `field_table()` pamqp-3.3.0/pamqp/encode.py000066400000000000000000000312361455032117300155660ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ Functions for encoding data of various types including field tables and arrays """ import calendar import datetime import decimal as _decimal import logging import struct import time import typing from pamqp import common LOGGER = logging.getLogger(__name__) DEPRECATED_RABBITMQ_SUPPORT = False """Toggle to support older versions of RabbitMQ.""" def support_deprecated_rabbitmq(enabled: bool = True) -> None: """Toggle the data types available in field-tables If called with `True`, than RabbitMQ versions, the field-table integer types will not support the full AMQP spec. :param enabled: Specify if deprecated RabbitMQ versions are supported """ global DEPRECATED_RABBITMQ_SUPPORT DEPRECATED_RABBITMQ_SUPPORT = enabled def by_type(value: common.FieldValue, data_type: str) -> bytes: """Takes a value of any type and tries to encode it with the specified encoder. :param value: The value to encode :type value: :const:`pamqp.common.FieldValue` :param data_type: The data type name to use for encoding :raises TypeError: when the :data:`data_type` is unknown """ try: return METHODS[str(data_type)](value) except KeyError: raise TypeError('Unknown type: {}'.format(value)) def bit(value: int, byte: int, position: int) -> int: """Encode a bit value :param value: Value to encode :param byte: The byte to apply the value to :param position: The position in the byte to set the bit on """ return byte | (value << position) def boolean(value: bool) -> bytes: """Encode a boolean value :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, bool): raise TypeError('bool required, received {}'.format(type(value))) return common.Struct.short_short_uint.pack(int(value)) def byte_array(value: bytearray) -> bytes: """Encode a byte array value :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, bytearray): raise TypeError('bytearray required, received {}'.format(type(value))) return common.Struct.integer.pack(len(value)) + value def decimal(value: _decimal.Decimal) -> bytes: """Encode a decimal.Decimal value :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, _decimal.Decimal): raise TypeError('decimal.Decimal required, received {}'.format( type(value))) tmp = str(value) if '.' in tmp: decimals = len(tmp.split('.')[-1]) value = value.normalize() raw = int(value * (_decimal.Decimal(10)**decimals)) return struct.pack('>Bi', decimals, raw) return struct.pack('>Bi', 0, int(value)) def double(value: float) -> bytes: """Encode a floating point value as a double :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, float): raise TypeError('float required, received {}'.format(type(value))) return common.Struct.double.pack(value) def floating_point(value: float) -> bytes: """Encode a floating point value :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, float): raise TypeError('float required, received {}'.format(type(value))) return common.Struct.float.pack(value) def long_int(value: int) -> bytes: """Encode a long integer :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) elif not (-2147483648 <= value <= 2147483647): raise TypeError('Long integer range: -2147483648 to 2147483647') return common.Struct.long.pack(value) def long_uint(value: int) -> bytes: """Encode a long unsigned integer :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) elif not (0 <= value <= 4294967295): raise TypeError('Long unsigned-integer range: 0 to 4294967295') return common.Struct.ulong.pack(value) def long_long_int(value: int) -> bytes: """Encode a long-long int :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) elif not (-9223372036854775808 <= value <= 9223372036854775807): raise TypeError('long-long integer range: ' '-9223372036854775808 to 9223372036854775807') return common.Struct.long_long_int.pack(value) def long_string(value: str) -> bytes: """Encode a "long string" :param value: Value to encode :raises TypeError: when the value is not the correct type """ return _string(common.Struct.integer, value) def octet(value: int) -> bytes: """Encode an octet value :param value: Value to encode :raises TypeError: when the value is not the correct type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) return common.Struct.byte.pack(value) def short_int(value: int) -> bytes: """Encode a short integer :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) elif not (-32768 <= value <= 32767): raise TypeError('Short integer range: -32678 to 32767') return common.Struct.short.pack(value) def short_uint(value: int) -> bytes: """Encode an unsigned short integer :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if not isinstance(value, int): raise TypeError('int required, received {}'.format(type(value))) elif not (0 <= value <= 65535): raise TypeError('Short unsigned integer range: 0 to 65535') return common.Struct.ushort.pack(value) def short_string(value: str) -> bytes: """ Encode a string :param value: Value to encode :raises TypeError: when the value is not the correct type """ return _string(common.Struct.byte, value) def timestamp(value: typing.Union[datetime.datetime, time.struct_time]) \ -> bytes: """Encode a datetime.datetime object or time.struct_time :param value: Value to encode :raises TypeError: when the value is not the correct type """ if isinstance(value, datetime.datetime): if value.tzinfo is None or value.tzinfo.utcoffset(value) is None: # assume datetime object is UTC value = value.replace(tzinfo=datetime.timezone.utc) return common.Struct.timestamp.pack(int(value.timestamp())) if isinstance(value, time.struct_time): return common.Struct.timestamp.pack(calendar.timegm(value)) raise TypeError( 'datetime.datetime or time.struct_time required, received {}'.format( type(value))) def field_array(value: common.FieldArray) -> bytes: """Encode a field array from a list of values :param value: Value to encode :type value: :const:`pamqp.common.FieldArray` :raises TypeError: when the value is not the correct type """ if not isinstance(value, list): raise TypeError('list of values required, received {}'.format( type(value))) data = [] for item in value: data.append(encode_table_value(item)) output = b''.join(data) return common.Struct.integer.pack(len(output)) + output def field_table(value: common.FieldTable) -> bytes: """Encode a field table from a dict :param value: Value to encode :type value: :const:`pamqp.common.FieldTable` :raises TypeError: when the value is not the correct type """ if not value: # If there is no value, return a standard 4 null bytes return common.Struct.integer.pack(0) elif not isinstance(value, dict): raise TypeError('dict required, received {}'.format(type(value))) data = [] for key, value in sorted(value.items()): if len(key) > 128: # field names have 128 char max LOGGER.warning('Truncating key %s to 128 bytes', key) key = key[0:128] data.append(short_string(key)) try: data.append(encode_table_value(value)) except TypeError as err: raise TypeError('{} error: {}/'.format(key, err)) output = b''.join(data) return common.Struct.integer.pack(len(output)) + output def table_integer(value: int) -> bytes: """Determines the best type of numeric type to encode value as, preferring the smallest data size first. :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if DEPRECATED_RABBITMQ_SUPPORT: return _deprecated_table_integer(value) if -128 <= value <= 127: return b'b' + octet(value) elif -32768 <= value <= 32767: return b's' + short_int(value) elif 0 <= value <= 65535: return b'u' + short_uint(value) elif -2147483648 <= value <= 2147483647: return b'I' + long_int(value) elif 0 <= value <= 4294967295: return b'i' + long_uint(value) elif -9223372036854775808 <= value <= 9223372036854775807: return b'l' + long_long_int(value) raise TypeError('Unsupported numeric value: {}'.format(value)) def _deprecated_table_integer(value: int) -> bytes: """Determines the best type of numeric type to encode value as, preferring the smallest data size first, supporting versions of RabbitMQ < 3.6 :param value: Value to encode :raises TypeError: when the value is not the correct type or outside the acceptable range for the data type """ if -128 <= value <= 127: return b'b' + octet(value) elif -32768 <= value <= 32767: return b's' + short_int(value) elif -2147483648 <= value <= 2147483647: return b'I' + long_int(value) elif -9223372036854775808 <= value <= 9223372036854775807: return b'l' + long_long_int(value) raise TypeError('Unsupported numeric value: {}'.format(value)) def _string(encoder: struct.Struct, value: str) -> bytes: """Reduce a small amount of duplication in string handling :raises: TypeError """ if not isinstance(value, str): raise TypeError('str required, received {}'.format(type(value))) temp = value.encode('utf-8') return encoder.pack(len(temp)) + temp def encode_table_value( value: typing.Union[common.FieldArray, common.FieldTable, common.FieldValue] ) -> bytes: """Takes a value of any type and tries to encode it with the proper encoder :param value: Value to encode :type value: :const:`pamqp.common.FieldArray` or :const:`pamqp.common.FieldTable` or :const:`pamqp.common.FieldValue` :raises TypeError: when the type of the value is not supported """ if isinstance(value, bool): return b't' + boolean(value) elif isinstance(value, int): return table_integer(value) elif isinstance(value, _decimal.Decimal): return b'D' + decimal(value) elif isinstance(value, float): return b'f' + floating_point(value) elif isinstance(value, str): return b'S' + long_string(value) elif isinstance(value, (datetime.datetime, time.struct_time)): return b'T' + timestamp(value) elif isinstance(value, dict): return b'F' + field_table(value) elif isinstance(value, list): return b'A' + field_array(value) elif isinstance(value, bytearray): return b'x' + byte_array(value) elif value is None: return b'V' raise TypeError('Unknown type: {} ({!r})'.format(type(value), value)) METHODS = { 'bytearray': byte_array, 'double': double, 'field_array': field_array, 'long': long_uint, 'longlong': long_long_int, 'longstr': long_string, 'octet': octet, 'short': short_uint, 'shortstr': short_string, 'table': field_table, 'timestamp': timestamp, 'void': lambda _: None, } pamqp-3.3.0/pamqp/exceptions.py000066400000000000000000000124641455032117300165140ustar00rootroot00000000000000# Auto-generated, do not edit this file. class PAMQPException(Exception): """Base exception for all pamqp specific exceptions.""" class UnmarshalingException(PAMQPException): """Raised when a frame is not able to be unmarshaled.""" def __str__(self) -> str: # pragma: nocover return 'Could not unmarshal {} frame: {}'.format( self.args[0], self.args[1]) class AMQPError(PAMQPException): """Base exception for all AMQP errors.""" class AMQPSoftError(AMQPError): """Base exception for all AMQP soft errors.""" class AMQPHardError(AMQPError): """Base exception for all AMQP hard errors.""" class AMQPContentTooLarge(AMQPSoftError): """ The client attempted to transfer content larger than the server could accept at the present time. The client may retry at a later time. """ name = 'CONTENT-TOO-LARGE' value = 311 class AMQPNoRoute(AMQPSoftError): """ Returned when RabbitMQ sends back with 'basic.return' when a 'mandatory' message cannot be delivered to any queue. """ name = 'NO-ROUTE' value = 312 class AMQPNoConsumers(AMQPSoftError): """ When the exchange cannot deliver to a consumer when the immediate flag is set. As a result of pending data on the queue or the absence of any consumers of the queue. """ name = 'NO-CONSUMERS' value = 313 class AMQPAccessRefused(AMQPSoftError): """ The client attempted to work with a server entity to which it has no access due to security settings. """ name = 'ACCESS-REFUSED' value = 403 class AMQPNotFound(AMQPSoftError): """ The client attempted to work with a server entity that does not exist. """ name = 'NOT-FOUND' value = 404 class AMQPResourceLocked(AMQPSoftError): """ The client attempted to work with a server entity to which it has no access because another client is working with it. """ name = 'RESOURCE-LOCKED' value = 405 class AMQPPreconditionFailed(AMQPSoftError): """ The client requested a method that was not allowed because some precondition failed. """ name = 'PRECONDITION-FAILED' value = 406 class AMQPConnectionForced(AMQPHardError): """ An operator intervened to close the connection for some reason. The client may retry at some later date. """ name = 'CONNECTION-FORCED' value = 320 class AMQPInvalidPath(AMQPHardError): """ The client tried to work with an unknown virtual host. """ name = 'INVALID-PATH' value = 402 class AMQPFrameError(AMQPHardError): """ The sender sent a malformed frame that the recipient could not decode. This strongly implies a programming error in the sending peer. """ name = 'FRAME-ERROR' value = 501 class AMQPSyntaxError(AMQPHardError): """ The sender sent a frame that contained illegal values for one or more fields. This strongly implies a programming error in the sending peer. """ name = 'SYNTAX-ERROR' value = 502 class AMQPCommandInvalid(AMQPHardError): """ 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. """ name = 'COMMAND-INVALID' value = 503 class AMQPChannelError(AMQPHardError): """ The client attempted to work with a channel that had not been correctly opened. This most likely indicates a fault in the client layer. """ name = 'CHANNEL-ERROR' value = 504 class AMQPUnexpectedFrame(AMQPHardError): """ 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. """ name = 'UNEXPECTED-FRAME' value = 505 class AMQPResourceError(AMQPHardError): """ 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. """ name = 'RESOURCE-ERROR' value = 506 class AMQPNotAllowed(AMQPHardError): """ 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. """ name = 'NOT-ALLOWED' value = 530 class AMQPNotImplemented(AMQPHardError): """ The client tried to use functionality that is not implemented in the server. """ name = 'NOT-IMPLEMENTED' value = 540 class AMQPInternalError(AMQPHardError): """ 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. """ name = 'INTERNAL-ERROR' value = 541 # AMQP Error code to class mapping CLASS_MAPPING = { 311: AMQPContentTooLarge, 312: AMQPNoRoute, 313: AMQPNoConsumers, 403: AMQPAccessRefused, 404: AMQPNotFound, 405: AMQPResourceLocked, 406: AMQPPreconditionFailed, 320: AMQPConnectionForced, 402: AMQPInvalidPath, 501: AMQPFrameError, 502: AMQPSyntaxError, 503: AMQPCommandInvalid, 504: AMQPChannelError, 505: AMQPUnexpectedFrame, 506: AMQPResourceError, 530: AMQPNotAllowed, 540: AMQPNotImplemented, 541: AMQPInternalError } pamqp-3.3.0/pamqp/frame.py000066400000000000000000000145611455032117300154250ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """Manage the marshaling and unmarshaling of AMQP frames unmarshal will turn a raw AMQP byte stream into the appropriate AMQP objects from the specification file. marshal will take an object created from the specification file and turn it into a raw byte stream. """ import logging import struct import typing from pamqp import (base, body, commands, common, constants, decode, exceptions, header, heartbeat) LOGGER = logging.getLogger(__name__) UNMARSHAL_FAILURE = 0, 0, None FrameTypes = typing.Union[base.Frame, body.ContentBody, header.ContentHeader, header.ProtocolHeader, heartbeat.Heartbeat] def marshal(frame_value: FrameTypes, channel_id: int) -> bytes: """Marshal a frame to be sent over the wire. :raises: ValueError """ if isinstance(frame_value, header.ProtocolHeader): return frame_value.marshal() elif isinstance(frame_value, base.Frame): return _marshal_method_frame(frame_value, channel_id) elif isinstance(frame_value, header.ContentHeader): return _marshal_content_header_frame(frame_value, channel_id) elif isinstance(frame_value, body.ContentBody): return _marshal_content_body_frame(frame_value, channel_id) elif isinstance(frame_value, heartbeat.Heartbeat): return frame_value.marshal() raise ValueError('Could not determine frame type: {}'.format(frame_value)) def unmarshal(data_in: bytes) -> typing.Tuple[int, int, FrameTypes]: """Takes in binary data and maps builds the appropriate frame type, returning a frame object. :returns: tuple of bytes consumed, channel, and a frame object :raises: exceptions.UnmarshalingException """ try: # Look to see if it's a protocol header frame value = _unmarshal_protocol_header_frame(data_in) except ValueError as error: raise exceptions.UnmarshalingException(header.ProtocolHeader, error) else: if value: return 8, 0, value frame_type, channel_id, frame_size = frame_parts(data_in) # Heartbeats do not have frame length indicators if frame_type == constants.FRAME_HEARTBEAT and frame_size == 0: return 8, channel_id, heartbeat.Heartbeat() if not frame_size: raise exceptions.UnmarshalingException('Unknown', 'No frame size') byte_count = constants.FRAME_HEADER_SIZE + frame_size + 1 if byte_count > len(data_in): raise exceptions.UnmarshalingException('Unknown', 'Not all data received') if data_in[byte_count - 1] != constants.FRAME_END: raise exceptions.UnmarshalingException('Unknown', 'Last byte error') frame_data = data_in[constants.FRAME_HEADER_SIZE:byte_count - 1] if frame_type == constants.FRAME_METHOD: return byte_count, channel_id, _unmarshal_method_frame(frame_data) elif frame_type == constants.FRAME_HEADER: return byte_count, channel_id, _unmarshal_header_frame(frame_data) elif frame_type == constants.FRAME_BODY: return byte_count, channel_id, _unmarshal_body_frame(frame_data) raise exceptions.UnmarshalingException( 'Unknown', 'Unknown frame type: {}'.format(frame_type)) def frame_parts(data: bytes) -> typing.Tuple[int, int, typing.Optional[int]]: """Attempt to decode a low-level frame, returning frame parts""" try: # Get the Frame Type, Channel Number and Frame Size return struct.unpack('>BHI', data[0:constants.FRAME_HEADER_SIZE]) except struct.error: # Did not receive a full frame return UNMARSHAL_FAILURE def _marshal(frame_type: int, channel_id: int, payload: bytes) -> bytes: """Marshal the low-level AMQ frame""" return b''.join([ struct.pack('>BHI', frame_type, channel_id, len(payload)), payload, constants.FRAME_END_CHAR ]) def _marshal_content_body_frame(value: body.ContentBody, channel_id: int) -> bytes: """Marshal as many content body frames as needed to transmit the content""" return _marshal(constants.FRAME_BODY, channel_id, value.marshal()) def _marshal_content_header_frame(value: header.ContentHeader, channel_id: int) -> bytes: """Marshal a content header frame""" return _marshal(constants.FRAME_HEADER, channel_id, value.marshal()) def _marshal_method_frame(value: base.Frame, channel_id: int) -> bytes: """Marshal a method frame""" return _marshal(constants.FRAME_METHOD, channel_id, common.Struct.integer.pack(value.index) + value.marshal()) def _unmarshal_protocol_header_frame(data_in: bytes) \ -> typing.Optional[header.ProtocolHeader]: """Attempt to unmarshal a protocol header frame The ProtocolHeader is abbreviated in size and functionality compared to the rest of the frame types, so return UNMARSHAL_ERROR doesn't apply as cleanly since we don't have all of the attributes to return even regardless of success or failure. :raises: ValueError """ if data_in[0:4] == constants.AMQP: # Do the first four bytes match? frame = header.ProtocolHeader() frame.unmarshal(data_in) return frame return None def _unmarshal_method_frame(frame_data: bytes) -> base.Frame: """Attempt to unmarshal a method frame :raises: pamqp.exceptions.UnmarshalingException """ bytes_used, method_index = decode.long_int(frame_data[0:4]) try: method = commands.INDEX_MAPPING[method_index]() except KeyError: raise exceptions.UnmarshalingException( 'Unknown', 'Unknown method index: {}'.format(str(method_index))) try: method.unmarshal(frame_data[bytes_used:]) except struct.error as error: raise exceptions.UnmarshalingException(method, error) return method def _unmarshal_header_frame(frame_data: bytes) -> header.ContentHeader: """Attempt to unmarshal a header frame :raises: pamqp.exceptions.UnmarshalingException """ content_header = header.ContentHeader() try: content_header.unmarshal(frame_data) except struct.error as error: raise exceptions.UnmarshalingException('ContentHeader', error) return content_header def _unmarshal_body_frame(frame_data: bytes) -> body.ContentBody: """Attempt to unmarshal a body frame""" content_body = body.ContentBody(b'') content_body.unmarshal(frame_data) return content_body pamqp-3.3.0/pamqp/header.py000066400000000000000000000077541455032117300155710ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ AMQP Header Class Definitions For encoding AMQP Header frames into binary AMQP stream data and decoding AMQP binary data into AMQP Header frames. """ import struct import typing from pamqp import commands, constants, decode BasicProperties = typing.Optional[commands.Basic.Properties] class ProtocolHeader: """Class that represents the AMQP Protocol Header""" name = 'ProtocolHeader' def __init__(self, major_version: int = constants.VERSION[0], minor_version: int = constants.VERSION[1], revision: int = constants.VERSION[2]): """Construct a Protocol Header frame object for the specified AMQP version. :param major_version: The AMQP major version (``0``) :param minor_version: The AMQP major version (``9``) :param revision: The AMQP major version (``1``) """ self.major_version = major_version self.minor_version = minor_version self.revision = revision def marshal(self) -> bytes: """Return the full AMQP wire protocol frame data representation of the ProtocolHeader frame. """ return constants.AMQP + struct.pack('BBBB', 0, self.major_version, self.minor_version, self.revision) def unmarshal(self, data: bytes) -> int: """Dynamically decode the frame data applying the values to the method object by iterating through the attributes in order and decoding them. :param data: The frame value to unpack :raises: ValueError """ try: (self.major_version, self.minor_version, self.revision) = struct.unpack('BBB', data[5:8]) except struct.error: raise ValueError( 'Could not unpack protocol header from {!r}'.format(data)) return 8 class ContentHeader: """Represent a content header frame A Content Header frame is received after a Basic.Deliver or Basic.GetOk frame and has the data and properties for the Content Body frames that follow. """ name = 'ContentHeader' def __init__(self, weight: int = 0, body_size: int = 0, properties: typing.Optional[BasicProperties] = None): """Initialize the Exchange.DeleteOk class Weight is unused and must be `0` :param weight: The unused content weight field :param body_size: The size in bytes of the message body :param properties: The message properties """ self.class_id = None self.weight = weight self.body_size = body_size self.properties = properties or commands.Basic.Properties() def marshal(self) -> bytes: """Return the AMQP binary encoded value of the frame""" return struct.pack('>HxxQ', commands.Basic.frame_id, self.body_size) + self.properties.marshal() def unmarshal(self, data: bytes) -> None: """Dynamically decode the frame data applying the values to the method object by iterating through the attributes in order and decoding them. :param data: The raw frame data to unmarshal """ self.class_id, self.weight, self.body_size = struct.unpack( '>HHQ', data[0:12]) offset, flags = self._get_flags(data[12:]) self.properties.unmarshal(flags, data[12 + offset:]) @staticmethod def _get_flags(data: bytes) -> typing.Tuple[int, int]: """Decode the flags from the data returning the bytes consumed and flags. """ bytes_consumed, flags, flagword_index = 0, 0, 0 while True: consumed, partial_flags = decode.short_int(data) bytes_consumed += consumed flags |= (partial_flags << (flagword_index * 16)) if not partial_flags & 1: # pragma: nocover break flagword_index += 1 # pragma: nocover return bytes_consumed, flags pamqp-3.3.0/pamqp/heartbeat.py000066400000000000000000000011401455032117300162570ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ AMQP Heartbeat Frame, used to create new Heartbeat frames for sending to a peer """ import struct from pamqp import constants class Heartbeat(object): """Heartbeat frame object mapping class. AMQP Heartbeat frames are mapped on to this class for a common access structure to the attributes/data values. """ name: str = 'Heartbeat' value = struct.pack('>BHI', constants.FRAME_HEARTBEAT, 0, 0) + \ constants.FRAME_END_CHAR @classmethod def marshal(cls) -> bytes: """Return the binary frame content""" return cls.value pamqp-3.3.0/pamqp/py.typed000066400000000000000000000000001455032117300154370ustar00rootroot00000000000000pamqp-3.3.0/setup.cfg000066400000000000000000000041651455032117300144630ustar00rootroot00000000000000[metadata] name = pamqp version = attr: pamqp.__version__ description = RabbitMQ Focused AMQP low-level library long_description = file: README.rst long_description_content_type = text/x-rst; charset=UTF-8 license = BSD 3-Clause License license-file = LICENSE home-page = https://github.com/gmr/pamqp project_urls = Bug Tracker = https://github.com/gmr/pamqp/issues Documentation = https://pamqp.readthedocs.io Source Code = https://github.com/gmr/pamqp/ author = Gavin M. Roy author_email = gavinmroy@gmail.com classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: BSD License Natural Language :: English Operating System :: OS Independent Programming Language :: Python :: 3 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3 :: Only Topic :: Communications Topic :: Internet Topic :: Software Development Typing :: Typed requires-dist = setuptools keywords = amqp rabbitmq [options] include_package_data = True packages = pamqp python_requires = >=3.7 zip_safe = true [options.extras_require] codegen = lxml requests yapf testing = coverage flake8 flake8-comprehensions flake8-deprecated flake8-import-order flake8-print flake8-quotes flake8-rst-docstrings flake8-tuple yapf [bdist_wheel] universal = 1 [build_sphinx] all-files = 1 [coverage:run] branch = True command_line = -m unittest discover tests --verbose data_file = build/.coverage [coverage:report] show_missing = True include = pamqp/* omit = tests/*.py [coverage:html] directory = build/coverage [coverage:xml] output = build/coverage.xml [flake8] application-import-names = pamqp exclude = bak,build,docs,env,tools import-order-style = google ignore = RST306 rst-directives = deprecated rst-roles = attr,class,const,data,exc,func,meth,mod,obj,py:class,py:mod pamqp-3.3.0/setup.py000066400000000000000000000000461455032117300143460ustar00rootroot00000000000000import setuptools setuptools.setup() pamqp-3.3.0/tests/000077500000000000000000000000001455032117300137765ustar00rootroot00000000000000pamqp-3.3.0/tests/test_command_argument_errors.py000066400000000000000000000223131455032117300223240ustar00rootroot00000000000000import unittest import uuid from pamqp import commands class ArgumentErrorsTestCase(unittest.TestCase): def test_basic_consume_queue_length(self): with self.assertRaises(ValueError): commands.Basic.Consume(queue=str.ljust('A', 257)) def test_basic_consume_queue_characters(self): with self.assertRaises(ValueError): commands.Basic.Consume(queue='*') def test_basic_consume_ticket(self): with self.assertRaises(ValueError): commands.Basic.Consume(ticket=46 & 2) # Just ahead of me def test_basic_deliver_exchange_length(self): with self.assertRaises(ValueError): commands.Basic.Deliver('ctag0', 1, False, str.ljust('A', 128), 'k') def test_basic_deliver_exchange_characters(self): with self.assertRaises(ValueError): commands.Basic.Deliver('ctag0', 1, False, '*', 'k') def test_basic_get_queue_length(self): with self.assertRaises(ValueError): commands.Basic.Get(queue=str.ljust('A', 257)) def test_basic_get_queue_characters(self): with self.assertRaises(ValueError): commands.Basic.Get(queue='*') def test_get_ticket(self): with self.assertRaises(ValueError): commands.Basic.Get(ticket=46 & 2) def test_basic_getempty_cluster_id(self): with self.assertRaises(ValueError): commands.Basic.GetEmpty(cluster_id='See my shadow changing') def test_basic_getok_exchange_length(self): with self.assertRaises(ValueError): commands.Basic.GetOk(1, False, str.ljust('A', 128), 'k', 0) def test_basic_getok_exchange_characters(self): with self.assertRaises(ValueError): commands.Basic.GetOk(1, False, '*', 'k', 0) def test_basic_properties_cluster_id(self): with self.assertRaises(ValueError): commands.Basic.Properties(cluster_id='See my shadow changing') def test_basic_publish_exchange_length(self): with self.assertRaises(ValueError): commands.Basic.Publish(exchange=str.ljust('A', 128)) def test_basic_publish_exchange_characters(self): with self.assertRaises(ValueError): commands.Basic.Publish(exchange='*') def test_basic_publish_ticket(self): with self.assertRaises(ValueError): commands.Basic.Publish(ticket=46 & 2) def test_basic_return_exchange_length(self): with self.assertRaises(ValueError): commands.Basic.Return(404, 'Not Found', str.ljust('A', 128), 'k') def test_basic_return_exchange_characters(self): with self.assertRaises(ValueError): commands.Basic.Return(404, 'Not Found', '*', 'k') def test_connection_open_vhost(self): with self.assertRaises(ValueError): commands.Connection.Open(str.ljust('A', 128)) def test_connection_open_capabilities(self): with self.assertRaises(ValueError): commands.Connection.Open(capabilities=str(uuid.uuid4())) def test_connection_open_insist(self): with self.assertRaises(ValueError): commands.Connection.Open(insist=True) def test_connection_openok_known_hosts(self): with self.assertRaises(ValueError): commands.Connection.OpenOk(known_hosts=str(uuid.uuid4())) def test_channel_open_out_of_band(self): with self.assertRaises(ValueError): commands.Channel.Open(out_of_band=str(uuid.uuid4())) def test_channel_openok_channel_id(self): with self.assertRaises(ValueError): commands.Channel.OpenOk(channel_id=str(uuid.uuid4())) def test_exchange_declare_ticket(self): with self.assertRaises(ValueError): commands.Exchange.Declare(ticket=46 & 2) # Just ahead of me def test_exchange_declare_exchange_length(self): with self.assertRaises(ValueError): commands.Exchange.Declare(exchange=str.ljust('A', 128)) def test_exchange_declare_exchange_characters(self): with self.assertRaises(ValueError): commands.Exchange.Declare(exchange='***') def test_exchange_delete_ticket(self): with self.assertRaises(ValueError): commands.Exchange.Delete(ticket=46 & 2) def test_exchange_delete_exchange_length(self): with self.assertRaises(ValueError): commands.Exchange.Delete(exchange=str.ljust('A', 128)) def test_exchange_delete_exchange_characters(self): with self.assertRaises(ValueError): commands.Exchange.Delete(exchange='***') def test_exchange_bind_ticket(self): with self.assertRaises(ValueError): commands.Exchange.Bind(ticket=46 & 2) def test_exchange_bind_destination_length(self): with self.assertRaises(ValueError): commands.Exchange.Bind(destination=''.rjust(32768, '*')) def test_exchange_bind_destination_pattern(self): with self.assertRaises(ValueError): commands.Exchange.Bind(destination='Fortysix & 2') def test_exchange_bind_source_length(self): with self.assertRaises(ValueError): commands.Exchange.Bind(source=''.rjust(32768, '*')) def test_exchange_bind_source_pattern(self): with self.assertRaises(ValueError): commands.Exchange.Bind(source='Fortysix & 2') def test_exchange_unbind_ticket(self): with self.assertRaises(ValueError): commands.Exchange.Unbind(ticket=46 & 2) def test_exchange_unbind_destination_length(self): with self.assertRaises(ValueError): commands.Exchange.Unbind(destination=''.rjust(32768, '*')) def test_exchange_unbind_destination_pattern(self): with self.assertRaises(ValueError): commands.Exchange.Unbind(destination='Fortysix & 2') def test_exchange_unbind_source_length(self): with self.assertRaises(ValueError): commands.Exchange.Unbind(source=''.rjust(32768, '*')) def test_exchange_unbind_source_pattern(self): with self.assertRaises(ValueError): commands.Exchange.Unbind(source='Fortysix & 2') def test_queue_declare_ticket(self): with self.assertRaises(ValueError): commands.Queue.Declare(ticket=46 & 2) def test_queue_declare_queue_length(self): with self.assertRaises(ValueError): commands.Queue.Declare(queue=str.ljust('A', 257)) def test_queue_declare_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.Declare(queue='***') def test_queue_declare_queue_with_valid_name(self): self.assertIsNotNone(commands.Queue.Declare(queue='foo')) def test_queue_declareok_queue_length(self): with self.assertRaises(ValueError): commands.Queue.DeclareOk(str.ljust('A', 257), 0, 0) def test_queue_declareok_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.DeclareOk('***', 0, 0) def test_queue_delete_ticket(self): with self.assertRaises(ValueError): commands.Queue.Delete(ticket=46 & 2) def test_queue_delete_queue_length(self): with self.assertRaises(ValueError): commands.Queue.Delete(queue=str.ljust('A', 257)) def test_queue_delete_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.Delete(queue='***') def test_queue_bind_ticket(self): with self.assertRaises(ValueError): commands.Queue.Bind(ticket=46 & 2) def test_queue_bind_queue_length(self): with self.assertRaises(ValueError): commands.Queue.Bind(queue=str.ljust('A', 257), exchange='B') def test_queue_bind_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.Bind(queue='***', exchange='B') def test_queue_bind_exchange_length(self): with self.assertRaises(ValueError): commands.Queue.Bind(exchange=str.ljust('A', 128), queue='B') def test_queue_bind_exchange_characters(self): with self.assertRaises(ValueError): commands.Queue.Bind(exchange='***', queue='B') def test_queue_unbind_ticket(self): with self.assertRaises(ValueError): commands.Queue.Unbind(ticket=46 & 2) def test_queue_unbind_queue_length(self): with self.assertRaises(ValueError): commands.Queue.Unbind(queue=str.ljust('A', 257), exchange='B') def test_queue_unbind_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.Unbind(queue='***', exchange='B') def test_queue_unbind_exchange_length(self): with self.assertRaises(ValueError): commands.Queue.Unbind(exchange=str.ljust('A', 128), queue='B') def test_queue_unbind_exchange_characters(self): with self.assertRaises(ValueError): commands.Queue.Unbind(exchange='***', queue='B') def test_queue_purge_ticket(self): with self.assertRaises(ValueError): commands.Queue.Purge(ticket=46 & 2) def test_queue_purge_queue_length(self): with self.assertRaises(ValueError): commands.Queue.Purge(queue=str.ljust('A', 257)) def test_queue_purge_queue_characters(self): with self.assertRaises(ValueError): commands.Queue.Purge(queue='***') pamqp-3.3.0/tests/test_commands.py000066400000000000000000001732461455032117300172250ustar00rootroot00000000000000import unittest from pamqp import commands class ArgumentTypeTests(unittest.TestCase): def test_basic_ack_has_delivery_tag(self): self.assertEqual(commands.Basic.Ack.amqp_type('delivery_tag'), 'longlong') def test_basic_ack_has_multiple(self): self.assertEqual(commands.Basic.Ack.amqp_type('multiple'), 'bit') def test_basic_cancel_has_consumer_tag(self): self.assertEqual(commands.Basic.Cancel.amqp_type('consumer_tag'), 'shortstr') def test_basic_cancel_has_nowait(self): self.assertEqual(commands.Basic.Cancel.amqp_type('nowait'), 'bit') def test_basic_cancelok_has_consumer_tag(self): self.assertEqual( commands.Basic.CancelOk.amqp_type('consumer_tag'), 'shortstr') def test_basic_consume_has_ticket(self): self.assertEqual(commands.Basic.Consume.amqp_type('ticket'), 'short') def test_basic_consume_has_queue(self): self.assertEqual(commands.Basic.Consume.amqp_type('queue'), 'shortstr') def test_basic_consume_has_consumer_tag(self): self.assertEqual(commands.Basic.Consume.amqp_type('consumer_tag'), 'shortstr') def test_basic_consume_has_no_local(self): self.assertEqual(commands.Basic.Consume.amqp_type('no_local'), 'bit') def test_basic_consume_has_no_ack(self): self.assertEqual(commands.Basic.Consume.amqp_type('no_ack'), 'bit') def test_basic_consume_has_exclusive(self): self.assertEqual(commands.Basic.Consume.amqp_type('exclusive'), 'bit') def test_basic_consume_has_nowait(self): self.assertEqual(commands.Basic.Consume.amqp_type('nowait'), 'bit') def test_basic_consume_has_arguments(self): self.assertEqual(commands.Basic.Consume.amqp_type('arguments'), 'table') def test_basic_consumeok_has_consumer_tag(self): self.assertEqual( commands.Basic.ConsumeOk.amqp_type('consumer_tag'), 'shortstr') def test_basic_deliver_has_consumer_tag(self): self.assertEqual(commands.Basic.Deliver.amqp_type('consumer_tag'), 'shortstr') def test_basic_deliver_has_delivery_tag(self): self.assertEqual(commands.Basic.Deliver.amqp_type('delivery_tag'), 'longlong') def test_basic_deliver_has_redelivered(self): self.assertEqual(commands.Basic.Deliver.amqp_type('redelivered'), 'bit') def test_basic_deliver_has_exchange(self): self.assertEqual(commands.Basic.Deliver.amqp_type('exchange'), 'shortstr') def test_basic_deliver_has_routing_key(self): self.assertEqual(commands.Basic.Deliver.amqp_type('routing_key'), 'shortstr') def test_basic_get_has_ticket(self): self.assertEqual(commands.Basic.Get.amqp_type('ticket'), 'short') def test_basic_get_has_queue(self): self.assertEqual(commands.Basic.Get.amqp_type('queue'), 'shortstr') def test_basic_get_has_no_ack(self): self.assertEqual(commands.Basic.Get.amqp_type('no_ack'), 'bit') def test_basic_getempty_has_cluster_id(self): self.assertEqual(commands.Basic.GetEmpty.amqp_type('cluster_id'), 'shortstr') def test_basic_getok_has_delivery_tag(self): self.assertEqual(commands.Basic.GetOk.amqp_type('delivery_tag'), 'longlong') def test_basic_getok_has_redelivered(self): self.assertEqual(commands.Basic.GetOk.amqp_type('redelivered'), 'bit') def test_basic_getok_has_exchange(self): self.assertEqual(commands.Basic.GetOk.amqp_type('exchange'), 'shortstr') def test_basic_getok_has_routing_key(self): self.assertEqual(commands.Basic.GetOk.amqp_type('routing_key'), 'shortstr') def test_basic_getok_has_message_count(self): self.assertEqual(commands.Basic.GetOk.amqp_type('message_count'), 'long') def test_basic_nack_has_delivery_tag(self): self.assertEqual(commands.Basic.Nack.amqp_type('delivery_tag'), 'longlong') def test_basic_nack_has_multiple(self): self.assertEqual(commands.Basic.Nack.amqp_type('multiple'), 'bit') def test_basic_nack_has_requeue(self): self.assertEqual(commands.Basic.Nack.amqp_type('requeue'), 'bit') def test_basic_publish_has_ticket(self): self.assertEqual(commands.Basic.Publish.amqp_type('ticket'), 'short') def test_basic_publish_has_exchange(self): self.assertEqual(commands.Basic.Publish.amqp_type('exchange'), 'shortstr') def test_basic_publish_has_routing_key(self): self.assertEqual(commands.Basic.Publish.amqp_type('routing_key'), 'shortstr') def test_basic_publish_has_mandatory(self): self.assertEqual(commands.Basic.Publish.amqp_type('mandatory'), 'bit') def test_basic_publish_has_immediate(self): self.assertEqual(commands.Basic.Publish.amqp_type('immediate'), 'bit') def test_basic_qos_has_prefetch_size(self): self.assertEqual(commands.Basic.Qos.amqp_type('prefetch_size'), 'long') def test_basic_qos_has_prefetch_count(self): self.assertEqual(commands.Basic.Qos.amqp_type('prefetch_count'), 'short') def test_basic_qos_has_global_(self): self.assertEqual(commands.Basic.Qos.amqp_type('global_'), 'bit') def test_basic_recover_has_requeue(self): self.assertEqual(commands.Basic.Recover.amqp_type('requeue'), 'bit') def test_basic_recoverasync_has_requeue(self): self.assertEqual(commands.Basic.RecoverAsync.amqp_type('requeue'), 'bit') def test_basic_reject_has_delivery_tag(self): self.assertEqual(commands.Basic.Reject.amqp_type('delivery_tag'), 'longlong') def test_basic_reject_has_requeue(self): self.assertEqual(commands.Basic.Reject.amqp_type('requeue'), 'bit') def test_basic_return_has_reply_code(self): self.assertEqual(commands.Basic.Return.amqp_type('reply_code'), 'short') def test_basic_return_has_reply_text(self): self.assertEqual(commands.Basic.Return.amqp_type('reply_text'), 'shortstr') def test_basic_return_has_exchange(self): self.assertEqual(commands.Basic.Return.amqp_type('exchange'), 'shortstr') def test_basic_return_has_routing_key(self): self.assertEqual(commands.Basic.Return.amqp_type('routing_key'), 'shortstr') def test_channel_close_has_reply_code(self): self.assertEqual(commands.Channel.Close.amqp_type('reply_code'), 'short') def test_channel_close_has_reply_text(self): self.assertEqual(commands.Channel.Close.amqp_type('reply_text'), 'shortstr') def test_channel_close_has_class_id(self): self.assertEqual(commands.Channel.Close.amqp_type('class_id'), 'short') def test_channel_close_has_method_id(self): self.assertEqual(commands.Channel.Close.amqp_type('method_id'), 'short') def test_channel_flow_has_active(self): self.assertEqual(commands.Channel.Flow.amqp_type('active'), 'bit') def test_channel_flowok_has_active(self): self.assertEqual(commands.Channel.FlowOk.amqp_type('active'), 'bit') def test_channel_open_has_out_of_band(self): self.assertEqual(commands.Channel.Open.amqp_type('out_of_band'), 'shortstr') def test_channel_openok_has_channel_id(self): self.assertEqual(commands.Channel.OpenOk.amqp_type('channel_id'), 'longstr') def test_confirm_select_has_nowait(self): self.assertEqual(commands.Confirm.Select.amqp_type('nowait'), 'bit') def test_connection_blocked_has_reason(self): self.assertEqual(commands.Connection.Blocked.amqp_type('reason'), 'shortstr') def test_connection_close_has_reply_code(self): self.assertEqual( commands.Connection.Close.amqp_type('reply_code'), 'short') def test_connection_close_has_reply_text(self): self.assertEqual( commands.Connection.Close.amqp_type('reply_text'), 'shortstr') def test_connection_close_has_class_id(self): self.assertEqual(commands.Connection.Close.amqp_type('class_id'), 'short') def test_connection_close_has_method_id(self): self.assertEqual(commands.Connection.Close.amqp_type('method_id'), 'short') def test_connection_open_has_virtual_host(self): self.assertEqual( commands.Connection.Open.amqp_type('virtual_host'), 'shortstr') def test_connection_open_has_capabilities(self): self.assertEqual( commands.Connection.Open.amqp_type('capabilities'), 'shortstr') def test_connection_open_has_insist(self): self.assertEqual(commands.Connection.Open.amqp_type('insist'), 'bit') def test_connection_openok_has_known_hosts(self): self.assertEqual( commands.Connection.OpenOk.amqp_type('known_hosts'), 'shortstr') def test_connection_secure_has_challenge(self): self.assertEqual( commands.Connection.Secure.amqp_type('challenge'), 'longstr') def test_connection_secureok_has_response(self): self.assertEqual( commands.Connection.SecureOk.amqp_type('response'), 'longstr') def test_connection_start_has_version_major(self): self.assertEqual( commands.Connection.Start.amqp_type('version_major'), 'octet') def test_connection_start_has_version_minor(self): self.assertEqual( commands.Connection.Start.amqp_type('version_minor'), 'octet') def test_connection_start_has_server_properties(self): self.assertEqual( commands.Connection.Start.amqp_type('server_properties'), 'table') def test_connection_start_has_mechanisms(self): self.assertEqual( commands.Connection.Start.amqp_type('mechanisms'), 'longstr') def test_connection_start_has_locales(self): self.assertEqual(commands.Connection.Start.amqp_type('locales'), 'longstr') def test_connection_startok_has_client_properties(self): self.assertEqual( commands.Connection.StartOk.amqp_type('client_properties'), 'table') def test_connection_startok_has_mechanism(self): self.assertEqual( commands.Connection.StartOk.amqp_type('mechanism'), 'shortstr') def test_connection_startok_has_response(self): self.assertEqual( commands.Connection.StartOk.amqp_type('response'), 'longstr') def test_connection_startok_has_locale(self): self.assertEqual(commands.Connection.StartOk.amqp_type('locale'), 'shortstr') def test_connection_update_secret(self): self.assertEqual( commands.Connection.UpdateSecret.amqp_type('new_secret'), 'longstr') def test_connection_tune_has_channel_max(self): self.assertEqual( commands.Connection.Tune.amqp_type('channel_max'), 'short') def test_connection_tune_has_frame_max(self): self.assertEqual(commands.Connection.Tune.amqp_type('frame_max'), 'long') def test_connection_tune_has_heartbeat(self): self.assertEqual(commands.Connection.Tune.amqp_type('heartbeat'), 'short') def test_connection_tuneok_has_channel_max(self): self.assertEqual( commands.Connection.TuneOk.amqp_type('channel_max'), 'short') def test_connection_tuneok_has_frame_max(self): self.assertEqual( commands.Connection.TuneOk.amqp_type('frame_max'), 'long') def test_connection_tuneok_has_heartbeat(self): self.assertEqual( commands.Connection.TuneOk.amqp_type('heartbeat'), 'short') def test_exchange_bind_has_ticket(self): self.assertEqual(commands.Exchange.Bind.amqp_type('ticket'), 'short') def test_exchange_bind_has_destination(self): self.assertEqual(commands.Exchange.Bind.amqp_type('destination'), 'shortstr') def test_exchange_bind_has_source(self): self.assertEqual(commands.Exchange.Bind.amqp_type('source'), 'shortstr') def test_exchange_bind_has_routing_key(self): self.assertEqual(commands.Exchange.Bind.amqp_type('routing_key'), 'shortstr') def test_exchange_bind_has_nowait(self): self.assertEqual(commands.Exchange.Bind.amqp_type('nowait'), 'bit') def test_exchange_bind_has_arguments(self): self.assertEqual(commands.Exchange.Bind.amqp_type('arguments'), 'table') def test_exchange_declare_has_ticket(self): self.assertEqual(commands.Exchange.Declare.amqp_type('ticket'), 'short') def test_exchange_declare_has_exchange(self): self.assertEqual(commands.Exchange.Declare.amqp_type('exchange'), 'shortstr') def test_exchange_declare_has_exchange_type(self): self.assertEqual( commands.Exchange.Declare.amqp_type('exchange_type'), 'shortstr') def test_exchange_declare_has_passive(self): self.assertEqual(commands.Exchange.Declare.amqp_type('passive'), 'bit') def test_exchange_declare_has_durable(self): self.assertEqual(commands.Exchange.Declare.amqp_type('durable'), 'bit') def test_exchange_declare_has_auto_delete(self): self.assertEqual( commands.Exchange.Declare.amqp_type('auto_delete'), 'bit') def test_exchange_declare_has_internal(self): self.assertEqual(commands.Exchange.Declare.amqp_type('internal'), 'bit') def test_exchange_declare_has_nowait(self): self.assertEqual(commands.Exchange.Declare.amqp_type('nowait'), 'bit') def test_exchange_declare_has_arguments(self): self.assertEqual(commands.Exchange.Declare.amqp_type('arguments'), 'table') def test_exchange_delete_has_ticket(self): self.assertEqual(commands.Exchange.Delete.amqp_type('ticket'), 'short') def test_exchange_delete_has_exchange(self): self.assertEqual(commands.Exchange.Delete.amqp_type('exchange'), 'shortstr') def test_exchange_delete_has_if_unused(self): self.assertEqual(commands.Exchange.Delete.amqp_type('if_unused'), 'bit') def test_exchange_delete_has_nowait(self): self.assertEqual(commands.Exchange.Delete.amqp_type('nowait'), 'bit') def test_exchange_unbind_has_ticket(self): self.assertEqual(commands.Exchange.Unbind.amqp_type('ticket'), 'short') def test_exchange_unbind_has_destination(self): self.assertEqual( commands.Exchange.Unbind.amqp_type('destination'), 'shortstr') def test_exchange_unbind_has_source(self): self.assertEqual(commands.Exchange.Unbind.amqp_type('source'), 'shortstr') def test_exchange_unbind_has_routing_key(self): self.assertEqual( commands.Exchange.Unbind.amqp_type('routing_key'), 'shortstr') def test_exchange_unbind_has_nowait(self): self.assertEqual(commands.Exchange.Unbind.amqp_type('nowait'), 'bit') def test_exchange_unbind_has_arguments(self): self.assertEqual(commands.Exchange.Unbind.amqp_type('arguments'), 'table') def test_queue_bind_has_ticket(self): self.assertEqual(commands.Queue.Bind.amqp_type('ticket'), 'short') def test_queue_bind_has_queue(self): self.assertEqual(commands.Queue.Bind.amqp_type('queue'), 'shortstr') def test_queue_bind_has_exchange(self): self.assertEqual(commands.Queue.Bind.amqp_type('exchange'), 'shortstr') def test_queue_bind_has_routing_key(self): self.assertEqual(commands.Queue.Bind.amqp_type('routing_key'), 'shortstr') def test_queue_bind_has_nowait(self): self.assertEqual(commands.Queue.Bind.amqp_type('nowait'), 'bit') def test_queue_bind_has_arguments(self): self.assertEqual(commands.Queue.Bind.amqp_type('arguments'), 'table') def test_queue_declare_has_ticket(self): self.assertEqual(commands.Queue.Declare.amqp_type('ticket'), 'short') def test_queue_declare_has_queue(self): self.assertEqual(commands.Queue.Declare.amqp_type('queue'), 'shortstr') def test_queue_declare_has_passive(self): self.assertEqual(commands.Queue.Declare.amqp_type('passive'), 'bit') def test_queue_declare_has_durable(self): self.assertEqual(commands.Queue.Declare.amqp_type('durable'), 'bit') def test_queue_declare_has_exclusive(self): self.assertEqual(commands.Queue.Declare.amqp_type('exclusive'), 'bit') def test_queue_declare_has_auto_delete(self): self.assertEqual(commands.Queue.Declare.amqp_type('auto_delete'), 'bit') def test_queue_declare_has_nowait(self): self.assertEqual(commands.Queue.Declare.amqp_type('nowait'), 'bit') def test_queue_declare_has_arguments(self): self.assertEqual(commands.Queue.Declare.amqp_type('arguments'), 'table') def test_queue_declareok_has_queue(self): self.assertEqual(commands.Queue.DeclareOk.amqp_type('queue'), 'shortstr') def test_queue_declareok_has_message_count(self): self.assertEqual( commands.Queue.DeclareOk.amqp_type('message_count'), 'long') def test_queue_declareok_has_consumer_count(self): self.assertEqual( commands.Queue.DeclareOk.amqp_type('consumer_count'), 'long') def test_queue_delete_has_ticket(self): self.assertEqual(commands.Queue.Delete.amqp_type('ticket'), 'short') def test_queue_delete_has_queue(self): self.assertEqual(commands.Queue.Delete.amqp_type('queue'), 'shortstr') def test_queue_delete_has_if_unused(self): self.assertEqual(commands.Queue.Delete.amqp_type('if_unused'), 'bit') def test_queue_delete_has_if_empty(self): self.assertEqual(commands.Queue.Delete.amqp_type('if_empty'), 'bit') def test_queue_delete_has_nowait(self): self.assertEqual(commands.Queue.Delete.amqp_type('nowait'), 'bit') def test_queue_deleteok_has_message_count(self): self.assertEqual( commands.Queue.DeleteOk.amqp_type('message_count'), 'long') def test_queue_purge_has_ticket(self): self.assertEqual(commands.Queue.Purge.amqp_type('ticket'), 'short') def test_queue_purge_has_queue(self): self.assertEqual(commands.Queue.Purge.amqp_type('queue'), 'shortstr') def test_queue_purge_has_nowait(self): self.assertEqual(commands.Queue.Purge.amqp_type('nowait'), 'bit') def test_queue_purgeok_has_message_count(self): self.assertEqual( commands.Queue.PurgeOk.amqp_type('message_count'), 'long') def test_queue_unbind_has_ticket(self): self.assertEqual(commands.Queue.Unbind.amqp_type('ticket'), 'short') def test_queue_unbind_has_queue(self): self.assertEqual(commands.Queue.Unbind.amqp_type('queue'), 'shortstr') def test_queue_unbind_has_exchange(self): self.assertEqual(commands.Queue.Unbind.amqp_type('exchange'), 'shortstr') def test_queue_unbind_has_routing_key(self): self.assertEqual(commands.Queue.Unbind.amqp_type('routing_key'), 'shortstr') def test_queue_unbind_has_arguments(self): self.assertEqual(commands.Queue.Unbind.amqp_type('arguments'), 'table') class AttributeInMethodTests(unittest.TestCase): def test_basic_ack_has_delivery_tag(self): self.assertIn('delivery_tag', commands.Basic.Ack()) def test_basic_ack_has_multiple(self): self.assertIn('multiple', commands.Basic.Ack()) def test_basic_cancel_has_consumer_tag(self): self.assertIn('consumer_tag', commands.Basic.Cancel('foo', False)) def test_basic_cancel_has_nowait(self): self.assertIn('nowait', commands.Basic.Cancel('foo', False)) def test_basic_cancelok_has_consumer_tag(self): self.assertIn('consumer_tag', commands.Basic.CancelOk('foo')) def test_basic_consume_has_ticket(self): self.assertIn('ticket', commands.Basic.Consume()) def test_basic_consume_has_queue(self): self.assertIn('queue', commands.Basic.Consume()) def test_basic_consume_has_consumer_tag(self): self.assertIn('consumer_tag', commands.Basic.Consume()) def test_basic_consume_has_no_local(self): self.assertIn('no_local', commands.Basic.Consume()) def test_basic_consume_has_no_ack(self): self.assertIn('no_ack', commands.Basic.Consume()) def test_basic_consume_has_exclusive(self): self.assertIn('exclusive', commands.Basic.Consume()) def test_basic_consume_has_nowait(self): self.assertIn('nowait', commands.Basic.Consume()) def test_basic_consume_has_arguments(self): self.assertIn('arguments', commands.Basic.Consume(0)) def test_basic_consumeok_has_consumer_tag(self): self.assertIn('consumer_tag', commands.Basic.ConsumeOk('foo')) def test_basic_deliver_has_consumer_tag(self): self.assertIn('consumer_tag', commands.Basic.Deliver( 'foo', 1, False, 'amq.direct', 'bar')) def test_basic_deliver_has_delivery_tag(self): self.assertIn('delivery_tag', commands.Basic.Deliver( 'foo', 1, False, 'amq.direct', 'bar')) def test_basic_deliver_has_redelivered(self): self.assertIn('redelivered', commands.Basic.Deliver( 'foo', 1, False, 'amq.direct', 'bar')) def test_basic_deliver_has_exchange(self): self.assertIn('exchange', commands.Basic.Deliver( 'foo', 1, False, 'amq.direct', 'bar')) def test_basic_deliver_has_routing_key(self): self.assertIn('routing_key', commands.Basic.Deliver( 'foo', 1, False, 'amq.direct', 'bar')) def test_basic_get_has_ticket(self): self.assertIn('ticket', commands.Basic.Get()) def test_basic_get_has_queue(self): self.assertIn('queue', commands.Basic.Get()) def test_basic_get_has_no_ack(self): self.assertIn('no_ack', commands.Basic.Get()) def test_basic_getempty_has_cluster_id(self): self.assertIn('cluster_id', commands.Basic.GetEmpty()) def test_basic_getok_has_delivery_tag(self): self.assertIn('delivery_tag', commands.Basic.GetOk( 0, False, 'amq.direct', 'foo', 1)) def test_basic_getok_has_redelivered(self): self.assertIn('redelivered', commands.Basic.GetOk( 0, False, 'amq.direct', 'foo', 1)) def test_basic_getok_has_exchange(self): self.assertIn('exchange', commands.Basic.GetOk( 0, False, 'amq.direct', 'foo', 1)) def test_basic_getok_has_routing_key(self): self.assertIn('routing_key', commands.Basic.GetOk( 0, False, 'amq.direct', 'foo', 1)) def test_basic_getok_has_message_count(self): self.assertIn('message_count', commands.Basic.GetOk( 0, False, 'amq.direct', 'foo', 1)) def test_basic_nack_has_delivery_tag(self): self.assertIn('delivery_tag', commands.Basic.Nack()) def test_basic_nack_has_multiple(self): self.assertIn('multiple', commands.Basic.Nack()) def test_basic_nack_has_requeue(self): self.assertIn('requeue', commands.Basic.Nack()) def test_basic_publish_has_ticket(self): self.assertIn('ticket', commands.Basic.Publish()) def test_basic_publish_has_exchange(self): self.assertIn('exchange', commands.Basic.Publish()) def test_basic_publish_has_routing_key(self): self.assertIn('routing_key', commands.Basic.Publish()) def test_basic_publish_has_mandatory(self): self.assertIn('mandatory', commands.Basic.Publish()) def test_basic_publish_has_immediate(self): self.assertIn('immediate', commands.Basic.Publish()) def test_basic_qos_has_prefetch_size(self): self.assertIn('prefetch_size', commands.Basic.Qos()) def test_basic_qos_has_prefetch_count(self): self.assertIn('prefetch_count', commands.Basic.Qos()) def test_basic_qos_has_global_(self): self.assertIn('global_', commands.Basic.Qos()) def test_basic_recover_has_requeue(self): self.assertIn('requeue', commands.Basic.Recover()) def test_basic_reject_has_delivery_tag(self): self.assertIn('delivery_tag', commands.Basic.Reject(1, True)) def test_basic_reject_has_requeue(self): self.assertIn('requeue', commands.Basic.Reject(1, True)) def test_basic_return_has_reply_code(self): self.assertIn('reply_code', commands.Basic.Return( 404, 'Not Found', 'amq.direct', 'foo')) def test_basic_return_has_reply_text(self): self.assertIn('reply_text', commands.Basic.Return( 404, 'Not Found', 'amq.direct', 'foo')) def test_basic_return_has_exchange(self): self.assertIn('exchange', commands.Basic.Return( 404, 'Not Found', 'amq.direct', 'foo')) def test_basic_return_has_routing_key(self): self.assertIn('routing_key', commands.Basic.Return( 404, 'Not Found', 'amq.direct', 'foo')) def test_channel_close_has_reply_code(self): self.assertIn('reply_code', commands.Channel.Close( 404, 'Not Found', 'amq.direct', 'foo')) def test_channel_close_has_reply_text(self): self.assertIn('reply_text', commands.Channel.Close( 404, 'Not Found', 'amq.direct', 'foo')) def test_channel_close_has_class_id(self): self.assertIn('class_id', commands.Channel.Close( 404, 'Not Found', 'amq.direct', 'foo')) def test_channel_close_has_method_id(self): self.assertIn('method_id', commands.Channel.Close( 404, 'Not Found', 'amq.direct', 'foo')) def test_channel_flow_has_active(self): self.assertIn('active', commands.Channel.Flow(True)) def test_channel_flowok_has_active(self): self.assertIn('active', commands.Channel.FlowOk(True)) def test_channel_open_has_out_of_band(self): self.assertIn('out_of_band', commands.Channel.Open()) def test_channel_openok_has_channel_id(self): self.assertIn('channel_id', commands.Channel.OpenOk()) def test_confirm_select_has_nowait(self): self.assertIn('nowait', commands.Confirm.Select()) def test_connection_blocked_has_reason(self): self.assertIn('reason', commands.Connection.Blocked()) def test_connection_close_has_reply_code(self): self.assertIn('reply_code', commands.Connection.Close( 200, 'Client Request', 0, 0)) def test_connection_close_has_reply_text(self): self.assertIn('reply_text', commands.Connection.Close( 200, 'Client Request', 0, 0)) def test_connection_close_has_class_id(self): self.assertIn('class_id', commands.Connection.Close( 200, 'Client Request', 0, 0)) def test_connection_close_has_method_id(self): self.assertIn('method_id', commands.Connection.Close( 200, 'Client Request', 0, 0)) def test_connection_open_has_virtual_host(self): self.assertIn('virtual_host', commands.Connection.Open()) def test_connection_open_has_capabilities(self): self.assertIn('capabilities', commands.Connection.Open()) def test_connection_open_has_insist(self): self.assertIn('insist', commands.Connection.Open()) def test_connection_openok_has_known_hosts(self): self.assertIn('known_hosts', commands.Connection.OpenOk()) def test_connection_secure_has_challenge(self): self.assertIn('challenge', commands.Connection.Secure('foo')) def test_connection_secureok_has_response(self): self.assertIn('response', commands.Connection.SecureOk('bar')) def test_connection_start_has_version_major(self): self.assertIn('version_major', commands.Connection.Start()) def test_connection_start_has_version_minor(self): self.assertIn('version_minor', commands.Connection.Start()) def test_connection_start_has_server_properties(self): self.assertIn('server_properties', commands.Connection.Start()) def test_connection_start_has_mechanisms(self): self.assertIn('mechanisms', commands.Connection.Start()) def test_connection_start_has_locales(self): self.assertIn('locales', commands.Connection.Start()) def test_connection_startok_has_mechanism(self): self.assertIn('mechanism', commands.Connection.StartOk()) def test_connection_startok_has_response(self): self.assertIn('response', commands.Connection.StartOk()) def test_connection_startok_has_locale(self): self.assertIn('locale', commands.Connection.StartOk()) def test_connection_tune_has_channel_max(self): self.assertIn('channel_max', commands.Connection.Tune()) def test_connection_tune_has_frame_max(self): self.assertIn('frame_max', commands.Connection.Tune()) def test_connection_tune_has_heartbeat(self): self.assertIn('heartbeat', commands.Connection.Tune()) def test_connection_tuneok_has_channel_max(self): self.assertIn('channel_max', commands.Connection.TuneOk()) def test_connection_tuneok_has_frame_max(self): self.assertIn('frame_max', commands.Connection.TuneOk()) def test_connection_tuneok_has_heartbeat(self): self.assertIn('heartbeat', commands.Connection.TuneOk()) def test_exchange_bind_has_ticket(self): self.assertIn('ticket', commands.Exchange.Bind()) def test_exchange_bind_has_destination(self): self.assertIn('destination', commands.Exchange.Bind()) def test_exchange_bind_has_source(self): self.assertIn('source', commands.Exchange.Bind()) def test_exchange_bind_has_routing_key(self): self.assertIn('routing_key', commands.Exchange.Bind()) def test_exchange_bind_has_nowait(self): self.assertIn('nowait', commands.Exchange.Bind()) def test_exchange_bind_has_arguments(self): self.assertIn('arguments', commands.Exchange.Bind()) def test_exchange_declare_has_ticket(self): self.assertIn('ticket', commands.Exchange.Declare()) def test_exchange_declare_has_exchange(self): self.assertIn('exchange', commands.Exchange.Declare()) def test_exchange_declare_has_exchange_type(self): self.assertIn('exchange_type', commands.Exchange.Declare()) def test_exchange_declare_has_passive(self): self.assertIn('passive', commands.Exchange.Declare()) def test_exchange_declare_has_durable(self): self.assertIn('durable', commands.Exchange.Declare()) def test_exchange_declare_has_auto_delete(self): self.assertIn('auto_delete', commands.Exchange.Declare()) def test_exchange_declare_has_internal(self): self.assertIn('internal', commands.Exchange.Declare()) def test_exchange_declare_has_nowait(self): self.assertIn('nowait', commands.Exchange.Declare()) def test_exchange_declare_has_arguments(self): self.assertIn('arguments', commands.Exchange.Declare()) def test_exchange_delete_has_ticket(self): self.assertIn('ticket', commands.Exchange.Delete()) def test_exchange_delete_has_exchange(self): self.assertIn('exchange', commands.Exchange.Delete()) def test_exchange_delete_has_if_unused(self): self.assertIn('if_unused', commands.Exchange.Delete()) def test_exchange_delete_has_nowait(self): self.assertIn('nowait', commands.Exchange.Delete()) def test_exchange_unbind_has_ticket(self): self.assertIn('ticket', commands.Exchange.Unbind()) def test_exchange_unbind_has_destination(self): self.assertIn('destination', commands.Exchange.Unbind()) def test_exchange_unbind_has_source(self): self.assertIn('source', commands.Exchange.Unbind()) def test_exchange_unbind_has_routing_key(self): self.assertIn('routing_key', commands.Exchange.Unbind()) def test_exchange_unbind_has_nowait(self): self.assertIn('nowait', commands.Exchange.Unbind()) def test_exchange_unbind_has_arguments(self): self.assertIn('arguments', commands.Exchange.Unbind()) def test_queue_bind_has_ticket(self): self.assertIn('ticket', commands.Queue.Bind()) def test_queue_bind_has_queue(self): self.assertIn('queue', commands.Queue.Bind()) def test_queue_bind_has_exchange(self): self.assertIn('exchange', commands.Queue.Bind()) def test_queue_bind_has_routing_key(self): self.assertIn('routing_key', commands.Queue.Bind()) def test_queue_bind_has_nowait(self): self.assertIn('nowait', commands.Queue.Bind()) def test_queue_bind_has_arguments(self): self.assertIn('arguments', commands.Queue.Bind()) def test_queue_declare_has_ticket(self): self.assertIn('ticket', commands.Queue.Declare()) def test_queue_declare_has_queue(self): self.assertIn('queue', commands.Queue.Declare()) def test_queue_declare_has_passive(self): self.assertIn('passive', commands.Queue.Declare()) def test_queue_declare_has_durable(self): self.assertIn('durable', commands.Queue.Declare()) def test_queue_declare_has_exclusive(self): self.assertIn('exclusive', commands.Queue.Declare()) def test_queue_declare_has_auto_delete(self): self.assertIn('auto_delete', commands.Queue.Declare()) def test_queue_declare_has_nowait(self): self.assertIn('nowait', commands.Queue.Declare()) def test_queue_declare_has_arguments(self): self.assertIn('arguments', commands.Queue.Declare()) def test_queue_declareok_has_queue(self): self.assertIn('queue', commands.Queue.DeclareOk('foo', 0, 0)) def test_queue_declareok_has_message_count(self): self.assertIn('message_count', commands.Queue.DeclareOk('foo', 0, 0)) def test_queue_declareok_has_consumer_count(self): self.assertIn('consumer_count', commands.Queue.DeclareOk('foo', 0, 0)) def test_queue_delete_has_ticket(self): self.assertIn('ticket', commands.Queue.Delete()) def test_queue_delete_has_queue(self): self.assertIn('queue', commands.Queue.Delete()) def test_queue_delete_has_if_unused(self): self.assertIn('if_unused', commands.Queue.Delete()) def test_queue_delete_has_if_empty(self): self.assertIn('if_empty', commands.Queue.Delete()) def test_queue_delete_has_nowait(self): self.assertIn('nowait', commands.Queue.Delete()) def test_queue_deleteok_has_message_count(self): self.assertIn('message_count', commands.Queue.DeleteOk(0)) def test_queue_purge_has_ticket(self): self.assertIn('ticket', commands.Queue.Purge()) def test_queue_purge_has_queue(self): self.assertIn('queue', commands.Queue.Purge()) def test_queue_purge_has_nowait(self): self.assertIn('nowait', commands.Queue.Purge()) def test_queue_purgeok_has_message_count(self): self.assertIn('message_count', commands.Queue.PurgeOk(0)) def test_queue_unbind_has_ticket(self): self.assertIn('ticket', commands.Queue.Unbind()) def test_queue_unbind_has_queue(self): self.assertIn('queue', commands.Queue.Unbind()) def test_queue_unbind_has_exchange(self): self.assertIn('exchange', commands.Queue.Unbind()) def test_queue_unbind_has_routing_key(self): self.assertIn('routing_key', commands.Queue.Unbind()) def test_queue_unbind_has_arguments(self): self.assertIn('arguments', commands.Queue.Unbind()) def test_connection_update_secret_has_new_secret(self): self.assertIn('new_secret', commands.Connection.UpdateSecret( 'foo', 'bar')) def test_connection_update_secret_has_reason(self): self.assertIn('reason', commands.Connection.UpdateSecret('foo', 'bar')) class DeprecationWarningTests(unittest.TestCase): def test_basic_recoverasync_raises_deprecation_error(self): with self.assertWarns(DeprecationWarning): commands.Basic.RecoverAsync() class BasicPropertiesTests(unittest.TestCase): def test_basic_properties_has_content_type(self): self.assertEqual( commands.Basic.Properties.amqp_type('content_type'), 'shortstr') def test_basic_properties_has_content_encoding(self): self.assertEqual( commands.Basic.Properties.amqp_type('content_encoding'), 'shortstr') def test_basic_properties_has_headers(self): self.assertEqual(commands.Basic.Properties.amqp_type('headers'), 'table') def test_basic_properties_has_delivery_mode(self): self.assertEqual( commands.Basic.Properties.amqp_type('delivery_mode'), 'octet') def test_basic_properties_has_priority(self): self.assertEqual(commands.Basic.Properties.amqp_type('priority'), 'octet') def test_basic_properties_has_correlation_id(self): self.assertEqual( commands.Basic.Properties.amqp_type('correlation_id'), 'shortstr') def test_basic_properties_has_reply_to(self): self.assertEqual(commands.Basic.Properties.amqp_type('reply_to'), 'shortstr') def test_basic_properties_has_expiration(self): self.assertEqual( commands.Basic.Properties.amqp_type('expiration'), 'shortstr') def test_basic_properties_has_message_id(self): self.assertEqual( commands.Basic.Properties.amqp_type('message_id'), 'shortstr') def test_basic_properties_has_timestamp(self): self.assertEqual(commands.Basic.Properties.amqp_type('timestamp'), 'timestamp') def test_basic_properties_has_message_type(self): self.assertEqual( commands.Basic.Properties.amqp_type('message_type'), 'shortstr') def test_basic_properties_has_user_id(self): self.assertEqual(commands.Basic.Properties.amqp_type('user_id'), 'shortstr') def test_basic_properties_has_app_id(self): self.assertEqual(commands.Basic.Properties.amqp_type('app_id'), 'shortstr') def test_basic_properties_has_cluster_id(self): self.assertEqual( commands.Basic.Properties.amqp_type('cluster_id'), 'shortstr') class MethodAttributeLengthTests(unittest.TestCase): def test_basic_ack_attribute_count(self): self.assertEqual(len(commands.Basic.Ack()), 2) def test_basic_cancel_attribute_count(self): self.assertEqual(len(commands.Basic.Cancel('ctag0', False)), 2) def test_basic_cancelok_attribute_count(self): self.assertEqual(len(commands.Basic.CancelOk('ctag0')), 1) def test_basic_consume_attribute_count(self): self.assertEqual(len(commands.Basic.Consume()), 8) def test_basic_consumeok_attribute_count(self): self.assertEqual(len(commands.Basic.ConsumeOk('ctag0')), 1) def test_basic_deliver_attribute_count(self): self.assertEqual(len(commands.Basic.Deliver( 'ctag0', 1, False, 'amq.direct', 'foo')), 5) def test_basic_get_attribute_count(self): self.assertEqual(len(commands.Basic.Get()), 3) def test_basic_getempty_attribute_count(self): self.assertEqual(len(commands.Basic.GetEmpty()), 1) def test_basic_getok_attribute_count(self): self.assertEqual(len(commands.Basic.GetOk( 1, False, 'amq.direct', 'foo', 0)), 5) def test_basic_nack_attribute_count(self): self.assertEqual(len(commands.Basic.Nack()), 3) def test_basic_publish_attribute_count(self): self.assertEqual(len(commands.Basic.Publish()), 5) def test_basic_qos_attribute_count(self): self.assertEqual(len(commands.Basic.Qos()), 3) def test_basic_qosok_attribute_count(self): self.assertEqual(len(commands.Basic.QosOk()), 0) def test_basic_recover_attribute_count(self): self.assertEqual(len(commands.Basic.Recover()), 1) def test_basic_recoverok_attribute_count(self): self.assertEqual(len(commands.Basic.RecoverOk()), 0) def test_basic_reject_attribute_count(self): self.assertEqual(len(commands.Basic.Reject(1, False)), 2) def test_basic_return_attribute_count(self): self.assertEqual(len(commands.Basic.Return( 404, 'Not Found', 'amq.direct', 'foo')), 4) def test_channel_close_attribute_count(self): self.assertEqual(len(commands.Channel.Close( 200, 'Requested', 0, 0)), 4) def test_channel_closeok_attribute_count(self): self.assertEqual(len(commands.Channel.CloseOk()), 0) def test_channel_flow_attribute_count(self): self.assertEqual(len(commands.Channel.Flow(True)), 1) def test_channel_flowok_attribute_count(self): self.assertEqual(len(commands.Channel.FlowOk(False)), 1) def test_channel_open_attribute_count(self): self.assertEqual(len(commands.Channel.Open()), 1) def test_channel_openok_attribute_count(self): self.assertEqual(len(commands.Channel.OpenOk()), 1) def test_confirm_select_attribute_count(self): self.assertEqual(len(commands.Confirm.Select()), 1) def test_confirm_selectok_attribute_count(self): self.assertEqual(len(commands.Confirm.SelectOk()), 0) def test_connection_blocked_attribute_count(self): self.assertEqual(len(commands.Connection.Blocked()), 1) def test_connection_close_attribute_count(self): self.assertEqual(len(commands.Connection.Close( 200, 'Requested', 0, 0)), 4) def test_connection_closeok_attribute_count(self): self.assertEqual(len(commands.Connection.CloseOk()), 0) def test_connection_open_attribute_count(self): self.assertEqual(len(commands.Connection.Open()), 3) def test_connection_openok_attribute_count(self): self.assertEqual(len(commands.Connection.OpenOk()), 1) def test_connection_secure_attribute_count(self): self.assertEqual(len(commands.Connection.Secure('foo')), 1) def test_connection_secureok_attribute_count(self): self.assertEqual(len(commands.Connection.SecureOk('bar')), 1) def test_connection_start_attribute_count(self): self.assertEqual(len(commands.Connection.Start()), 5) def test_connection_startok_attribute_count(self): self.assertEqual(len(commands.Connection.StartOk()), 4) def test_connection_tune_attribute_count(self): self.assertEqual(len(commands.Connection.Tune()), 3) def test_connection_tuneok_attribute_count(self): self.assertEqual(len(commands.Connection.TuneOk()), 3) def test_connection_unblocked_attribute_count(self): self.assertEqual(len(commands.Connection.Unblocked()), 0) def test_exchange_bind_attribute_count(self): self.assertEqual(len(commands.Exchange.Bind()), 6) def test_exchange_bindok_attribute_count(self): self.assertEqual(len(commands.Exchange.BindOk()), 0) def test_exchange_declare_attribute_count(self): self.assertEqual(len(commands.Exchange.Declare()), 9) def test_exchange_declareok_attribute_count(self): self.assertEqual(len(commands.Exchange.DeclareOk()), 0) def test_exchange_delete_attribute_count(self): self.assertEqual(len(commands.Exchange.Delete()), 4) def test_exchange_deleteok_attribute_count(self): self.assertEqual(len(commands.Exchange.DeleteOk()), 0) def test_exchange_unbind_attribute_count(self): self.assertEqual(len(commands.Exchange.Unbind()), 6) def test_exchange_unbindok_attribute_count(self): self.assertEqual(len(commands.Exchange.UnbindOk()), 0) def test_queue_bind_attribute_count(self): self.assertEqual(len(commands.Queue.Bind()), 6) def test_queue_bindok_attribute_count(self): self.assertEqual(len(commands.Queue.BindOk()), 0) def test_queue_declare_attribute_count(self): self.assertEqual(len(commands.Queue.Declare()), 8) def test_queue_declareok_attribute_count(self): self.assertEqual(len(commands.Queue.DeclareOk('foo', 0, 0)), 3) def test_queue_delete_attribute_count(self): self.assertEqual(len(commands.Queue.Delete()), 5) def test_queue_deleteok_attribute_count(self): self.assertEqual(len(commands.Queue.DeleteOk(0)), 1) def test_queue_purge_attribute_count(self): self.assertEqual(len(commands.Queue.Purge()), 3) def test_queue_purgeok_attribute_count(self): self.assertEqual(len(commands.Queue.PurgeOk(0)), 1) def test_queue_unbind_attribute_count(self): self.assertEqual(len(commands.Queue.Unbind()), 5) def test_queue_unbindok_attribute_count(self): self.assertEqual(len(commands.Queue.UnbindOk()), 0) def test_tx_commit_attribute_count(self): self.assertEqual(len(commands.Tx.Commit()), 0) def test_tx_commitok_attribute_count(self): self.assertEqual(len(commands.Tx.CommitOk()), 0) def test_tx_rollback_attribute_count(self): self.assertEqual(len(commands.Tx.Rollback()), 0) def test_tx_rollbackok_attribute_count(self): self.assertEqual(len(commands.Tx.RollbackOk()), 0) def test_tx_select_attribute_count(self): self.assertEqual(len(commands.Tx.Select()), 0) def test_tx_selectok_attribute_count(self): self.assertEqual(len(commands.Tx.SelectOk()), 0) class MethodAttributeDefaultTests(unittest.TestCase): def test_basic_ack_default_for_delivery_tag(self): obj = commands.Basic.Ack() self.assertEqual(obj['delivery_tag'], 0) def test_basic_ack_default_for_multiple(self): obj = commands.Basic.Ack() self.assertEqual(obj['multiple'], False) def test_basic_consume_default_for_ticket(self): obj = commands.Basic.Consume() self.assertEqual(obj['ticket'], 0) def test_basic_consume_default_for_queue(self): obj = commands.Basic.Consume() self.assertEqual(obj['queue'], '') def test_basic_consume_default_for_consumer_tag(self): obj = commands.Basic.Consume() self.assertEqual(obj['consumer_tag'], '') def test_basic_consume_default_for_no_local(self): obj = commands.Basic.Consume() self.assertEqual(obj['no_local'], False) def test_basic_consume_default_for_no_ack(self): obj = commands.Basic.Consume() self.assertEqual(obj['no_ack'], False) def test_basic_consume_default_for_exclusive(self): obj = commands.Basic.Consume() self.assertEqual(obj['exclusive'], False) def test_basic_consume_default_for_nowait(self): obj = commands.Basic.Consume() self.assertEqual(obj['nowait'], False) def test_basic_consume_default_for_arguments(self): obj = commands.Basic.Consume() self.assertDictEqual(obj['arguments'], {}) def test_basic_get_default_for_ticket(self): obj = commands.Basic.Get() self.assertEqual(obj['ticket'], 0) def test_basic_get_default_for_queue(self): obj = commands.Basic.Get() self.assertEqual(obj['queue'], '') def test_basic_get_default_for_no_ack(self): obj = commands.Basic.Get() self.assertEqual(obj['no_ack'], False) def test_basic_getempty_default_for_cluster_id(self): obj = commands.Basic.GetEmpty() self.assertEqual(obj['cluster_id'], '') def test_basic_nack_default_for_delivery_tag(self): obj = commands.Basic.Nack() self.assertEqual(obj['delivery_tag'], 0) def test_basic_nack_default_for_multiple(self): obj = commands.Basic.Nack() self.assertEqual(obj['multiple'], False) def test_basic_nack_default_for_requeue(self): obj = commands.Basic.Nack() self.assertEqual(obj['requeue'], True) def test_basic_publish_default_for_ticket(self): obj = commands.Basic.Publish() self.assertEqual(obj['ticket'], 0) def test_basic_publish_default_for_exchange(self): obj = commands.Basic.Publish() self.assertEqual(obj['exchange'], '') def test_basic_publish_default_for_routing_key(self): obj = commands.Basic.Publish() self.assertEqual(obj['routing_key'], '') def test_basic_publish_default_for_mandatory(self): obj = commands.Basic.Publish() self.assertEqual(obj['mandatory'], False) def test_basic_publish_default_for_immediate(self): obj = commands.Basic.Publish() self.assertEqual(obj['immediate'], False) def test_basic_qos_default_for_prefetch_size(self): obj = commands.Basic.Qos() self.assertEqual(obj['prefetch_size'], 0) def test_basic_qos_default_for_prefetch_count(self): obj = commands.Basic.Qos() self.assertEqual(obj['prefetch_count'], 0) def test_basic_qos_default_for_globally(self): obj = commands.Basic.Qos() self.assertEqual(obj['global_'], False) def test_basic_recover_default_for_requeue(self): obj = commands.Basic.Recover() self.assertEqual(obj['requeue'], False) def test_channel_open_default_for_out_of_band(self): obj = commands.Channel.Open() self.assertEqual(obj['out_of_band'], '0') def test_channel_openok_default_for_channel_id(self): obj = commands.Channel.OpenOk() self.assertEqual(obj['channel_id'], '0') def test_confirm_select_default_for_nowait(self): obj = commands.Confirm.Select() self.assertEqual(obj['nowait'], False) def test_connection_blocked_default_for_reason(self): obj = commands.Connection.Blocked() self.assertEqual(obj['reason'], '') def test_connection_open_default_for_virtual_host(self): obj = commands.Connection.Open() self.assertEqual(obj['virtual_host'], '/') def test_connection_open_default_for_capabilities(self): obj = commands.Connection.Open() self.assertEqual(obj['capabilities'], '') def test_connection_open_default_for_insist(self): obj = commands.Connection.Open() self.assertEqual(obj['insist'], False) def test_connection_openok_default_for_known_hosts(self): obj = commands.Connection.OpenOk() self.assertEqual(obj['known_hosts'], '') def test_connection_start_default_for_version_major(self): obj = commands.Connection.Start() self.assertEqual(obj['version_major'], 0) def test_connection_start_default_for_version_minor(self): obj = commands.Connection.Start() self.assertEqual(obj['version_minor'], 9) def test_connection_start_default_for_mechanisms(self): obj = commands.Connection.Start() self.assertEqual(obj['mechanisms'], 'PLAIN') def test_connection_start_default_for_locales(self): obj = commands.Connection.Start() self.assertEqual(obj['locales'], 'en_US') def test_connection_startok_default_for_mechanism(self): obj = commands.Connection.StartOk() self.assertEqual(obj['mechanism'], 'PLAIN') def test_connection_startok_default_for_response(self): obj = commands.Connection.StartOk() self.assertEqual(obj['response'], '') def test_connection_startok_default_for_locale(self): obj = commands.Connection.StartOk() self.assertEqual(obj['locale'], 'en_US') def test_connection_tune_default_for_channel_max(self): obj = commands.Connection.Tune() self.assertEqual(obj['channel_max'], 0) def test_connection_tune_default_for_frame_max(self): obj = commands.Connection.Tune() self.assertEqual(obj['frame_max'], 0) def test_connection_tune_default_for_heartbeat(self): obj = commands.Connection.Tune() self.assertEqual(obj['heartbeat'], 0) def test_connection_tuneok_default_for_channel_max(self): obj = commands.Connection.TuneOk() self.assertEqual(obj['channel_max'], 0) def test_connection_tuneok_default_for_frame_max(self): obj = commands.Connection.TuneOk() self.assertEqual(obj['frame_max'], 0) def test_connection_tuneok_default_for_heartbeat(self): obj = commands.Connection.TuneOk() self.assertEqual(obj['heartbeat'], 0) def test_exchange_bind_default_for_ticket(self): obj = commands.Exchange.Bind() self.assertEqual(obj['ticket'], 0) def test_exchange_bind_default_for_destination(self): obj = commands.Exchange.Bind() self.assertEqual(obj['destination'], '') def test_exchange_bind_default_for_source(self): obj = commands.Exchange.Bind() self.assertEqual(obj['source'], '') def test_exchange_bind_default_for_routing_key(self): obj = commands.Exchange.Bind() self.assertEqual(obj['routing_key'], '') def test_exchange_bind_default_for_nowait(self): obj = commands.Exchange.Bind() self.assertEqual(obj['nowait'], False) def test_exchange_bind_default_for_arguments(self): obj = commands.Exchange.Bind() self.assertDictEqual(obj['arguments'], {}) def test_exchange_declare_default_for_ticket(self): obj = commands.Exchange.Declare() self.assertEqual(obj['ticket'], 0) def test_exchange_declare_default_for_exchange(self): obj = commands.Exchange.Declare() self.assertEqual(obj['exchange'], '') def test_exchange_declare_default_for_exchange_type(self): obj = commands.Exchange.Declare() self.assertEqual(obj['exchange_type'], 'direct') def test_exchange_declare_default_for_passive(self): obj = commands.Exchange.Declare() self.assertEqual(obj['passive'], False) def test_exchange_declare_default_for_durable(self): obj = commands.Exchange.Declare() self.assertEqual(obj['durable'], False) def test_exchange_declare_default_for_auto_delete(self): obj = commands.Exchange.Declare() self.assertEqual(obj['auto_delete'], False) def test_exchange_declare_default_for_internal(self): obj = commands.Exchange.Declare() self.assertEqual(obj['internal'], False) def test_exchange_declare_default_for_nowait(self): obj = commands.Exchange.Declare() self.assertEqual(obj['nowait'], False) def test_exchange_declare_default_for_arguments(self): obj = commands.Exchange.Declare() self.assertDictEqual(obj['arguments'], {}) def test_exchange_delete_default_for_ticket(self): obj = commands.Exchange.Delete() self.assertEqual(obj['ticket'], 0) def test_exchange_delete_default_for_exchange(self): obj = commands.Exchange.Delete() self.assertEqual(obj['exchange'], '') def test_exchange_delete_default_for_if_unused(self): obj = commands.Exchange.Delete() self.assertEqual(obj['if_unused'], False) def test_exchange_delete_default_for_nowait(self): obj = commands.Exchange.Delete() self.assertEqual(obj['nowait'], False) def test_exchange_unbind_default_for_ticket(self): obj = commands.Exchange.Unbind() self.assertEqual(obj['ticket'], 0) def test_exchange_unbind_default_for_destination(self): obj = commands.Exchange.Unbind() self.assertEqual(obj['destination'], '') def test_exchange_unbind_default_for_source(self): obj = commands.Exchange.Unbind() self.assertEqual(obj['source'], '') def test_exchange_unbind_default_for_routing_key(self): obj = commands.Exchange.Unbind() self.assertEqual(obj['routing_key'], '') def test_exchange_unbind_default_for_nowait(self): obj = commands.Exchange.Unbind() self.assertEqual(obj['nowait'], False) def test_exchange_unbind_default_for_arguments(self): obj = commands.Exchange.Unbind() self.assertDictEqual(obj['arguments'], {}) def test_queue_bind_default_for_ticket(self): obj = commands.Queue.Bind() self.assertEqual(obj['ticket'], 0) def test_queue_bind_default_for_queue(self): obj = commands.Queue.Bind() self.assertEqual(obj['queue'], '') def test_queue_bind_default_for_exchange(self): obj = commands.Queue.Bind() self.assertEqual(obj['exchange'], '') def test_queue_bind_default_for_routing_key(self): obj = commands.Queue.Bind() self.assertEqual(obj['routing_key'], '') def test_queue_bind_default_for_nowait(self): obj = commands.Queue.Bind() self.assertEqual(obj['nowait'], False) def test_queue_bind_default_for_arguments(self): obj = commands.Queue.Bind() self.assertDictEqual(obj['arguments'], {}) def test_queue_declare_default_for_ticket(self): obj = commands.Queue.Declare() self.assertEqual(obj['ticket'], 0) def test_queue_declare_default_for_queue(self): obj = commands.Queue.Declare() self.assertEqual(obj['queue'], '') def test_queue_declare_with_space_in_queue_name(self): obj = commands.Queue.Declare(queue='Test Queue') self.assertEqual(obj['queue'], 'Test Queue') def test_queue_declare_raises_with_linefeed(self): with self.assertRaises(ValueError): _ = commands.Queue.Declare(queue='Test\nQueue') def test_queue_declare_default_for_passive(self): obj = commands.Queue.Declare() self.assertEqual(obj['passive'], False) def test_queue_declare_default_for_durable(self): obj = commands.Queue.Declare() self.assertEqual(obj['durable'], False) def test_queue_declare_default_for_exclusive(self): obj = commands.Queue.Declare() self.assertEqual(obj['exclusive'], False) def test_queue_declare_default_for_auto_delete(self): obj = commands.Queue.Declare() self.assertEqual(obj['auto_delete'], False) def test_queue_declare_default_for_nowait(self): obj = commands.Queue.Declare() self.assertEqual(obj['nowait'], False) def test_queue_declare_default_for_arguments(self): obj = commands.Queue.Declare() self.assertDictEqual(obj['arguments'], {}) def test_queue_delete_default_for_ticket(self): obj = commands.Queue.Delete() self.assertEqual(obj['ticket'], 0) def test_queue_delete_default_for_queue(self): obj = commands.Queue.Delete() self.assertEqual(obj['queue'], '') def test_queue_delete_default_for_if_unused(self): obj = commands.Queue.Delete() self.assertEqual(obj['if_unused'], False) def test_queue_delete_default_for_if_empty(self): obj = commands.Queue.Delete() self.assertEqual(obj['if_empty'], False) def test_queue_delete_default_for_nowait(self): obj = commands.Queue.Delete() self.assertEqual(obj['nowait'], False) def test_queue_purge_default_for_ticket(self): obj = commands.Queue.Purge() self.assertEqual(obj['ticket'], 0) def test_queue_purge_default_for_queue(self): obj = commands.Queue.Purge() self.assertEqual(obj['queue'], '') def test_queue_purge_default_for_nowait(self): obj = commands.Queue.Purge() self.assertEqual(obj['nowait'], False) def test_queue_unbind_default_for_ticket(self): obj = commands.Queue.Unbind() self.assertEqual(obj['ticket'], 0) def test_queue_unbind_default_for_queue(self): obj = commands.Queue.Unbind() self.assertEqual(obj['queue'], '') def test_queue_unbind_default_for_exchange(self): obj = commands.Queue.Unbind() self.assertEqual(obj['exchange'], '') def test_queue_unbind_default_for_routing_key(self): obj = commands.Queue.Unbind() self.assertEqual(obj['routing_key'], '') def test_queue_unbind_default_for_arguments(self): obj = commands.Queue.Unbind() self.assertDictEqual(obj['arguments'], {}) def test_basic_properties_repr(self): self.assertTrue(repr( commands.Basic.Properties()).startswith( 'Q', int(dt.timestamp() * 1000)) self.assertEqual(decode.timestamp(large_timestamp_bytes)[0], 8) def test_decode_large_timestamp_data_type(self): dt = datetime.datetime(2107, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) large_timestamp_bytes = struct.pack('>Q', int(dt.timestamp() * 1000)) self.assertIsInstance(decode.timestamp(large_timestamp_bytes)[1], datetime.datetime) def test_decode_large_timestamp_value(self): dt = datetime.datetime(2107, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) large_timestamp_bytes = struct.pack('>Q', int(dt.timestamp() * 1000)) self.assertEqual(decode.timestamp(large_timestamp_bytes)[1], dt) pamqp-3.3.0/tests/test_encode_decode.py000066400000000000000000000025061455032117300201520ustar00rootroot00000000000000# -*- encoding: utf-8 -*- import datetime import unittest from pamqp import decode, encode class EncodeDecodeTests(unittest.TestCase): def test_encode_decode_field_table_long_keys(self): """Encoding and decoding a field_table with too long keys.""" # second key is 126 A's + \N{PILE OF POO} data = {'A' * 256: 1, ((b'A' * 128) + b'\xf0\x9f\x92\xa9').decode('utf-8'): 2} encoded = encode.field_table(data) decoded = decode.field_table(encoded)[1] self.assertIn('A' * 128, decoded) def test_timestamp_with_dst(self): # this test assumes the system is set up using a northern hemisphere # timesone with DST (America/New_York as per github CI is fine) data = datetime.datetime(2006, 5, 21, 16, 30, 10, tzinfo=datetime.timezone.utc) encoded = encode.timestamp(data) decoded = decode.timestamp(encoded)[1] self.assertEqual(decoded, data) def test_timestamp_without_timezone(self): naive = datetime.datetime(2006, 5, 21, 16, 30, 10) aware = datetime.datetime(2006, 5, 21, 16, 30, 10, tzinfo=datetime.timezone.utc) encoded = encode.timestamp(naive) decoded = decode.timestamp(encoded)[1] self.assertEqual(decoded, aware) pamqp-3.3.0/tests/test_encoding.py000066400000000000000000000447561455032117300172150ustar00rootroot00000000000000# -*- encoding: utf-8 -*- import datetime import decimal import unittest from pamqp import encode class MarshalingTests(unittest.TestCase): def test_encode_bool_wrong_type(self): self.assertRaises(TypeError, encode.boolean, 'hi') def test_encode_bool_false(self): self.assertEqual(encode.boolean(False), b'\x00') def test_encode_bool_true(self): self.assertEqual(encode.boolean(True), b'\x01') def test_encode_byte_array(self): self.assertEqual(encode.byte_array(bytearray([65, 66, 67])), b'\x00\x00\x00\x03ABC') def test_encode_byte_array_wrong_type(self): self.assertRaises(TypeError, encode.byte_array, b'ABC') def test_encode_decimal_wrong_type(self): self.assertRaises(TypeError, encode.decimal, 3.141597) def test_encode_decimal(self): self.assertEqual(encode.decimal(decimal.Decimal('3.14159')), b'\x05\x00\x04\xcb/') def test_encode_decimal_whole(self): self.assertEqual(encode.decimal(decimal.Decimal('314159')), b'\x00\x00\x04\xcb/') def test_encode_double_invalid_value(self): self.assertRaises(TypeError, encode.double, '1234') def test_encode_double(self): self.assertEqual(encode.double(float(3.14159)), b'@\t!\xf9\xf0\x1b\x86n') def test_encode_floating_point_type(self): self.assertRaises(TypeError, encode.floating_point, '1234') def test_encode_float(self): self.assertEqual(encode.floating_point(float(3.14159)), b'@I\x0f\xd0') def test_encode_long_int_wrong_type(self): self.assertRaises(TypeError, encode.long_int, 3.141597) def test_encode_table_integer_bad_value_error(self): self.assertRaises(TypeError, encode.long_int, 9223372036854775808) def test_encode_long_int(self): self.assertEqual(encode.long_int(2147483647), b'\x7f\xff\xff\xff') def test_encode_long_int_error(self): self.assertRaises(TypeError, encode.long_int, 21474836449) def test_encode_long_uint(self): self.assertEqual(encode.long_uint(4294967295), b'\xff\xff\xff\xff') def test_encode_long_uint_error(self): self.assertRaises(TypeError, encode.long_uint, 4294967296) def test_encode_long_uint_wrong_type(self): self.assertRaises(TypeError, encode.long_uint, 3.141597) def test_encode_long_long_int_wrong_type(self): self.assertRaises(TypeError, encode.long_long_int, 3.141597) def test_encode_long_long_int_error(self): self.assertRaises(TypeError, encode.long_long_int, 9223372036854775808) def test_encode_octet(self): self.assertEqual(encode.octet(1), b'\x01') def test_encode_octet_error(self): self.assertRaises(TypeError, encode.octet, 'hi') def test_encode_short_wrong_type(self): self.assertRaises(TypeError, encode.short_int, 3.141597) def test_encode_short(self): self.assertEqual(encode.short_int(32767), b'\x7f\xff') def test_encode_short_error(self): self.assertRaises(TypeError, encode.short_int, 32768) def test_encode_short_uint(self): self.assertEqual(encode.short_uint(65535), b'\xff\xff') def test_encode_short_uint_error(self): self.assertRaises(TypeError, encode.short_uint, 65536) def test_encode_short_uint_type_error(self): self.assertRaises(TypeError, encode.short_uint, 'hello') def test_encode_table_integer_error(self): self.assertRaises(TypeError, encode.table_integer, 9223372036854775808) def test_encode_short_string(self): self.assertEqual(encode.short_string('Hello'), b'\x05Hello') def test_encode_short_string_error(self): self.assertRaises(TypeError, encode.short_string, 32768) def test_encode_short_string_utf8_python3(self): self.assertEqual(encode.short_string('🐰'), b'\x04\xf0\x9f\x90\xb0') def test_encode_long_string(self): self.assertEqual(encode.long_string('0123456789'), b'\x00\x00\x00\n0123456789') def test_encode_long_string_bytes(self): self.assertEqual(encode.long_string('rabbitmq'), b'\x00\x00\x00\x08rabbitmq') def test_encode_long_string_utf8_python3(self): self.assertEqual(encode.long_string('🐰'), b'\x00\x00\x00\x04\xf0\x9f\x90\xb0') def test_encode_long_string_error(self): self.assertRaises(TypeError, encode.long_string, 100) def test_encode_timestamp_from_datetime(self): self.assertEqual( encode.timestamp(datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc)), b'\x00\x00\x00\x00Ec)\x92') def test_encode_timestamp_from_struct_time(self): value = encode.timestamp( datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc).timetuple()) self.assertEqual(value, b'\x00\x00\x00\x00Ec)\x92') def test_encode_timestamp_error(self): self.assertRaises(TypeError, encode.timestamp, 'hi') def test_encode_field_array(self): expectation = (b'\x00\x00\x00:b\x01u\xaf\xc8I\x02bZ\x00S\x00\x00\x00' b'\x04TestT\x00\x00\x00\x00Ec)\x92I\xbb\x9a\xca\x00D' b'\x02\x00\x00\x01:f@H\xf5\xc3i\xc4e5\xffl\x80\x00\x00' b'\x00\x00\x00\x00\x08') data = [ 1, 45000, 40000000, 'Test', datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc), -1147483648, decimal.Decimal('3.14'), 3.14, 3294967295, -9223372036854775800 ] self.assertEqual(encode.field_array(data), expectation) def test_encode_field_array_error(self): self.assertRaises(TypeError, encode.field_array, 'hi') def test_encode_field_table_empty(self): self.assertEqual(encode.field_table(None), b'\x00\x00\x00\x00') def test_encode_field_table_type_error(self): self.assertRaises(TypeError, encode.field_table, [1, 2, 3]) def test_encode_field_table_object(self): self.assertRaises(TypeError, encode.field_table, {'key': encode.field_table}) def test_encode_field_table(self): expectation = (b"\x00\x00\x04'\x08arrayvalA\x00\x00\x00\x06b\x01b" b'\x02b\x03\x07boolvalt\x01\tbytearrayx\x00\x00\x00' b'\x03AAA\x06decvalD\x02\x00\x00\x01:\x07dictvalF\x00' b'\x00\x00\x0c\x03fooS\x00\x00\x00\x03bar\x08floatval' b'f@H\xf5\xc3\x06intvalb\x01\x07longstrS\x00\x00\x03t' b'000000000000000000000000000000000000000000000000000' b'011111111111111111111111111111111111111111111111111' b'112222222222222222222222222222222222222222222222222' b'222111111111111111111111111111111111111111111111111' b'111122222222222222222222222222222222222222222222222' b'222221111111111111111111111111111111111111111111111' b'111111222222222222222222222222222222222222222222222' b'222222211111111111111111111111111111111111111111111' b'111111112222222222222222222222222222222222222222222' b'222222222111111111111111111111111111111111111111111' b'111111111122222222222222222222222222222222222222222' b'222222222221111111111111111111111111111111111111111' b'111111111111222222222222222222222222222222222222222' b'222222222222211111111111111111111111111111111111111' b'111111111111112222222222222222222222222222222222222' b'222222222222222111111111111111111111111111111111111' b'111111111111111100000000000000000000000000000000000' b'00000000000000000\x07longvalI6e&U\x04noneV\x06strva' b'lS\x00\x00\x00\x04Test\x0ctimestampvalT\x00\x00\x00' b'\x00Ec)\x92') data = { 'intval': 1, 'strval': 'Test', 'boolval': True, 'timestampval': datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc), 'decval': decimal.Decimal('3.14'), 'floatval': 3.14, 'longval': 912598613, 'dictval': { 'foo': 'bar' }, 'arrayval': [1, 2, 3], 'none': None, 'bytearray': bytearray((65, 65, 65)), 'longstr': ('0000000000000000000000000000000000000000000000000' '0001111111111111111111111111111111111111111111111' '1111112222222222222222222222222222222222222222222' '2222222221111111111111111111111111111111111111111' '1111111111112222222222222222222222222222222222222' '2222222222222221111111111111111111111111111111111' '1111111111111111112222222222222222222222222222222' '2222222222222222222221111111111111111111111111111' '1111111111111111111111112222222222222222222222222' '2222222222222222222222222221111111111111111111111' '1111111111111111111111111111112222222222222222222' '2222222222222222222222222222222221111111111111111' '1111111111111111111111111111111111112222222222222' '2222222222222222222222222222222222222221111111111' '1111111111111111111111111111111111111111112222222' '2222222222222222222222222222222222222222222221111' '1111111111111111111111111111111111111111111111110' '0000000000000000000000000000000000000000000000000' '00') } self.assertEqual(encode.field_table(data), expectation) def test_encode_by_type_field_array(self): expectation = (b'\x00\x00\x008b\x01sBhu\xaf\xc8S\x00\x00\x00\x04TestT' b'\x00\x00\x00\x00Ec)\x92I\xbb\x9a\xca\x00D\x02\x00' b'\x00\x01:f@H\xf5\xc3i\xc4e5\xffl\x80\x00\x00\x00\x00' b'\x00\x00\x08') data = [ 1, 17000, 45000, 'Test', datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc), -1147483648, decimal.Decimal('3.14'), 3.14, 3294967295, -9223372036854775800 ] self.assertEqual(encode.by_type(data, 'field_array'), expectation) def test_encode_by_type_byte_array(self): self.assertEqual(encode.by_type(bytearray((65, 66, 67)), 'bytearray'), b'\x00\x00\x00\x03ABC') def test_encode_by_type_double(self): self.assertEqual(encode.by_type(float(4294967295), 'double'), b'A\xef\xff\xff\xff\xe0\x00\x00') def test_encode_by_type_long_uint(self): self.assertEqual(encode.by_type(4294967295, 'long'), b'\xff\xff\xff\xff') def test_encode_by_type_long_long_int(self): self.assertEqual(encode.by_type(9223372036854775800, 'longlong'), b'\x7f\xff\xff\xff\xff\xff\xff\xf8') def test_encode_by_type_long_str(self): self.assertEqual(encode.by_type('0123456789', 'longstr'), b'\x00\x00\x00\n0123456789') def test_encode_by_type_none(self): self.assertEqual(encode.by_type(None, 'void'), None) def test_encode_by_type_octet(self): self.assertEqual(encode.by_type(1, 'octet'), b'\x01') def test_encode_by_type_short(self): self.assertEqual(encode.by_type(32767, 'short'), b'\x7f\xff') def test_encode_by_type_timestamp(self): self.assertEqual( encode.by_type( datetime.datetime(2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc), 'timestamp'), b'\x00\x00\x00\x00Ec)\x92') def test_encode_by_type_field_table(self): expectation = (b'\x00\x00\x04B\x08arrayvalA\x00\x00\x00\x08b\x01s\x10' b'`u\xa4\x10\x07boolvalt\x01\x06decvalD\x02\x00\x00\x01' b':\x07dictvalF\x00\x00\x00\x0c\x03fooS\x00\x00\x00\x03' b'bar\x08floatvalf@H\xf5\xc3\x07longstrS\x00\x00\x03t00' b'00000000000000000000000000000000000000000000000000111' b'11111111111111111111111111111111111111111111111112222' b'22222222222222222222222222222222222222222222222211111' b'11111111111111111111111111111111111111111111111222222' b'22222222222222222222222222222222222222222222221111111' b'11111111111111111111111111111111111111111111122222222' b'22222222222222222222222222222222222222222222111111111' b'11111111111111111111111111111111111111111112222222222' b'22222222222222222222222222222222222222222211111111111' b'11111111111111111111111111111111111111111222222222222' b'22222222222222222222222222222222222222221111111111111' b'11111111111111111111111111111111111111122222222222222' b'22222222222222222222222222222222222222111111111111111' b'11111111111111111111111111111111111112222222222222222' b'22222222222222222222222222222222222211111111111111111' b'11111111111111111111111111111111111000000000000000000' b'0000000000000000000000000000000000\x07longvalI6e&U' b'\x06s32intI\xc4e6\x00\x06s64intl7\x82\xda\xce\x9d\x90' b'\x00\x00\x06strvalS\x00\x00\x00\x04Test\x0ctimestamp' b'valT\x00\x00\x00\x00Ec)\x92\x06u16ints \x00\x06u32int' b'i\xeek(\x00\x05u8bitb ') data = { 'u8bit': 32, 'u16int': 8192, 's32int': -1000000000, 'u32int': 4000000000, 's64int': 4000000000000000000, 'strval': 'Test', 'boolval': True, 'timestampval': datetime.datetime( 2006, 11, 21, 16, 30, 10, tzinfo=datetime.timezone.utc), 'decval': decimal.Decimal('3.14'), 'floatval': 3.14, 'longval': 912598613, 'dictval': { 'foo': 'bar' }, 'arrayval': [1, 4192, 42000], 'longstr': ('0000000000000000000000000000000000000000000000000' '0001111111111111111111111111111111111111111111111' '1111112222222222222222222222222222222222222222222' '2222222221111111111111111111111111111111111111111' '1111111111112222222222222222222222222222222222222' '2222222222222221111111111111111111111111111111111' '1111111111111111112222222222222222222222222222222' '2222222222222222222221111111111111111111111111111' '1111111111111111111111112222222222222222222222222' '2222222222222222222222222221111111111111111111111' '1111111111111111111111111111112222222222222222222' '2222222222222222222222222222222221111111111111111' '1111111111111111111111111111111111112222222222222' '2222222222222222222222222222222222222221111111111' '1111111111111111111111111111111111111111112222222' '2222222222222222222222222222222222222222222221111' '1111111111111111111111111111111111111111111111110' '0000000000000000000000000000000000000000000000000' '00') } self.assertEqual(encode.by_type(data, 'table'), expectation) def test_encode_by_type_error(self): self.assertRaises(TypeError, encode.by_type, 12345.12434, 'foo') class EncodeTableIntegerTestCase(unittest.TestCase): def setUp(self): encode.support_deprecated_rabbitmq(False) def tearDown(self): encode.support_deprecated_rabbitmq(False) def test_table_integer(self): tests = { 'short-short': (32, b'b '), 'short': (1024, b's\x04\x00'), 'short-negative': (-1024, b's\xfc\x00'), 'short-unsigned': (32768, b'u\x80\x00'), 'long': (65536, b'I\x00\x01\x00\x00'), 'long-negative': (65536, b'I\x00\x01\x00\x00'), 'long-unsigned': (4294967295, b'i\xff\xff\xff\xff'), 'long-long': (9223372036854775805, b'l\x7f\xff\xff\xff\xff\xff\xff\xfd'), } for key, value in tests.items(): result = encode.table_integer(value[0]) self.assertEqual(result, value[1], 'encode {} mismatch ({!r} != {!r})'.format( key, result, value[1])) def test_deprecated_table_integer(self): self.assertFalse(encode.DEPRECATED_RABBITMQ_SUPPORT) encode.support_deprecated_rabbitmq(True) self.assertTrue(encode.DEPRECATED_RABBITMQ_SUPPORT) tests = { 'short-short': (32, b'b '), 'short': (1024, b's\x04\x00'), 'short-negative': (-1024, b's\xfc\x00'), 'long': (65536, b'I\x00\x01\x00\x00'), 'long-negative': (65536, b'I\x00\x01\x00\x00'), 'long-long': (2147483648, b'l\x00\x00\x00\x00\x80\x00\x00\x00'), } for key, value in tests.items(): result = encode.table_integer(value[0]) self.assertEqual( result, value[1], 'encode {} mismatch of {!r} ({!r} != {!r})'.format( key, value[0], result, value[1])) def test_too_large_int(self): self.assertFalse(encode.DEPRECATED_RABBITMQ_SUPPORT) with self.assertRaises(TypeError): encode.table_integer(9223372036854775809) encode.support_deprecated_rabbitmq(True) self.assertTrue(encode.DEPRECATED_RABBITMQ_SUPPORT) with self.assertRaises(TypeError): encode.table_integer(9223372036854775809) pamqp-3.3.0/tests/test_frame_marshaling.py000066400000000000000000000101601455032117300207040ustar00rootroot00000000000000# -*- encoding: utf-8 -*- import datetime import unittest import uuid from pamqp import body, commands, frame, header, heartbeat class MarshalingTests(unittest.TestCase): def test_protocol_header(self): expectation = b'AMQP\x00\x00\t\x01' response = frame.marshal(header.ProtocolHeader(), 0) self.assertEqual(response, expectation, 'ProtocolHeader did not match expectation') def test_basic_ack(self): expectation = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00P\x00\x00\x00' b'\x00\x00\x00\x00\x01\x00\xce') frame_obj = commands.Basic.Ack(1, False) response = frame.marshal(frame_obj, 1) self.assertEqual(response, expectation, 'Basic.Ack did not match expectation') def test_basic_cancel(self): expectation = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00\x1e\x07' b'ctag1.0\x00\xce') frame_obj = commands.Basic.Cancel('ctag1.0', False) response = frame.marshal(frame_obj, 1) self.assertEqual(response, expectation, 'Basic.Cancel did not match expectation') def test_basic_cancelok(self): expectation = (b'\x01\x00\x01\x00\x00\x00\x0c\x00<\x00\x1f\x07' b'ctag1.0\xce') frame_obj = commands.Basic.CancelOk(consumer_tag='ctag1.0') response = frame.marshal(frame_obj, 1) self.assertEqual(response, expectation, 'Basic.Cancel did not match expectation') def test_basic_consume(self): expectation = (b'\x01\x00\x01\x00\x00\x00\x15\x00<\x00\x14\x00' b'\x00\x03bar\x05ctag0\x00\x00\x00\x00\x00\xce') frame_obj = commands.Basic.Consume(0, 'bar', 'ctag0', False, False, False, False) response = frame.marshal(frame_obj, 1) self.assertEqual(response, expectation, 'Basic.Consume did not match expectation') def test_heartbeat(self): expectation = b'\x08\x00\x00\x00\x00\x00\x00\xce' response = frame.marshal(heartbeat.Heartbeat(), 0) self.assertEqual(response, expectation, 'Heartbeat did not match expectation') def test_content_body(self): value = str(uuid.uuid4()).encode('utf-8') expectation = b'\x03\x00\x01\x00\x00\x00$' + value + b'\xce' self.assertEqual(frame.marshal(body.ContentBody(value), 1), expectation) def test_content_header(self): expectation = (b'\x02\x00\x01\x00\x00\x00\x0e\x00<\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\n\x00\x00\xce') self.assertEqual(frame.marshal(header.ContentHeader(body_size=10), 1), expectation) def test_content_header_with_basic_properties(self): props = commands.Basic.Properties( app_id='unittest', content_type='application/json', content_encoding='bzip2', correlation_id='d146482a-42dd-4b8b-a620-63d62ef686f3', delivery_mode=2, expiration='100', headers={'foo': 'Test ✈'}, message_id='4b5baed7-66e3-49da-bfe4-20a9651e0db4', message_type='foo', priority=10, reply_to='q1', timestamp=datetime.datetime(2019, 12, 19, 23, 29, 00, tzinfo=datetime.timezone.utc)) expectation = (b'\x02\x00\x01\x00\x00\x00\xa2\x00<\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\n\xff\xe8\x10application/json\x05' b'bzip2\x00\x00\x00\x11\x03fooS\x00\x00\x00\x08Test ' b'\xe2\x9c\x88\x02\n$d146482a-42dd-4b8b-a620-63d62ef68' b'6f3\x02q1\x03100$4b5baed7-66e3-49da-bfe4-20a9651e0db4' b'\x00\x00\x00\x00]\xfc\x07\xbc\x03foo\x08unittest\xce') self.assertEqual(frame.marshal(header.ContentHeader(0, 10, props), 1), expectation) def test_unknown_frame_type(self): with self.assertRaises(ValueError): frame.marshal(self, 1) pamqp-3.3.0/tests/test_frame_unmarshaling.py000066400000000000000000002004741455032117300212600ustar00rootroot00000000000000# -*- encoding: utf-8 -*- import datetime import unittest from pamqp import body, commands, frame, header class DemarshalingTests(unittest.TestCase): def test_protocol_header(self): # Decode the frame and validate lengths frame_data = b'AMQP\x00\x00\t\x01' consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 8, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 8)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'ProtocolHeader', ('Frame was of wrong type, expected ProtocolHeader, ' 'received %s' % frame_obj.name)) self.assertEqual((frame_obj.major_version, frame_obj.minor_version, frame_obj.revision), (0, 9, 1), 'Protocol version is incorrect') def test_heartbeat(self): frame_data = b'\x08\x00\x00\x00\x00\x00\x00\xce' consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 8, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 8)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Heartbeat', ('Frame was of wrong type, expected Heartbeat, ' 'received %s' % frame_obj.name)) def test_basic_ack(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00P\x00\x00\x00\x00' b'\x00\x00\x00\x01\x00\xce') expectation = {'multiple': False, 'delivery_tag': 1} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 21, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 21)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Ack', ('Frame was of wrong type, expected Basic.Ack, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_cancel(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00\x1e\x07ctag1.0\x00' b'\xce') expectation = {'consumer_tag': 'ctag1.0', 'nowait': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 21, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 21)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Cancel', ('Frame was of wrong type, expected Basic.Cancel, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_cancelok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0c\x00<\x00\x1f\x07ctag1.0' b'\xce') expectation = {'consumer_tag': 'ctag1.0'} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.CancelOk', ('Frame was of wrong type, expected Basic.CancelOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_consume(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x18\x00<\x00\x14\x00\x00\x04' b'test\x07ctag1.0\x00\x00\x00\x00\x00\xce') expectation = { 'exclusive': False, 'nowait': False, 'no_local': False, 'consumer_tag': 'ctag1.0', 'queue': 'test', 'arguments': {}, 'ticket': 0, 'no_ack': False } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 32, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 32)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Consume', ('Frame was of wrong type, expected Basic.Consume, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_consumeok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0c\x00<\x00\x15\x07ctag1.0' b'\xce') expectation = {'consumer_tag': 'ctag1.0'} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.ConsumeOk', ('Frame was of wrong type, expected Basic.ConsumeOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_deliver(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x1b\x00<\x00<\x07ctag1.0' b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x04test\xce') expectation = { 'consumer_tag': 'ctag1.0', 'delivery_tag': 1, 'redelivered': False, 'exchange': '', 'routing_key': 'test' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 35, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 35)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Deliver', ('Frame was of wrong type, expected Basic.Deliver, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_get(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0c\x00<\x00F\x00\x00\x04' b'test\x00\xce') expectation = {'queue': 'test', 'ticket': 0, 'no_ack': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Get', ('Frame was of wrong type, expected Basic.Get, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_getempty(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00<\x00H\x00\xce' expectation = {'cluster_id': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.GetEmpty', ('Frame was of wrong type, expected Basic.GetEmpty, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_getok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x17\x00<\x00G\x00\x00\x00\x00' b'\x00\x00\x00\x10\x00\x00\x04test\x00\x00\x12\x06\xce') expectation = { 'message_count': 4614, 'redelivered': False, 'routing_key': 'test', 'delivery_tag': 16, 'exchange': '' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 31, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 31)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.GetOk', ('Frame was of wrong type, expected Basic.GetOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_nack(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00x\x00\x00\x00\x00' b'\x00\x00\x00\x01\x00\xce') expectation = {'requeue': False, 'multiple': False, 'delivery_tag': 1} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 21, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 21)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Nack', ('Frame was of wrong type, expected Basic.Nack, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_content_header(self): result = frame.unmarshal(frame.marshal(header.ContentHeader(0, 100), 1)) self.assertEqual(result[2].body_size, 100) def test_content_body(self): result = frame.unmarshal(frame.marshal(body.ContentBody(b'TEST'), 1)) self.assertEqual(len(result[2]), 4) def test_basic_properties(self): encoded_properties = (b'\xff\xfc\x10application/json\x04gzip\x00\x00' b'\x00\x1d\x03bazS\x00\x00\x00\x08Test \xe2\x9c' b'\x88\x03fooS\x00\x00\x00\x03bar\x01\x00$a5304' b'5ef-f174-4621-9ff2-ac0b8fbe6e4a\x12unmarshali' b'ng_tests\n1345274026$746a1902-39dc-47cf-9471-' b'9feecda35660\x00\x00\x00\x00Pj\xb9\x07\x08uni' b'ttest\x04pika\x18frame_unmarshaling_tests\x00') properties = { 'content_type': 'application/json', 'content_encoding': 'gzip', 'headers': { 'foo': 'bar', 'baz': 'Test ✈' }, 'delivery_mode': 1, 'priority': 0, 'correlation_id': 'a53045ef-f174-4621-9ff2-ac0b8fbe6e4a', 'reply_to': 'unmarshaling_tests', 'expiration': '1345274026', 'message_id': '746a1902-39dc-47cf-9471-9feecda35660', 'timestamp': datetime.datetime(2012, 10, 2, 9, 51, 3, tzinfo=datetime.timezone.utc), 'message_type': 'unittest', 'user_id': 'pika', 'app_id': 'frame_unmarshaling_tests', 'cluster_id': '' } obj = header.ContentHeader() offset, flags = obj._get_flags(encoded_properties) # Decode the frame and validate lengths obj = commands.Basic.Properties() obj.unmarshal(flags, encoded_properties[offset:]) for key in properties: self.assertEqual( getattr(obj, key), properties[key], '%r value of %r did not match expectation of %r' % (key, getattr(obj, key), properties[key])) def test_basic_publish(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00(\x00\x00\x00' b'\x04test\x00\xce') expectation = { 'ticket': 0, 'mandatory': False, 'routing_key': 'test', 'immediate': False, 'exchange': '' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 21, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 21)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Publish', ('Frame was of wrong type, expected Basic.Publish, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_qos(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0b\x00<\x00\n\x00\x00\x00' b'\x00\x00\x01\x00\xce') expectation = { 'prefetch_count': 1, 'prefetch_size': 0, 'global_': False } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 19, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 19)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Qos', ('Frame was of wrong type, expected Basic.Qos, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_qosok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00<\x00\x0b\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.QosOk', ('Frame was of wrong type, expected Basic.QosOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_recover(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00<\x00n\x00\xce' expectation = {'requeue': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Recover', ('Frame was of wrong type, expected Basic.Recover, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_recoverasync(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00<\x00d\x00\xce' expectation = {'requeue': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual( frame_obj.name, 'Basic.RecoverAsync', ('Frame was of wrong type, expected Basic.RecoverAsync, ' 'received %s' % frame_obj.name)) self.assertDictEqual(dict(frame_obj), expectation) def test_basic_recoverok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00<\x00o\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.RecoverOk', ('Frame was of wrong type, expected Basic.RecoverOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_reject(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00Z\x00\x00\x00\x00' b'\x00\x00\x00\x10\x01\xce') expectation = {'requeue': True, 'delivery_tag': 16} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 21, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 21)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Reject', ('Frame was of wrong type, expected Basic.Reject, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_basic_return(self): frame_data = (b'\x01\x00\x01\x00\x00\x00"\x00<\x002\x00\xc8\x0f' b'Normal shutdown\x03foo\x07foo.bar\xce') expectation = { 'reply_code': 200, 'reply_text': 'Normal shutdown', 'routing_key': 'foo.bar', 'exchange': 'foo' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 42, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 42)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Basic.Return', ('Frame was of wrong type, expected Basic.Return, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_close(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x1a\x00\x14\x00(\x00\xc8\x0f' b'Normal shutdown\x00\x00\x00\x00\xce') expectation = { 'class_id': 0, 'method_id': 0, 'reply_code': 200, 'reply_text': 'Normal shutdown' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 34, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 34)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.Close', ('Frame was of wrong type, expected Channel.Close, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_closeok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00\x14\x00)\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.CloseOk', ('Frame was of wrong type, expected Channel.CloseOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_flow(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00\x14\x00\x14\x01\xce' expectation = {'active': True} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.Flow', ('Frame was of wrong type, expected Channel.Flow, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_flowok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00\x14\x00\x15\x01\xce' expectation = {'active': True} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.FlowOk', ('Frame was of wrong type, expected Channel.FlowOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_open(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00\x14\x00\n\x00\xce' expectation = {'out_of_band': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.Open', ('Frame was of wrong type, expected Channel.Open, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_channel_openok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x08\x00\x14\x00\x0b\x00\x00' b'\x00\x00\xce') expectation = {'channel_id': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: {}, {}'.format( consumed, 16)) self.assertEqual( channel, 1, 'Channel number did not match expectation: {}, {}'.format( channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Channel.OpenOk', ('Frame was of wrong type, expected Channel.OpenOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_confirm_select(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x05\x00U\x00\n\x00\xce' expectation = {'nowait': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Confirm.Select', ('Frame was of wrong type, expected Confirm.Select, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_confirm_selectok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00U\x00\x0b\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Confirm.SelectOk', ('Frame was of wrong type, expected ' 'Confirm.SelectOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_close(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x1a\x00\n\x002\x00\xc8\x0f' b'Normal shutdown\x00\x00\x00\x00\xce') expectation = { 'class_id': 0, 'method_id': 0, 'reply_code': 200, 'reply_text': 'Normal shutdown' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 34, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 34)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.Close', ('Frame was of wrong type, expected ' 'Connection.Close, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_closeok(self): frame_data = b'\x01\x00\x00\x00\x00\x00\x04\x00\n\x003\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.CloseOk', ('Frame was of wrong type, expected ' 'Connection.CloseOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_open(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x08\x00\n\x00(\x01/\x00' b'\x01\xce') expectation = {'insist': True, 'capabilities': '', 'virtual_host': '/'} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 16)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.Open', ('Frame was of wrong type, expected Connection.Open, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_openok(self): frame_data = b'\x01\x00\x00\x00\x00\x00\x05\x00\n\x00)\x00\xce' expectation = {'known_hosts': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 13, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 13)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.OpenOk', ('Frame was of wrong type, expected ' 'Connection.OpenOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_secure(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x08\x00\n\x00\x14\x00\x00' b'\x00\x00\xce') expectation = {'challenge': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 16)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.Secure', ('Frame was of wrong type, expected ' 'Connection.Secure, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_secureok(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x08\x00\n\x00\x15\x00\x00' b'\x00\x00\xce') expectation = {'response': ''} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 16)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.SecureOk', ('Frame was of wrong type, expected ' 'Connection.SecureOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_start(self): frame_data = (b'\x01\x00\x00\x00\x00\x01G\x00\n\x00\n\x00\t' b'\x00\x00\x01"\x0ccapabilitiesF\x00\x00\x00X' b'\x12publisher_confirmst\x01\x1aexchange_exc' b'hange_bindingst\x01\nbasic.nackt\x01\x16con' b'sumer_cancel_notifyt\x01\tcopyrightS\x00' b'\x00\x00$Copyright (C) 2007-2011 VMware, I' b'nc.\x0binformationS\x00\x00\x005Licensed u' b'nder the MPL. See http://www.rabbitmq.com' b'/\x08platformS\x00\x00\x00\nErlang/OTP\x07' b'productS\x00\x00\x00\x08RabbitMQ\x07versio' b'nS\x00\x00\x00\x052.6.1\x00\x00\x00\x0ePLA' b'IN AMQPLAIN\x00\x00\x00\x05en_US\xce') expectation = { 'server_properties': { 'information': ( 'Licensed under the MPL. See http://www.rabbitmq.com/'), 'product': 'RabbitMQ', 'copyright': 'Copyright (C) 2007-2011 VMware, Inc.', 'capabilities': { 'exchange_exchange_bindings': True, 'consumer_cancel_notify': True, 'publisher_confirms': True, 'basic.nack': True }, 'platform': 'Erlang/OTP', 'version': '2.6.1' }, 'version_minor': 9, 'mechanisms': 'PLAIN AMQPLAIN', 'locales': 'en_US', 'version_major': 0 } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 335, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 335)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.Start', ('Frame was of wrong type, expected ' 'Connection.Start, received %s' % frame_obj.name)) self.maxDiff = 32768 # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_startok(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\xf4\x00\n\x00\x0b' b'\x00\x00\x00\xd0\x08platformS\x00\x00\x00' b'\x0cPython 2.7.1\x07productS\x00\x00\x00' b'\x1aPika Python Client Library\x07versionS' b'\x00\x00\x00\n0.9.6-pre0\x0ccapabilitiesF' b'\x00\x00\x00;\x16consumer_cancel_notifyt' b'\x01\x12publisher_confirmst\x01\nbasic.nack' b't\x01\x0binformationS\x00\x00\x00\x1aSee ' b'http://pika.github.com\x05PLAIN\x00\x00' b'\x00\x0c\x00guest\x00guest\x05en_US\xce') expectation = { 'locale': 'en_US', 'mechanism': 'PLAIN', 'client_properties': { 'platform': 'Python 2.7.1', 'product': 'Pika Python Client Library', 'version': '0.9.6-pre0', 'capabilities': { 'consumer_cancel_notify': True, 'publisher_confirms': True, 'basic.nack': True }, 'information': 'See http://pika.github.com' }, 'response': '\x00guest\x00guest' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 252, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 252)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.StartOk', ('Frame was of wrong type, expected ' 'Connection.StartOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_tune(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x0c\x00\n\x00\x1e\x00\x00' b'\x00\x02\x00\x00\x00\x00\xce') expectation = {'frame_max': 131072, 'channel_max': 0, 'heartbeat': 0} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.Tune', ('Frame was of wrong type, expected Connection.Tune, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_connection_tuneok(self): frame_data = (b'\x01\x00\x00\x00\x00\x00\x0c\x00\n\x00\x1f\x00\x00' b'\x00\x02\x00\x00\x00\x00\xce') expectation = {'frame_max': 131072, 'channel_max': 0, 'heartbeat': 0} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the frame name self.assertEqual(frame_obj.name, 'Connection.TuneOk', ('Frame was of wrong type, expected ' 'Connection.TuneOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_content_body_frame(self): frame_data = (b'\x03\x00\x00\x00\x00\x00"Hello World #0:1316899165.' b'75516605\xce') expectation = b'Hello World #0:1316899165.75516605' # Decode the frame and validate lengths consumed, channel, data = frame.unmarshal(frame_data) self.assertEqual( consumed, 42, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 42)) self.assertEqual( channel, 0, 'Channel number did not match expectation: %i, %i' % (channel, 0)) # Validate the unmarshaled data matches the expectation self.assertEqual(data.value, expectation) def test_exchange_bind(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x15\x00(\x00\x1e\x00\x00' b'\x00\x00\x07foo.bar\x00\x00\x00\x00\x00\xce') expectation = { 'arguments': {}, 'source': '', 'ticket': 0, 'destination': '', 'nowait': False, 'routing_key': 'foo.bar' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 29, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 29)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.Bind', ('Frame was of wrong type, expected Exchange.Bind, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_bindok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00(\x00\x1f\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.BindOk', ('Frame was of wrong type, expected Exchange.BindOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_declare(self): frame_data = (b'\x01\x00\x01\x00\x00\x00%\x00(\x00\n\x00\x00\x12pika_' b'test_exchange\x06direct\x00\x00\x00\x00\x00\xce') expectation = { 'nowait': False, 'exchange': 'pika_test_exchange', 'durable': False, 'passive': False, 'internal': False, 'arguments': {}, 'ticket': 0, 'exchange_type': 'direct', 'auto_delete': False } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 45, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 45)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.Declare', ('Frame was of wrong type, expected ' 'Exchange.Declare, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_declareok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00(\x00\x0b\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.DeclareOk', ('Frame was of wrong type, expected Exchange.' 'DeclareOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_delete(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x1a\x00(\x00\x14\x00\x00\x12' b'pika_test_exchange\x00\xce') expectation = { 'ticket': 0, 'if_unused': False, 'nowait': False, 'exchange': 'pika_test_exchange' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 34, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 34)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.Delete', ('Frame was of wrong type, expected Exchange.Delete, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_deleteok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00(\x00\x15\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.DeleteOk', ('Frame was of wrong type, expected ' 'Exchange.DeleteOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_unbind(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x15\x00(\x00(\x00\x00\x00' b'\x00\x07foo.bar\x00\x00\x00\x00\x00\xce') expectation = { 'arguments': {}, 'source': '', 'ticket': 0, 'destination': '', 'nowait': False, 'routing_key': 'foo.bar' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 29, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 29)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.Unbind', ('Frame was of wrong type, expected Exchange.Unbind, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_exchange_unbindok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00(\x003\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Exchange.UnbindOk', ('Frame was of wrong type, expected ' 'Exchange.UnbindOk, received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_bind(self): frame_data = (b'\x01\x00\x01\x00\x00\x00?\x002\x00\x14\x00' b'\x00\x0fpika_test_queue\x12pika_test_excha' b'nge\x10test_routing_key\x00\x00\x00\x00\x00' b'\xce') expectation = { 'nowait': False, 'exchange': 'pika_test_exchange', 'routing_key': 'test_routing_key', 'queue': 'pika_test_queue', 'arguments': {}, 'ticket': 0 } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 71, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 71)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.Bind', ('Frame was of wrong type, expected Queue.Bind, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_bindok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x002\x00\x15\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.BindOk', ('Frame was of wrong type, expected Queue.BindOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_declare(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x10\x002\x00\n\x00\x00\x04' b'test\x02\x00\x00\x00\x00\xce') expectation = { 'passive': False, 'nowait': False, 'exclusive': False, 'durable': True, 'queue': 'test', 'arguments': {}, 'ticket': 0, 'auto_delete': False } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 24, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 24)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.Declare', ('Frame was of wrong type, expected Queue.Declare, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_declareok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x11\x002\x00\x0b\x04test' b'\x00\x00\x12\x07\x00\x00\x00\x00\xce') expectation = { 'queue': 'test', 'message_count': 4615, 'consumer_count': 0 } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 25, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 25)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.DeclareOk', ('Frame was of wrong type, expected Queue.DeclareOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_delete(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x17\x002\x00(\x00\x00\x0f' b'pika_test_queue\x00\xce') expectation = { 'queue': 'pika_test_queue', 'ticket': 0, 'if_empty': False, 'nowait': False, 'if_unused': False } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 31, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 31)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.Delete', ('Frame was of wrong type, expected Queue.Delete, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_deleteok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x08\x002\x00)\x00\x00\x00' b'\x00\xce') expectation = {'message_count': 0} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 16)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.DeleteOk', ('Frame was of wrong type, expected Queue.DeleteOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_purge(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0c\x002\x00\x1e\x00\x00' b'\x04test\x00\xce') expectation = {'queue': 'test', 'ticket': 0, 'nowait': False} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 20, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 20)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.Purge', ('Frame was of wrong type, expected Queue.Purge, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_purgeok(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x08\x002\x00\x1f\x00\x00\x00' b'\x01\xce') expectation = {'message_count': 1} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 16, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 16)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.PurgeOk', ('Frame was of wrong type, expected Queue.PurgeOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_unbind(self): frame_data = (b'\x01\x00\x01\x00\x00\x00>\x002\x002\x00\x00\x0f' b'pika_test_queue\x12pika_test_exchange\x10test_routing' b'_key\x00\x00\x00\x00\xce') expectation = { 'queue': 'pika_test_queue', 'arguments': {}, 'ticket': 0, 'routing_key': 'test_routing_key', 'exchange': 'pika_test_exchange' } # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 70, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 70)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.Unbind', ('Frame was of wrong type, expected Queue.Unbind, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_queue_unbindok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x002\x003\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Queue.UnbindOk', ('Frame was of wrong type, expected Queue.UnbindOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_commit(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\x14\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.Commit', ('Frame was of wrong type, expected Tx.Commit, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_commitok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\x15\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.CommitOk', ('Frame was of wrong type, expected Tx.CommitOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_rollback(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\x1e\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.Rollback', ('Frame was of wrong type, expected Tx.Rollback, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_rollbackok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\x1f\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.RollbackOk', ('Frame was of wrong type, expected Tx.RollbackOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_select(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\n\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.Select', ('Frame was of wrong type, expected Tx.Select, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_tx_selectok(self): frame_data = b'\x01\x00\x01\x00\x00\x00\x04\x00Z\x00\x0b\xce' expectation = {} # Decode the frame and validate lengths consumed, channel, frame_obj = frame.unmarshal(frame_data) self.assertEqual( consumed, 12, 'Bytes consumed did not match expectation: %i, %i' % (consumed, 12)) self.assertEqual( channel, 1, 'Channel number did not match expectation: %i, %i' % (channel, 1)) # Validate the frame name self.assertEqual(frame_obj.name, 'Tx.SelectOk', ('Frame was of wrong type, expected Tx.SelectOk, ' 'received %s' % frame_obj.name)) # Validate the unmarshaled data matches the expectation self.assertDictEqual(dict(frame_obj), expectation) def test_properties(self): props = commands.Basic.Properties( app_id='unittest', content_type='application/json', content_encoding='bzip2', correlation_id='d146482a-42dd-4b8b-a620-63d62ef686f3', delivery_mode=2, expiration='100', headers={'foo': 'Test ✈'}, message_id='4b5baed7-66e3-49da-bfe4-20a9651e0db4', message_type='foo', priority=10, reply_to='q1', timestamp=datetime.datetime(2019, 12, 19, 23, 29, 00, tzinfo=datetime.timezone.utc)) ch = frame.marshal(header.ContentHeader(0, 10, props), 1) rt_props = frame.unmarshal(ch)[2].properties self.assertEqual(rt_props, props) pamqp-3.3.0/tests/test_frame_unmarshaling_errors.py000066400000000000000000000075661455032117300226630ustar00rootroot00000000000000# coding=utf-8 import struct import unittest from pamqp import constants, exceptions, frame class TestCase(unittest.TestCase): def test_invalid_protocol_header(self): with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(b'AMQP\x00\x00\t') self.assertTrue(str(err).startswith( "Could not unmarshal " 'frame: Data did not match the ProtocolHeader format')) def test_invalid_frame_header(self): frame_data = struct.pack('>BI', 255, 0) with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_data) self.assertEqual( str(err), 'Could not unmarshal Unknown frame: No frame size') def test_frame_with_no_length(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x00\x00<\x00P\x00\x00\x00\x00' b'\x00\x00\x00\x01\x00\xce') with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_data) self.assertEqual( str(err), 'Could not unmarshal Unknown frame: No frame size') def test_frame_malformed_length(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\x0c\x00<\x00P\x00\x00\x00\x00' b'\x00\x00\x00\xce') with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_data) self.assertEqual( str(err), 'Could not unmarshal Unknown frame: Not all data received') def test_frame_malformed_end_byte(self): frame_data = (b'\x01\x00\x01\x00\x00\x00\r\x00<\x00P\x00\x00\x00\x00' b'\x00\x00\x00\x01\x00\x00') with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_data) self.assertEqual( str(err), 'Could not unmarshal Unknown frame: Last byte error') def test_malformed_frame_content(self): payload = struct.pack('>HxxQ', 8192, 32768) frame_value = b''.join([struct.pack('>BHI', 5, 0, len(payload)), payload, constants.FRAME_END_CHAR]) with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_value) self.assertEqual( str(err), 'Could not unmarshal Unknown frame: Unknown frame type: 5') def test_invalid_method_frame_index(self): payload = struct.pack('>L', 42949) frame_value = b''.join([struct.pack('>BHI', 1, 0, len(payload)), payload, constants.FRAME_END_CHAR]) with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_value) self.assertEqual( str(err), ('Could not unmarshal Unknown frame: ' 'Unknown method index: 42949')) def test_invalid_method_frame_content(self): payload = struct.pack('>L', 0x000A0029) frame_value = b''.join([struct.pack('>BHI', 1, 0, len(payload)), payload, constants.FRAME_END_CHAR]) with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_value) self.assertTrue(str(err).startswith( 'Could not unmarshal L', 0x000A0029) frame_value = b''.join([struct.pack('>BHI', 2, 0, len(payload)), payload, constants.FRAME_END_CHAR]) with self.assertRaises(exceptions.UnmarshalingException) as err: frame.unmarshal(frame_value) self.assertTrue(str(err).startswith( 'Could not unmarshal ContentHeader frame:')) pamqp-3.3.0/tests/test_tag_uri_scheme.py000066400000000000000000000014371455032117300203720ustar00rootroot00000000000000import unittest from pamqp import commands # Tag uri examples from https://en.wikipedia.org/wiki/Tag_URI_scheme TAG_URIS = [ 'tag:timothy@hpl.hp.com,2001:web/externalHome', 'tag:sandro@w3.org,2004-05:Sandro', 'tag:my-ids.com,2001-09-15:TimKindberg:presentations:UBath2004-05-19', 'tag:blogger.com,1999:blog-555', 'tag:yaml.org,2002:int#section1' ] class TagUriScheme(unittest.TestCase): def test_tag_uri_scheme_tag1(self): commands.Exchange.Declare(exchange=TAG_URIS[0]) def test_tag_uri_scheme_tag2(self): commands.Exchange.Declare(exchange=TAG_URIS[1]) def test_tag_uri_scheme_tag3(self): commands.Exchange.Declare(exchange=TAG_URIS[2]) def test_tag_uri_scheme_tag4(self): commands.Exchange.Declare(exchange=TAG_URIS[3]) pamqp-3.3.0/tools/000077500000000000000000000000001455032117300137745ustar00rootroot00000000000000pamqp-3.3.0/tools/codegen.py000077500000000000000000001303661455032117300157660ustar00rootroot00000000000000#!/usr/bin/env python """Generates the pamqp/specification.py file used as a foundation for AMQP communication. """ import copy import dataclasses import functools import json import keyword import logging import pathlib import sys import textwrap import typing import lxml.etree, lxml.objectify import requests from yapf import yapf_api __author__ = 'Gavin M. Roy' __email__ = 'gavinmroy@gmail.com' __since__ = '2011-03-31' LOGGER = logging.getLogger(__name__) CODEGEN_DIR = pathlib.Path('./codegen/') CODEGEN_IGNORE_CLASSES = ['access'] CODEGEN_JSON = CODEGEN_DIR / 'amqp-rabbitmq-0.9.1.json' CODEGEN_XML = CODEGEN_DIR / 'amqp0-9-1.xml' EXTENSIONS_XML = CODEGEN_DIR / 'extensions.xml' COMMANDS = pathlib.Path('./pamqp/commands.py') COMMANDS_HEADER = '''""" The classes inside :mod:`pamqp.commands` allow for the automatic marshaling and unmarshaling of AMQP method frames and :class:`Basic.Properties `. In addition the command classes contain information that designates if they are synchronous commands and if so, what the expected responses are. Each commands arguments are detailed in the class and are listed in the attributes property. .. note:: All AMQ classes and methods extend :class:`pamqp.base.Frame`. """ # Auto-generated, do not edit this file. import datetime import typing import warnings from pamqp import base, common, constants ''' CONSTANTS = pathlib.Path('./pamqp/constants.py') CONSTANTS_HEADER = ''' # Auto-generated, do not edit this file. import re ''' EXCEPTIONS = pathlib.Path('./pamqp/exceptions.py') EXCEPTIONS_HEADER = ''' # Auto-generated, do not edit this file. class PAMQPException(Exception): """Base exception for all pamqp specific exceptions.""" class UnmarshalingException(PAMQPException): """Raised when a frame is not able to be unmarshaled.""" def __str__(self) -> str: # pragma: nocover return 'Could not unmarshal {} frame: {}'.format( self.args[0], self.args[1]) class AMQPError(PAMQPException): """Base exception for all AMQP errors.""" class AMQPSoftError(AMQPError): """Base exception for all AMQP soft errors.""" class AMQPHardError(AMQPError): """Base exception for all AMQP hard errors.""" ''' CODEGEN_JSON_URL = ('https://raw.githubusercontent.com/rabbitmq/' 'rabbitmq-codegen/master/amqp-rabbitmq-0.9.1.json') CODEGEN_XML_URL = ('https://raw.githubusercontent.com/rabbitmq/' 'rabbitmq-website/master/site/resources/specs/' 'amqp0-9-1.extended.xml') XPATH_ORDER = ['class', 'constant', 'method', 'field'] AMQ_TYPE_TO_ANNOTATION = { 'bit': 'bool', 'long': 'int', 'longlong': 'int', 'longstr': 'str', 'octet': 'int', 'short': 'int', 'shortstr': 'str', 'table': 'common.FieldTable', 'timestamp': 'common.Timestamp', } @dataclasses.dataclass class Domain: name: str type: str documentation: typing.Optional[str] label: typing.Optional[str] nullable: bool regex: typing.Optional[str] max_length: typing.Optional[int] default_value: typing.Optional[str] class Codegen: AMQP_VERSION = '0-9-1' DEPRECATION_WARNING = 'Deprecated' YAPF = True def __init__(self): self._amqp_json = self._load_codegen_json() self._codegen_xml = self._load_codegen_xml() self._extensions_xml = self._load_extensions_xml() self._output_buffer: typing.List[str] = [] self.AMQP_VERSION = ('-'.join([ str(self._amqp_json['major-version']), str(self._amqp_json['minor-version']), str(self._amqp_json['revision'])])) self.DEPRECATION_WARNING = \ 'This command is deprecated in AMQP {}'.format(self.AMQP_VERSION) def build(self): self._build_commands() self._build_constants() self._build_exceptions() def _add_comment(self, value: str, indent: int = 0, prefix: str = '# ') -> None: """Append a comment to the output buffer""" if not value: return self._add_line() indent_text = prefix.rjust(indent + len(prefix)) wrapped = textwrap.wrap(value, 79 - len(indent_text)) if value.startswith(':') or value.startswith('..'): self._add_line(indent_text + wrapped[0], width=79) indent += 4 indent_text = prefix.rjust(indent + len(prefix)) value = value[len(wrapped[0]) + 1:] for line in textwrap.wrap(value, 79 - len(indent_text)): self._add_line(indent_text + line, width=79) else: for line in wrapped: self._add_line(indent_text + line, width=79) def _add_documentation(self, label: str, documentation: str, indent: int) -> None: if not documentation: self._add_line('"""{}"""'.format(label), indent) else: for key, value in sorted(self._commands().items(), reverse=True): documentation = documentation.replace( ' {} '.format(key), ' :class:`{}` '.format(value)) self._add_line('"""{}'.format(label), indent) self._add_line() for line in documentation.split('\n'): self._add_comment(line, indent, '') self._add_line() self._add_line('"""', indent) def _add_function(self, name: str, args: list, indent: int) -> None: """Create a new function""" self._add_line() if not len(args): self._add_line('def {}(self) -> None:'.format(name), indent) else: self._add_line('def {}(self,'.format(name), indent) indent += len('def {}('.format(name)) for index, arg in enumerate(args): annotation = self._arg_annotation(arg) default = self._arg_default(arg) if arg['name'] == 'arguments' and default == '{}': default = 'None' if default == 'None' or default is None: annotation = 'typing.Optional[{}]'.format(annotation) if index == len(args) - 1: self._add_line('{}: {} = {}) -> None:'.format( self._arg_name(arg['pyname']), annotation, default), indent) else: self._add_line('{}: {} = {},'.format( self._arg_name(arg['pyname']), annotation, default), indent) def _add_line(self, value: str = '', indent: int = 0, secondary_indent: int = 0, width: typing.Optional[int] = None) -> None: """Append a new line to the output buffer""" if not value: self._output_buffer.append(value) return initial = ''.rjust(indent) secondary = ''.rjust(secondary_indent or indent) wrapper = textwrap.TextWrapper( width=width or 120 if self.YAPF else 79, drop_whitespace=True, initial_indent=initial, break_on_hyphens=False, subsequent_indent=secondary) for value in wrapper.wrap(value.rstrip()): self._output_buffer.append(value) def _all_defaults(self, arguments: list) -> bool: args = [] for arg in arguments: arg_type = AMQ_TYPE_TO_ANNOTATION[self._arg_type(arg)] if arg_type in {'common.FieldTable', 'common.Arguments'}: args.append(True) else: args.append(self._arg_default(arg) is not None) return all(args) def _arg_annotation(self, arg: dict) -> str: arg_type = AMQ_TYPE_TO_ANNOTATION[self._arg_type(arg)] if arg_type == 'common.FieldTable': if arg['name'] == 'arguments': return 'common.Arguments' elif arg_type == 'common.Timestamp': return 'datetime.datetime' return arg_type def _arg_default(self, arg: dict, repr_=True) -> typing.Optional[str]: ext = self._extensions_xml.xpath( '//rabbitmq/field[@name="{}"]'.format(arg['name'])) if ext and ext[0].attrib.get('default-value') is not None: if repr_ and arg['type'] != 'short' and arg['type'] != 'bit': return '{!r}'.format(ext[0].attrib.get('default-value')) return ext[0].attrib.get('default-value') elif arg.get('default-value') is not None: if repr_ and arg['type'] != 'short' and arg['type'] != 'bit': return '{!r}'.format(arg['default-value']) return arg['default-value'] elif 'domain' in arg: domain = self._domain(arg['domain']) if domain and domain.name == arg['domain'] \ and domain.default_value is not None: if repr_ and domain.type != 'short' and arg['type'] != 'bit': return '{!r}'.format(domain.default_value) return domain.default_value return None @staticmethod def _arg_name(name: str) -> str: """Returns a valid python argument name for the AMQP argument""" value = name.replace('-', '_') if value in keyword.kwlist: LOGGER.debug('%s is in the keyword list', value) value += '_' return value def _arg_type(self, arg: dict) -> str: """Get the argument type""" if 'domain' in arg: for domain, data_type in self._amqp_json['domains']: if arg['domain'] == domain: arg['type'] = data_type break if 'type' in arg: return arg['type'] raise ValueError('Unknown argument type') def _build_command_basic_properties(self, class_name: str, class_id: int, properties: list) -> None: for offset, arg in enumerate(properties): properties[offset]['pyname'] = self._arg_name(arg['name']) if properties[offset]['pyname'] == 'type': properties[offset]['pyname'] = 'message_type' indent = 4 self._add_line('class Properties(base.BasicProperties):', indent) indent += 4 self._add_comment('"""Content Properties', indent, '') self._add_line() self._add_line( '.. Note:: The AMQP property type is named ``message_type`` as to ' 'not conflict with the Python ``type`` keyword', indent, indent + 10, width=79) self._add_line() for arg in properties: label = self._label({'class': class_name, 'field': arg['name']}) if label: line = ':param {}: {}'.format(arg['pyname'], label or '') self._add_line(line.strip(), indent, indent + 4, width=79) else: self._add_line(':param {}: Deprecated, must be empty'.format( arg['pyname']), indent, indent + 4, width=79) if arg['name'] == 'headers': self._add_line( ':type headers: typing.Optional[' ':const:`~pamqp.common.FieldTable`]', indent, indent + 4, width=79) self._add_line(':raises: ValueError', indent) self._add_line() self._add_line('"""', indent) self._add_line('__annotations__: typing.Dict[str, object] = {', indent) for offset, arg in enumerate(properties): if offset == len(properties) - 1: self._add_line( '{!r}: {}'.format(arg['pyname'], self._arg_annotation(arg)), indent + 4) else: self._add_line( '{!r}: {},'.format(arg['pyname'], self._arg_annotation(arg)), indent + 4) self._add_line('}', indent) self._add_line( '__slots__: typing.List[str] = [ # AMQ Properties Attributes', indent) for offset, arg in enumerate(properties): if offset == len(properties) - 1: self._add_line('{!r}'.format(arg['pyname']), indent + 4) else: self._add_line('{!r},'.format(arg['pyname']), indent + 4) self._add_line(']', indent) self._add_line() flag_value = 15 self._add_comment('Flag values for marshaling / unmarshaling', indent) self._add_line( "flags = {{'{}': {},".format( properties[0]['pyname'], 1 << flag_value), indent) for arg in properties[1:-1]: flag_value -= 1 self._add_line("'{}': {},".format(arg['pyname'], 1 << flag_value), indent + 9), flag_value -= 1 self._add_line("'{}': {}}}".format( properties[-1]['pyname'], 1 << flag_value), indent + 9) self._add_line() self._add_line('frame_id = {} # AMQP Frame ID'.format(class_id), indent) self._add_line( 'index = 0x%04X # pamqp Mapping Index' % class_id, indent) self._add_line("name = '{}.Properties'".format( self._pep8_class_name(class_name)), indent) self._add_line() self._add_comment('Class Attribute Types for unmarshaling', indent) for arg in properties: self._add_line("_{} = '{}'".format( arg['pyname'], self._arg_type(arg)), indent) self._add_function('__init__', properties, indent) indent += 4 self._add_line('"""Initialize the {}.Properties class"""'.format( self._pep8_class_name(class_name)), indent) for arg in properties: self._add_line('self.{} = {}'.format( arg['pyname'], arg['pyname']), indent) self._add_line('self.validate()', indent) self._add_line() def _build_command_map(self) -> None: self._add_line() self._add_comment('AMQP Class.Method Index Mapping') self._add_line('INDEX_MAPPING = {') lines = [] for amqp_class in self._amqp_json['classes']: if amqp_class['name'] not in CODEGEN_IGNORE_CLASSES: for method in amqp_class['methods']: key = amqp_class['id'] << 16 | method['id'] lines.append( '0x%08X: %s.%s,' % ( key, self._pep8_class_name(amqp_class['name']), self._pep8_class_name(method['name']))) lines[-1] = lines[-1][:-1] [self._add_line(line, 4) for line in lines] self._add_line('}') def _build_command_method(self, class_name: str, class_id: int, method: dict) -> None: deprecated, needs_linefeed = False, True arguments = copy.deepcopy(method['arguments']) method_xml = self._search_xml( {'class': class_name, 'method': method['name']}) indent = 4 self._add_line('class {}(base.Frame):'.format( self._pep8_class_name(method['name'])), indent) documentation = [] docs = self._documentation( {'class': class_name, 'method': method['name']}) if docs is not None and docs.strip(): documentation.append(docs.strip()) needs_linefeed = True for arg in arguments: name = self._arg_name(arg['name']) if name == 'type' and class_name == 'exchange': documentation.append( '\n.. note:: The AMQP type argument is referred to as ' '"{}_type" to not conflict with the Python type ' 'keyword.'.format(class_name)) needs_linefeed = True # Note the deprecation warning in the docblock if method_xml and method_xml[0].attrib.get('deprecated'): deprecated = True documentation.append( '\n.. deprecated:: {}'.format(self.DEPRECATION_WARNING)) needs_linefeed = True if arguments and needs_linefeed: documentation.append('') for arg in arguments: arg_type = AMQ_TYPE_TO_ANNOTATION[self._arg_type(arg)] name = self._arg_name(arg['name']) default = self._arg_default(arg, False) if name == 'arguments' or arg_type in {'common.FieldTable', 'common.Arguments'}: default = '{}' domain = self._domain(arg.get('domain', arg['name'])) if name == 'type' and class_name == 'exchange': name = 'exchange_type' search_path = { 'class': class_name, 'method': method['name'], 'field': arg['name']} arg_doc = self._documentation(search_path) label = self._label(search_path) or arg_doc or '' if not label \ and domain and (domain.label or domain.documentation): label = domain.label or domain.documentation if default is not None: if default == '': default = "''" if label: label = '{}\n - Default: ``{}``'.format(label, default) else: label = 'Default: ``{}``'.format(default) documentation.append(':param {}: {}'.format(name, label)) if name == 'arguments': documentation.append( ':type {}: :const:`~pamqp.common.Arguments`'.format(name)) elif arg_type.startswith('common'): documentation.append( ':type {}: :const:`~pamqp.{}`'.format(name, arg_type)) raises = set() for arg in arguments: domain = self._domain(arg.get('domain', arg['name'])) if domain and (domain.max_length or domain.regex): raises.add('ValueError') if raises: for exc_name in raises: documentation.append( ':raises {}: when an argument fails to validate'.format( exc_name)) label = self._label( {'class': class_name, 'method': method['name']}) \ or 'Undocumented function' indent = 8 self._add_documentation(label, '\n'.join(documentation), indent) if not len(arguments): self._add_line('__annotations__: typing.Dict[str, object] = {}', indent) else: self._add_line('__annotations__: typing.Dict[str, object] = {', indent) for offset, arg in enumerate(arguments): name = self._arg_name(arg['name']) if name == 'type' and class_name == 'exchange': name = 'exchange_type' arguments[offset]['pyname'] = name if offset == len(method['arguments']) - 1: self._add_line( '{!r}: {}'.format(name, self._arg_annotation(arg)), indent + 4) else: self._add_line( '{!r}: {},'.format(name, self._arg_annotation(arg)), indent + 4) self._add_line('}', indent) if not len(arguments): self._add_line( '__slots__: typing.List[str] = [] # AMQ Method Attributes', indent) else: self._add_line( '__slots__: typing.List[str] = [ # AMQ Method Attributes', indent) for offset, arg in enumerate(arguments): if offset == len(method['arguments']) - 1: self._add_line('{!r}'.format(arg['pyname']), indent + 4) else: self._add_line('{!r},'.format(arg['pyname']), indent + 4) self._add_line(']', indent) self._add_line() self._add_line('frame_id = %i # AMQP Frame ID' % method['id'], indent) index_value = class_id << 16 | method['id'] self._add_line( 'index = 0x%08X # pamqp Mapping Index' % index_value, indent) self._add_line("name = '{}.{}'".format( self._pep8_class_name(class_name), self._pep8_class_name(method['name'])), indent) self._add_line( 'synchronous = {} # Indicates if this is a synchronous AMQP ' 'method'.format(method.get('synchronous', False)), indent) if method.get('synchronous'): responses = [] if method_xml: responses = [ "'{}.{}'".format( self._pep8_class_name(class_name), self._pep8_class_name(response.attrib['name'])) for response in method_xml[0].iter('response')] if not responses: responses = ["'{}.{}Ok'".format( self._pep8_class_name(class_name), self._pep8_class_name(method['name']))] line = 'valid_responses = [{}]'.format(', '.join(responses)) if len(line) <= 36: self._add_line('{} # Valid responses to this method'.format( line), indent) else: self._add_comment('Valid responses to this method', indent) self._add_line(line, indent) if arguments: self._add_line() self._add_comment('Class Attribute Types for unmarshaling', indent) for arg in arguments: self._add_line("_{} = '{}'".format(arg['pyname'], arg['type']), indent) if len(arguments): self._add_function('__init__', arguments, indent) indent += 4 self._add_line( '"""Initialize the :class:`{}.{}` class"""'.format( self._pep8_class_name(class_name), self._pep8_class_name(method['name'])), indent) # Function all_defaults = self._all_defaults(arguments) for arg in arguments: domain = self._domain(arg.get('domain', arg['name'])) default = self._arg_default(arg) if ((domain and not domain.nullable and domain.type == 'table') or arg['name'] == 'arguments' or (arg['type'] == 'table' and not default)): self._add_line('self.{} = {} or {{}}'.format( arg['pyname'], arg['pyname']), indent) elif default is not None \ and not all_defaults \ and default != '': self._add_line('self.{} = {} or {}'.format( arg['pyname'], arg['pyname'], default), indent) else: self._add_line('self.{} = {}'.format( arg['pyname'], arg['pyname']), indent) if deprecated: self._add_line('warnings.warn(', indent) self._add_line( 'constants.DEPRECATION_WARNING, ' 'category=DeprecationWarning)', indent + 4) add_validate = False for arg in arguments: domain = self._domain(arg.get('domain', arg['name'])) if ((arg['pyname'] in {'capabilities', 'channel_id', 'cluster_id', 'known_hosts', 'out_of_band', 'internal', 'insist', 'ticket'}) or domain and domain.max_length is not None or domain and domain.regex is not None): add_validate = True break if add_validate: self._add_line('self.validate()', indent) indent -=4 def format_deprecated_value( field_name: str, operator: str, expected_value: typing.Union[str, bool, int], error_value: str) -> None: self._add_line( "if self.{} is not None and self.{} {} {}:".format( field_name, field_name, operator, expected_value), indent) self._add_line( "raise ValueError('{} must be {}')".format( field_name, error_value), indent + 4) if add_validate: self._add_function('validate', [], indent) indent += 4 self._add_comment( '"""Validate the frame data ensuring all domains or ' 'attributes adhere to the protocol specification.', indent, '') self._add_line() self._add_line(':raises ValueError: on validation error', indent) self._add_line() self._add_line('"""', indent) for arg in arguments: domain = self._domain(arg.get('domain', arg['name'])) if arg['pyname'] in {'capabilities', 'cluster_id', 'known_hosts'}: format_deprecated_value( arg['pyname'], '!=', "''", 'empty') continue elif arg['name'] in {'internal', 'insist'}: format_deprecated_value(arg['pyname'], 'is not', 'False', 'False') continue elif arg['pyname'] == 'ticket': format_deprecated_value(arg['pyname'], '!=', '0', '0') continue elif arg['pyname'] in {'channel_id', 'out_of_band'}: format_deprecated_value( arg['pyname'], '!=', "'0'", '0') continue if domain and domain.max_length is not None: self._add_line( 'if self.{} is not None and ' 'len(self.{}) > {}:'.format( arg['pyname'], arg['pyname'], domain.max_length), indent) line = ("raise ValueError('Max length " "exceeded for {}')".format(arg['pyname'])) self._add_line(line, indent + 4) if domain and domain.regex is not None: self._add_line( 'if self.{} is not None and ' "not constants.DOMAIN_REGEX['{}']" ".fullmatch(self.{}):".format( arg['pyname'], domain.name, arg['pyname']), indent) line = 'raise ValueError(' \ "'Invalid value for {}')".format(arg['pyname']) self._add_line(line, indent + 4) self._add_line() def _build_commands(self): LOGGER.info('Generating %s', COMMANDS) self._output_buffer = [COMMANDS_HEADER] for name in [c['name'] for c in self._amqp_json['classes'] if c['name'] not in CODEGEN_IGNORE_CLASSES]: indent = 4 definition = self._class_definition(name) self._add_line() self._add_line('class %s:' % self._pep8_class_name(name)) documentation = self._documentation({'class': name}) label = self._label({'class': name}) or 'Undefined label' if documentation: self._add_documentation(label, documentation, indent) self._add_line('__slots__: typing.List[str] = []', indent) self._add_line() self._add_line('frame_id = {} # AMQP Frame ID'.format( definition['id']), indent) self._add_line('index = 0x%08X # pamqp Mapping Index' % ( definition['id'] << 16), indent) self._add_line() for method_defn in definition['methods']: self._build_command_method(name, definition['id'], method_defn) if 'properties' in definition and definition['properties']: self._build_command_basic_properties( name, definition['id'], definition['properties']) self._build_command_map() self._write_file(COMMANDS) def _build_constants(self): LOGGER.info('Generating %s', CONSTANTS) def _build_domain_output() -> typing.Tuple[typing.List[str], typing.List[str], typing.List[str]]: data_types, domains, dom_regex = [], [], [] for domain in self._domains(): if domain.name == domain.type: value = " '{}',".format(domain.name) comment = '{} {}'.format('#'.rjust(28 - len(value)), domain.label) data_types.append('{}{}'.format(value, comment)) else: domains.append(" '{}': '{}',".format( domain.name, domain.type)) if domain.regex: dom_regex.append( " '{}': re.compile(r'{}'),".format( domain.name, domain.regex)) data_types[0] = data_types[0].replace( ' ', 'DATA_TYPES = [') data_types[-1] = data_types[-1].replace(',', ' ') data_types.append(']') domains[0] = domains[0].replace(' ', 'DOMAINS = {') domains[-1] = domains[-1].replace(',', '}') dom_regex[0] = dom_regex[0].replace( ' ', 'DOMAIN_REGEX = {') dom_regex[-1] = dom_regex[-1].replace(',', '}') return data_types, domains, dom_regex self._output_buffer = [CONSTANTS_HEADER] self._add_comment('AMQP Protocol Frame Prefix') self._add_line("AMQP = b'AMQP'") self._add_line() self._add_comment('AMQP Protocol Version') self._add_line('VERSION = ({}, {}, {})'.format( self._amqp_json['major-version'], self._amqp_json['minor-version'], self._amqp_json['revision'])) self._add_line() # Defaults self._add_comment('RabbitMQ Defaults') self._add_line("DEFAULT_HOST = 'localhost'") self._add_line('DEFAULT_PORT = {}'.format(self._amqp_json['port'])) self._add_line("DEFAULT_USER = 'guest'") self._add_line("DEFAULT_PASS = 'guest'") self._add_line("DEFAULT_VHOST = '/'") self._add_line() # Constant self._add_comment('AMQP Constants') for constant in self._amqp_json['constants']: if 'class' not in constant: documentation = self._documentation( {'constant': constant['name'].lower()}) if documentation: self._add_comment(documentation) self._add_line('{} = {}'.format( self._dashify(constant['name']), constant['value'])) self._add_line() self._add_comment('Not included in the spec XML or JSON files.') self._add_line("FRAME_END_CHAR = b'\\xce'") self._add_line('FRAME_HEADER_SIZE = 7') self._add_line('FRAME_MAX_SIZE = 131072') self._add_line() data_type_output, domain_output, domain_regex = _build_domain_output() self._add_comment('AMQP data types') self._output_buffer += data_type_output self._add_line() self._add_comment('AMQP domains') self._output_buffer += domain_output self._add_line() self._add_comment('AMQP domain patterns') self._output_buffer += domain_regex self._add_line() self._add_comment('Other constants') self._add_line("DEPRECATION_WARNING = '{}'".format( self.DEPRECATION_WARNING)) self._add_line() self._write_file(CONSTANTS) def _build_exceptions(self): LOGGER.info('Generating %s', EXCEPTIONS) self._output_buffer = [EXCEPTIONS_HEADER] errors = {} for constant in self._amqp_json['constants']: if 'class' in constant: class_name = self._classify(constant['name']) if constant['class'] == 'soft-error': extends = 'AMQPSoftError' elif constant['class'] == 'hard-error': extends = 'AMQPHardError' else: raise ValueError('Unexpected class: %s', constant['class']) self._add_line('class AMQP{}({}):'.format(class_name, extends)) self._add_line(' """') documentation = self._documentation( {'constant': constant['name'].lower()}) if documentation: self._add_comment(documentation, 4, '') else: if extends == 'AMQPSoftError': self._add_line(' Undocumented AMQP Soft Error') else: self._add_line(' Undocumented AMQP Hard Error') self._add_line() self._add_line(' """') self._add_line(" name = '%s'" % constant['name']) self._add_line(' value = %i' % constant['value']) self._add_line() self._add_line() errors[constant['value']] = class_name error_lines = [] for error_code in errors.keys(): error_lines.append( ' {}: AMQP{},'.format(error_code, errors[error_code])) self._add_comment('AMQP Error code to class mapping') error_lines[0] = error_lines[0].replace( ' ', 'CLASS_MAPPING = {') error_lines[-1] = error_lines[-1].replace(',', '}') self._output_buffer += error_lines self._write_file(EXCEPTIONS) def _class_definition(self, name: str) -> dict: """Iterates through classes trying to match the name against what was passed in. """ for definition in self._amqp_json['classes']: if definition['name'] == name: for method in definition['methods']: for index, ar in enumerate(method['arguments']): method['arguments'][index].update( self._lookup_field( name, method['name'], ar['name'])) return definition @staticmethod def _classify(name: str) -> str: """Replace the AMQP constant with a more pythonic classname""" return ''.join([part.title() for part in name.split('-')]) @functools.lru_cache() def _commands(self) -> typing.List[str]: """Create a list of commands to replace in documentation with links to the appropriate classes. """ commands = {'Basic.Properties': 'Basic.Properties'} for name in set([c['name'] for c in self._amqp_json['classes'] if c['name'] not in CODEGEN_IGNORE_CLASSES]): definition = self._class_definition(name) for method in definition['methods']: method_name = '-'.join( [part.title() for part in method['name'].split('-')]) command = '{}.{}'.format(name.title(), method_name) if '-' in command: commands[command] = command.replace('-', '') else: commands[command] = command return commands @staticmethod def _dashify(value: str) -> str: """Replace ``-`` with ``_`` for the passed in text""" return value.replace('-', '_') def _documentation(self, search_path: dict) -> typing.Optional[str]: """Find the documentation in the xpath""" def strip_whitespace(value: typing.List[lxml.etree.Element]) -> str: return ' '.join( [dl.strip() for dl in value[0].text.split('\n')]).strip() node = self._search_xml(search_path, 'doc') if node: return strip_whitespace(node) elif 'field' in search_path: # Look elsewhere domain = self._domain(search_path['field']) if domain and domain.documentation: return domain.documentation node = self._search_xml(search_path, 'doc') if node: return strip_whitespace(node) node = self._search_xml( {'field': search_path['field']}, 'doc', True) if node: return strip_whitespace(node) LOGGER.warning('Could not find documentation for %r', search_path) @functools.lru_cache() def _domain(self, value: str) -> typing.Optional[Domain]: for domain in self._domains(): if domain.name == value: return domain @functools.lru_cache() def _domains(self) -> typing.List[Domain]: values = [] for value in self._amqp_json['domains']: domain = self._codegen_xml.xpath( '//amqp/domain[@name="{}"]'.format(value[0])) extension = self._extensions_xml.xpath( '//rabbitmq/domain[@name="{}"]'.format(value[0])) doc = self._codegen_xml.xpath( '//amqp/domain[@name="{}"]/doc'.format(value[0])) kwargs = { 'name': value[0], 'type': value[1], 'documentation': self._remove_extra_whitespace(doc[0].text) if doc else None, 'label': self._remove_extra_whitespace( domain[0].attrib.get('label', '') if domain else ''), 'nullable': True, 'regex': None, 'max_length': None, 'default_value': domain[0].attrib.get('default-value', None) if domain else None } if extension \ and extension[0].attrib.get('default-value') is not None: kwargs['default_value'] = \ extension[0].attrib.get('default-value') for assertion in self._codegen_xml.xpath( '//amqp/domain[@name="{}"]/assert'.format(value[0])) or []: if assertion.attrib.get('check') == 'length': kwargs['max_length'] = int(assertion.attrib['value']) elif assertion.attrib.get('check') == 'notnull': kwargs['nullable'] = False elif assertion.attrib.get('check') == 'regexp': kwargs['regex'] = assertion.attrib['value'] for assertion in self._extensions_xml.xpath( '//rabbitmq/domain[@name="{}"]/assert'.format( value[0])) or []: if assertion.attrib.get('check') == 'length': kwargs['max_length'] = int(assertion.attrib['value']) elif assertion.attrib.get('check') == 'notnull': kwargs['nullable'] = False elif assertion.attrib.get('check') == 'regexp': kwargs['regex'] = assertion.attrib['value'] values.append(Domain(**kwargs)) return values def _label(self, search_path: dict) -> typing.Optional[str]: """Attempt to return the short label documentation""" node = self._search_xml(search_path) if node and 'label' in node[0].attrib: return '{}{}'.format( node[0].attrib['label'][0:1].upper(), node[0].attrib['label'][1:]) elif node and node[0].text: return '{}{}'.format(node[0].text.strip()[0:1].upper(), node[0].text.strip()[1:].strip()) if 'field' in search_path: # Look in domains and extensions domain = self._domain(search_path['field']) if domain and domain.label: return '{}{}'.format(domain.label[0:1].upper(), domain.label[1:].strip()) node = self._search_xml(search_path, only_extensions=True) if node and 'label' in node[0].attrib: return '{}{}'.format( node[0].attrib['label'][0:1].upper(), node[0].attrib['label'][1:]) node = self._search_xml({'field': search_path['field']}) if node and 'label' in node[0].attrib: return '{}{}'.format( node[0].attrib['label'][0:1].upper(), node[0].attrib['label'][1:]) LOGGER.warning('Could not find label for %r', search_path) @staticmethod def _load_codegen_json() -> dict: if not CODEGEN_JSON.exists(): print('Downloading codegen JSON file to %s.' % CODEGEN_JSON) response = requests.get(CODEGEN_JSON_URL) if not response.ok: print('Error downloading JSON file: {}'.format(response)) sys.exit(1) with CODEGEN_JSON.open('w') as handle: handle.write(response.content.decode('utf-8')) with CODEGEN_JSON.open('r') as handle: return json.load(handle) @staticmethod def _load_codegen_xml() -> lxml.etree.Element: if not CODEGEN_XML.exists(): print('Downloading codegen XML file.') response = requests.get(CODEGEN_XML_URL) if not response.ok: print('Error downloading XML file: {}'.format(response)) sys.exit(1) with CODEGEN_XML.open('w') as handle: handle.write(response.content.decode('utf-8')) with CODEGEN_XML.open('r') as handle: return lxml.etree.parse(handle).xpath('//amqp')[0] @staticmethod def _load_extensions_xml() -> lxml.etree.Element: with EXTENSIONS_XML.open('r') as handle: return lxml.etree.parse(handle).xpath('//rabbitmq')[0] def _lookup_field(self, class_name: str, method: str, field: str) -> dict: field_def = {} result = self._codegen_xml.xpath( '//amqp/class[@name="{}"]/method[@name="{}"]/' 'field[@name="{}"]'.format(class_name, method, field)) if result: for attribute in result[0].attrib: field_def[attribute] = result[0].attrib[attribute] result = self._extensions_xml.xpath( '//rabbitmq/class[@name="{}"]/method[@name="{}"]/' 'field[@name="{}"]'.format(class_name, method, field)) if result: for attribute in result[0].attrib: field_def[attribute] = result[0].attrib[attribute] return field_def @staticmethod def _pep8_class_name(value: str) -> str: """Returns a class name in the proper case per PEP8""" return_value = [] parts = value.split('-') for part in parts: return_value.append(part[0:1].upper() + part[1:]) return ''.join(return_value) @staticmethod def _remove_extra_whitespace(value: str) -> str: return ' '.join([dl.strip() for dl in value.split('\n')]).strip() def reset_output_buffer(self) -> None: self._output_buffer = [] def _search_xml(self, search_path: dict, suffix: typing.Optional[str] = None, only_extensions: bool = False) \ -> typing.Optional[lxml.etree.Element]: search = [] for key in XPATH_ORDER: if key in search_path: search.append('%s[@name="%s"]' % (key, search_path[key])) if suffix: search.append(suffix) # LOGGER.debug('Searching for %s', '/'.join(search)) if only_extensions: return self._extensions_xml.xpath('/'.join(search)) codegen = self._codegen_xml.xpath('/'.join(search)) extension = self._extensions_xml.xpath('/'.join(search)) if extension: codegen.extend(extension) return codegen def _write_file(self, path: pathlib.Path) -> None: if self.YAPF: code = yapf_api.FormatCode( '\n'.join(self._output_buffer), style_config='pep8')[0] else: code = '\n'.join(self._output_buffer) with path.open('w') as handle: handle.write(code) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) Codegen().build()