From 77e23231e09699c4e876f2fcc936c2eb873eaa29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Schneider?= Date: Sat, 1 Nov 2014 18:57:58 +0100 Subject: [PATCH] Cleanning debian branch --- Changelog | 484 ----- LICENSE | 458 ---- MANIFEST.in | 6 - PKG-INFO | 128 -- README.rst | 104 - amqp.egg-info/PKG-INFO | 128 -- amqp.egg-info/SOURCES.txt | 67 - amqp.egg-info/dependency_links.txt | 1 - amqp.egg-info/not-zip-safe | 1 - amqp.egg-info/top_level.txt | 1 - amqp/__init__.py | 70 - amqp/abstract_channel.py | 93 - amqp/basic_message.py | 124 -- amqp/channel.py | 2537 ---------------------- amqp/connection.py | 1004 --------- amqp/exceptions.py | 258 --- amqp/five.py | 188 -- amqp/method_framing.py | 231 -- amqp/protocol.py | 13 - amqp/serialization.py | 510 ----- amqp/transport.py | 294 --- amqp/utils.py | 102 - demo/amqp_clock.py | 78 - demo/demo_receive.py | 83 - demo/demo_send.py | 66 - docs/.static/.keep | 0 docs/.templates/page.html | 4 - docs/.templates/sidebarintro.html | 4 - docs/.templates/sidebarlogo.html | 3 - docs/Makefile | 81 - docs/_ext/applyxrefs.py | 92 - docs/_ext/literals_to_xrefs.py | 173 -- docs/_theme/celery/static/celery.css_t | 401 ---- docs/_theme/celery/theme.conf | 5 - docs/changelog.rst | 484 ----- docs/conf.py | 127 -- docs/includes/intro.txt | 96 - docs/index.rst | 22 - docs/reference/amqp.abstract_channel.rst | 11 - docs/reference/amqp.basic_message.rst | 11 - docs/reference/amqp.channel.rst | 11 - docs/reference/amqp.connection.rst | 11 - docs/reference/amqp.exceptions.rst | 11 - docs/reference/amqp.five.rst | 11 - docs/reference/amqp.method_framing.rst | 11 - docs/reference/amqp.protocol.rst | 11 - docs/reference/amqp.serialization.rst | 11 - docs/reference/amqp.transport.rst | 11 - docs/reference/amqp.utils.rst | 11 - docs/reference/index.rst | 23 - docs/templates/readme.txt | 5 - extra/README | 10 - extra/generate_skeleton_0_8.py | 377 ---- extra/release/bump_version.py | 181 -- extra/release/sphinx-to-rst.py | 75 - extra/update_comments_from_spec.py | 76 - funtests/run_all.py | 38 - funtests/settings.py | 91 - funtests/test_basic_message.py | 132 -- funtests/test_channel.py | 317 --- funtests/test_connection.py | 127 -- funtests/test_exceptions.py | 47 - funtests/test_serialization.py | 411 ---- funtests/test_with.py | 70 - requirements/docs.txt | 2 - requirements/pkgutils.txt | 5 - requirements/test.txt | 5 - setup.cfg | 5 - setup.py | 132 -- 69 files changed, 10771 deletions(-) delete mode 100644 Changelog delete mode 100644 LICENSE delete mode 100644 MANIFEST.in delete mode 100644 PKG-INFO delete mode 100644 README.rst delete mode 100644 amqp.egg-info/PKG-INFO delete mode 100644 amqp.egg-info/SOURCES.txt delete mode 100644 amqp.egg-info/dependency_links.txt delete mode 100644 amqp.egg-info/not-zip-safe delete mode 100644 amqp.egg-info/top_level.txt delete mode 100644 amqp/__init__.py delete mode 100644 amqp/abstract_channel.py delete mode 100644 amqp/basic_message.py delete mode 100644 amqp/channel.py delete mode 100644 amqp/connection.py delete mode 100644 amqp/exceptions.py delete mode 100644 amqp/five.py delete mode 100644 amqp/method_framing.py delete mode 100644 amqp/protocol.py delete mode 100644 amqp/serialization.py delete mode 100644 amqp/transport.py delete mode 100644 amqp/utils.py delete mode 100755 demo/amqp_clock.py delete mode 100755 demo/demo_receive.py delete mode 100755 demo/demo_send.py delete mode 100644 docs/.static/.keep delete mode 100644 docs/.templates/page.html delete mode 100644 docs/.templates/sidebarintro.html delete mode 100644 docs/.templates/sidebarlogo.html delete mode 100644 docs/Makefile delete mode 100644 docs/_ext/applyxrefs.py delete mode 100644 docs/_ext/literals_to_xrefs.py delete mode 100644 docs/_theme/celery/static/celery.css_t delete mode 100644 docs/_theme/celery/theme.conf delete mode 100644 docs/changelog.rst delete mode 100644 docs/conf.py delete mode 100644 docs/includes/intro.txt delete mode 100644 docs/index.rst delete mode 100644 docs/reference/amqp.abstract_channel.rst delete mode 100644 docs/reference/amqp.basic_message.rst delete mode 100644 docs/reference/amqp.channel.rst delete mode 100644 docs/reference/amqp.connection.rst delete mode 100644 docs/reference/amqp.exceptions.rst delete mode 100644 docs/reference/amqp.five.rst delete mode 100644 docs/reference/amqp.method_framing.rst delete mode 100644 docs/reference/amqp.protocol.rst delete mode 100644 docs/reference/amqp.serialization.rst delete mode 100644 docs/reference/amqp.transport.rst delete mode 100644 docs/reference/amqp.utils.rst delete mode 100644 docs/reference/index.rst delete mode 100644 docs/templates/readme.txt delete mode 100644 extra/README delete mode 100755 extra/generate_skeleton_0_8.py delete mode 100755 extra/release/bump_version.py delete mode 100755 extra/release/sphinx-to-rst.py delete mode 100644 extra/update_comments_from_spec.py delete mode 100755 funtests/run_all.py delete mode 100644 funtests/settings.py delete mode 100755 funtests/test_basic_message.py delete mode 100755 funtests/test_channel.py delete mode 100755 funtests/test_connection.py delete mode 100755 funtests/test_exceptions.py delete mode 100755 funtests/test_serialization.py delete mode 100644 funtests/test_with.py delete mode 100644 requirements/docs.txt delete mode 100644 requirements/pkgutils.txt delete mode 100644 requirements/test.txt delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/Changelog b/Changelog deleted file mode 100644 index e036942..0000000 --- a/Changelog +++ /dev/null @@ -1,484 +0,0 @@ -Changes -======= - -py-amqp is fork of amqplib used by Kombu containing additional features and improvements. -The previous amqplib changelog is here: -http://code.google.com/p/py-amqplib/source/browse/CHANGES - -.. _version-1.4.5: - -1.4.5 -===== -:release-date: 2014-04-15 09:00 P.M UTC -:release-by: Ask Solem - -- Can now deserialize more AMQP types. - - Now handles types ``short string``, ``short short int``, - ``short short unsigned int``, ``short int``, ``short unsigned int``, - ``long unsigned int``, ``long long int``, ``long long unsigned int`` - and ``float`` which for some reason was missing, even in the original - amqplib module. - -- SSL: Workaround for Python SSL bug. - - A bug in the python socket library causes ``ssl.read/write()`` - on a closed socket to raise :exc:`AttributeError` instead of - :exc:`IOError`. - - Fix contributed by Craig Jellick. - -- ``Transport.__del_`` now handles errors occurring at late interpreter - shutdown (Issue #36). - -.. _version-1.4.4: - -1.4.4 -===== -:release-date: 2014-03-03 04:00 P.M UTC -:release-by: Ask Solem - -- SSL transport accidentally disconnected after read timeout. - - Fix contributed by Craig Jellick. - -.. _version-1.4.3: - -1.4.3 -===== -:release-date: 2014-02-09 03:00 P.M UTC -:release-by: Ask Solem - -- Fixed bug where more data was requested from the socket - than was actually needed. - - Contributed by Ionel Cristian Mărieș. - -.. _version-1.4.2: - -1.4.2 -===== -:release-date: 2014-01-23 05:00 P.M UTC - -- Heartbeat negotiation would use heartbeat value from server even - if heartbeat disabled (Issue #31). - -.. _version-1.4.1: - -1.4.1 -===== -:release-date: 2014-01-14 09:30 P.M UTC -:release-by: Ask Solem - -- Fixed error occurring when heartbeats disabled. - -.. _version-1.4.0: - -1.4.0 -===== -:release-date: 2014-01-13 03:00 P.M UTC -:release-by: Ask Solem - -- Heartbeat implementation improved (Issue #6). - - The new heartbeat behavior is the same approach as taken by the - RabbitMQ java library. - - This also means that clients should preferably call the ``heartbeat_tick`` - method more frequently (like every second) instead of using the old - ``rate`` argument (which is now ignored). - - - Heartbeat interval is negotiated with the server. - - Some delay is allowed if the heartbeat is late. - - Monotonic time is used to keep track of the heartbeat - instead of relying on the caller to call the checking function - at the right time. - - Contributed by Dustin J. Mitchell. - -- NoneType is now supported in tables and arrays. - - Contributed by Dominik Fässler. - -- SSLTransport: Now handles ``ENOENT``. - - Fix contributed by Adrien Guinet. - -.. _version-1.3.3: - -1.3.3 -===== -:release-date: 2013-11-11 03:30 P.M UTC -:release-by: Ask Solem - -- SSLTransport: Now keeps read buffer if an exception is raised - (Issue #26). - - Fix contributed by Tommie Gannert. - -.. _version-1.3.2: - -1.3.2 -===== -:release-date: 2013-10-29 02:00 P.M UTC -:release-by: Ask Solem - -- Message.channel is now a channel object (not the channel id). - -- Bug in previous version caused the socket to be flagged as disconnected - at EAGAIN/EINTR. - -.. _version-1.3.1: - -1.3.1 -===== -:release-date: 2013-10-24 04:00 P.M UTC -:release-by: Ask Solem - -- Now implements Connection.connected (Issue #22). - -- Fixed bug where ``str(AMQPError)`` did not return string. - -.. _version-1.3.0: - -1.3.0 -===== -:release-date: 2013-09-04 02:39 P.M UTC -:release-by: Ask Solem - -- Now sets ``Message.channel`` on delivery (Issue #12) - - amqplib used to make the channel object available - as ``Message.delivery_info['channel']``, but this was removed - in py-amqp. librabbitmq sets ``Message.channel``, - which is a more reasonable solution in our opinion as that - keeps the delivery info intact. - -- New option to wait for publish confirmations (Issue #3) - - There is now a new Connection ``confirm_publish`` that will - force any ``basic_publish`` call to wait for confirmation. - - Enabling publisher confirms like this degrades performance - considerably, but can be suitable for some applications - and now it's possible by configuration. - -- ``queue_declare`` now returns named tuple of type - :class:`~amqp.protocol.basic_declare_ok_t`. - - Supporting fields: ``queue``, ``message_count``, and - ``consumer_count``. - -- Contents of ``Channel.returned_messages`` is now named tuples. - - Supporting fields: ``reply_code``, ``reply_text``, ``exchange``, - ``routing_key``, and ``message``. - -- Sockets now set to close on exec using the ``FD_CLOEXEC`` flag. - - Currently only supported on platforms supporting this flag, - which does not include Windows. - - Contributed by Tommie Gannert. - -.. _version-1.2.1: - -1.2.1 -===== -:release-date: 2013-08-16 05:30 P.M UTC -:release-by: Ask Solem - -- Adds promise type: :meth:`amqp.utils.promise` - -- Merges fixes from 1.0.x - -.. _version-1.2.0: - -1.2.0 -===== -:release-date: 2012-11-12 04:00 P.M UTC -:release-by: Ask Solem - -- New exception hierarchy: - - - :class:`~amqp.AMQPError` - - :class:`~amqp.ConnectionError` - - :class:`~amqp.RecoverableConnectionError` - - :class:`~amqp.ConsumerCancelled` - - :class:`~amqp.ConnectionForced` - - :class:`~amqp.ResourceError` - - :class:`~IrrecoverableConnectionError` - - :class:`~amqp.ChannelNotOpen` - - :class:`~amqp.FrameError` - - :class:`~amqp.FrameSyntaxError` - - :class:`~amqp.InvalidCommand` - - :class:`~amqp.InvalidPath` - - :class:`~amqp.NotAllowed` - - :class:`~amqp.UnexpectedFrame` - - :class:`~amqp.AMQPNotImplementedError` - - :class:`~amqp.InternalError` - - :class:`~amqp.ChannelError` - - :class:`~RecoverableChannelError` - - :class:`~amqp.ContentTooLarge` - - :class:`~amqp.NoConsumers` - - :class:`~amqp.ResourceLocked` - - :class:`~IrrecoverableChannelError` - - :class:`~amqp.AccessRefused` - - :class:`~amqp.NotFound` - - :class:`~amqp.PreconditionFailed` - - -.. _version-1.1.0: - -1.1.0 -===== -:release-date: 2012-11-08 10:36 P.M UTC -:release-by: Ask Solem - -- No longer supports Python 2.5 - -- Fixed receiving of float table values. - -- Now Supports Python 3 and Python 2.6+ in the same source code. - -- Python 3 related fixes. - -.. _version-1.0.13: - -1.0.13 -====== -:release-date: 2013-07-31 04:00 P.M BST -:release-by: Ask Solem - -- Fixed problems with the SSL transport (Issue #15). - - Fix contributed by Adrien Guinet. - -- Small optimizations - -.. _version-1.0.12: - -1.0.12 -====== -:release-date: 2013-06-25 02:00 P.M BST -:release-by: Ask Solem - -- Fixed another Python 3 compatibility problem. - -.. _version-1.0.11: - -1.0.11 -====== -:release-date: 2013-04-11 06:00 P.M BST -:release-by: Ask Solem - -- Fixed Python 3 incompatibility in ``amqp/transport.py``. - -.. _version-1.0.10: - -1.0.10 -====== -:release-date: 2013-03-21 03:30 P.M UTC -:release-by: Ask Solem - -- Fixed Python 3 incompatibility in ``amqp/serialization.py``. - (Issue #11). - -.. _version-1.0.9: - -1.0.9 -===== -:release-date: 2013-03-08 10:40 A.M UTC -:release-by: Ask Solem - -- Publisher ack callbacks should now work after typo fix (Issue #9). - -- ``channel(explicit_id)`` will now claim that id from the array - of unused channel ids. - -- Fixes Jython compatibility. - -.. _version-1.0.8: - -1.0.8 -===== -:release-date: 2013-02-08 01:00 P.M UTC -:release-by: Ask Solem - -- Fixed SyntaxError on Python 2.5 - -.. _version-1.0.7: - -1.0.7 -===== -:release-date: 2013-02-08 01:00 P.M UTC -:release-by: Ask Solem - -- Workaround for bug on some Python 2.5 installations where (2**32) is 0. - -- Can now serialize the ARRAY type. - - Contributed by Adam Wentz. - -- Fixed tuple format bug in exception (Issue #4). - -.. _version-1.0.6: - -1.0.6 -===== -:release-date: 2012-11-29 01:14 P.M UTC -:release-by: Ask Solem - -- ``Channel.close`` is now ignored if the connection attribute is None. - -.. _version-1.0.5: - -1.0.5 -===== -:release-date: 2012-11-21 04:00 P.M UTC -:release-by: Ask Solem - -- ``Channel.basic_cancel`` is now ignored if the channel was already closed. - -- ``Channel.events`` is now a dict of sets:: - - >>> channel.events['basic_return'].add(on_basic_return) - >>> channel.events['basic_return'].discard(on_basic_return) - -.. _version-1.0.4: - -1.0.4 -===== -:release-date: 2012-11-13 04:00 P.M UTC -:release-by: Ask Solem - -- Fixes Python 2.5 support - -.. _version-1.0.3: - -1.0.3 -===== -:release-date: 2012-11-12 04:00 P.M UTC -:release-by: Ask Solem - -- Now can also handle float in headers/tables when receiving messages. - -- Now uses :class:`array.array` to keep track of unused channel ids. - -- The :data:`~amqp.exceptions.METHOD_NAME_MAP` has been updated for - amqp/0.9.1 and Rabbit extensions. - -- Removed a bunch of accidentally included images. - -.. _version-1.0.2: - -1.0.2 -===== -:release-date: 2012-11-06 05:00 P.M UTC -:release-by: Ask Solem - -- Now supports float values in headers/tables. - -.. _version-1.0.1: - -1.0.1 -===== -:release-date: 2012-11-05 01:00 P.M UTC -:release-by: Ask Solem - -- Connection errors no longer includes :exc:`AttributeError`. - -- Fixed problem with using the SSL transport in a non-blocking context. - - Fix contributed by Mher Movsisyan. - - -.. _version-1.0.0: - -1.0.0 -===== -:release-date: 2012-11-05 01:00 P.M UTC -:release-by: Ask Solem - -- Channels are now restored on channel error, so that the connection does not - have to closed. - -.. _version-0.9.4: - -Version 0.9.4 -============= - -- Adds support for ``exchange_bind`` and ``exchange_unbind``. - - Contributed by Rumyana Neykova - -- Fixed bugs in funtests and demo scripts. - - Contributed by Rumyana Neykova - -.. _version-0.9.3: - -Version 0.9.3 -============= - -- Fixed bug that could cause the consumer to crash when reading - large message payloads asynchronously. - -- Serialization error messages now include the invalid value. - -.. _version-0.9.2: - -Version 0.9.2 -============= - -- Consumer cancel notification support was broken (Issue #1) - - Fix contributed by Andrew Grangaard - -.. _version-0.9.1: - -Version 0.9.1 -============= - -- Supports draining events from multiple channels (``Connection.drain_events``) -- Support for timeouts -- Support for heartbeats - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. -- Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. -- Support for ``basic_return`` -- Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. -- Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". -- Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. -- Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. -- Exposes the underlying socket as ``Connection.sock``. -- Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. -- Slightly better at error recovery diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 3b473db..0000000 --- a/LICENSE +++ /dev/null @@ -1,458 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index d17e9d2..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include README.rst Changelog LICENSE -recursive-include docs * -recursive-include demo *.py -recursive-include extra README *.py -recursive-include funtests *.py -recursive-include requirements *.txt diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index c71dc31..0000000 --- a/PKG-INFO +++ /dev/null @@ -1,128 +0,0 @@ -Metadata-Version: 1.1 -Name: amqp -Version: 1.4.5 -Summary: Low-level AMQP client for Python (fork of amqplib) -Home-page: http://github.com/celery/py-amqp -Author: Ask Solem -Author-email: pyamqp@celeryproject.org -License: LGPL -Description: ===================================================================== - Python AMQP 0.9.1 client library - ===================================================================== - - :Version: 1.4.5 - :Web: http://amqp.readthedocs.org/ - :Download: http://pypi.python.org/pypi/amqp/ - :Source: http://github.com/celery/py-amqp/ - :Keywords: amqp, rabbitmq - - About - ===== - - This is a fork of amqplib_ which was originally written by Barry Pederson. - It is maintained by the Celery_ project, and used by `kombu`_ as a pure python - alternative when `librabbitmq`_ is not available. - - This library should be API compatible with `librabbitmq`_. - - .. _amqplib: http://pypi.python.org/pypi/amqplib - .. _Celery: http://celeryproject.org/ - .. _kombu: http://kombu.readthedocs.org/ - .. _librabbitmq: http://pypi.python.org/pypi/librabbitmq - - Differences from `amqplib`_ - =========================== - - - Supports draining events from multiple channels (``Connection.drain_events``) - - Support for timeouts - - Channels are restored after channel error, instead of having to close the - connection. - - Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. - - Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Support for ``basic_return`` - - Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. - - Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". - - Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. - - Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. - - Exposes the underlying socket as ``Connection.sock``. - - Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. - - Slightly better at error recovery - - Further - ======= - - - Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - - - AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - - - RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - - - For more information about AMQP, visit - - http://www.amqp.org - - - For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev - - .. image:: https://d2weczhvl823v0.cloudfront.net/celery/celery/trend.png - :alt: Bitdeli badge - :target: https://bitdeli.com/free - -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.0 -Classifier: Programming Language :: Python :: 3.1 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent diff --git a/README.rst b/README.rst deleted file mode 100644 index 661d6bd..0000000 --- a/README.rst +++ /dev/null @@ -1,104 +0,0 @@ -===================================================================== - Python AMQP 0.9.1 client library -===================================================================== - -:Version: 1.4.5 -:Web: http://amqp.readthedocs.org/ -:Download: http://pypi.python.org/pypi/amqp/ -:Source: http://github.com/celery/py-amqp/ -:Keywords: amqp, rabbitmq - -About -===== - -This is a fork of amqplib_ which was originally written by Barry Pederson. -It is maintained by the Celery_ project, and used by `kombu`_ as a pure python -alternative when `librabbitmq`_ is not available. - -This library should be API compatible with `librabbitmq`_. - -.. _amqplib: http://pypi.python.org/pypi/amqplib -.. _Celery: http://celeryproject.org/ -.. _kombu: http://kombu.readthedocs.org/ -.. _librabbitmq: http://pypi.python.org/pypi/librabbitmq - -Differences from `amqplib`_ -=========================== - -- Supports draining events from multiple channels (``Connection.drain_events``) -- Support for timeouts -- Channels are restored after channel error, instead of having to close the - connection. -- Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. -- Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. -- Support for ``basic_return`` -- Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. -- Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". -- Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. -- Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. -- Exposes the underlying socket as ``Connection.sock``. -- Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. -- Slightly better at error recovery - -Further -======= - -- Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - -- AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - -- RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - -- For more information about AMQP, visit - - http://www.amqp.org - -- For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev - -.. image:: https://d2weczhvl823v0.cloudfront.net/celery/celery/trend.png - :alt: Bitdeli badge - :target: https://bitdeli.com/free diff --git a/amqp.egg-info/PKG-INFO b/amqp.egg-info/PKG-INFO deleted file mode 100644 index c71dc31..0000000 --- a/amqp.egg-info/PKG-INFO +++ /dev/null @@ -1,128 +0,0 @@ -Metadata-Version: 1.1 -Name: amqp -Version: 1.4.5 -Summary: Low-level AMQP client for Python (fork of amqplib) -Home-page: http://github.com/celery/py-amqp -Author: Ask Solem -Author-email: pyamqp@celeryproject.org -License: LGPL -Description: ===================================================================== - Python AMQP 0.9.1 client library - ===================================================================== - - :Version: 1.4.5 - :Web: http://amqp.readthedocs.org/ - :Download: http://pypi.python.org/pypi/amqp/ - :Source: http://github.com/celery/py-amqp/ - :Keywords: amqp, rabbitmq - - About - ===== - - This is a fork of amqplib_ which was originally written by Barry Pederson. - It is maintained by the Celery_ project, and used by `kombu`_ as a pure python - alternative when `librabbitmq`_ is not available. - - This library should be API compatible with `librabbitmq`_. - - .. _amqplib: http://pypi.python.org/pypi/amqplib - .. _Celery: http://celeryproject.org/ - .. _kombu: http://kombu.readthedocs.org/ - .. _librabbitmq: http://pypi.python.org/pypi/librabbitmq - - Differences from `amqplib`_ - =========================== - - - Supports draining events from multiple channels (``Connection.drain_events``) - - Support for timeouts - - Channels are restored after channel error, instead of having to close the - connection. - - Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. - - Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Support for ``basic_return`` - - Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. - - Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". - - Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. - - Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. - - Exposes the underlying socket as ``Connection.sock``. - - Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. - - Slightly better at error recovery - - Further - ======= - - - Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - - - AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - - - RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - - - For more information about AMQP, visit - - http://www.amqp.org - - - For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev - - .. image:: https://d2weczhvl823v0.cloudfront.net/celery/celery/trend.png - :alt: Bitdeli badge - :target: https://bitdeli.com/free - -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.0 -Classifier: Programming Language :: Python :: 3.1 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent diff --git a/amqp.egg-info/SOURCES.txt b/amqp.egg-info/SOURCES.txt deleted file mode 100644 index dd32cc2..0000000 --- a/amqp.egg-info/SOURCES.txt +++ /dev/null @@ -1,67 +0,0 @@ -Changelog -LICENSE -MANIFEST.in -README.rst -setup.py -amqp/__init__.py -amqp/abstract_channel.py -amqp/basic_message.py -amqp/channel.py -amqp/connection.py -amqp/exceptions.py -amqp/five.py -amqp/method_framing.py -amqp/protocol.py -amqp/serialization.py -amqp/transport.py -amqp/utils.py -amqp.egg-info/PKG-INFO -amqp.egg-info/SOURCES.txt -amqp.egg-info/dependency_links.txt -amqp.egg-info/not-zip-safe -amqp.egg-info/top_level.txt -demo/amqp_clock.py -demo/demo_receive.py -demo/demo_send.py -docs/Makefile -docs/changelog.rst -docs/conf.py -docs/index.rst -docs/.static/.keep -docs/.templates/page.html -docs/.templates/sidebarintro.html -docs/.templates/sidebarlogo.html -docs/_ext/applyxrefs.py -docs/_ext/literals_to_xrefs.py -docs/_theme/celery/theme.conf -docs/_theme/celery/static/celery.css_t -docs/includes/intro.txt -docs/reference/amqp.abstract_channel.rst -docs/reference/amqp.basic_message.rst -docs/reference/amqp.channel.rst -docs/reference/amqp.connection.rst -docs/reference/amqp.exceptions.rst -docs/reference/amqp.five.rst -docs/reference/amqp.method_framing.rst -docs/reference/amqp.protocol.rst -docs/reference/amqp.serialization.rst -docs/reference/amqp.transport.rst -docs/reference/amqp.utils.rst -docs/reference/index.rst -docs/templates/readme.txt -extra/README -extra/generate_skeleton_0_8.py -extra/update_comments_from_spec.py -extra/release/bump_version.py -extra/release/sphinx-to-rst.py -funtests/run_all.py -funtests/settings.py -funtests/test_basic_message.py -funtests/test_channel.py -funtests/test_connection.py -funtests/test_exceptions.py -funtests/test_serialization.py -funtests/test_with.py -requirements/docs.txt -requirements/pkgutils.txt -requirements/test.txt \ No newline at end of file diff --git a/amqp.egg-info/dependency_links.txt b/amqp.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/amqp.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/amqp.egg-info/not-zip-safe b/amqp.egg-info/not-zip-safe deleted file mode 100644 index 8b13789..0000000 --- a/amqp.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/amqp.egg-info/top_level.txt b/amqp.egg-info/top_level.txt deleted file mode 100644 index 5e610d3..0000000 --- a/amqp.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -amqp diff --git a/amqp/__init__.py b/amqp/__init__.py deleted file mode 100644 index 3bd57c9..0000000 --- a/amqp/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Low-level AMQP client for Python (fork of amqplib)""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -VERSION = (1, 4, 5) -__version__ = '.'.join(map(str, VERSION[0:3])) + ''.join(VERSION[3:]) -__author__ = 'Barry Pederson' -__maintainer__ = 'Ask Solem' -__contact__ = 'pyamqp@celeryproject.org' -__homepage__ = 'http://github.com/celery/py-amqp' -__docformat__ = 'restructuredtext' - -# -eof meta- - -# -# Pull in the public items from the various sub-modules -# -from .basic_message import Message # noqa -from .channel import Channel # noqa -from .connection import Connection # noqa -from .exceptions import ( # noqa - AMQPError, - ConnectionError, - RecoverableConnectionError, - IrrecoverableConnectionError, - ChannelError, - RecoverableChannelError, - IrrecoverableChannelError, - ConsumerCancelled, - ContentTooLarge, - NoConsumers, - ConnectionForced, - InvalidPath, - AccessRefused, - NotFound, - ResourceLocked, - PreconditionFailed, - FrameError, - FrameSyntaxError, - InvalidCommand, - ChannelNotOpen, - UnexpectedFrame, - ResourceError, - NotAllowed, - AMQPNotImplementedError, - InternalError, - error_for_code, - __all__ as _all_exceptions, -) -from .utils import promise # noqa - -__all__ = [ - 'Connection', - 'Channel', - 'Message', -] + _all_exceptions diff --git a/amqp/abstract_channel.py b/amqp/abstract_channel.py deleted file mode 100644 index 28cfe13..0000000 --- a/amqp/abstract_channel.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Code common to Connection and Channel objects.""" -# Copyright (C) 2007-2008 Barry Pederson ) -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -from .exceptions import AMQPNotImplementedError, RecoverableConnectionError -from .serialization import AMQPWriter - -__all__ = ['AbstractChannel'] - - -class AbstractChannel(object): - """Superclass for both the Connection, which is treated - as channel 0, and other user-created Channel objects. - - The subclasses must have a _METHOD_MAP class property, mapping - between AMQP method signatures and Python methods. - - """ - def __init__(self, connection, channel_id): - self.connection = connection - self.channel_id = channel_id - connection.channels[channel_id] = self - self.method_queue = [] # Higher level queue for methods - self.auto_decode = False - - def __enter__(self): - return self - - def __exit__(self, *exc_info): - self.close() - - def _send_method(self, method_sig, args=bytes(), content=None): - """Send a method for our channel.""" - conn = self.connection - if conn is None: - raise RecoverableConnectionError('connection already closed') - - if isinstance(args, AMQPWriter): - args = args.getvalue() - - conn.method_writer.write_method( - self.channel_id, method_sig, args, content, - ) - - def close(self): - """Close this Channel or Connection""" - raise NotImplementedError('Must be overriden in subclass') - - def wait(self, allowed_methods=None): - """Wait for a method that matches our allowed_methods parameter (the - default value of None means match any method), and dispatch to it.""" - method_sig, args, content = self.connection._wait_method( - self.channel_id, allowed_methods) - - return self.dispatch_method(method_sig, args, content) - - def dispatch_method(self, method_sig, args, content): - if content and \ - self.auto_decode and \ - hasattr(content, 'content_encoding'): - try: - content.body = content.body.decode(content.content_encoding) - except Exception: - pass - - try: - amqp_method = self._METHOD_MAP[method_sig] - except KeyError: - raise AMQPNotImplementedError( - 'Unknown AMQP method {0!r}'.format(method_sig)) - - if content is None: - return amqp_method(self, args) - else: - return amqp_method(self, args, content) - - #: Placeholder, the concrete implementations will have to - #: supply their own versions of _METHOD_MAP - _METHOD_MAP = {} diff --git a/amqp/basic_message.py b/amqp/basic_message.py deleted file mode 100644 index 192ede9..0000000 --- a/amqp/basic_message.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Messages for AMQP""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -from .serialization import GenericContent - -__all__ = ['Message'] - - -class Message(GenericContent): - """A Message for use with the Channnel.basic_* methods.""" - - #: Instances of this class have these attributes, which - #: are passed back and forth as message properties between - #: client and server - PROPERTIES = [ - ('content_type', 'shortstr'), - ('content_encoding', 'shortstr'), - ('application_headers', 'table'), - ('delivery_mode', 'octet'), - ('priority', 'octet'), - ('correlation_id', 'shortstr'), - ('reply_to', 'shortstr'), - ('expiration', 'shortstr'), - ('message_id', 'shortstr'), - ('timestamp', 'timestamp'), - ('type', 'shortstr'), - ('user_id', 'shortstr'), - ('app_id', 'shortstr'), - ('cluster_id', 'shortstr') - ] - - def __init__(self, body='', children=None, channel=None, **properties): - """Expected arg types - - body: string - children: (not supported) - - Keyword properties may include: - - content_type: shortstr - MIME content type - - content_encoding: shortstr - MIME content encoding - - application_headers: table - Message header field table, a dict with string keys, - and string | int | Decimal | datetime | dict values. - - delivery_mode: octet - Non-persistent (1) or persistent (2) - - priority: octet - The message priority, 0 to 9 - - correlation_id: shortstr - The application correlation identifier - - reply_to: shortstr - The destination to reply to - - expiration: shortstr - Message expiration specification - - message_id: shortstr - The application message identifier - - timestamp: datetime.datetime - The message timestamp - - type: shortstr - The message type name - - user_id: shortstr - The creating user id - - app_id: shortstr - The creating application id - - cluster_id: shortstr - Intra-cluster routing identifier - - Unicode bodies are encoded according to the 'content_encoding' - argument. If that's None, it's set to 'UTF-8' automatically. - - example:: - - msg = Message('hello world', - content_type='text/plain', - application_headers={'foo': 7}) - - """ - super(Message, self).__init__(**properties) - self.body = body - self.channel = channel - - def __eq__(self, other): - """Check if the properties and bodies of this Message and another - Message are the same. - - Received messages may contain a 'delivery_info' attribute, - which isn't compared. - - """ - try: - return (super(Message, self).__eq__(other) and - self.body == other.body) - except AttributeError: - return NotImplemented diff --git a/amqp/channel.py b/amqp/channel.py deleted file mode 100644 index 05eb09a..0000000 --- a/amqp/channel.py +++ /dev/null @@ -1,2537 +0,0 @@ -"""AMQP Channels""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -import logging - -from collections import defaultdict -from warnings import warn - -from .abstract_channel import AbstractChannel -from .exceptions import ChannelError, ConsumerCancelled, error_for_code -from .five import Queue -from .protocol import basic_return_t, queue_declare_ok_t -from .serialization import AMQPWriter - -__all__ = ['Channel'] - -AMQP_LOGGER = logging.getLogger('amqp') - -EXCHANGE_AUTODELETE_DEPRECATED = """\ -The auto_delete flag for exchanges has been deprecated and will be removed -from py-amqp v1.5.0.\ -""" - - -class VDeprecationWarning(DeprecationWarning): - pass - - -class Channel(AbstractChannel): - """Work with channels - - The channel class provides methods for a client to establish a - virtual connection - a channel - to a server and for both peers to - operate the virtual connection thereafter. - - GRAMMAR:: - - channel = open-channel *use-channel close-channel - open-channel = C:OPEN S:OPEN-OK - use-channel = C:FLOW S:FLOW-OK - / S:FLOW C:FLOW-OK - / functional-class - close-channel = C:CLOSE S:CLOSE-OK - / S:CLOSE C:CLOSE-OK - - """ - - def __init__(self, connection, channel_id=None, auto_decode=True): - """Create a channel bound to a connection and using the specified - numeric channel_id, and open on the server. - - The 'auto_decode' parameter (defaults to True), indicates - whether the library should attempt to decode the body - of Messages to a Unicode string if there's a 'content_encoding' - property for the message. If there's no 'content_encoding' - property, or the decode raises an Exception, the message body - is left as plain bytes. - - """ - if channel_id: - connection._claim_channel_id(channel_id) - else: - channel_id = connection._get_free_channel_id() - - AMQP_LOGGER.debug('using channel_id: %d', channel_id) - - super(Channel, self).__init__(connection, channel_id) - - self.is_open = False - self.active = True # Flow control - self.returned_messages = Queue() - self.callbacks = {} - self.cancel_callbacks = {} - self.auto_decode = auto_decode - self.events = defaultdict(set) - self.no_ack_consumers = set() - - # set first time basic_publish_confirm is called - # and publisher confirms are enabled for this channel. - self._confirm_selected = False - if self.connection.confirm_publish: - self.basic_publish = self.basic_publish_confirm - - self._x_open() - - def _do_close(self): - """Tear down this object, after we've agreed to close - with the server.""" - AMQP_LOGGER.debug('Closed channel #%d', self.channel_id) - self.is_open = False - channel_id, self.channel_id = self.channel_id, None - connection, self.connection = self.connection, None - if connection: - connection.channels.pop(channel_id, None) - connection._avail_channel_ids.append(channel_id) - self.callbacks.clear() - self.cancel_callbacks.clear() - self.events.clear() - self.no_ack_consumers.clear() - - def _do_revive(self): - self.is_open = False - self._x_open() - - def close(self, reply_code=0, reply_text='', method_sig=(0, 0)): - """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. - - RULE: - - After sending this method any received method except - Channel.Close-OK MUST be discarded. - - RULE: - - The peer sending this method MAY use a counter or timeout - to detect failure of the other peer to respond correctly - with Channel.Close-OK.. - - PARAMETERS: - reply_code: short - - The reply code. The AMQ reply codes are defined in AMQ - RFC 011. - - reply_text: shortstr - - The localised reply text. This text can be logged as an - aid to resolving issues. - - class_id: short - - failing method class - - When the close is provoked by a method exception, this - is the class of the method. - - method_id: short - - failing method ID - - When the close is provoked by a method exception, this - is the ID of the method. - - """ - try: - if not self.is_open or self.connection is None: - return - - args = AMQPWriter() - args.write_short(reply_code) - args.write_shortstr(reply_text) - args.write_short(method_sig[0]) # class_id - args.write_short(method_sig[1]) # method_id - self._send_method((20, 40), args) - return self.wait(allowed_methods=[ - (20, 40), # Channel.close - (20, 41), # Channel.close_ok - ]) - finally: - self.connection = None - - def _close(self, args): - """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. - - RULE: - - After sending this method any received method except - Channel.Close-OK MUST be discarded. - - RULE: - - The peer sending this method MAY use a counter or timeout - to detect failure of the other peer to respond correctly - with Channel.Close-OK.. - - PARAMETERS: - reply_code: short - - The reply code. The AMQ reply codes are defined in AMQ - RFC 011. - - reply_text: shortstr - - The localised reply text. This text can be logged as an - aid to resolving issues. - - class_id: short - - failing method class - - When the close is provoked by a method exception, this - is the class of the method. - - method_id: short - - failing method ID - - When the close is provoked by a method exception, this - is the ID of the method. - - """ - - reply_code = args.read_short() - reply_text = args.read_shortstr() - class_id = args.read_short() - method_id = args.read_short() - - self._send_method((20, 41)) - self._do_revive() - - raise error_for_code( - reply_code, reply_text, (class_id, method_id), ChannelError, - ) - - def _close_ok(self, args): - """Confirm a channel close - - This method confirms a Channel.Close method and tells the - recipient that it is safe to release resources for the channel - and close the socket. - - RULE: - - A peer that detects a socket closure without having - received a Channel.Close-Ok handshake method SHOULD log - the error. - - """ - self._do_close() - - def flow(self, active): - """Enable/disable flow from peer - - This method asks the peer to pause or restart the flow of - content data. This is a simple flow-control mechanism that a - peer can use to avoid oveflowing its queues or otherwise - finding itself receiving more messages than it can process. - Note that this method is not intended for window control. The - peer that receives a request to stop sending content should - finish sending the current content, if any, and then wait - until it receives a Flow restart method. - - RULE: - - When a new channel is opened, it is active. Some - applications assume that channels are inactive until - started. To emulate this behaviour a client MAY open the - channel, then pause it. - - RULE: - - When sending content data in multiple frames, a peer - SHOULD monitor the channel for incoming methods and - respond to a Channel.Flow as rapidly as possible. - - RULE: - - A peer MAY use the Channel.Flow method to throttle - incoming content data for internal reasons, for example, - when exchangeing data over a slower connection. - - RULE: - - The peer that requests a Channel.Flow method MAY - disconnect and/or ban a peer that does not respect the - request. - - PARAMETERS: - active: boolean - - start/stop content frames - - If True, the peer starts sending content frames. If - False, the peer stops sending content frames. - - """ - args = AMQPWriter() - args.write_bit(active) - self._send_method((20, 20), args) - return self.wait(allowed_methods=[ - (20, 21), # Channel.flow_ok - ]) - - def _flow(self, args): - """Enable/disable flow from peer - - This method asks the peer to pause or restart the flow of - content data. This is a simple flow-control mechanism that a - peer can use to avoid oveflowing its queues or otherwise - finding itself receiving more messages than it can process. - Note that this method is not intended for window control. The - peer that receives a request to stop sending content should - finish sending the current content, if any, and then wait - until it receives a Flow restart method. - - RULE: - - When a new channel is opened, it is active. Some - applications assume that channels are inactive until - started. To emulate this behaviour a client MAY open the - channel, then pause it. - - RULE: - - When sending content data in multiple frames, a peer - SHOULD monitor the channel for incoming methods and - respond to a Channel.Flow as rapidly as possible. - - RULE: - - A peer MAY use the Channel.Flow method to throttle - incoming content data for internal reasons, for example, - when exchangeing data over a slower connection. - - RULE: - - The peer that requests a Channel.Flow method MAY - disconnect and/or ban a peer that does not respect the - request. - - PARAMETERS: - active: boolean - - start/stop content frames - - If True, the peer starts sending content frames. If - False, the peer stops sending content frames. - - """ - self.active = args.read_bit() - self._x_flow_ok(self.active) - - def _x_flow_ok(self, active): - """Confirm a flow method - - Confirms to the peer that a flow command was received and - processed. - - PARAMETERS: - active: boolean - - current flow setting - - Confirms the setting of the processed flow method: - True means the peer will start sending or continue - to send content frames; False means it will not. - - """ - args = AMQPWriter() - args.write_bit(active) - self._send_method((20, 21), args) - - def _flow_ok(self, args): - """Confirm a flow method - - Confirms to the peer that a flow command was received and - processed. - - PARAMETERS: - active: boolean - - current flow setting - - Confirms the setting of the processed flow method: - True means the peer will start sending or continue - to send content frames; False means it will not. - - """ - return args.read_bit() - - def _x_open(self): - """Open a channel for use - - This method opens a virtual connection (a channel). - - RULE: - - This method MUST NOT be called when the channel is already - open. - - PARAMETERS: - out_of_band: shortstr (DEPRECATED) - - out-of-band settings - - Configures out-of-band transfers on this channel. The - syntax and meaning of this field will be formally - defined at a later date. - - """ - if self.is_open: - return - - args = AMQPWriter() - args.write_shortstr('') # out_of_band: deprecated - self._send_method((20, 10), args) - return self.wait(allowed_methods=[ - (20, 11), # Channel.open_ok - ]) - - def _open_ok(self, args): - """Signal that the channel is ready - - This method signals to the client that the channel is ready - for use. - - """ - self.is_open = True - AMQP_LOGGER.debug('Channel open') - - ############# - # - # Exchange - # - # - # work with exchanges - # - # Exchanges match and distribute messages across queues. - # Exchanges can be configured in the server or created at runtime. - # - # GRAMMAR:: - # - # exchange = C:DECLARE S:DECLARE-OK - # / C:DELETE S:DELETE-OK - # - # RULE: - # - # The server MUST implement the direct and fanout exchange - # types, and predeclare the corresponding exchanges named - # amq.direct and amq.fanout in each virtual host. The server - # MUST also predeclare a direct exchange to act as the default - # exchange for content Publish methods and for default queue - # bindings. - # - # RULE: - # - # The server SHOULD implement the topic exchange type, and - # predeclare the corresponding exchange named amq.topic in - # each virtual host. - # - # RULE: - # - # The server MAY implement the system exchange type, and - # predeclare the corresponding exchanges named amq.system in - # each virtual host. If the client attempts to bind a queue to - # the system exchange, the server MUST raise a connection - # exception with reply code 507 (not allowed). - # - - def exchange_declare(self, exchange, type, passive=False, durable=False, - auto_delete=True, nowait=False, arguments=None): - """Declare exchange, 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. - - RULE: - - The server SHOULD support a minimum of 16 exchanges per - virtual host and ideally, impose no limit except as - defined by available resources. - - PARAMETERS: - exchange: shortstr - - RULE: - - Exchange names starting with "amq." are reserved - for predeclared and standardised exchanges. If - the client attempts to create an exchange starting - with "amq.", the server MUST raise a channel - exception with reply code 403 (access refused). - - type: shortstr - - exchange type - - Each exchange belongs to one of a set of exchange - types implemented by the server. The exchange types - define the functionality of the exchange - i.e. how - messages are routed through it. It is not valid or - meaningful to attempt to change the type of an - existing exchange. - - RULE: - - If the exchange already exists with a different - type, the server MUST raise a connection exception - with a reply code 507 (not allowed). - - RULE: - - If the server does not support the requested - exchange type it MUST raise a connection exception - with a reply code 503 (command invalid). - - passive: boolean - - do not create exchange - - If set, the server will not create the exchange. The - client can use this to check whether an exchange - exists without modifying the server state. - - RULE: - - If set, and the exchange does not already exist, - the server MUST raise a channel exception with - reply code 404 (not found). - - durable: boolean - - request a durable exchange - - If set when creating a new exchange, the exchange will - be marked as durable. Durable exchanges remain active - when a server restarts. Non-durable exchanges - (transient exchanges) are purged if/when a server - restarts. - - RULE: - - The server MUST support both durable and transient - exchanges. - - RULE: - - The server MUST ignore the durable field if the - exchange already exists. - - auto_delete: boolean - - auto-delete when unused - - If set, the exchange is deleted when all queues have - finished using it. - - RULE: - - The server SHOULD allow for a reasonable delay - between the point when it determines that an - exchange is not being used (or no longer used), - and the point when it deletes the exchange. At - the least it must allow a client to create an - exchange and then bind a queue to it, with a small - but non-zero delay between these two actions. - - RULE: - - The server MUST ignore the auto-delete field if - the exchange already exists. - - nowait: boolean - - do not send a reply method - - 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. - - arguments: table - - arguments for declaration - - A set of arguments for the declaration. The syntax and - semantics of these arguments depends on the server - implementation. This field is ignored if passive is - True. - - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(exchange) - args.write_shortstr(type) - args.write_bit(passive) - args.write_bit(durable) - args.write_bit(auto_delete) - args.write_bit(False) # internal: deprecated - args.write_bit(nowait) - args.write_table(arguments) - self._send_method((40, 10), args) - - if auto_delete: - warn(VDeprecationWarning(EXCHANGE_AUTODELETE_DEPRECATED)) - - if not nowait: - return self.wait(allowed_methods=[ - (40, 11), # Channel.exchange_declare_ok - ]) - - def _exchange_declare_ok(self, args): - """Confirms an exchange declaration - - This method confirms a Declare method and confirms the name of - the exchange, essential for automatically-named exchanges. - - """ - pass - - def exchange_delete(self, exchange, if_unused=False, nowait=False): - """Delete an exchange - - This method deletes an exchange. When an exchange is deleted - all queue bindings on the exchange are cancelled. - - PARAMETERS: - exchange: shortstr - - RULE: - - The exchange MUST exist. Attempting to delete a - non-existing exchange causes a channel exception. - - if_unused: boolean - - delete only if unused - - If set, the server will only delete the exchange if it - has no queue bindings. If the exchange has queue - bindings the server does not delete it but raises a - channel exception instead. - - RULE: - - If set, the server SHOULD delete the exchange but - only if it has no queue bindings. - - RULE: - - If set, the server SHOULD raise a channel - exception if the exchange is in use. - - nowait: boolean - - do not send a reply method - - 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. - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(exchange) - args.write_bit(if_unused) - args.write_bit(nowait) - self._send_method((40, 20), args) - - if not nowait: - return self.wait(allowed_methods=[ - (40, 21), # Channel.exchange_delete_ok - ]) - - def _exchange_delete_ok(self, args): - """Confirm deletion of an exchange - - This method confirms the deletion of an exchange. - - """ - pass - - def exchange_bind(self, destination, source='', routing_key='', - nowait=False, arguments=None): - """This method binds an exchange to an exchange. - - RULE: - - A server MUST allow and ignore duplicate bindings - that - is, two or more bind methods for a specific exchanges, - with identical arguments - without treating these as an - error. - - RULE: - - A server MUST allow cycles of exchange bindings to be - created including allowing an exchange to be bound to - itself. - - RULE: - - A server MUST not deliver the same message more than once - to a destination exchange, even if the topology of - exchanges and bindings results in multiple (even infinite) - routes to that exchange. - - PARAMETERS: - reserved-1: short - - destination: shortstr - - Specifies the name of the destination exchange to - bind. - - RULE: - - A client MUST NOT be allowed to bind a non- - existent destination exchange. - - RULE: - - The server MUST accept a blank exchange name to - mean the default exchange. - - source: shortstr - - Specifies the name of the source exchange to bind. - - RULE: - - A client MUST NOT be allowed to bind a non- - existent source exchange. - - RULE: - - The server MUST accept a blank exchange name to - mean the default exchange. - - routing-key: shortstr - - Specifies the routing key for the binding. The routing - key is used for routing messages depending on the - exchange configuration. Not all exchanges use a - routing key - refer to the specific exchange - documentation. - - no-wait: bit - - arguments: table - - A set of arguments for the binding. The syntax and - semantics of these arguments depends on the exchange - class. - - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(destination) - args.write_shortstr(source) - args.write_shortstr(routing_key) - args.write_bit(nowait) - args.write_table(arguments) - self._send_method((40, 30), args) - - if not nowait: - return self.wait(allowed_methods=[ - (40, 31), # Channel.exchange_bind_ok - ]) - - def exchange_unbind(self, destination, source='', routing_key='', - nowait=False, arguments=None): - """This method unbinds an exchange from an exchange. - - RULE: - - If a unbind fails, the server MUST raise a connection - exception. - - PARAMETERS: - reserved-1: short - - destination: shortstr - - Specifies the name of the destination exchange to - unbind. - - RULE: - - The client MUST NOT attempt to unbind an exchange - that does not exist from an exchange. - - RULE: - - The server MUST accept a blank exchange name to - mean the default exchange. - - source: shortstr - - Specifies the name of the source exchange to unbind. - - RULE: - - The client MUST NOT attempt to unbind an exchange - from an exchange that does not exist. - - RULE: - - The server MUST accept a blank exchange name to - mean the default exchange. - - routing-key: shortstr - - Specifies the routing key of the binding to unbind. - - no-wait: bit - - arguments: table - - Specifies the arguments of the binding to unbind. - - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(destination) - args.write_shortstr(source) - args.write_shortstr(routing_key) - args.write_bit(nowait) - args.write_table(arguments) - self._send_method((40, 40), args) - - if not nowait: - return self.wait(allowed_methods=[ - (40, 51), # Channel.exchange_unbind_ok - ]) - - def _exchange_bind_ok(self, args): - """Confirm bind successful - - This method confirms that the bind was successful. - - """ - pass - - def _exchange_unbind_ok(self, args): - """Confirm unbind successful - - This method confirms that the unbind was successful. - - """ - pass - - ############# - # - # 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. - # - # GRAMMAR:: - # - # queue = C:DECLARE S:DECLARE-OK - # / C:BIND S:BIND-OK - # / C:PURGE S:PURGE-OK - # / C:DELETE S:DELETE-OK - # - # RULE: - # - # A server MUST allow any content class to be sent to any - # queue, in any mix, and queue and delivery these content - # classes independently. Note that all methods that fetch - # content off queues are specific to a given content class. - # - - def queue_bind(self, queue, exchange='', routing_key='', - nowait=False, arguments=None): - """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 dest - exchange and subscription queues are bound to a dest_wild - exchange. - - RULE: - - A server MUST allow ignore duplicate bindings - that is, - two or more bind methods for a specific queue, with - identical arguments - without treating these as an error. - - RULE: - - If a bind fails, the server MUST raise a connection - exception. - - RULE: - - The server MUST NOT allow a durable queue to bind to a - transient exchange. If the client attempts this the server - MUST raise a channel exception. - - RULE: - - Bindings for durable queues are automatically durable and - the server SHOULD restore such bindings after a server - restart. - - RULE: - - The server SHOULD support at least 4 bindings per queue, - and ideally, impose no limit except as defined by - available resources. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to bind. If the queue - name is empty, refers to the current queue for the - channel, which is the last declared queue. - - RULE: - - If the client did not previously declare a queue, - and the queue name in this method is empty, the - server MUST raise a connection exception with - reply code 530 (not allowed). - - RULE: - - If the queue does not exist the server MUST raise - a channel exception with reply code 404 (not - found). - - exchange: shortstr - - The name of the exchange to bind to. - - RULE: - - If the exchange does not exist the server MUST - raise a channel exception with reply code 404 (not - found). - - routing_key: shortstr - - message routing key - - Specifies the routing key for the binding. The - routing key is used for routing messages depending on - the exchange configuration. Not all exchanges use a - routing key - refer to the specific exchange - documentation. If the routing key is empty and the - queue name is empty, the routing key will be the - current queue for the channel, which is the last - declared queue. - - nowait: boolean - - do not send a reply method - - 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. - - arguments: table - - arguments for binding - - A set of arguments for the binding. The syntax and - semantics of these arguments depends on the exchange - class. - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_shortstr(exchange) - args.write_shortstr(routing_key) - args.write_bit(nowait) - args.write_table(arguments) - self._send_method((50, 20), args) - - if not nowait: - return self.wait(allowed_methods=[ - (50, 21), # Channel.queue_bind_ok - ]) - - def _queue_bind_ok(self, args): - """Confirm bind successful - - This method confirms that the bind was successful. - - """ - pass - - def queue_unbind(self, queue, exchange, routing_key='', - nowait=False, arguments=None): - """Unbind a queue from an exchange - - This method unbinds a queue from an exchange. - - RULE: - - If a unbind fails, the server MUST raise a connection exception. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to unbind. - - RULE: - - The client MUST either specify a queue name or have - previously declared a queue on the same channel - - RULE: - - The client MUST NOT attempt to unbind a queue that - does not exist. - - exchange: shortstr - - The name of the exchange to unbind from. - - RULE: - - The client MUST NOT attempt to unbind a queue from an - exchange that does not exist. - - RULE: - - The server MUST accept a blank exchange name to mean - the default exchange. - - routing_key: shortstr - - routing key of binding - - Specifies the routing key of the binding to unbind. - - arguments: table - - arguments of binding - - Specifies the arguments of the binding to unbind. - - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_shortstr(exchange) - args.write_shortstr(routing_key) - #args.write_bit(nowait) - args.write_table(arguments) - self._send_method((50, 50), args) - - if not nowait: - return self.wait(allowed_methods=[ - (50, 51), # Channel.queue_unbind_ok - ]) - - def _queue_unbind_ok(self, args): - """Confirm unbind successful - - This method confirms that the unbind was successful. - - """ - pass - - def queue_declare(self, queue='', passive=False, durable=False, - exclusive=False, auto_delete=True, nowait=False, - arguments=None): - """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. - - RULE: - - The server MUST create a default binding for a newly- - created queue to the default exchange, which is an - exchange of type 'direct'. - - RULE: - - The server SHOULD support a minimum of 256 queues per - virtual host and ideally, impose no limit except as - defined by available resources. - - PARAMETERS: - queue: shortstr - - RULE: - - The queue name MAY be empty, in which case the - server MUST create a new queue with a unique - generated name and return this to the client in - the Declare-Ok method. - - RULE: - - Queue names starting with "amq." are reserved for - predeclared and standardised server queues. If - the queue name starts with "amq." and the passive - option is False, the server MUST raise a connection - exception with reply code 403 (access refused). - - passive: boolean - - do not create queue - - If set, the server will not create the queue. The - client can use this to check whether a queue exists - without modifying the server state. - - RULE: - - If set, and the queue does not already exist, the - server MUST respond with a reply code 404 (not - found) and raise a channel exception. - - durable: boolean - - request a durable queue - - If set when creating a new queue, the queue will be - marked as durable. Durable queues remain active when - a server restarts. Non-durable queues (transient - queues) are purged if/when a server restarts. Note - that durable queues do not necessarily hold persistent - messages, although it does not make sense to send - persistent messages to a transient queue. - - RULE: - - The server MUST recreate the durable queue after a - restart. - - RULE: - - The server MUST support both durable and transient - queues. - - RULE: - - The server MUST ignore the durable field if the - queue already exists. - - exclusive: boolean - - request an exclusive queue - - Exclusive queues may only be consumed from by the - current connection. Setting the 'exclusive' flag - always implies 'auto-delete'. - - RULE: - - The server MUST support both exclusive (private) - and non-exclusive (shared) queues. - - RULE: - - The server MUST raise a channel exception if - 'exclusive' is specified and the queue already - exists and is owned by a different connection. - - auto_delete: boolean - - auto-delete queue when unused - - If set, the queue is deleted when all consumers have - finished using it. Last consumer can be cancelled - either explicitly or because its channel is closed. If - there was no consumer ever on the queue, it won't be - deleted. - - RULE: - - The server SHOULD allow for a reasonable delay - between the point when it determines that a queue - is not being used (or no longer used), and the - point when it deletes the queue. At the least it - must allow a client to create a queue and then - create a consumer to read from it, with a small - but non-zero delay between these two actions. The - server should equally allow for clients that may - be disconnected prematurely, and wish to re- - consume from the same queue without losing - messages. We would recommend a configurable - timeout, with a suitable default value being one - minute. - - RULE: - - The server MUST ignore the auto-delete field if - the queue already exists. - - nowait: boolean - - do not send a reply method - - 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. - - arguments: table - - arguments for declaration - - A set of arguments for the declaration. The syntax and - semantics of these arguments depends on the server - implementation. This field is ignored if passive is - True. - - Returns a tuple containing 3 items: - the name of the queue (essential for automatically-named queues) - message count - consumer count - - """ - arguments = {} if arguments is None else arguments - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_bit(passive) - args.write_bit(durable) - args.write_bit(exclusive) - args.write_bit(auto_delete) - args.write_bit(nowait) - args.write_table(arguments) - self._send_method((50, 10), args) - - if not nowait: - return self.wait(allowed_methods=[ - (50, 11), # Channel.queue_declare_ok - ]) - - def _queue_declare_ok(self, args): - """Confirms a queue definition - - This method confirms a Declare method and confirms the name of - the queue, essential for automatically-named queues. - - PARAMETERS: - queue: shortstr - - Reports the name of the queue. If the server generated - a queue name, this field contains that name. - - message_count: long - - number of messages in queue - - Reports the number of messages in the queue, which - will be zero for newly-created queues. - - consumer_count: long - - number of consumers - - Reports the number of active consumers for the queue. - Note that consumers can suspend activity - (Channel.Flow) in which case they do not appear in - this count. - - """ - return queue_declare_ok_t( - args.read_shortstr(), - args.read_long(), - args.read_long(), - ) - - def queue_delete(self, queue='', - if_unused=False, if_empty=False, nowait=False): - """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. - - RULE: - - The server SHOULD use a dead-letter queue to hold messages - that were pending on a deleted queue, and MAY provide - facilities for a system administrator to move these - messages back to an active queue. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to delete. If the - queue name is empty, refers to the current queue for - the channel, which is the last declared queue. - - RULE: - - If the client did not previously declare a queue, - and the queue name in this method is empty, the - server MUST raise a connection exception with - reply code 530 (not allowed). - - RULE: - - The queue must exist. Attempting to delete a non- - existing queue causes a channel exception. - - if_unused: boolean - - delete only if unused - - If set, the server will only delete the queue if it - has no consumers. If the queue has consumers the - server does does not delete it but raises a channel - exception instead. - - RULE: - - The server MUST respect the if-unused flag when - deleting a queue. - - if_empty: boolean - - delete only if empty - - If set, the server will only delete the queue if it - has no messages. If the queue is not empty the server - raises a channel exception. - - nowait: boolean - - do not send a reply method - - 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. - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_bit(if_unused) - args.write_bit(if_empty) - args.write_bit(nowait) - self._send_method((50, 40), args) - - if not nowait: - return self.wait(allowed_methods=[ - (50, 41), # Channel.queue_delete_ok - ]) - - def _queue_delete_ok(self, args): - """Confirm deletion of a queue - - This method confirms the deletion of a queue. - - PARAMETERS: - message_count: long - - number of messages purged - - Reports the number of messages purged. - - """ - return args.read_long() - - def queue_purge(self, queue='', nowait=False): - """Purge a queue - - This method removes all messages from a queue. It does not - cancel consumers. Purged messages are deleted without any - formal "undo" mechanism. - - RULE: - - A call to purge MUST result in an empty queue. - - RULE: - - On transacted channels the server MUST not purge messages - that have already been sent to a client but not yet - acknowledged. - - RULE: - - The server MAY implement a purge queue or log that allows - system administrators to recover accidentally-purged - messages. The server SHOULD NOT keep purged messages in - the same storage spaces as the live messages since the - volumes of purged messages may get very large. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to purge. If the - queue name is empty, refers to the current queue for - the channel, which is the last declared queue. - - RULE: - - If the client did not previously declare a queue, - and the queue name in this method is empty, the - server MUST raise a connection exception with - reply code 530 (not allowed). - - RULE: - - The queue must exist. Attempting to purge a non- - existing queue causes a channel exception. - - nowait: boolean - - do not send a reply method - - 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. - - if nowait is False, returns a message_count - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_bit(nowait) - self._send_method((50, 30), args) - - if not nowait: - return self.wait(allowed_methods=[ - (50, 31), # Channel.queue_purge_ok - ]) - - def _queue_purge_ok(self, args): - """Confirms a queue purge - - This method confirms the purge of a queue. - - PARAMETERS: - message_count: long - - number of messages purged - - Reports the number of messages purged. - - """ - return args.read_long() - - ############# - # - # Basic - # - # - # work with basic content - # - # The Basic class provides methods that support an industry- - # standard messaging model. - # - # GRAMMAR:: - # - # basic = C:QOS S:QOS-OK - # / C:CONSUME S:CONSUME-OK - # / C:CANCEL S:CANCEL-OK - # / C:PUBLISH content - # / S:RETURN content - # / S:DELIVER content - # / C:GET ( S:GET-OK content / S:GET-EMPTY ) - # / C:ACK - # / C:REJECT - # - # RULE: - # - # The server SHOULD respect the persistent property of basic - # messages and SHOULD make a best-effort to hold persistent - # basic messages on a reliable storage mechanism. - # - # RULE: - # - # The server MUST NOT discard a persistent basic message in - # case of a queue overflow. The server MAY use the - # Channel.Flow method to slow or stop a basic message - # publisher when necessary. - # - # RULE: - # - # The server MAY overflow non-persistent basic messages to - # persistent storage and MAY discard or dead-letter non- - # persistent basic messages on a priority basis if the queue - # size exceeds some configured limit. - # - # RULE: - # - # The server MUST implement at least 2 priority levels for - # basic messages, where priorities 0-4 and 5-9 are treated as - # two distinct levels. The server MAY implement up to 10 - # priority levels. - # - # RULE: - # - # The server MUST deliver messages of the same priority in - # order irrespective of their individual persistence. - # - # RULE: - # - # The server MUST support both automatic and explicit - # acknowledgements on Basic content. - # - - def basic_ack(self, delivery_tag, multiple=False): - """Acknowledge one or more messages - - This method acknowledges one or more messages delivered via - the Deliver or Get-Ok methods. The client can ask to confirm - a single message or a set of messages up to and including a - specific message. - - PARAMETERS: - delivery_tag: longlong - - server-assigned delivery tag - - The server-assigned and channel-specific delivery tag - - RULE: - - The delivery tag is valid only within the channel - from which the message was received. I.e. a client - MUST NOT receive a message on one channel and then - acknowledge it on another. - - RULE: - - The server MUST NOT use a zero value for delivery - tags. Zero is reserved for client use, meaning "all - messages so far received". - - multiple: boolean - - acknowledge multiple messages - - If set to True, the delivery tag is treated as "up to - and including", so that the client can acknowledge - multiple messages with a single method. If set to - False, the delivery tag refers to a single message. - If the multiple field is True, and the delivery tag - is zero, tells the server to acknowledge all - outstanding mesages. - - RULE: - - The server MUST validate that a non-zero delivery- - tag refers to an delivered message, and raise a - channel exception if this is not the case. - - """ - args = AMQPWriter() - args.write_longlong(delivery_tag) - args.write_bit(multiple) - self._send_method((60, 80), args) - - def basic_cancel(self, consumer_tag, nowait=False): - """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 abitrary number of messages in between sending the cancel - method and receiving the cancel-ok reply. - - RULE: - - If the queue no longer exists when the client sends a - cancel command, or the consumer has been cancelled for - other reasons, this command has no effect. - - PARAMETERS: - consumer_tag: shortstr - - consumer tag - - Identifier for the consumer, valid within the current - connection. - - RULE: - - The consumer tag is valid only within the channel - from which the consumer was created. I.e. a client - MUST NOT create a consumer in one channel and then - use it in another. - - nowait: boolean - - do not send a reply method - - 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. - - """ - if self.connection is not None: - self.no_ack_consumers.discard(consumer_tag) - args = AMQPWriter() - args.write_shortstr(consumer_tag) - args.write_bit(nowait) - self._send_method((60, 30), args) - return self.wait(allowed_methods=[ - (60, 31), # Channel.basic_cancel_ok - ]) - - def _basic_cancel_notify(self, args): - """Consumer cancelled by server. - - Most likely the queue was deleted. - - """ - consumer_tag = args.read_shortstr() - callback = self._on_cancel(consumer_tag) - if callback: - callback(consumer_tag) - else: - raise ConsumerCancelled(consumer_tag, (60, 30)) - - def _basic_cancel_ok(self, args): - """Confirm a cancelled consumer - - This method confirms that the cancellation was completed. - - PARAMETERS: - consumer_tag: shortstr - - consumer tag - - Identifier for the consumer, valid within the current - connection. - - RULE: - - The consumer tag is valid only within the channel - from which the consumer was created. I.e. a client - MUST NOT create a consumer in one channel and then - use it in another. - - """ - consumer_tag = args.read_shortstr() - self._on_cancel(consumer_tag) - - def _on_cancel(self, consumer_tag): - self.callbacks.pop(consumer_tag, None) - return self.cancel_callbacks.pop(consumer_tag, None) - - def basic_consume(self, queue='', consumer_tag='', no_local=False, - no_ack=False, exclusive=False, nowait=False, - callback=None, arguments=None, on_cancel=None): - """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 created on, or - until the client cancels them. - - RULE: - - The server SHOULD support at least 16 consumers per queue, - unless the queue was declared as private, and ideally, - impose no limit except as defined by available resources. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to consume from. If - the queue name is null, refers to the current queue - for the channel, which is the last declared queue. - - RULE: - - If the client did not previously declare a queue, - and the queue name in this method is empty, the - server MUST raise a connection exception with - reply code 530 (not allowed). - - consumer_tag: shortstr - - Specifies the identifier for the consumer. The - consumer tag is local to a connection, so two clients - can use the same consumer tags. If this field is empty - the server will generate a unique tag. - - RULE: - - The tag MUST NOT refer to an existing consumer. If - the client attempts to create two consumers with - the same non-empty tag the server MUST raise a - connection exception with reply code 530 (not - allowed). - - no_local: boolean - - do not deliver own messages - - If the no-local field is set the server will not send - messages to the client that published them. - - no_ack: boolean - - no acknowledgement needed - - If this field is set the server does not expect - acknowledgments for messages. That is, when a message - is delivered to the client the server automatically and - silently acknowledges it on behalf of the client. This - functionality increases performance but at the cost of - reliability. Messages can get lost if a client dies - before it can deliver them to the application. - - exclusive: boolean - - request exclusive access - - Request exclusive consumer access, meaning only this - consumer can access the queue. - - RULE: - - If the server cannot grant exclusive access to the - queue when asked, - because there are other - consumers active - it MUST raise a channel - exception with return code 403 (access refused). - - nowait: boolean - - do not send a reply method - - 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. - - callback: Python callable - - function/method called with each delivered message - - For each message delivered by the broker, the - callable will be called with a Message object - as the single argument. If no callable is specified, - messages are quietly discarded, no_ack should probably - be set to True in that case. - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_shortstr(consumer_tag) - args.write_bit(no_local) - args.write_bit(no_ack) - args.write_bit(exclusive) - args.write_bit(nowait) - args.write_table(arguments or {}) - self._send_method((60, 20), args) - - if not nowait: - consumer_tag = self.wait(allowed_methods=[ - (60, 21), # Channel.basic_consume_ok - ]) - - self.callbacks[consumer_tag] = callback - - if on_cancel: - self.cancel_callbacks[consumer_tag] = on_cancel - if no_ack: - self.no_ack_consumers.add(consumer_tag) - - return consumer_tag - - def _basic_consume_ok(self, args): - """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. - - PARAMETERS: - consumer_tag: shortstr - - Holds the consumer tag specified by the client or - provided by the server. - - """ - return args.read_shortstr() - - def _basic_deliver(self, args, msg): - """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. - - RULE: - - The server SHOULD track the number of times a message has - been delivered to clients and when a message is - redelivered a certain number of times - e.g. 5 times - - without being acknowledged, the server SHOULD consider the - message to be unprocessable (possibly causing client - applications to abort), and move the message to a dead - letter queue. - - PARAMETERS: - consumer_tag: shortstr - - consumer tag - - Identifier for the consumer, valid within the current - connection. - - RULE: - - The consumer tag is valid only within the channel - from which the consumer was created. I.e. a client - MUST NOT create a consumer in one channel and then - use it in another. - - delivery_tag: longlong - - server-assigned delivery tag - - The server-assigned and channel-specific delivery tag - - RULE: - - The delivery tag is valid only within the channel - from which the message was received. I.e. a client - MUST NOT receive a message on one channel and then - acknowledge it on another. - - RULE: - - The server MUST NOT use a zero value for delivery - tags. Zero is reserved for client use, meaning "all - messages so far received". - - redelivered: boolean - - message is being redelivered - - This indicates that the message has been previously - delivered to this or another client. - - exchange: shortstr - - Specifies the name of the exchange that the message - was originally published to. - - routing_key: shortstr - - Message routing key - - Specifies the routing key name specified when the - message was published. - - """ - consumer_tag = args.read_shortstr() - delivery_tag = args.read_longlong() - redelivered = args.read_bit() - exchange = args.read_shortstr() - routing_key = args.read_shortstr() - - msg.channel = self - msg.delivery_info = { - 'consumer_tag': consumer_tag, - 'delivery_tag': delivery_tag, - 'redelivered': redelivered, - 'exchange': exchange, - 'routing_key': routing_key, - } - - try: - fun = self.callbacks[consumer_tag] - except KeyError: - pass - else: - fun(msg) - - def basic_get(self, queue='', no_ack=False): - """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. - - PARAMETERS: - queue: shortstr - - Specifies the name of the queue to consume from. If - the queue name is null, refers to the current queue - for the channel, which is the last declared queue. - - RULE: - - If the client did not previously declare a queue, - and the queue name in this method is empty, the - server MUST raise a connection exception with - reply code 530 (not allowed). - - no_ack: boolean - - no acknowledgement needed - - If this field is set the server does not expect - acknowledgments for messages. That is, when a message - is delivered to the client the server automatically and - silently acknowledges it on behalf of the client. This - functionality increases performance but at the cost of - reliability. Messages can get lost if a client dies - before it can deliver them to the application. - - Non-blocking, returns a message object, or None. - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(queue) - args.write_bit(no_ack) - self._send_method((60, 70), args) - return self.wait(allowed_methods=[ - (60, 71), # Channel.basic_get_ok - (60, 72), # Channel.basic_get_empty - ]) - - def _basic_get_empty(self, args): - """Indicate no messages available - - This method tells the client that the queue has no messages - available for the client. - - PARAMETERS: - cluster_id: shortstr - - Cluster id - - For use by cluster applications, should not be used by - client applications. - - """ - cluster_id = args.read_shortstr() # noqa - - def _basic_get_ok(self, args, msg): - """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. - - PARAMETERS: - delivery_tag: longlong - - server-assigned delivery tag - - The server-assigned and channel-specific delivery tag - - RULE: - - The delivery tag is valid only within the channel - from which the message was received. I.e. a client - MUST NOT receive a message on one channel and then - acknowledge it on another. - - RULE: - - The server MUST NOT use a zero value for delivery - tags. Zero is reserved for client use, meaning "all - messages so far received". - - redelivered: boolean - - message is being redelivered - - This indicates that the message has been previously - delivered to this or another client. - - exchange: shortstr - - Specifies the name of the exchange that the message - was originally published to. If empty, the message - was published to the default exchange. - - routing_key: shortstr - - Message routing key - - Specifies the routing key name specified when the - message was published. - - message_count: long - - number of messages pending - - This field reports the number of messages pending on - the queue, excluding the message being delivered. - Note that this figure is indicative, not reliable, and - can change arbitrarily as messages are added to the - queue and removed by other clients. - - """ - delivery_tag = args.read_longlong() - redelivered = args.read_bit() - exchange = args.read_shortstr() - routing_key = args.read_shortstr() - message_count = args.read_long() - - msg.channel = self - msg.delivery_info = { - 'delivery_tag': delivery_tag, - 'redelivered': redelivered, - 'exchange': exchange, - 'routing_key': routing_key, - 'message_count': message_count - } - return msg - - def _basic_publish(self, msg, exchange='', routing_key='', - mandatory=False, immediate=False): - """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. - - PARAMETERS: - exchange: shortstr - - 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. - - RULE: - - The server MUST accept a blank exchange name to - mean the default exchange. - - RULE: - - The exchange MAY refuse basic content in which - case it MUST raise a channel exception with reply - code 540 (not implemented). - - routing_key: shortstr - - Message routing key - - Specifies the routing key for the message. The - routing key is used for routing messages depending on - the exchange configuration. - - mandatory: boolean - - indicate mandatory routing - - This flag tells the server how to react if the message - cannot be routed to a queue. If this flag is True, the - server will return an unroutable message with a Return - method. If this flag is False, the server silently - drops the message. - - RULE: - - The server SHOULD implement the mandatory flag. - - immediate: boolean - - request immediate delivery - - This flag tells the server how to react if the message - cannot be routed to a queue consumer immediately. If - this flag is set, the server will return an - undeliverable message with a Return method. If this - flag is zero, the server will queue the message, but - with no guarantee that it will ever be consumed. - - RULE: - - The server SHOULD implement the immediate flag. - - """ - args = AMQPWriter() - args.write_short(0) - args.write_shortstr(exchange) - args.write_shortstr(routing_key) - args.write_bit(mandatory) - args.write_bit(immediate) - - self._send_method((60, 40), args, msg) - basic_publish = _basic_publish - - def basic_publish_confirm(self, *args, **kwargs): - if not self._confirm_selected: - self._confirm_selected = True - self.confirm_select() - ret = self._basic_publish(*args, **kwargs) - self.wait([(60, 80)]) - return ret - - def basic_qos(self, prefetch_size, prefetch_count, a_global): - """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. - - PARAMETERS: - prefetch_size: long - - prefetch window in octets - - The client can request that messages be sent in - advance so that when the client finishes processing a - message, the following message is already held - locally, rather than needing to be sent down the - channel. Prefetching gives a performance improvement. - This field specifies the prefetch window size in - octets. The server will send a message in advance if - it is equal to or smaller in size than the available - prefetch size (and also falls into other prefetch - limits). May be set to zero, meaning "no specific - limit", although other prefetch limits may still - apply. The prefetch-size is ignored if the no-ack - option is set. - - RULE: - - The server MUST ignore this setting when the - client is not processing any messages - i.e. the - prefetch size does not limit the transfer of - single messages to a client, only the sending in - advance of more messages while the client still - has one or more unacknowledged messages. - - prefetch_count: short - - prefetch window in messages - - Specifies a prefetch window in terms of whole - messages. This field may be used in combination with - the prefetch-size field; a message will only be sent - in advance if both prefetch windows (and those at the - channel and connection level) allow it. The prefetch- - count is ignored if the no-ack option is set. - - RULE: - - The server MAY send less data in advance than - allowed by the client's specified prefetch windows - but it MUST NOT send more. - - a_global: boolean - - apply to entire connection - - By default the QoS settings apply to the current - channel only. If this field is set, they are applied - to the entire connection. - - """ - args = AMQPWriter() - args.write_long(prefetch_size) - args.write_short(prefetch_count) - args.write_bit(a_global) - self._send_method((60, 10), args) - return self.wait(allowed_methods=[ - (60, 11), # Channel.basic_qos_ok - ]) - - def _basic_qos_ok(self, args): - """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. - - """ - pass - - def basic_recover(self, requeue=False): - """Redeliver unacknowledged messages - - This method asks the broker to redeliver all unacknowledged - messages on a specified channel. Zero or more messages may be - redelivered. This method is only allowed on non-transacted - channels. - - RULE: - - The server MUST set the redelivered flag on all messages - that are resent. - - RULE: - - The server MUST raise a channel exception if this is - called on a transacted channel. - - PARAMETERS: - requeue: boolean - - requeue the message - - If this field is False, the message will be redelivered - to the original recipient. If this field is True, the - server will attempt to requeue the message, - potentially then delivering it to an alternative - subscriber. - - """ - args = AMQPWriter() - args.write_bit(requeue) - self._send_method((60, 110), args) - - def basic_recover_async(self, requeue=False): - args = AMQPWriter() - args.write_bit(requeue) - self._send_method((60, 100), args) - - def _basic_recover_ok(self, args): - """In 0-9-1 the deprecated recover solicits a response.""" - pass - - def basic_reject(self, delivery_tag, requeue): - """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. - - RULE: - - The server SHOULD be capable of accepting and process the - Reject method while sending message content with a Deliver - or Get-Ok method. I.e. the server should read and process - incoming methods while sending output frames. To cancel a - partially-send content, the server sends a content body - frame of size 1 (i.e. with no data except the frame-end - octet). - - RULE: - - The server SHOULD interpret this method as meaning that - the client is unable to process the message at this time. - - RULE: - - A client MUST NOT use this method as a means of selecting - messages to process. A rejected message MAY be discarded - or dead-lettered, not necessarily passed to another - client. - - PARAMETERS: - delivery_tag: longlong - - server-assigned delivery tag - - The server-assigned and channel-specific delivery tag - - RULE: - - The delivery tag is valid only within the channel - from which the message was received. I.e. a client - MUST NOT receive a message on one channel and then - acknowledge it on another. - - RULE: - - The server MUST NOT use a zero value for delivery - tags. Zero is reserved for client use, meaning "all - messages so far received". - - requeue: boolean - - requeue the message - - If this field is False, the message will be discarded. - If this field is True, the server will attempt to - requeue the message. - - RULE: - - The server MUST NOT deliver the message to the - same client within the context of the current - channel. The recommended strategy is to attempt - to deliver the message to an alternative consumer, - and if that is not possible, to move the message - to a dead-letter queue. The server MAY use more - sophisticated tracking to hold the message on the - queue and redeliver it to the same client at a - later stage. - - """ - args = AMQPWriter() - args.write_longlong(delivery_tag) - args.write_bit(requeue) - self._send_method((60, 90), args) - - def _basic_return(self, args, msg): - """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. - - PARAMETERS: - reply_code: short - - The reply code. The AMQ reply codes are defined in AMQ - RFC 011. - - reply_text: shortstr - - The localised reply text. This text can be logged as an - aid to resolving issues. - - exchange: shortstr - - Specifies the name of the exchange that the message - was originally published to. - - routing_key: shortstr - - Message routing key - - Specifies the routing key name specified when the - message was published. - - """ - self.returned_messages.put(basic_return_t( - args.read_short(), - args.read_shortstr(), - args.read_shortstr(), - args.read_shortstr(), - msg, - )) - - ############# - # - # Tx - # - # - # work with standard transactions - # - # Standard transactions provide so-called "1.5 phase commit". We - # can ensure that work is never lost, but there is a chance of - # confirmations being lost, so that messages may be resent. - # Applications that use standard transactions must be able to - # detect and ignore duplicate messages. - # - # GRAMMAR:: - # - # tx = C:SELECT S:SELECT-OK - # / C:COMMIT S:COMMIT-OK - # / C:ROLLBACK S:ROLLBACK-OK - # - # RULE: - # - # An client using standard transactions SHOULD be able to - # track all messages received within a reasonable period, and - # thus detect and reject duplicates of the same message. It - # SHOULD NOT pass these to the application layer. - # - # - - def tx_commit(self): - """Commit the current transaction - - This method commits all messages published and acknowledged in - the current transaction. A new transaction starts immediately - after a commit. - - """ - self._send_method((90, 20)) - return self.wait(allowed_methods=[ - (90, 21), # Channel.tx_commit_ok - ]) - - def _tx_commit_ok(self, args): - """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. - - """ - pass - - def tx_rollback(self): - """Abandon the current transaction - - This method abandons all messages published and acknowledged - in the current transaction. A new transaction starts - immediately after a rollback. - - """ - self._send_method((90, 30)) - return self.wait(allowed_methods=[ - (90, 31), # Channel.tx_rollback_ok - ]) - - def _tx_rollback_ok(self, args): - """Confirm a successful rollback - - This method confirms to the client that the rollback - succeeded. Note that if an rollback fails, the server raises a - channel exception. - - """ - pass - - def tx_select(self): - """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. - - """ - self._send_method((90, 10)) - return self.wait(allowed_methods=[ - (90, 11), # Channel.tx_select_ok - ]) - - def _tx_select_ok(self, args): - """Confirm transaction mode - - This method confirms to the client that the channel was - successfully set to use standard transactions. - - """ - pass - - def confirm_select(self, nowait=False): - """Enables publisher confirms for this channel (an RabbitMQ - extension). - - Can now be used if the channel is in transactional mode. - - :param nowait: - 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. - - """ - args = AMQPWriter() - args.write_bit(nowait) - - self._send_method((85, 10), args) - if not nowait: - self.wait(allowed_methods=[ - (85, 11), # Confirm.select_ok - ]) - - def _confirm_select_ok(self, args): - """With this method the broker confirms to the client that - the channel is now using publisher confirms.""" - pass - - def _basic_ack_recv(self, args): - delivery_tag = args.read_longlong() - multiple = args.read_bit() - for callback in self.events['basic_ack']: - callback(delivery_tag, multiple) - - _METHOD_MAP = { - (20, 11): _open_ok, - (20, 20): _flow, - (20, 21): _flow_ok, - (20, 40): _close, - (20, 41): _close_ok, - (40, 11): _exchange_declare_ok, - (40, 21): _exchange_delete_ok, - (40, 31): _exchange_bind_ok, - (40, 51): _exchange_unbind_ok, - (50, 11): _queue_declare_ok, - (50, 21): _queue_bind_ok, - (50, 31): _queue_purge_ok, - (50, 41): _queue_delete_ok, - (50, 51): _queue_unbind_ok, - (60, 11): _basic_qos_ok, - (60, 21): _basic_consume_ok, - (60, 30): _basic_cancel_notify, - (60, 31): _basic_cancel_ok, - (60, 50): _basic_return, - (60, 60): _basic_deliver, - (60, 71): _basic_get_ok, - (60, 72): _basic_get_empty, - (60, 80): _basic_ack_recv, - (60, 111): _basic_recover_ok, - (85, 11): _confirm_select_ok, - (90, 11): _tx_select_ok, - (90, 21): _tx_commit_ok, - (90, 31): _tx_rollback_ok, - } - - _IMMEDIATE_METHODS = [ - (60, 50), # basic_return - ] diff --git a/amqp/connection.py b/amqp/connection.py deleted file mode 100644 index c93d91f..0000000 --- a/amqp/connection.py +++ /dev/null @@ -1,1004 +0,0 @@ -"""AMQP Connections""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -import logging -import socket - -from array import array -try: - from ssl import SSLError -except ImportError: - class SSLError(Exception): # noqa - pass - -from . import __version__ -from .abstract_channel import AbstractChannel -from .channel import Channel -from .exceptions import ( - AMQPNotImplementedError, ChannelError, ResourceError, - ConnectionForced, ConnectionError, error_for_code, - RecoverableConnectionError, RecoverableChannelError, -) -from .five import items, range, values, monotonic -from .method_framing import MethodReader, MethodWriter -from .serialization import AMQPWriter -from .transport import create_transport - -HAS_MSG_PEEK = hasattr(socket, 'MSG_PEEK') - -START_DEBUG_FMT = """ -Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s -""".strip() - -__all__ = ['Connection'] - -# -# Client property info that gets sent to the server on connection startup -# -LIBRARY_PROPERTIES = { - 'product': 'py-amqp', - 'product_version': __version__, - 'capabilities': {}, -} - -AMQP_LOGGER = logging.getLogger('amqp') - - -class Connection(AbstractChannel): - """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. - - GRAMMAR:: - - connection = open-connection *use-connection close-connection - open-connection = C:protocol-header - S:START C:START-OK - *challenge - S:TUNE C:TUNE-OK - C:OPEN S:OPEN-OK - challenge = S:SECURE C:SECURE-OK - use-connection = *channel - close-connection = C:CLOSE S:CLOSE-OK - / S:CLOSE C:CLOSE-OK - - """ - Channel = Channel - - #: Final heartbeat interval value (in float seconds) after negotiation - heartbeat = None - - #: Original heartbeat interval value proposed by client. - client_heartbeat = None - - #: Original heartbeat interval proposed by server. - server_heartbeat = None - - #: Time of last heartbeat sent (in monotonic time, if available). - last_heartbeat_sent = 0 - - #: Time of last heartbeat received (in monotonic time, if available). - last_heartbeat_received = 0 - - #: Number of bytes sent to socket at the last heartbeat check. - prev_sent = None - - #: Number of bytes received from socket at the last heartbeat check. - prev_recv = None - - def __init__(self, host='localhost', userid='guest', password='guest', - login_method='AMQPLAIN', login_response=None, - virtual_host='/', locale='en_US', client_properties=None, - ssl=False, connect_timeout=None, channel_max=None, - frame_max=None, heartbeat=0, on_blocked=None, - on_unblocked=None, confirm_publish=False, **kwargs): - """Create a connection to the specified host, which should be - a 'host[:port]', such as 'localhost', or '1.2.3.4:5672' - (defaults to 'localhost', if a port is not specified then - 5672 is used) - - If login_response is not specified, one is built up for you from - userid and password if they are present. - - The 'ssl' parameter may be simply True/False, or for Python >= 2.6 - a dictionary of options to pass to ssl.wrap_socket() such as - requiring certain certificates. - - """ - channel_max = channel_max or 65535 - frame_max = frame_max or 131072 - if (login_response is None) \ - and (userid is not None) \ - and (password is not None): - login_response = AMQPWriter() - login_response.write_table({'LOGIN': userid, 'PASSWORD': password}) - login_response = login_response.getvalue()[4:] # Skip the length - # at the beginning - - d = dict(LIBRARY_PROPERTIES, **client_properties or {}) - self._method_override = {(60, 50): self._dispatch_basic_return} - - self.channels = {} - # The connection object itself is treated as channel 0 - super(Connection, self).__init__(self, 0) - - self.transport = None - - # Properties set in the Tune method - self.channel_max = channel_max - self.frame_max = frame_max - self.client_heartbeat = heartbeat - - self.confirm_publish = confirm_publish - - # Callbacks - self.on_blocked = on_blocked - self.on_unblocked = on_unblocked - - self._avail_channel_ids = array('H', range(self.channel_max, 0, -1)) - - # Properties set in the Start method - self.version_major = 0 - self.version_minor = 0 - self.server_properties = {} - self.mechanisms = [] - self.locales = [] - - # Let the transport.py module setup the actual - # socket connection to the broker. - # - self.transport = create_transport(host, connect_timeout, ssl) - - self.method_reader = MethodReader(self.transport) - self.method_writer = MethodWriter(self.transport, self.frame_max) - - self.wait(allowed_methods=[ - (10, 10), # start - ]) - - self._x_start_ok(d, login_method, login_response, locale) - - self._wait_tune_ok = True - while self._wait_tune_ok: - self.wait(allowed_methods=[ - (10, 20), # secure - (10, 30), # tune - ]) - - return self._x_open(virtual_host) - - @property - def connected(self): - return self.transport and self.transport.connected - - def _do_close(self): - try: - self.transport.close() - - temp_list = [x for x in values(self.channels) if x is not self] - for ch in temp_list: - ch._do_close() - except socket.error: - pass # connection already closed on the other end - finally: - self.transport = self.connection = self.channels = None - - def _get_free_channel_id(self): - try: - return self._avail_channel_ids.pop() - except IndexError: - raise ResourceError( - 'No free channel ids, current={0}, channel_max={1}'.format( - len(self.channels), self.channel_max), (20, 10)) - - def _claim_channel_id(self, channel_id): - try: - return self._avail_channel_ids.remove(channel_id) - except ValueError: - raise ConnectionError( - 'Channel %r already open' % (channel_id, )) - - def _wait_method(self, channel_id, allowed_methods): - """Wait for a method from the server destined for - a particular channel.""" - # - # Check the channel's deferred methods - # - method_queue = self.channels[channel_id].method_queue - - for queued_method in method_queue: - method_sig = queued_method[0] - if (allowed_methods is None) \ - or (method_sig in allowed_methods) \ - or (method_sig == (20, 40)): - method_queue.remove(queued_method) - return queued_method - - # - # Nothing queued, need to wait for a method from the peer - # - while 1: - channel, method_sig, args, content = \ - self.method_reader.read_method() - - if channel == channel_id and ( - allowed_methods is None or - method_sig in allowed_methods or - method_sig == (20, 40)): - return method_sig, args, content - - # - # Certain methods like basic_return should be dispatched - # immediately rather than being queued, even if they're not - # one of the 'allowed_methods' we're looking for. - # - if channel and method_sig in self.Channel._IMMEDIATE_METHODS: - self.channels[channel].dispatch_method( - method_sig, args, content, - ) - continue - - # - # Not the channel and/or method we were looking for. Queue - # this method for later - # - self.channels[channel].method_queue.append( - (method_sig, args, content), - ) - - # - # If we just queued up a method for channel 0 (the Connection - # itself) it's probably a close method in reaction to some - # error, so deal with it right away. - # - if not channel: - self.wait() - - def channel(self, channel_id=None): - """Fetch a Channel object identified by the numeric channel_id, or - create that object if it doesn't already exist.""" - try: - return self.channels[channel_id] - except KeyError: - return self.Channel(self, channel_id) - - def is_alive(self): - if HAS_MSG_PEEK: - sock = self.sock - prev = sock.gettimeout() - sock.settimeout(0.0001) - try: - sock.recv(1, socket.MSG_PEEK) - except socket.timeout: - pass - except socket.error: - return False - finally: - sock.settimeout(prev) - return True - - def drain_events(self, timeout=None): - """Wait for an event on a channel.""" - chanmap = self.channels - chanid, method_sig, args, content = self._wait_multiple( - chanmap, None, timeout=timeout, - ) - - channel = chanmap[chanid] - - if (content and - channel.auto_decode and - hasattr(content, 'content_encoding')): - try: - content.body = content.body.decode(content.content_encoding) - except Exception: - pass - - amqp_method = (self._method_override.get(method_sig) or - channel._METHOD_MAP.get(method_sig, None)) - - if amqp_method is None: - raise AMQPNotImplementedError( - 'Unknown AMQP method {0!r}'.format(method_sig)) - - if content is None: - return amqp_method(channel, args) - else: - return amqp_method(channel, args, content) - - def read_timeout(self, timeout=None): - if timeout is None: - return self.method_reader.read_method() - sock = self.sock - prev = sock.gettimeout() - if prev != timeout: - sock.settimeout(timeout) - try: - try: - return self.method_reader.read_method() - except SSLError as exc: - # http://bugs.python.org/issue10272 - if 'timed out' in str(exc): - raise socket.timeout() - # Non-blocking SSL sockets can throw SSLError - if 'The operation did not complete' in str(exc): - raise socket.timeout() - raise - finally: - if prev != timeout: - sock.settimeout(prev) - - def _wait_multiple(self, channels, allowed_methods, timeout=None): - for channel_id, channel in items(channels): - method_queue = channel.method_queue - for queued_method in method_queue: - method_sig = queued_method[0] - if (allowed_methods is None or - method_sig in allowed_methods or - method_sig == (20, 40)): - method_queue.remove(queued_method) - method_sig, args, content = queued_method - return channel_id, method_sig, args, content - - # Nothing queued, need to wait for a method from the peer - read_timeout = self.read_timeout - wait = self.wait - while 1: - channel, method_sig, args, content = read_timeout(timeout) - - if channel in channels and ( - allowed_methods is None or - method_sig in allowed_methods or - method_sig == (20, 40)): - return channel, method_sig, args, content - - # Not the channel and/or method we were looking for. Queue - # this method for later - channels[channel].method_queue.append((method_sig, args, content)) - - # - # If we just queued up a method for channel 0 (the Connection - # itself) it's probably a close method in reaction to some - # error, so deal with it right away. - # - if channel == 0: - wait() - - def _dispatch_basic_return(self, channel, args, msg): - reply_code = args.read_short() - reply_text = args.read_shortstr() - exchange = args.read_shortstr() - routing_key = args.read_shortstr() - - exc = error_for_code(reply_code, reply_text, (50, 60), ChannelError) - handlers = channel.events.get('basic_return') - if not handlers: - raise exc - for callback in handlers: - callback(exc, exchange, routing_key, msg) - - def close(self, reply_code=0, reply_text='', method_sig=(0, 0)): - """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. - - RULE: - - After sending this method any received method except the - Close-OK method MUST be discarded. - - RULE: - - The peer sending this method MAY use a counter or timeout - to detect failure of the other peer to respond correctly - with the Close-OK method. - - RULE: - - When a server receives the Close method from a client it - MUST delete all server-side resources associated with the - client's context. A client CANNOT reconnect to a context - after sending or receiving a Close method. - - PARAMETERS: - reply_code: short - - The reply code. The AMQ reply codes are defined in AMQ - RFC 011. - - reply_text: shortstr - - The localised reply text. This text can be logged as an - aid to resolving issues. - - class_id: short - - failing method class - - When the close is provoked by a method exception, this - is the class of the method. - - method_id: short - - failing method ID - - When the close is provoked by a method exception, this - is the ID of the method. - - """ - if self.transport is None: - # already closed - return - - args = AMQPWriter() - args.write_short(reply_code) - args.write_shortstr(reply_text) - args.write_short(method_sig[0]) # class_id - args.write_short(method_sig[1]) # method_id - self._send_method((10, 50), args) - return self.wait(allowed_methods=[ - (10, 50), # Connection.close - (10, 51), # Connection.close_ok - ]) - - def _close(self, args): - """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. - - RULE: - - After sending this method any received method except the - Close-OK method MUST be discarded. - - RULE: - - The peer sending this method MAY use a counter or timeout - to detect failure of the other peer to respond correctly - with the Close-OK method. - - RULE: - - When a server receives the Close method from a client it - MUST delete all server-side resources associated with the - client's context. A client CANNOT reconnect to a context - after sending or receiving a Close method. - - PARAMETERS: - reply_code: short - - The reply code. The AMQ reply codes are defined in AMQ - RFC 011. - - reply_text: shortstr - - The localised reply text. This text can be logged as an - aid to resolving issues. - - class_id: short - - failing method class - - When the close is provoked by a method exception, this - is the class of the method. - - method_id: short - - failing method ID - - When the close is provoked by a method exception, this - is the ID of the method. - - """ - reply_code = args.read_short() - reply_text = args.read_shortstr() - class_id = args.read_short() - method_id = args.read_short() - - self._x_close_ok() - - raise error_for_code(reply_code, reply_text, - (class_id, method_id), ConnectionError) - - def _blocked(self, args): - """RabbitMQ Extension.""" - reason = args.read_shortstr() - if self.on_blocked: - return self.on_blocked(reason) - - def _unblocked(self, *args): - if self.on_unblocked: - return self.on_unblocked() - - def _x_close_ok(self): - """Confirm a connection close - - This method confirms a Connection.Close method and tells the - recipient that it is safe to release resources for the - connection and close the socket. - - RULE: - - A peer that detects a socket closure without having - received a Close-Ok handshake method SHOULD log the error. - - """ - self._send_method((10, 51)) - self._do_close() - - def _close_ok(self, args): - """Confirm a connection close - - This method confirms a Connection.Close method and tells the - recipient that it is safe to release resources for the - connection and close the socket. - - RULE: - - A peer that detects a socket closure without having - received a Close-Ok handshake method SHOULD log the error. - - """ - self._do_close() - - def _x_open(self, virtual_host, capabilities=''): - """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. - - RULE: - - The client MUST open the context before doing any work on - the connection. - - PARAMETERS: - virtual_host: shortstr - - virtual host name - - The name of the virtual host to work with. - - RULE: - - If the server supports multiple virtual hosts, it - MUST enforce a full separation of exchanges, - queues, and all associated entities per virtual - host. An application, connected to a specific - virtual host, MUST NOT be able to access resources - of another virtual host. - - RULE: - - The server SHOULD verify that the client has - permission to access the specified virtual host. - - RULE: - - The server MAY configure arbitrary limits per - virtual host, such as the number of each type of - entity that may be used, per connection and/or in - total. - - capabilities: shortstr - - required capabilities - - The client may specify a number of capability names, - delimited by spaces. The server can use this string - to how to process the client's connection request. - - """ - args = AMQPWriter() - args.write_shortstr(virtual_host) - args.write_shortstr(capabilities) - args.write_bit(False) - self._send_method((10, 40), args) - return self.wait(allowed_methods=[ - (10, 41), # Connection.open_ok - ]) - - def _open_ok(self, args): - """Signal that the connection is ready - - This method signals to the client that the connection is ready - for use. - - PARAMETERS: - known_hosts: shortstr (deprecated) - - """ - AMQP_LOGGER.debug('Open OK!') - - def _secure(self, args): - """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. - - PARAMETERS: - challenge: longstr - - security challenge data - - Challenge information, a block of opaque binary data - passed to the security mechanism. - - """ - challenge = args.read_longstr() # noqa - - def _x_secure_ok(self, response): - """Security mechanism response - - This method attempts to authenticate, passing a block of SASL - data for the security mechanism at the server side. - - PARAMETERS: - response: longstr - - security response data - - A block of opaque data passed to the security - mechanism. The contents of this data are defined by - the SASL security mechanism. - - """ - args = AMQPWriter() - args.write_longstr(response) - self._send_method((10, 21), args) - - def _start(self, args): - """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. - - RULE: - - If the client cannot handle the protocol version suggested - by the server it MUST close the socket connection. - - RULE: - - The server MUST provide a protocol version that is lower - than or equal to that requested by the client in the - protocol header. If the server cannot support the - specified protocol it MUST NOT send this method, but MUST - close the socket connection. - - PARAMETERS: - version_major: octet - - protocol major version - - The protocol major version that the server agrees to - use, which cannot be higher than the client's major - version. - - version_minor: octet - - protocol major version - - The protocol minor version that the server agrees to - use, which cannot be higher than the client's minor - version. - - server_properties: table - - server properties - - mechanisms: longstr - - available security mechanisms - - A list of the security mechanisms that the server - supports, delimited by spaces. Currently ASL supports - these mechanisms: PLAIN. - - locales: longstr - - available message locales - - A list of the message locales that the server - supports, delimited by spaces. The locale defines the - language in which the server will send reply texts. - - RULE: - - All servers MUST support at least the en_US - locale. - - """ - self.version_major = args.read_octet() - self.version_minor = args.read_octet() - self.server_properties = args.read_table() - self.mechanisms = args.read_longstr().split(' ') - self.locales = args.read_longstr().split(' ') - - AMQP_LOGGER.debug( - START_DEBUG_FMT, - self.version_major, self.version_minor, - self.server_properties, self.mechanisms, self.locales, - ) - - def _x_start_ok(self, client_properties, mechanism, response, locale): - """Select security mechanism and locale - - This method selects a SASL security mechanism. ASL uses SASL - (RFC2222) to negotiate authentication and encryption. - - PARAMETERS: - client_properties: table - - client properties - - mechanism: shortstr - - selected security mechanism - - A single security mechanisms selected by the client, - which must be one of those specified by the server. - - RULE: - - The client SHOULD authenticate using the highest- - level security profile it can handle from the list - provided by the server. - - RULE: - - The mechanism field MUST contain one of the - security mechanisms proposed by the server in the - Start method. If it doesn't, the server MUST close - the socket. - - response: longstr - - security response data - - A block of opaque data passed to the security - mechanism. The contents of this data are defined by - the SASL security mechanism. For the PLAIN security - mechanism this is defined as a field table holding two - fields, LOGIN and PASSWORD. - - locale: shortstr - - selected message locale - - A single message local selected by the client, which - must be one of those specified by the server. - - """ - if self.server_capabilities.get('consumer_cancel_notify'): - if 'capabilities' not in client_properties: - client_properties['capabilities'] = {} - client_properties['capabilities']['consumer_cancel_notify'] = True - if self.server_capabilities.get('connection.blocked'): - if 'capabilities' not in client_properties: - client_properties['capabilities'] = {} - client_properties['capabilities']['connection.blocked'] = True - args = AMQPWriter() - args.write_table(client_properties) - args.write_shortstr(mechanism) - args.write_longstr(response) - args.write_shortstr(locale) - self._send_method((10, 11), args) - - def _tune(self, args): - """Propose connection tuning parameters - - This method proposes a set of connection configuration values - to the client. The client can accept and/or adjust these. - - PARAMETERS: - channel_max: short - - proposed maximum channels - - The maximum total number of channels that the server - allows per connection. Zero means that the server does - not impose a fixed limit, but the number of allowed - channels may be limited by available server resources. - - frame_max: long - - proposed maximum frame size - - The largest frame size that the server proposes for - the connection. The client can negotiate a lower - value. Zero means that the server does not impose any - specific limit but may reject very large frames if it - cannot allocate resources for them. - - RULE: - - Until the frame-max has been negotiated, both - peers MUST accept frames of up to 4096 octets - large. The minimum non-zero value for the frame- - max field is 4096. - - heartbeat: short - - desired heartbeat delay - - The delay, in seconds, of the connection heartbeat - that the server wants. Zero means the server does not - want a heartbeat. - - """ - client_heartbeat = self.client_heartbeat or 0 - self.channel_max = args.read_short() or self.channel_max - self.frame_max = args.read_long() or self.frame_max - self.method_writer.frame_max = self.frame_max - self.server_heartbeat = args.read_short() or 0 - - # negotiate the heartbeat interval to the smaller of the - # specified values - if self.server_heartbeat == 0 or client_heartbeat == 0: - self.heartbeat = max(self.server_heartbeat, client_heartbeat) - else: - self.heartbeat = min(self.server_heartbeat, client_heartbeat) - - # Ignore server heartbeat if client_heartbeat is disabled - if not self.client_heartbeat: - self.heartbeat = 0 - - self._x_tune_ok(self.channel_max, self.frame_max, self.heartbeat) - - def send_heartbeat(self): - self.transport.write_frame(8, 0, bytes()) - - def heartbeat_tick(self, rate=2): - """Send heartbeat packets, if necessary, and fail if none have been - received recently. This should be called frequently, on the order of - once per second. - - :keyword rate: Ignored - """ - if not self.heartbeat: - return - - # treat actual data exchange in either direction as a heartbeat - sent_now = self.method_writer.bytes_sent - recv_now = self.method_reader.bytes_recv - if self.prev_sent is None or self.prev_sent != sent_now: - self.last_heartbeat_sent = monotonic() - if self.prev_recv is None or self.prev_recv != recv_now: - self.last_heartbeat_received = monotonic() - self.prev_sent, self.prev_recv = sent_now, recv_now - - # send a heartbeat if it's time to do so - if monotonic() > self.last_heartbeat_sent + self.heartbeat: - self.send_heartbeat() - self.last_heartbeat_sent = monotonic() - - # if we've missed two intervals' heartbeats, fail; this gives the - # server enough time to send heartbeats a little late - if (self.last_heartbeat_received and - self.last_heartbeat_received + 2 * - self.heartbeat < monotonic()): - raise ConnectionForced('Too many heartbeats missed') - - def _x_tune_ok(self, channel_max, frame_max, heartbeat): - """Negotiate connection tuning parameters - - This method sends the client's connection tuning parameters to - the server. Certain fields are negotiated, others provide - capability information. - - PARAMETERS: - channel_max: short - - negotiated maximum channels - - The maximum total number of channels that the client - will use per connection. May not be higher than the - value specified by the server. - - RULE: - - The server MAY ignore the channel-max value or MAY - use it for tuning its resource allocation. - - frame_max: long - - negotiated maximum frame size - - The largest frame size that the client and server will - use for the connection. Zero means that the client - does not impose any specific limit but may reject very - large frames if it cannot allocate resources for them. - Note that the frame-max limit applies principally to - content frames, where large contents can be broken - into frames of arbitrary size. - - RULE: - - Until the frame-max has been negotiated, both - peers must accept frames of up to 4096 octets - large. The minimum non-zero value for the frame- - max field is 4096. - - heartbeat: short - - desired heartbeat delay - - The delay, in seconds, of the connection heartbeat - that the client wants. Zero means the client does not - want a heartbeat. - - """ - args = AMQPWriter() - args.write_short(channel_max) - args.write_long(frame_max) - args.write_short(heartbeat or 0) - self._send_method((10, 31), args) - self._wait_tune_ok = False - - @property - def sock(self): - return self.transport.sock - - @property - def server_capabilities(self): - return self.server_properties.get('capabilities') or {} - - _METHOD_MAP = { - (10, 10): _start, - (10, 20): _secure, - (10, 30): _tune, - (10, 41): _open_ok, - (10, 50): _close, - (10, 51): _close_ok, - (10, 60): _blocked, - (10, 61): _unblocked, - } - - _IMMEDIATE_METHODS = [] - connection_errors = ( - ConnectionError, - socket.error, - IOError, - OSError, - ) - channel_errors = (ChannelError, ) - recoverable_connection_errors = ( - RecoverableConnectionError, - socket.error, - IOError, - OSError, - ) - recoverable_channel_errors = ( - RecoverableChannelError, - ) diff --git a/amqp/exceptions.py b/amqp/exceptions.py deleted file mode 100644 index e3e144a..0000000 --- a/amqp/exceptions.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Exceptions used by amqp""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -from struct import pack, unpack - -__all__ = [ - 'AMQPError', - 'ConnectionError', 'ChannelError', - 'RecoverableConnectionError', 'IrrecoverableConnectionError', - 'RecoverableChannelError', 'IrrecoverableChannelError', - 'ConsumerCancelled', 'ContentTooLarge', 'NoConsumers', - 'ConnectionForced', 'InvalidPath', 'AccessRefused', 'NotFound', - 'ResourceLocked', 'PreconditionFailed', 'FrameError', 'FrameSyntaxError', - 'InvalidCommand', 'ChannelNotOpen', 'UnexpectedFrame', 'ResourceError', - 'NotAllowed', 'AMQPNotImplementedError', 'InternalError', -] - - -class AMQPError(Exception): - code = 0 - - def __init__(self, reply_text=None, method_sig=None, - method_name=None, reply_code=None): - self.message = reply_text - self.reply_code = reply_code or self.code - self.reply_text = reply_text - self.method_sig = method_sig - self.method_name = method_name or '' - if method_sig and not self.method_name: - self.method_name = METHOD_NAME_MAP.get(method_sig, '') - Exception.__init__(self, reply_code, - reply_text, method_sig, self.method_name) - - def __str__(self): - if self.method: - return '{0.method}: ({0.reply_code}) {0.reply_text}'.format(self) - return self.reply_text or '' - - @property - def method(self): - return self.method_name or self.method_sig - - -class ConnectionError(AMQPError): - pass - - -class ChannelError(AMQPError): - pass - - -class RecoverableChannelError(ChannelError): - pass - - -class IrrecoverableChannelError(ChannelError): - pass - - -class RecoverableConnectionError(ConnectionError): - pass - - -class IrrecoverableConnectionError(ConnectionError): - pass - - -class Blocked(RecoverableConnectionError): - pass - - -class ConsumerCancelled(RecoverableConnectionError): - pass - - -class ContentTooLarge(RecoverableChannelError): - code = 311 - - -class NoConsumers(RecoverableChannelError): - code = 313 - - -class ConnectionForced(RecoverableConnectionError): - code = 320 - - -class InvalidPath(IrrecoverableConnectionError): - code = 402 - - -class AccessRefused(IrrecoverableChannelError): - code = 403 - - -class NotFound(IrrecoverableChannelError): - code = 404 - - -class ResourceLocked(RecoverableChannelError): - code = 405 - - -class PreconditionFailed(IrrecoverableChannelError): - code = 406 - - -class FrameError(IrrecoverableConnectionError): - code = 501 - - -class FrameSyntaxError(IrrecoverableConnectionError): - code = 502 - - -class InvalidCommand(IrrecoverableConnectionError): - code = 503 - - -class ChannelNotOpen(IrrecoverableConnectionError): - code = 504 - - -class UnexpectedFrame(IrrecoverableConnectionError): - code = 505 - - -class ResourceError(RecoverableConnectionError): - code = 506 - - -class NotAllowed(IrrecoverableConnectionError): - code = 530 - - -class AMQPNotImplementedError(IrrecoverableConnectionError): - code = 540 - - -class InternalError(IrrecoverableConnectionError): - code = 541 - - -ERROR_MAP = { - 311: ContentTooLarge, - 313: NoConsumers, - 320: ConnectionForced, - 402: InvalidPath, - 403: AccessRefused, - 404: NotFound, - 405: ResourceLocked, - 406: PreconditionFailed, - 501: FrameError, - 502: FrameSyntaxError, - 503: InvalidCommand, - 504: ChannelNotOpen, - 505: UnexpectedFrame, - 506: ResourceError, - 530: NotAllowed, - 540: AMQPNotImplementedError, - 541: InternalError, -} - - -def error_for_code(code, text, method, default): - try: - return ERROR_MAP[code](text, method, reply_code=code) - except KeyError: - return default(text, method, reply_code=code) - - -def raise_for_code(code, text, method, default): - raise error_for_code(code, text, method, default) - - -METHOD_NAME_MAP = { - (10, 10): 'Connection.start', - (10, 11): 'Connection.start_ok', - (10, 20): 'Connection.secure', - (10, 21): 'Connection.secure_ok', - (10, 30): 'Connection.tune', - (10, 31): 'Connection.tune_ok', - (10, 40): 'Connection.open', - (10, 41): 'Connection.open_ok', - (10, 50): 'Connection.close', - (10, 51): 'Connection.close_ok', - (20, 10): 'Channel.open', - (20, 11): 'Channel.open_ok', - (20, 20): 'Channel.flow', - (20, 21): 'Channel.flow_ok', - (20, 40): 'Channel.close', - (20, 41): 'Channel.close_ok', - (30, 10): 'Access.request', - (30, 11): 'Access.request_ok', - (40, 10): 'Exchange.declare', - (40, 11): 'Exchange.declare_ok', - (40, 20): 'Exchange.delete', - (40, 21): 'Exchange.delete_ok', - (40, 30): 'Exchange.bind', - (40, 31): 'Exchange.bind_ok', - (40, 40): 'Exchange.unbind', - (40, 41): 'Exchange.unbind_ok', - (50, 10): 'Queue.declare', - (50, 11): 'Queue.declare_ok', - (50, 20): 'Queue.bind', - (50, 21): 'Queue.bind_ok', - (50, 30): 'Queue.purge', - (50, 31): 'Queue.purge_ok', - (50, 40): 'Queue.delete', - (50, 41): 'Queue.delete_ok', - (50, 50): 'Queue.unbind', - (50, 51): 'Queue.unbind_ok', - (60, 10): 'Basic.qos', - (60, 11): 'Basic.qos_ok', - (60, 20): 'Basic.consume', - (60, 21): 'Basic.consume_ok', - (60, 30): 'Basic.cancel', - (60, 31): 'Basic.cancel_ok', - (60, 40): 'Basic.publish', - (60, 50): 'Basic.return', - (60, 60): 'Basic.deliver', - (60, 70): 'Basic.get', - (60, 71): 'Basic.get_ok', - (60, 72): 'Basic.get_empty', - (60, 80): 'Basic.ack', - (60, 90): 'Basic.reject', - (60, 100): 'Basic.recover_async', - (60, 110): 'Basic.recover', - (60, 111): 'Basic.recover_ok', - (60, 120): 'Basic.nack', - (90, 10): 'Tx.select', - (90, 11): 'Tx.select_ok', - (90, 20): 'Tx.commit', - (90, 21): 'Tx.commit_ok', - (90, 30): 'Tx.rollback', - (90, 31): 'Tx.rollback_ok', - (85, 10): 'Confirm.select', - (85, 11): 'Confirm.select_ok', -} - - -for _method_id, _method_name in list(METHOD_NAME_MAP.items()): - METHOD_NAME_MAP[unpack('>I', pack('>HH', *_method_id))[0]] = _method_name diff --git a/amqp/five.py b/amqp/five.py deleted file mode 100644 index 5157df5..0000000 --- a/amqp/five.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- -""" - celery.five - ~~~~~~~~~~~ - - Compatibility implementations of features - only available in newer Python versions. - - -""" -from __future__ import absolute_import - -############## py3k ######################################################### -import sys -PY3 = sys.version_info[0] == 3 - -try: - reload = reload # noqa -except NameError: # pragma: no cover - from imp import reload # noqa - -try: - from UserList import UserList # noqa -except ImportError: # pragma: no cover - from collections import UserList # noqa - -try: - from UserDict import UserDict # noqa -except ImportError: # pragma: no cover - from collections import UserDict # noqa - - -if PY3: - import builtins - - from queue import Queue, Empty - from itertools import zip_longest - from io import StringIO, BytesIO - - map = map - string = str - string_t = str - long_t = int - text_t = str - range = range - int_types = (int, ) - - open_fqdn = 'builtins.open' - - def items(d): - return d.items() - - def keys(d): - return d.keys() - - def values(d): - return d.values() - - def nextfun(it): - return it.__next__ - - exec_ = getattr(builtins, 'exec') - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - - class WhateverIO(StringIO): - - def write(self, data): - if isinstance(data, bytes): - data = data.encode() - StringIO.write(self, data) - -else: - import __builtin__ as builtins # noqa - from Queue import Queue, Empty # noqa - from itertools import imap as map, izip_longest as zip_longest # noqa - from StringIO import StringIO # noqa - string = unicode # noqa - string_t = basestring # noqa - text_t = unicode - long_t = long # noqa - range = xrange - int_types = (int, long) - - open_fqdn = '__builtin__.open' - - def items(d): # noqa - return d.iteritems() - - def keys(d): # noqa - return d.iterkeys() - - def values(d): # noqa - return d.itervalues() - - def nextfun(it): # noqa - return it.next - - def exec_(code, globs=None, locs=None): - """Execute code in a namespace.""" - if globs is None: - frame = sys._getframe(1) - globs = frame.f_globals - if locs is None: - locs = frame.f_locals - del frame - elif locs is None: - locs = globs - exec("""exec code in globs, locs""") - - exec_("""def reraise(tp, value, tb=None): raise tp, value, tb""") - - BytesIO = WhateverIO = StringIO # noqa - - -def with_metaclass(Type, skip_attrs=set(['__dict__', '__weakref__'])): - """Class decorator to set metaclass. - - Works with both Python 3 and Python 3 and it does not add - an extra class in the lookup order like ``six.with_metaclass`` does - (that is -- it copies the original class instead of using inheritance). - - """ - - def _clone_with_metaclass(Class): - attrs = dict((key, value) for key, value in items(vars(Class)) - if key not in skip_attrs) - return Type(Class.__name__, Class.__bases__, attrs) - - return _clone_with_metaclass - -############## time.monotonic ################################################ - -if sys.version_info < (3, 3): - - import platform - SYSTEM = platform.system() - - if SYSTEM == 'Darwin': - import ctypes - from ctypes.util import find_library - libSystem = ctypes.CDLL('libSystem.dylib') - CoreServices = ctypes.CDLL(find_library('CoreServices'), - use_errno=True) - mach_absolute_time = libSystem.mach_absolute_time - mach_absolute_time.restype = ctypes.c_uint64 - absolute_to_nanoseconds = CoreServices.AbsoluteToNanoseconds - absolute_to_nanoseconds.restype = ctypes.c_uint64 - absolute_to_nanoseconds.argtypes = [ctypes.c_uint64] - - def _monotonic(): - return absolute_to_nanoseconds(mach_absolute_time()) * 1e-9 - - elif SYSTEM == 'Linux': - # from stackoverflow: - # questions/1205722/how-do-i-get-monotonic-time-durations-in-python - import ctypes - import os - - CLOCK_MONOTONIC = 1 # see - - class timespec(ctypes.Structure): - _fields_ = [ - ('tv_sec', ctypes.c_long), - ('tv_nsec', ctypes.c_long), - ] - - librt = ctypes.CDLL('librt.so.1', use_errno=True) - clock_gettime = librt.clock_gettime - clock_gettime.argtypes = [ - ctypes.c_int, ctypes.POINTER(timespec), - ] - - def _monotonic(): # noqa - t = timespec() - if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0: - errno_ = ctypes.get_errno() - raise OSError(errno_, os.strerror(errno_)) - return t.tv_sec + t.tv_nsec * 1e-9 - else: - from time import time as _monotonic -try: - from time import monotonic -except ImportError: - monotonic = _monotonic # noqa diff --git a/amqp/method_framing.py b/amqp/method_framing.py deleted file mode 100644 index b454524..0000000 --- a/amqp/method_framing.py +++ /dev/null @@ -1,231 +0,0 @@ -"""Convert between frames and higher-level AMQP methods""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -from collections import defaultdict, deque -from struct import pack, unpack - -from .basic_message import Message -from .exceptions import AMQPError, UnexpectedFrame -from .five import range, string -from .serialization import AMQPReader - -__all__ = ['MethodReader'] - -# -# MethodReader needs to know which methods are supposed -# to be followed by content headers and bodies. -# -_CONTENT_METHODS = [ - (60, 50), # Basic.return - (60, 60), # Basic.deliver - (60, 71), # Basic.get_ok -] - - -class _PartialMessage(object): - """Helper class to build up a multi-frame method.""" - - def __init__(self, method_sig, args, channel): - self.method_sig = method_sig - self.args = args - self.msg = Message() - self.body_parts = [] - self.body_received = 0 - self.body_size = None - self.complete = False - - def add_header(self, payload): - class_id, weight, self.body_size = unpack('>HHQ', payload[:12]) - self.msg._load_properties(payload[12:]) - self.complete = (self.body_size == 0) - - def add_payload(self, payload): - parts = self.body_parts - self.body_received += len(payload) - if self.body_received == self.body_size: - if parts: - parts.append(payload) - self.msg.body = bytes().join(parts) - else: - self.msg.body = payload - self.complete = True - else: - parts.append(payload) - - -class MethodReader(object): - """Helper class to receive frames from the broker, combine them if - necessary with content-headers and content-bodies into complete methods. - - Normally a method is represented as a tuple containing - (channel, method_sig, args, content). - - In the case of a framing error, an :exc:`ConnectionError` is placed - in the queue. - - In the case of unexpected frames, a tuple made up of - ``(channel, ChannelError)`` is placed in the queue. - - """ - - def __init__(self, source): - self.source = source - self.queue = deque() - self.running = False - self.partial_messages = {} - self.heartbeats = 0 - # For each channel, which type is expected next - self.expected_types = defaultdict(lambda: 1) - # not an actual byte count, just incremented whenever we receive - self.bytes_recv = 0 - self._quick_put = self.queue.append - self._quick_get = self.queue.popleft - - def _next_method(self): - """Read the next method from the source, once one complete method has - been assembled it is placed in the internal queue.""" - queue = self.queue - put = self._quick_put - read_frame = self.source.read_frame - while not queue: - try: - frame_type, channel, payload = read_frame() - except Exception as exc: - # - # Connection was closed? Framing Error? - # - put(exc) - break - - self.bytes_recv += 1 - - if frame_type not in (self.expected_types[channel], 8): - put(( - channel, - UnexpectedFrame( - 'Received frame {0} while expecting type: {1}'.format( - frame_type, self.expected_types[channel])))) - elif frame_type == 1: - self._process_method_frame(channel, payload) - elif frame_type == 2: - self._process_content_header(channel, payload) - elif frame_type == 3: - self._process_content_body(channel, payload) - elif frame_type == 8: - self._process_heartbeat(channel, payload) - - def _process_heartbeat(self, channel, payload): - self.heartbeats += 1 - - def _process_method_frame(self, channel, payload): - """Process Method frames""" - method_sig = unpack('>HH', payload[:4]) - args = AMQPReader(payload[4:]) - - if method_sig in _CONTENT_METHODS: - # - # Save what we've got so far and wait for the content-header - # - self.partial_messages[channel] = _PartialMessage( - method_sig, args, channel, - ) - self.expected_types[channel] = 2 - else: - self._quick_put((channel, method_sig, args, None)) - - def _process_content_header(self, channel, payload): - """Process Content Header frames""" - partial = self.partial_messages[channel] - partial.add_header(payload) - - if partial.complete: - # - # a bodyless message, we're done - # - self._quick_put((channel, partial.method_sig, - partial.args, partial.msg)) - self.partial_messages.pop(channel, None) - self.expected_types[channel] = 1 - else: - # - # wait for the content-body - # - self.expected_types[channel] = 3 - - def _process_content_body(self, channel, payload): - """Process Content Body frames""" - partial = self.partial_messages[channel] - partial.add_payload(payload) - if partial.complete: - # - # Stick the message in the queue and go back to - # waiting for method frames - # - self._quick_put((channel, partial.method_sig, - partial.args, partial.msg)) - self.partial_messages.pop(channel, None) - self.expected_types[channel] = 1 - - def read_method(self): - """Read a method from the peer.""" - self._next_method() - m = self._quick_get() - if isinstance(m, Exception): - raise m - if isinstance(m, tuple) and isinstance(m[1], AMQPError): - raise m[1] - return m - - -class MethodWriter(object): - """Convert AMQP methods into AMQP frames and send them out - to the peer.""" - - def __init__(self, dest, frame_max): - self.dest = dest - self.frame_max = frame_max - self.bytes_sent = 0 - - def write_method(self, channel, method_sig, args, content=None): - write_frame = self.dest.write_frame - payload = pack('>HH', method_sig[0], method_sig[1]) + args - - if content: - # do this early, so we can raise an exception if there's a - # problem with the content properties, before sending the - # first frame - body = content.body - if isinstance(body, string): - coding = content.properties.get('content_encoding', None) - if coding is None: - coding = content.properties['content_encoding'] = 'UTF-8' - - body = body.encode(coding) - properties = content._serialize_properties() - - write_frame(1, channel, payload) - - if content: - payload = pack('>HHQ', method_sig[0], 0, len(body)) + properties - - write_frame(2, channel, payload) - - chunk_size = self.frame_max - 8 - for i in range(0, len(body), chunk_size): - write_frame(3, channel, body[i:i + chunk_size]) - self.bytes_sent += 1 diff --git a/amqp/protocol.py b/amqp/protocol.py deleted file mode 100644 index 0856eb4..0000000 --- a/amqp/protocol.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from collections import namedtuple - - -queue_declare_ok_t = namedtuple( - 'queue_declare_ok_t', ('queue', 'message_count', 'consumer_count'), -) - -basic_return_t = namedtuple( - 'basic_return_t', - ('reply_code', 'reply_text', 'exchange', 'routing_key', 'message'), -) diff --git a/amqp/serialization.py b/amqp/serialization.py deleted file mode 100644 index 4ad1b06..0000000 --- a/amqp/serialization.py +++ /dev/null @@ -1,510 +0,0 @@ -""" -Convert between bytestreams and higher-level AMQP types. - -2007-11-05 Barry Pederson - -""" -# Copyright (C) 2007 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -import sys - -from datetime import datetime -from decimal import Decimal -from io import BytesIO -from struct import pack, unpack -from time import mktime - -from .exceptions import FrameSyntaxError -from .five import int_types, long_t, string, string_t, items - -IS_PY3K = sys.version_info[0] >= 3 - -if IS_PY3K: - def byte(n): - return bytes([n]) -else: - byte = chr - - -ILLEGAL_TABLE_TYPE_WITH_KEY = """\ -Table type {0!r} for key {1!r} not handled by amqp. [value: {2!r}] -""" - -ILLEGAL_TABLE_TYPE = """\ - Table type {0!r} not handled by amqp. [value: {1!r}] -""" - - -class AMQPReader(object): - """Read higher-level AMQP types from a bytestream.""" - def __init__(self, source): - """Source should be either a file-like object with a read() method, or - a plain (non-unicode) string.""" - if isinstance(source, bytes): - self.input = BytesIO(source) - elif hasattr(source, 'read'): - self.input = source - else: - raise ValueError( - 'AMQPReader needs a file-like object or plain string') - - self.bitcount = self.bits = 0 - - def close(self): - self.input.close() - - def read(self, n): - """Read n bytes.""" - self.bitcount = self.bits = 0 - return self.input.read(n) - - def read_bit(self): - """Read a single boolean value.""" - if not self.bitcount: - self.bits = ord(self.input.read(1)) - self.bitcount = 8 - result = (self.bits & 1) == 1 - self.bits >>= 1 - self.bitcount -= 1 - return result - - def read_octet(self): - """Read one byte, return as an integer""" - self.bitcount = self.bits = 0 - return unpack('B', self.input.read(1))[0] - - def read_short(self): - """Read an unsigned 16-bit integer""" - self.bitcount = self.bits = 0 - return unpack('>H', self.input.read(2))[0] - - def read_long(self): - """Read an unsigned 32-bit integer""" - self.bitcount = self.bits = 0 - return unpack('>I', self.input.read(4))[0] - - def read_longlong(self): - """Read an unsigned 64-bit integer""" - self.bitcount = self.bits = 0 - return unpack('>Q', self.input.read(8))[0] - - def read_float(self): - """Read float value.""" - self.bitcount = self.bits = 0 - return unpack('>d', self.input.read(8))[0] - - def read_shortstr(self): - """Read a short string that's stored in up to 255 bytes. - - The encoding isn't specified in the AMQP spec, so - assume it's utf-8 - - """ - self.bitcount = self.bits = 0 - slen = unpack('B', self.input.read(1))[0] - return self.input.read(slen).decode('utf-8') - - def read_longstr(self): - """Read a string that's up to 2**32 bytes. - - The encoding isn't specified in the AMQP spec, so - assume it's utf-8 - - """ - self.bitcount = self.bits = 0 - slen = unpack('>I', self.input.read(4))[0] - return self.input.read(slen).decode('utf-8') - - def read_table(self): - """Read an AMQP table, and return as a Python dictionary.""" - self.bitcount = self.bits = 0 - tlen = unpack('>I', self.input.read(4))[0] - table_data = AMQPReader(self.input.read(tlen)) - result = {} - while table_data.input.tell() < tlen: - name = table_data.read_shortstr() - val = table_data.read_item() - result[name] = val - return result - - def read_item(self): - ftype = ord(self.input.read(1)) - - # 'S': long string - if ftype == 83: - val = self.read_longstr() - # 's': short string - elif ftype == 115: - val = self.read_shortstr() - # 'b': short-short int - elif ftype == 98: - val, = unpack('>B', self.input.read(1)) - # 'B': short-short unsigned int - elif ftype == 66: - val, = unpack('>b', self.input.read(1)) - # 'U': short int - elif ftype == 85: - val, = unpack('>h', self.input.read(2)) - # 'u': short unsigned int - elif ftype == 117: - val, = unpack('>H', self.input.read(2)) - # 'I': long int - elif ftype == 73: - val, = unpack('>i', self.input.read(4)) - # 'i': long unsigned int - elif ftype == 105: # 'l' - val, = unpack('>I', self.input.read(4)) - # 'L': long long int - elif ftype == 76: - val, = unpack('>q', self.input.read(8)) - # 'l': long long unsigned int - elif ftype == 108: - val, = unpack('>Q', self.input.read(8)) - # 'f': float - elif ftype == 102: - val, = unpack('>f', self.input.read(4)) - # 'd': double - elif ftype == 100: - val = self.read_float() - # 'D': decimal - elif ftype == 68: - d = self.read_octet() - n, = unpack('>i', self.input.read(4)) - val = Decimal(n) / Decimal(10 ** d) - # 'F': table - elif ftype == 70: - val = self.read_table() # recurse - # 'A': array - elif ftype == 65: - val = self.read_array() - # 't' (bool) - elif ftype == 116: - val = self.read_bit() - # 'T': timestamp - elif ftype == 84: - val = self.read_timestamp() - # 'V': void - elif ftype == 86: - val = None - else: - raise FrameSyntaxError( - 'Unknown value in table: {0!r} ({1!r})'.format( - ftype, type(ftype))) - return val - - def read_array(self): - array_length = unpack('>I', self.input.read(4))[0] - array_data = AMQPReader(self.input.read(array_length)) - result = [] - while array_data.input.tell() < array_length: - val = array_data.read_item() - result.append(val) - return result - - def read_timestamp(self): - """Read and AMQP timestamp, which is a 64-bit integer representing - seconds since the Unix epoch in 1-second resolution. - - Return as a Python datetime.datetime object, - expressed as localtime. - - """ - return datetime.fromtimestamp(self.read_longlong()) - - -class AMQPWriter(object): - """Convert higher-level AMQP types to bytestreams.""" - - def __init__(self, dest=None): - """dest may be a file-type object (with a write() method). If None - then a BytesIO is created, and the contents can be accessed with - this class's getvalue() method.""" - self.out = BytesIO() if dest is None else dest - self.bits = [] - self.bitcount = 0 - - def _flushbits(self): - if self.bits: - out = self.out - for b in self.bits: - out.write(pack('B', b)) - self.bits = [] - self.bitcount = 0 - - def close(self): - """Pass through if possible to any file-like destinations.""" - try: - self.out.close() - except AttributeError: - pass - - def flush(self): - """Pass through if possible to any file-like destinations.""" - try: - self.out.flush() - except AttributeError: - pass - - def getvalue(self): - """Get what's been encoded so far if we're working with a BytesIO.""" - self._flushbits() - return self.out.getvalue() - - def write(self, s): - """Write a plain Python string with no special encoding in Python 2.x, - or bytes in Python 3.x""" - self._flushbits() - self.out.write(s) - - def write_bit(self, b): - """Write a boolean value.""" - b = 1 if b else 0 - shift = self.bitcount % 8 - if shift == 0: - self.bits.append(0) - self.bits[-1] |= (b << shift) - self.bitcount += 1 - - def write_octet(self, n): - """Write an integer as an unsigned 8-bit value.""" - if n < 0 or n > 255: - raise FrameSyntaxError( - 'Octet {0!r} out of range 0..255'.format(n)) - self._flushbits() - self.out.write(pack('B', n)) - - def write_short(self, n): - """Write an integer as an unsigned 16-bit value.""" - if n < 0 or n > 65535: - raise FrameSyntaxError( - 'Octet {0!r} out of range 0..65535'.format(n)) - self._flushbits() - self.out.write(pack('>H', int(n))) - - def write_long(self, n): - """Write an integer as an unsigned2 32-bit value.""" - if n < 0 or n >= 4294967296: - raise FrameSyntaxError( - 'Octet {0!r} out of range 0..2**31-1'.format(n)) - self._flushbits() - self.out.write(pack('>I', n)) - - def write_longlong(self, n): - """Write an integer as an unsigned 64-bit value.""" - if n < 0 or n >= 18446744073709551616: - raise FrameSyntaxError( - 'Octet {0!r} out of range 0..2**64-1'.format(n)) - self._flushbits() - self.out.write(pack('>Q', n)) - - def write_shortstr(self, s): - """Write a string up to 255 bytes long (after any encoding). - - If passed a unicode string, encode with UTF-8. - - """ - self._flushbits() - if isinstance(s, string): - s = s.encode('utf-8') - if len(s) > 255: - raise FrameSyntaxError( - 'Shortstring overflow ({0} > 255)'.format(len(s))) - self.write_octet(len(s)) - self.out.write(s) - - def write_longstr(self, s): - """Write a string up to 2**32 bytes long after encoding. - - If passed a unicode string, encode as UTF-8. - - """ - self._flushbits() - if isinstance(s, string): - s = s.encode('utf-8') - self.write_long(len(s)) - self.out.write(s) - - def write_table(self, d): - """Write out a Python dictionary made of up string keys, and values - that are strings, signed integers, Decimal, datetime.datetime, or - sub-dictionaries following the same constraints.""" - self._flushbits() - table_data = AMQPWriter() - for k, v in items(d): - table_data.write_shortstr(k) - table_data.write_item(v, k) - table_data = table_data.getvalue() - self.write_long(len(table_data)) - self.out.write(table_data) - - def write_item(self, v, k=None): - if isinstance(v, (string_t, bytes)): - if isinstance(v, string): - v = v.encode('utf-8') - self.write(b'S') - self.write_longstr(v) - elif isinstance(v, bool): - self.write(pack('>cB', b't', int(v))) - elif isinstance(v, float): - self.write(pack('>cd', b'd', v)) - elif isinstance(v, int_types): - self.write(pack('>ci', b'I', v)) - elif isinstance(v, Decimal): - self.write(b'D') - sign, digits, exponent = v.as_tuple() - v = 0 - for d in digits: - v = (v * 10) + d - if sign: - v = -v - self.write_octet(-exponent) - self.write(pack('>i', v)) - elif isinstance(v, datetime): - self.write(b'T') - self.write_timestamp(v) - ## FIXME: timezone ? - elif isinstance(v, dict): - self.write(b'F') - self.write_table(v) - elif isinstance(v, (list, tuple)): - self.write(b'A') - self.write_array(v) - elif v is None: - self.write(b'V') - else: - err = (ILLEGAL_TABLE_TYPE_WITH_KEY.format(type(v), k, v) if k - else ILLEGAL_TABLE_TYPE.format(type(v), v)) - raise FrameSyntaxError(err) - - def write_array(self, a): - array_data = AMQPWriter() - for v in a: - array_data.write_item(v) - array_data = array_data.getvalue() - self.write_long(len(array_data)) - self.out.write(array_data) - - def write_timestamp(self, v): - """Write out a Python datetime.datetime object as a 64-bit integer - representing seconds since the Unix epoch.""" - self.out.write(pack('>q', long_t(mktime(v.timetuple())))) - - -class GenericContent(object): - """Abstract base class for AMQP content. - - Subclasses should override the PROPERTIES attribute. - - """ - PROPERTIES = [('dummy', 'shortstr')] - - def __init__(self, **props): - """Save the properties appropriate to this AMQP content type - in a 'properties' dictionary.""" - d = {} - for propname, _ in self.PROPERTIES: - if propname in props: - d[propname] = props[propname] - # FIXME: should we ignore unknown properties? - - self.properties = d - - def __eq__(self, other): - """Check if this object has the same properties as another - content object.""" - try: - return self.properties == other.properties - except AttributeError: - return NotImplemented - - def __getattr__(self, name): - """Look for additional properties in the 'properties' - dictionary, and if present - the 'delivery_info' - dictionary.""" - if name == '__setstate__': - # Allows pickling/unpickling to work - raise AttributeError('__setstate__') - - if name in self.properties: - return self.properties[name] - - if 'delivery_info' in self.__dict__ \ - and name in self.delivery_info: - return self.delivery_info[name] - - raise AttributeError(name) - - def _load_properties(self, raw_bytes): - """Given the raw bytes containing the property-flags and property-list - from a content-frame-header, parse and insert into a dictionary - stored in this object as an attribute named 'properties'.""" - r = AMQPReader(raw_bytes) - - # - # Read 16-bit shorts until we get one with a low bit set to zero - # - flags = [] - while 1: - flag_bits = r.read_short() - flags.append(flag_bits) - if flag_bits & 1 == 0: - break - - shift = 0 - d = {} - for key, proptype in self.PROPERTIES: - if shift == 0: - if not flags: - break - flag_bits, flags = flags[0], flags[1:] - shift = 15 - if flag_bits & (1 << shift): - d[key] = getattr(r, 'read_' + proptype)() - shift -= 1 - - self.properties = d - - def _serialize_properties(self): - """serialize the 'properties' attribute (a dictionary) into - the raw bytes making up a set of property flags and a - property list, suitable for putting into a content frame header.""" - shift = 15 - flag_bits = 0 - flags = [] - raw_bytes = AMQPWriter() - for key, proptype in self.PROPERTIES: - val = self.properties.get(key, None) - if val is not None: - if shift == 0: - flags.append(flag_bits) - flag_bits = 0 - shift = 15 - - flag_bits |= (1 << shift) - if proptype != 'bit': - getattr(raw_bytes, 'write_' + proptype)(val) - - shift -= 1 - - flags.append(flag_bits) - result = AMQPWriter() - for flag_bits in flags: - result.write_short(flag_bits) - result.write(raw_bytes.getvalue()) - - return result.getvalue() diff --git a/amqp/transport.py b/amqp/transport.py deleted file mode 100644 index eac3dbc..0000000 --- a/amqp/transport.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright (C) 2009 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -from __future__ import absolute_import - -import errno -import re -import socket -import ssl - -# Jython does not have this attribute -try: - from socket import SOL_TCP -except ImportError: # pragma: no cover - from socket import IPPROTO_TCP as SOL_TCP # noqa - -try: - from ssl import SSLError -except ImportError: - class SSLError(Exception): # noqa - pass - -from struct import pack, unpack - -from .exceptions import UnexpectedFrame -from .utils import get_errno, set_cloexec - -_UNAVAIL = errno.EAGAIN, errno.EINTR, errno.ENOENT - -AMQP_PORT = 5672 - -EMPTY_BUFFER = bytes() - -# Yes, Advanced Message Queuing Protocol Protocol is redundant -AMQP_PROTOCOL_HEADER = 'AMQP\x01\x01\x00\x09'.encode('latin_1') - -# Match things like: [fe80::1]:5432, from RFC 2732 -IPV6_LITERAL = re.compile(r'\[([\.0-9a-f:]+)\](?::(\d+))?') - - -class _AbstractTransport(object): - """Common superclass for TCP and SSL transports""" - connected = False - - def __init__(self, host, connect_timeout): - self.connected = True - msg = None - port = AMQP_PORT - - m = IPV6_LITERAL.match(host) - if m: - host = m.group(1) - if m.group(2): - port = int(m.group(2)) - else: - if ':' in host: - host, port = host.rsplit(':', 1) - port = int(port) - - self.sock = None - last_err = None - for res in socket.getaddrinfo(host, port, 0, - socket.SOCK_STREAM, SOL_TCP): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - try: - set_cloexec(self.sock, True) - except NotImplementedError: - pass - self.sock.settimeout(connect_timeout) - self.sock.connect(sa) - except socket.error as exc: - msg = exc - self.sock.close() - self.sock = None - last_err = msg - continue - break - - if not self.sock: - # Didn't connect, return the most recent error message - raise socket.error(last_err) - - try: - self.sock.settimeout(None) - self.sock.setsockopt(SOL_TCP, socket.TCP_NODELAY, 1) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - - self._setup_transport() - - self._write(AMQP_PROTOCOL_HEADER) - except (OSError, IOError, socket.error) as exc: - if get_errno(exc) not in _UNAVAIL: - self.connected = False - raise - - def __del__(self): - try: - # socket module may have been collected by gc - # if this is called by a thread at shutdown. - if socket is not None: - try: - self.close() - except socket.error: - pass - finally: - self.sock = None - - def _read(self, n, initial=False): - """Read exactly n bytes from the peer""" - raise NotImplementedError('Must be overriden in subclass') - - def _setup_transport(self): - """Do any additional initialization of the class (used - by the subclasses).""" - pass - - def _shutdown_transport(self): - """Do any preliminary work in shutting down the connection.""" - pass - - def _write(self, s): - """Completely write a string to the peer.""" - raise NotImplementedError('Must be overriden in subclass') - - def close(self): - if self.sock is not None: - self._shutdown_transport() - # Call shutdown first to make sure that pending messages - # reach the AMQP broker if the program exits after - # calling this method. - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - self.sock = None - self.connected = False - - def read_frame(self, unpack=unpack): - read = self._read - try: - frame_type, channel, size = unpack('>BHI', read(7, True)) - payload = read(size) - ch = ord(read(1)) - except socket.timeout: - raise - except (OSError, IOError, socket.error) as exc: - # Don't disconnect for ssl read time outs - # http://bugs.python.org/issue10272 - if isinstance(exc, SSLError) and 'timed out' in str(exc): - raise socket.timeout() - if get_errno(exc) not in _UNAVAIL: - self.connected = False - raise - if ch == 206: # '\xce' - return frame_type, channel, payload - else: - raise UnexpectedFrame( - 'Received 0x{0:02x} while expecting 0xce'.format(ch)) - - def write_frame(self, frame_type, channel, payload): - size = len(payload) - try: - self._write(pack( - '>BHI%dsB' % size, - frame_type, channel, size, payload, 0xce, - )) - except socket.timeout: - raise - except (OSError, IOError, socket.error) as exc: - if get_errno(exc) not in _UNAVAIL: - self.connected = False - raise - - -class SSLTransport(_AbstractTransport): - """Transport that works over SSL""" - - def __init__(self, host, connect_timeout, ssl): - if isinstance(ssl, dict): - self.sslopts = ssl - self._read_buffer = EMPTY_BUFFER - super(SSLTransport, self).__init__(host, connect_timeout) - - def _setup_transport(self): - """Wrap the socket in an SSL object.""" - if hasattr(self, 'sslopts'): - self.sock = ssl.wrap_socket(self.sock, **self.sslopts) - else: - self.sock = ssl.wrap_socket(self.sock) - self.sock.do_handshake() - self._quick_recv = self.sock.read - - def _shutdown_transport(self): - """Unwrap a Python 2.6 SSL socket, so we can call shutdown()""" - if self.sock is not None: - try: - unwrap = self.sock.unwrap - except AttributeError: - return - self.sock = unwrap() - - def _read(self, n, initial=False, - _errnos=(errno.ENOENT, errno.EAGAIN, errno.EINTR)): - # According to SSL_read(3), it can at most return 16kb of data. - # Thus, we use an internal read buffer like TCPTransport._read - # to get the exact number of bytes wanted. - recv = self._quick_recv - rbuf = self._read_buffer - try: - while len(rbuf) < n: - try: - s = recv(n - len(rbuf)) # see note above - except socket.error as exc: - # ssl.sock.read may cause ENOENT if the - # operation couldn't be performed (Issue celery#1414). - if not initial and exc.errno in _errnos: - continue - raise - if not s: - raise IOError('Socket closed') - rbuf += s - except: - self._read_buffer = rbuf - raise - result, self._read_buffer = rbuf[:n], rbuf[n:] - return result - - def _write(self, s): - """Write a string out to the SSL socket fully.""" - try: - write = self.sock.write - except AttributeError: - # Works around a bug in python socket library - raise IOError('Socket closed') - else: - while s: - n = write(s) - if not n: - raise IOError('Socket closed') - s = s[n:] - - -class TCPTransport(_AbstractTransport): - """Transport that deals directly with TCP socket.""" - - def _setup_transport(self): - """Setup to _write() directly to the socket, and - do our own buffered reads.""" - self._write = self.sock.sendall - self._read_buffer = EMPTY_BUFFER - self._quick_recv = self.sock.recv - - def _read(self, n, initial=False, _errnos=(errno.EAGAIN, errno.EINTR)): - """Read exactly n bytes from the socket""" - recv = self._quick_recv - rbuf = self._read_buffer - try: - while len(rbuf) < n: - try: - s = recv(n - len(rbuf)) - except socket.error as exc: - if not initial and exc.errno in _errnos: - continue - raise - if not s: - raise IOError('Socket closed') - rbuf += s - except: - self._read_buffer = rbuf - raise - - result, self._read_buffer = rbuf[:n], rbuf[n:] - return result - - -def create_transport(host, connect_timeout, ssl=False): - """Given a few parameters from the Connection constructor, - select and create a subclass of _AbstractTransport.""" - if ssl: - return SSLTransport(host, connect_timeout, ssl) - else: - return TCPTransport(host, connect_timeout) diff --git a/amqp/utils.py b/amqp/utils.py deleted file mode 100644 index 900d2aa..0000000 --- a/amqp/utils.py +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import absolute_import - -import sys - -try: - import fcntl -except ImportError: - fcntl = None # noqa - - -class promise(object): - if not hasattr(sys, 'pypy_version_info'): - __slots__ = tuple( - 'fun args kwargs value ready failed ' - ' on_success on_error calls'.split() - ) - - def __init__(self, fun, args=(), kwargs=(), - on_success=None, on_error=None): - self.fun = fun - self.args = args - self.kwargs = kwargs - self.ready = False - self.failed = False - self.on_success = on_success - self.on_error = on_error - self.value = None - self.calls = 0 - - def __repr__(self): - return '<$: {0.fun.__name__}(*{0.args!r}, **{0.kwargs!r})'.format( - self, - ) - - def __call__(self, *args, **kwargs): - try: - self.value = self.fun( - *self.args + args if self.args else args, - **dict(self.kwargs, **kwargs) if self.kwargs else kwargs - ) - except Exception as exc: - self.set_error_state(exc) - else: - if self.on_success: - self.on_success(self.value) - finally: - self.ready = True - self.calls += 1 - - def then(self, callback=None, on_error=None): - self.on_success = callback - self.on_error = on_error - return callback - - def set_error_state(self, exc): - self.failed = True - if self.on_error is None: - raise - self.on_error(exc) - - def throw(self, exc): - try: - raise exc - except exc.__class__ as with_cause: - self.set_error_state(with_cause) - - -def noop(): - return promise(lambda *a, **k: None) - - -try: - from os import set_cloexec # Python 3.4? -except ImportError: - def set_cloexec(fd, cloexec): # noqa - try: - FD_CLOEXEC = fcntl.FD_CLOEXEC - except AttributeError: - raise NotImplementedError( - 'close-on-exec flag not supported on this platform', - ) - flags = fcntl.fcntl(fd, fcntl.F_GETFD) - if cloexec: - flags |= FD_CLOEXEC - else: - flags &= ~FD_CLOEXEC - return fcntl.fcntl(fd, fcntl.F_SETFD, flags) - - -def get_errno(exc): - """:exc:`socket.error` and :exc:`IOError` first got - the ``.errno`` attribute in Py2.7""" - try: - return exc.errno - except AttributeError: - try: - # e.args = (errno, reason) - if isinstance(exc.args, tuple) and len(exc.args) == 2: - return exc.args[0] - except AttributeError: - pass - return 0 diff --git a/demo/amqp_clock.py b/demo/amqp_clock.py deleted file mode 100755 index c718266..0000000 --- a/demo/amqp_clock.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -""" -AMQP Clock - -Fires off simple messages at one-minute intervals to a topic -exchange named 'clock', with the topic of the message being -the local time as 'year.month.date.dow.hour.minute', -for example: '2007.11.26.1.12.33', where the dow (day of week) -is 0 for Sunday, 1 for Monday, and so on (similar to Unix crontab). - -A consumer could then bind a queue to the routing key '#.0' -for example to get a message at the beginning of each hour. - -2007-11-26 Barry Pederson - -""" -from datetime import datetime -from optparse import OptionParser -from time import sleep - -import amqp -Message = amqp.Message - -EXCHANGE_NAME = 'clock' -TOPIC_PATTERN = '%Y.%m.%d.%w.%H.%M' # Python datetime.strftime() pattern - - -def main(): - parser = OptionParser() - parser.add_option( - '--host', dest='host', - help='AMQP server to connect to (default: %default)', - default='localhost', - ) - parser.add_option( - '-u', '--userid', dest='userid', - help='AMQP userid to authenticate as (default: %default)', - default='guest', - ) - parser.add_option( - '-p', '--password', dest='password', - help='AMQP password to authenticate with (default: %default)', - default='guest', - ) - parser.add_option( - '--ssl', dest='ssl', action='store_true', - help='Enable SSL with AMQP server (default: not enabled)', - default=False, - ) - - options, args = parser.parse_args() - - conn = amqp.Connection(options.host, options.userid, options.password) - ch = conn.channel() - ch.exchange_declare(EXCHANGE_NAME, type='topic') - - # Make sure our first message is close to the beginning - # of a minute - now = datetime.now() - if now.second > 0: - sleep(60 - now.second) - - while True: - now = datetime.now() - msg = Message(timestamp=now) - msg_topic = now.strftime(TOPIC_PATTERN) - ch.basic_publish(msg, EXCHANGE_NAME, routing_key=msg_topic) - - # Don't know how long the basic_publish took, so - # grab the time again. - now = datetime.now() - sleep(60 - now.second) - - ch.close() - conn.close() - -if __name__ == '__main__': - main() diff --git a/demo/demo_receive.py b/demo/demo_receive.py deleted file mode 100755 index bfda624..0000000 --- a/demo/demo_receive.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -""" -Test AMQP library. - -Repeatedly receive messages from the demo_send.py -script, until it receives a message with 'quit' as the body. - -2007-11-11 Barry Pederson - -""" -from optparse import OptionParser -from functools import partial - -import amqp - - -def callback(channel, msg): - for key, val in msg.properties.items(): - print('%s: %s' % (key, str(val))) - for key, val in msg.delivery_info.items(): - print('> %s: %s' % (key, str(val))) - - print('') - print(msg.body) - print('-------') - print(msg.delivery_tag) - channel.basic_ack(msg.delivery_tag) - - # - # Cancel this callback - # - if msg.body == 'quit': - channel.basic_cancel(msg.consumer_tag) - - -def main(): - parser = OptionParser() - parser.add_option( - '--host', dest='host', - help='AMQP server to connect to (default: %default)', - default='localhost', - ) - parser.add_option( - '-u', '--userid', dest='userid', - help='userid to authenticate as (default: %default)', - default='guest', - ) - parser.add_option( - '-p', '--password', dest='password', - help='password to authenticate with (default: %default)', - default='guest', - ) - parser.add_option( - '--ssl', dest='ssl', action='store_true', - help='Enable SSL (default: not enabled)', - default=False, - ) - - options, args = parser.parse_args() - - conn = amqp.Connection(options.host, userid=options.userid, - password=options.password, ssl=options.ssl) - - ch = conn.channel() - - ch.exchange_declare('myfan', 'fanout') - qname, _, _ = ch.queue_declare() - ch.queue_bind(qname, 'myfan') - ch.basic_consume(qname, callback=partial(callback, ch)) - - #pyamqp:// - - # - # Loop as long as the channel has callbacks registered - # - while ch.callbacks: - ch.wait() - - ch.close() - conn.close() - -if __name__ == '__main__': - main() diff --git a/demo/demo_send.py b/demo/demo_send.py deleted file mode 100755 index 27bb1b1..0000000 --- a/demo/demo_send.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -""" -Test AMQP library. - -Send a message to the corresponding demo_receive.py script, any -arguments to this program are joined together and sent as a message -body. - -2007-11-11 Barry Pederson - -""" -import sys -from optparse import OptionParser - -import amqp - - -def main(): - parser = OptionParser( - usage='usage: %prog [options] message\nexample: %prog hello world', - ) - parser.add_option( - '--host', dest='host', - help='AMQP server to connect to (default: %default)', - default='localhost', - ) - parser.add_option( - '-u', '--userid', dest='userid', - help='userid to authenticate as (default: %default)', - default='guest', - ) - parser.add_option( - '-p', '--password', dest='password', - help='password to authenticate with (default: %default)', - default='guest', - ) - parser.add_option( - '--ssl', dest='ssl', action='store_true', - help='Enable SSL (default: not enabled)', - default=False, - ) - - options, args = parser.parse_args() - - if not args: - parser.print_help() - sys.exit(1) - - msg_body = ' '.join(args) - - conn = amqp.Connection(options.host, userid=options.userid, - password=options.password, ssl=options.ssl) - - ch = conn.channel() - ch.exchange_declare('myfan', 'fanout') - - msg = amqp.Message(msg_body, content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}) - - ch.basic_publish(msg, 'myfan') - - ch.close() - conn.close() - -if __name__ == '__main__': - main() diff --git a/docs/.static/.keep b/docs/.static/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/.templates/page.html b/docs/.templates/page.html deleted file mode 100644 index f01b9d3..0000000 --- a/docs/.templates/page.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block body %} - {{ body }} -{% endblock %} diff --git a/docs/.templates/sidebarintro.html b/docs/.templates/sidebarintro.html deleted file mode 100644 index fbab09d..0000000 --- a/docs/.templates/sidebarintro.html +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/docs/.templates/sidebarlogo.html b/docs/.templates/sidebarlogo.html deleted file mode 100644 index 0e2da8c..0000000 --- a/docs/.templates/sidebarlogo.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 1ea219c..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,81 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html web pickle htmlhelp latex changes linkcheck - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview over all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - -clean: - -rm -rf .build/* - -html: - mkdir -p .build/html .build/doctrees - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html - @echo - @echo "Build finished. The HTML pages are in .build/html." - -coverage: - mkdir -p .build/coverage .build/doctrees - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) .build/coverage - @echo - @echo "Build finished." - -pickle: - mkdir -p .build/pickle .build/doctrees - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -web: pickle - -json: - mkdir -p .build/json .build/doctrees - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - mkdir -p .build/htmlhelp .build/doctrees - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in .build/htmlhelp." - -latex: - mkdir -p .build/latex .build/doctrees - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex - @echo - @echo "Build finished; the LaTeX files are in .build/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - mkdir -p .build/changes .build/doctrees - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes - @echo - @echo "The overview file is in .build/changes." - -linkcheck: - mkdir -p .build/linkcheck .build/doctrees - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in .build/linkcheck/output.txt." diff --git a/docs/_ext/applyxrefs.py b/docs/_ext/applyxrefs.py deleted file mode 100644 index deed5d9..0000000 --- a/docs/_ext/applyxrefs.py +++ /dev/null @@ -1,92 +0,0 @@ -"""Adds xref targets to the top of files.""" - -import sys -import os - -testing = False - -DONT_TOUCH = ( - './index.txt', -) - - -def target_name(fn): - if fn.endswith('.txt'): - fn = fn[:-4] - return '_' + fn.lstrip('./').replace('/', '-') - - -def process_file(fn, lines): - lines.insert(0, '\n') - lines.insert(0, '.. %s:\n' % target_name(fn)) - try: - f = open(fn, 'w') - except IOError: - print("Can't open %s for writing. Not touching it." % fn) - return - try: - f.writelines(lines) - except IOError: - print("Can't write to %s. Not touching it." % fn) - finally: - f.close() - - -def has_target(fn): - try: - f = open(fn, 'r') - except IOError: - print("Can't open %s. Not touching it." % fn) - return (True, None) - readok = True - try: - lines = f.readlines() - except IOError: - print("Can't read %s. Not touching it." % fn) - readok = False - finally: - f.close() - if not readok: - return (True, None) - - #print fn, len(lines) - if len(lines) < 1: - print("Not touching empty file %s." % fn) - return (True, None) - if lines[0].startswith('.. _'): - return (True, None) - return (False, lines) - - -def main(argv=None): - if argv is None: - argv = sys.argv - - if len(argv) == 1: - argv.extend('.') - - files = [] - for root in argv[1:]: - for (dirpath, dirnames, filenames) in os.walk(root): - files.extend([(dirpath, f) for f in filenames]) - files.sort() - files = [os.path.join(p, fn) for p, fn in files if fn.endswith('.txt')] - #print files - - for fn in files: - if fn in DONT_TOUCH: - print("Skipping blacklisted file %s." % fn) - continue - - target_found, lines = has_target(fn) - if not target_found: - if testing: - print '%s: %s' % (fn, lines[0]), - else: - print "Adding xref to %s" % fn - process_file(fn, lines) - else: - print "Skipping %s: already has a xref" % fn - -if __name__ == '__main__': - sys.exit(main()) diff --git a/docs/_ext/literals_to_xrefs.py b/docs/_ext/literals_to_xrefs.py deleted file mode 100644 index 41aa616..0000000 --- a/docs/_ext/literals_to_xrefs.py +++ /dev/null @@ -1,173 +0,0 @@ -""" -Runs through a reST file looking for old-style literals, and helps replace them -with new-style references. -""" - -import re -import sys -import shelve - -refre = re.compile(r'``([^`\s]+?)``') - -ROLES = ( - 'attr', - 'class', - "djadmin", - 'data', - 'exc', - 'file', - 'func', - 'lookup', - 'meth', - 'mod', - "djadminopt", - "ref", - "setting", - "term", - "tfilter", - "ttag", - - # special - "skip", -) - -ALWAYS_SKIP = [ - "NULL", - "True", - "False", -] - - -def fixliterals(fname): - data = open(fname).read() - - last = 0 - new = [] - storage = shelve.open("/tmp/literals_to_xref.shelve") - lastvalues = storage.get("lastvalues", {}) - - for m in refre.finditer(data): - - new.append(data[last:m.start()]) - last = m.end() - - line_start = data.rfind("\n", 0, m.start()) - line_end = data.find("\n", m.end()) - prev_start = data.rfind("\n", 0, line_start) - next_end = data.find("\n", line_end + 1) - - # Skip always-skip stuff - if m.group(1) in ALWAYS_SKIP: - new.append(m.group(0)) - continue - - # skip when the next line is a title - next_line = data[m.end():next_end].strip() - if next_line[0] in "!-/:-@[-`{-~" and \ - all(c == next_line[0] for c in next_line): - new.append(m.group(0)) - continue - - sys.stdout.write("\n" + "-" * 80 + "\n") - sys.stdout.write(data[prev_start + 1:m.start()]) - sys.stdout.write(colorize(m.group(0), fg="red")) - sys.stdout.write(data[m.end():next_end]) - sys.stdout.write("\n\n") - - replace_type = None - while replace_type is None: - replace_type = raw_input( - colorize("Replace role: ", fg="yellow")).strip().lower() - if replace_type and replace_type not in ROLES: - replace_type = None - - if replace_type == "": - new.append(m.group(0)) - continue - - if replace_type == "skip": - new.append(m.group(0)) - ALWAYS_SKIP.append(m.group(1)) - continue - - default = lastvalues.get(m.group(1), m.group(1)) - if default.endswith("()") and \ - replace_type in ("class", "func", "meth"): - default = default[:-2] - replace_value = raw_input( - colorize("Text [", fg="yellow") + default + - colorize("]: ", fg="yellow")).strip() - if not replace_value: - replace_value = default - new.append(":%s:`%s`" % (replace_type, replace_value)) - lastvalues[m.group(1)] = replace_value - - new.append(data[last:]) - open(fname, "w").write("".join(new)) - - storage["lastvalues"] = lastvalues - storage.close() - - -def colorize(text='', opts=(), **kwargs): - """ - Returns your text, enclosed in ANSI graphics codes. - - Depends on the keyword arguments 'fg' and 'bg', and the contents of - the opts tuple/list. - - Returns the RESET code if no parameters are given. - - Valid colors: - 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white' - - Valid options: - 'bold' - 'underscore' - 'blink' - 'reverse' - 'conceal' - 'noreset' - string will not be auto-terminated with the RESET code - - Examples: - colorize('hello', fg='red', bg='blue', opts=('blink',)) - colorize() - colorize('goodbye', opts=('underscore',)) - print colorize('first line', fg='red', opts=('noreset',)) - print 'this should be red too' - print colorize('and so should this') - print 'this should not be red' - """ - color_names = ('black', 'red', 'green', 'yellow', - 'blue', 'magenta', 'cyan', 'white') - foreground = dict([(color_names[x], '3%s' % x) for x in range(8)]) - background = dict([(color_names[x], '4%s' % x) for x in range(8)]) - - RESET = '0' - opt_dict = {'bold': '1', - 'underscore': '4', - 'blink': '5', - 'reverse': '7', - 'conceal': '8'} - - text = str(text) - code_list = [] - if text == '' and len(opts) == 1 and opts[0] == 'reset': - return '\x1b[%sm' % RESET - for k, v in kwargs.iteritems(): - if k == 'fg': - code_list.append(foreground[v]) - elif k == 'bg': - code_list.append(background[v]) - for o in opts: - if o in opt_dict: - code_list.append(opt_dict[o]) - if 'noreset' not in opts: - text = text + '\x1b[%sm' % RESET - return ('\x1b[%sm' % ';'.join(code_list)) + text - -if __name__ == '__main__': - try: - fixliterals(sys.argv[1]) - except (KeyboardInterrupt, SystemExit): - print diff --git a/docs/_theme/celery/static/celery.css_t b/docs/_theme/celery/static/celery.css_t deleted file mode 100644 index 807081a..0000000 --- a/docs/_theme/celery/static/celery.css_t +++ /dev/null @@ -1,401 +0,0 @@ -/* - * celery.css_t - * ~~~~~~~~~~~~ - * - * :copyright: Copyright 2010 by Armin Ronacher. - * :license: BSD, see LICENSE for details. - */ - -{% set page_width = 940 %} -{% set sidebar_width = 220 %} -{% set body_font_stack = 'Optima, Segoe, "Segoe UI", Candara, Calibri, Arial, sans-serif' %} -{% set headline_font_stack = 'Futura, "Trebuchet MS", Arial, sans-serif' %} -{% set code_font_stack = "'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace" %} - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: {{ body_font_stack }}; - font-size: 17px; - background-color: white; - color: #000; - margin: 30px 0 0 0; - padding: 0; -} - -div.document { - width: {{ page_width }}px; - margin: 0 auto; -} - -div.deck { - font-size: 18px; -} - -p.developmentversion { - color: red; -} - -div.related { - width: {{ page_width - 20 }}px; - padding: 5px 10px; - background: #F2FCEE; - margin: 15px auto 15px auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 {{ sidebar_width }}px; -} - -div.sphinxsidebar { - width: {{ sidebar_width }}px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -img.celerylogo { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - width: {{ page_width - 15 }}px; - margin: 10px auto 30px auto; - padding-right: 15px; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dashed #DCF0D5; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebar { - font-size: 14px; - line-height: 1.5; -} - -div.sphinxsidebarwrapper { - padding: 7px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0 0 20px 0; - margin: 0; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: {{ headline_font_stack }}; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: {{ body_font_stack }}; - font-size: 1em; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #348613; - text-decoration: underline; -} - -a:hover { - color: #59B833; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: {{ headline_font_stack }}; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 200%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -div.body h1 a.toc-backref, -div.body h2 a.toc-backref, -div.body h3 a.toc-backref, -div.body h4 a.toc-backref, -div.body h5 a.toc-backref, -div.body h6 a.toc-backref { - color: inherit!important; - text-decoration: none; -} - -a.headerlink { - color: #ddd; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition p.admonition-title { - font-family: {{ headline_font_stack }}; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight{ - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: {{ code_font_stack }}; - font-size: 0.9em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; - background: #fdfdfd; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td.label { - width: 0px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul { - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #F0FFEB; - padding: 7px 10px; - margin: 15px 0; - border: 1px solid #C7ECB8; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - line-height: 1.3em; -} - -tt { - background: #F0FFEB; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background: #F0FFEB; - border-bottom: 1px solid white; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dashed #DCF0D5; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dashed #DCF0D5; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt { - background: #EEE; -} diff --git a/docs/_theme/celery/theme.conf b/docs/_theme/celery/theme.conf deleted file mode 100644 index 9ad052c..0000000 --- a/docs/_theme/celery/theme.conf +++ /dev/null @@ -1,5 +0,0 @@ -[theme] -inherit = basic -stylesheet = celery.css - -[options] diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index e036942..0000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,484 +0,0 @@ -Changes -======= - -py-amqp is fork of amqplib used by Kombu containing additional features and improvements. -The previous amqplib changelog is here: -http://code.google.com/p/py-amqplib/source/browse/CHANGES - -.. _version-1.4.5: - -1.4.5 -===== -:release-date: 2014-04-15 09:00 P.M UTC -:release-by: Ask Solem - -- Can now deserialize more AMQP types. - - Now handles types ``short string``, ``short short int``, - ``short short unsigned int``, ``short int``, ``short unsigned int``, - ``long unsigned int``, ``long long int``, ``long long unsigned int`` - and ``float`` which for some reason was missing, even in the original - amqplib module. - -- SSL: Workaround for Python SSL bug. - - A bug in the python socket library causes ``ssl.read/write()`` - on a closed socket to raise :exc:`AttributeError` instead of - :exc:`IOError`. - - Fix contributed by Craig Jellick. - -- ``Transport.__del_`` now handles errors occurring at late interpreter - shutdown (Issue #36). - -.. _version-1.4.4: - -1.4.4 -===== -:release-date: 2014-03-03 04:00 P.M UTC -:release-by: Ask Solem - -- SSL transport accidentally disconnected after read timeout. - - Fix contributed by Craig Jellick. - -.. _version-1.4.3: - -1.4.3 -===== -:release-date: 2014-02-09 03:00 P.M UTC -:release-by: Ask Solem - -- Fixed bug where more data was requested from the socket - than was actually needed. - - Contributed by Ionel Cristian Mărieș. - -.. _version-1.4.2: - -1.4.2 -===== -:release-date: 2014-01-23 05:00 P.M UTC - -- Heartbeat negotiation would use heartbeat value from server even - if heartbeat disabled (Issue #31). - -.. _version-1.4.1: - -1.4.1 -===== -:release-date: 2014-01-14 09:30 P.M UTC -:release-by: Ask Solem - -- Fixed error occurring when heartbeats disabled. - -.. _version-1.4.0: - -1.4.0 -===== -:release-date: 2014-01-13 03:00 P.M UTC -:release-by: Ask Solem - -- Heartbeat implementation improved (Issue #6). - - The new heartbeat behavior is the same approach as taken by the - RabbitMQ java library. - - This also means that clients should preferably call the ``heartbeat_tick`` - method more frequently (like every second) instead of using the old - ``rate`` argument (which is now ignored). - - - Heartbeat interval is negotiated with the server. - - Some delay is allowed if the heartbeat is late. - - Monotonic time is used to keep track of the heartbeat - instead of relying on the caller to call the checking function - at the right time. - - Contributed by Dustin J. Mitchell. - -- NoneType is now supported in tables and arrays. - - Contributed by Dominik Fässler. - -- SSLTransport: Now handles ``ENOENT``. - - Fix contributed by Adrien Guinet. - -.. _version-1.3.3: - -1.3.3 -===== -:release-date: 2013-11-11 03:30 P.M UTC -:release-by: Ask Solem - -- SSLTransport: Now keeps read buffer if an exception is raised - (Issue #26). - - Fix contributed by Tommie Gannert. - -.. _version-1.3.2: - -1.3.2 -===== -:release-date: 2013-10-29 02:00 P.M UTC -:release-by: Ask Solem - -- Message.channel is now a channel object (not the channel id). - -- Bug in previous version caused the socket to be flagged as disconnected - at EAGAIN/EINTR. - -.. _version-1.3.1: - -1.3.1 -===== -:release-date: 2013-10-24 04:00 P.M UTC -:release-by: Ask Solem - -- Now implements Connection.connected (Issue #22). - -- Fixed bug where ``str(AMQPError)`` did not return string. - -.. _version-1.3.0: - -1.3.0 -===== -:release-date: 2013-09-04 02:39 P.M UTC -:release-by: Ask Solem - -- Now sets ``Message.channel`` on delivery (Issue #12) - - amqplib used to make the channel object available - as ``Message.delivery_info['channel']``, but this was removed - in py-amqp. librabbitmq sets ``Message.channel``, - which is a more reasonable solution in our opinion as that - keeps the delivery info intact. - -- New option to wait for publish confirmations (Issue #3) - - There is now a new Connection ``confirm_publish`` that will - force any ``basic_publish`` call to wait for confirmation. - - Enabling publisher confirms like this degrades performance - considerably, but can be suitable for some applications - and now it's possible by configuration. - -- ``queue_declare`` now returns named tuple of type - :class:`~amqp.protocol.basic_declare_ok_t`. - - Supporting fields: ``queue``, ``message_count``, and - ``consumer_count``. - -- Contents of ``Channel.returned_messages`` is now named tuples. - - Supporting fields: ``reply_code``, ``reply_text``, ``exchange``, - ``routing_key``, and ``message``. - -- Sockets now set to close on exec using the ``FD_CLOEXEC`` flag. - - Currently only supported on platforms supporting this flag, - which does not include Windows. - - Contributed by Tommie Gannert. - -.. _version-1.2.1: - -1.2.1 -===== -:release-date: 2013-08-16 05:30 P.M UTC -:release-by: Ask Solem - -- Adds promise type: :meth:`amqp.utils.promise` - -- Merges fixes from 1.0.x - -.. _version-1.2.0: - -1.2.0 -===== -:release-date: 2012-11-12 04:00 P.M UTC -:release-by: Ask Solem - -- New exception hierarchy: - - - :class:`~amqp.AMQPError` - - :class:`~amqp.ConnectionError` - - :class:`~amqp.RecoverableConnectionError` - - :class:`~amqp.ConsumerCancelled` - - :class:`~amqp.ConnectionForced` - - :class:`~amqp.ResourceError` - - :class:`~IrrecoverableConnectionError` - - :class:`~amqp.ChannelNotOpen` - - :class:`~amqp.FrameError` - - :class:`~amqp.FrameSyntaxError` - - :class:`~amqp.InvalidCommand` - - :class:`~amqp.InvalidPath` - - :class:`~amqp.NotAllowed` - - :class:`~amqp.UnexpectedFrame` - - :class:`~amqp.AMQPNotImplementedError` - - :class:`~amqp.InternalError` - - :class:`~amqp.ChannelError` - - :class:`~RecoverableChannelError` - - :class:`~amqp.ContentTooLarge` - - :class:`~amqp.NoConsumers` - - :class:`~amqp.ResourceLocked` - - :class:`~IrrecoverableChannelError` - - :class:`~amqp.AccessRefused` - - :class:`~amqp.NotFound` - - :class:`~amqp.PreconditionFailed` - - -.. _version-1.1.0: - -1.1.0 -===== -:release-date: 2012-11-08 10:36 P.M UTC -:release-by: Ask Solem - -- No longer supports Python 2.5 - -- Fixed receiving of float table values. - -- Now Supports Python 3 and Python 2.6+ in the same source code. - -- Python 3 related fixes. - -.. _version-1.0.13: - -1.0.13 -====== -:release-date: 2013-07-31 04:00 P.M BST -:release-by: Ask Solem - -- Fixed problems with the SSL transport (Issue #15). - - Fix contributed by Adrien Guinet. - -- Small optimizations - -.. _version-1.0.12: - -1.0.12 -====== -:release-date: 2013-06-25 02:00 P.M BST -:release-by: Ask Solem - -- Fixed another Python 3 compatibility problem. - -.. _version-1.0.11: - -1.0.11 -====== -:release-date: 2013-04-11 06:00 P.M BST -:release-by: Ask Solem - -- Fixed Python 3 incompatibility in ``amqp/transport.py``. - -.. _version-1.0.10: - -1.0.10 -====== -:release-date: 2013-03-21 03:30 P.M UTC -:release-by: Ask Solem - -- Fixed Python 3 incompatibility in ``amqp/serialization.py``. - (Issue #11). - -.. _version-1.0.9: - -1.0.9 -===== -:release-date: 2013-03-08 10:40 A.M UTC -:release-by: Ask Solem - -- Publisher ack callbacks should now work after typo fix (Issue #9). - -- ``channel(explicit_id)`` will now claim that id from the array - of unused channel ids. - -- Fixes Jython compatibility. - -.. _version-1.0.8: - -1.0.8 -===== -:release-date: 2013-02-08 01:00 P.M UTC -:release-by: Ask Solem - -- Fixed SyntaxError on Python 2.5 - -.. _version-1.0.7: - -1.0.7 -===== -:release-date: 2013-02-08 01:00 P.M UTC -:release-by: Ask Solem - -- Workaround for bug on some Python 2.5 installations where (2**32) is 0. - -- Can now serialize the ARRAY type. - - Contributed by Adam Wentz. - -- Fixed tuple format bug in exception (Issue #4). - -.. _version-1.0.6: - -1.0.6 -===== -:release-date: 2012-11-29 01:14 P.M UTC -:release-by: Ask Solem - -- ``Channel.close`` is now ignored if the connection attribute is None. - -.. _version-1.0.5: - -1.0.5 -===== -:release-date: 2012-11-21 04:00 P.M UTC -:release-by: Ask Solem - -- ``Channel.basic_cancel`` is now ignored if the channel was already closed. - -- ``Channel.events`` is now a dict of sets:: - - >>> channel.events['basic_return'].add(on_basic_return) - >>> channel.events['basic_return'].discard(on_basic_return) - -.. _version-1.0.4: - -1.0.4 -===== -:release-date: 2012-11-13 04:00 P.M UTC -:release-by: Ask Solem - -- Fixes Python 2.5 support - -.. _version-1.0.3: - -1.0.3 -===== -:release-date: 2012-11-12 04:00 P.M UTC -:release-by: Ask Solem - -- Now can also handle float in headers/tables when receiving messages. - -- Now uses :class:`array.array` to keep track of unused channel ids. - -- The :data:`~amqp.exceptions.METHOD_NAME_MAP` has been updated for - amqp/0.9.1 and Rabbit extensions. - -- Removed a bunch of accidentally included images. - -.. _version-1.0.2: - -1.0.2 -===== -:release-date: 2012-11-06 05:00 P.M UTC -:release-by: Ask Solem - -- Now supports float values in headers/tables. - -.. _version-1.0.1: - -1.0.1 -===== -:release-date: 2012-11-05 01:00 P.M UTC -:release-by: Ask Solem - -- Connection errors no longer includes :exc:`AttributeError`. - -- Fixed problem with using the SSL transport in a non-blocking context. - - Fix contributed by Mher Movsisyan. - - -.. _version-1.0.0: - -1.0.0 -===== -:release-date: 2012-11-05 01:00 P.M UTC -:release-by: Ask Solem - -- Channels are now restored on channel error, so that the connection does not - have to closed. - -.. _version-0.9.4: - -Version 0.9.4 -============= - -- Adds support for ``exchange_bind`` and ``exchange_unbind``. - - Contributed by Rumyana Neykova - -- Fixed bugs in funtests and demo scripts. - - Contributed by Rumyana Neykova - -.. _version-0.9.3: - -Version 0.9.3 -============= - -- Fixed bug that could cause the consumer to crash when reading - large message payloads asynchronously. - -- Serialization error messages now include the invalid value. - -.. _version-0.9.2: - -Version 0.9.2 -============= - -- Consumer cancel notification support was broken (Issue #1) - - Fix contributed by Andrew Grangaard - -.. _version-0.9.1: - -Version 0.9.1 -============= - -- Supports draining events from multiple channels (``Connection.drain_events``) -- Support for timeouts -- Support for heartbeats - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. -- Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. -- Support for ``basic_return`` -- Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. -- Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". -- Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. -- Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. -- Exposes the underlying socket as ``Connection.sock``. -- Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. -- Slightly better at error recovery diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 1191395..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- - -import sys -import os - -this = os.path.dirname(os.path.abspath(__file__)) - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -sys.path.append(os.path.join(os.pardir, "tests")) -sys.path.append(os.path.join(this, "_ext")) -import amqp - -# General configuration -# --------------------- - -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', - 'sphinx.ext.intersphinx', - 'sphinxcontrib.issuetracker'] - -html_show_sphinx = False - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['.templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'py-amqp' -copyright = u'2009-2012, Ask Solem & Contributors' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = ".".join(map(str, amqp.VERSION[0:2])) -# The full version, including alpha/beta/rc tags. -release = amqp.__version__ - -exclude_trees = ['.build'] - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -intersphinx_mapping = { -} - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'trac' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['.static'] - -html_use_smartypants = True - -# If false, no module index is generated. -html_use_modindex = True - -# If false, no index is generated. -html_use_index = True - -latex_documents = [ - ('index', 'py-amqp.tex', ur'py-amqp Documentation', - ur'Ask Solem & Contributors', 'manual'), -] - -html_theme = "celery" -html_theme_path = ["_theme"] -html_sidebars = { - 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], - '**': ['sidebarlogo.html', 'relations.html', - 'sourcelink.html', 'searchbox.html'], -} - -### Issuetracker - -if False: - issuetracker = "github" - issuetracker_project = "celery/py-amqp" - issuetracker_issue_pattern = r'[Ii]ssue #(\d+)' - -# -- Options for Epub output ------------------------------------------------ - -# Bibliographic Dublin Core info. -epub_title = 'py-amqp Manual, Version 1.0' -epub_author = 'Ask Solem' -epub_publisher = 'Celery Project' -epub_copyright = '2009-2012' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -epub_language = 'en' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -epub_scheme = 'ISBN' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -epub_identifier = 'celeryproject.org' - -# A unique identification for the text. -epub_uid = 'py-amqp Manual, Version 1.0' - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# The depth of the table of contents in toc.ncx. -epub_tocdepth = 3 diff --git a/docs/includes/intro.txt b/docs/includes/intro.txt deleted file mode 100644 index cc57ce1..0000000 --- a/docs/includes/intro.txt +++ /dev/null @@ -1,96 +0,0 @@ -:Version: 1.4.2 -:Web: http://amqp.readthedocs.org/ -:Download: http://pypi.python.org/pypi/amqp/ -:Source: http://github.com/celery/py-amqp/ -:Keywords: amqp, rabbitmq - -About -===== - -This is a fork of amqplib_ which was originally written by Barry Pederson. -It is maintained by the Celery_ project, and used by `kombu`_ as a pure python -alternative when `librabbitmq`_ is not available. - -This library should be API compatible with `librabbitmq`_. - -.. _amqplib: http://pypi.python.org/pypi/amqplib -.. _Celery: http://celeryproject.org/ -.. _kombu: http://kombu.readthedocs.org/ -.. _librabbitmq: http://pypi.python.org/pypi/librabbitmq - -Differences from `amqplib`_ -=========================== - -- Supports draining events from multiple channels (``Connection.drain_events``) -- Support for timeouts -- Channels are restored after channel error, instead of having to close the - connection. -- Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. -- Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. -- Support for ``basic_return`` -- Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. -- Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". -- Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. -- Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. -- Exposes the underlying socket as ``Connection.sock``. -- Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. -- Slightly better at error recovery - -Further -======= - -- Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - -- AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - -- RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - -- For more information about AMQP, visit - - http://www.amqp.org - -- For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 46c8ddd..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -============================================= - amqp - Python AMQP low-level client library -============================================= - -.. include:: includes/intro.txt - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - reference/index - changelog - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/reference/amqp.abstract_channel.rst b/docs/reference/amqp.abstract_channel.rst deleted file mode 100644 index 022a4f2..0000000 --- a/docs/reference/amqp.abstract_channel.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.abstract_channel -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.abstract_channel - -.. automodule:: amqp.abstract_channel - :members: - :undoc-members: diff --git a/docs/reference/amqp.basic_message.rst b/docs/reference/amqp.basic_message.rst deleted file mode 100644 index 43ee7f7..0000000 --- a/docs/reference/amqp.basic_message.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.basic_message -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.basic_message - -.. automodule:: amqp.basic_message - :members: - :undoc-members: diff --git a/docs/reference/amqp.channel.rst b/docs/reference/amqp.channel.rst deleted file mode 100644 index ff4b471..0000000 --- a/docs/reference/amqp.channel.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.channel -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.channel - -.. automodule:: amqp.channel - :members: - :undoc-members: diff --git a/docs/reference/amqp.connection.rst b/docs/reference/amqp.connection.rst deleted file mode 100644 index 4a2e68c..0000000 --- a/docs/reference/amqp.connection.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.connection -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.connection - -.. automodule:: amqp.connection - :members: - :undoc-members: diff --git a/docs/reference/amqp.exceptions.rst b/docs/reference/amqp.exceptions.rst deleted file mode 100644 index 5975c26..0000000 --- a/docs/reference/amqp.exceptions.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.exceptions -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.exceptions - -.. automodule:: amqp.exceptions - :members: - :undoc-members: diff --git a/docs/reference/amqp.five.rst b/docs/reference/amqp.five.rst deleted file mode 100644 index b4dc083..0000000 --- a/docs/reference/amqp.five.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.five -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.five - -.. automodule:: amqp.five - :members: - :undoc-members: diff --git a/docs/reference/amqp.method_framing.rst b/docs/reference/amqp.method_framing.rst deleted file mode 100644 index 8c45982..0000000 --- a/docs/reference/amqp.method_framing.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.method_framing -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.method_framing - -.. automodule:: amqp.method_framing - :members: - :undoc-members: diff --git a/docs/reference/amqp.protocol.rst b/docs/reference/amqp.protocol.rst deleted file mode 100644 index e47a469..0000000 --- a/docs/reference/amqp.protocol.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.protocol -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.protocol - -.. automodule:: amqp.protocol - :members: - :undoc-members: diff --git a/docs/reference/amqp.serialization.rst b/docs/reference/amqp.serialization.rst deleted file mode 100644 index 858e297..0000000 --- a/docs/reference/amqp.serialization.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.serialization -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.serialization - -.. automodule:: amqp.serialization - :members: - :undoc-members: diff --git a/docs/reference/amqp.transport.rst b/docs/reference/amqp.transport.rst deleted file mode 100644 index fae34b1..0000000 --- a/docs/reference/amqp.transport.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.transport -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.transport - -.. automodule:: amqp.transport - :members: - :undoc-members: diff --git a/docs/reference/amqp.utils.rst b/docs/reference/amqp.utils.rst deleted file mode 100644 index 1d590ae..0000000 --- a/docs/reference/amqp.utils.rst +++ /dev/null @@ -1,11 +0,0 @@ -===================================================== - amqp.utils -===================================================== - -.. contents:: - :local: -.. currentmodule:: amqp.utils - -.. automodule:: amqp.utils - :members: - :undoc-members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst deleted file mode 100644 index 3a19812..0000000 --- a/docs/reference/index.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _apiref: - -=============== - API Reference -=============== - -:Release: |version| -:Date: |today| - -.. toctree:: - :maxdepth: 1 - - amqp.connection - amqp.channel - amqp.basic_message - amqp.exceptions - amqp.abstract_channel - amqp.transport - amqp.method_framing - amqp.protocol - amqp.serialization - amqp.utils - amqp.five diff --git a/docs/templates/readme.txt b/docs/templates/readme.txt deleted file mode 100644 index 315cc1c..0000000 --- a/docs/templates/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -===================================================================== - Python AMQP 0.9.1 client library -===================================================================== - -.. include:: ../includes/intro.txt diff --git a/extra/README b/extra/README deleted file mode 100644 index 6cc994c..0000000 --- a/extra/README +++ /dev/null @@ -1,10 +0,0 @@ -generate_skeleton_0_8.py was used to create an initial Python -module from the AMQP 0.8 spec file. - -The 0-8 spec file is available from: - - https://svn.amqp.org/amqp/tags/amqp_spec_0.8/amqp.xml - -A skeleton module named 'myskeleton.py' is generated by running - - generate_skeleton_0_8.py amqp.xml myskeleton.py diff --git a/extra/generate_skeleton_0_8.py b/extra/generate_skeleton_0_8.py deleted file mode 100755 index 3bf4482..0000000 --- a/extra/generate_skeleton_0_8.py +++ /dev/null @@ -1,377 +0,0 @@ -#!/usr/bin/env python -""" -Utility for parsing an AMQP XML spec file -and generating a Python module skeleton. - -This is a fairly ugly program, but it's only intended -to be run once. - -2007-11-10 Barry Pederson - -""" -# Copyright (C) 2007 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301 USA - -import sys -import textwrap - -from xml.etree import ElementTree - - -######### -# -# Helper code inspired by -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498286 -# described in http://www.agapow.net/programming/python/the-etree-tail-quirk -# -def _textlist(self, _addtail=False): - '''Returns a list of text strings contained within - an element and its sub-elements. - - Helpful for extracting text from prose-oriented XML - (such as XHTML or DocBook). - ''' - result = [] - if (not _addtail) and (self.text is not None): - result.append(self.text) - for elem in self: - result.extend(elem.textlist(True)) - if _addtail and self.tail is not None: - result.append(self.tail) - return result - -# inject the new method into the ElementTree framework -ElementTree._Element.textlist = _textlist - -# -# -######### - -domains = {} -method_name_map = {} - - -def _fixup_method_name(class_element, method_element): - if class_element.attrib['name'] != class_element.attrib['handler']: - prefix = '%s_' % class_element.attrib['name'] - else: - prefix = '' - return ('%s%s' % (prefix, method_element.attrib['name'])).replace('-', '_') - - -def _fixup_field_name(field_element): - result = field_element.attrib['name'].replace(' ', '_') - if result == 'global': - result = 'a_global' - return result - - -def _field_type(field_element): - if 'type' in field_element.attrib: - return field_element.attrib['type'] - if 'domain' in field_element.attrib: - return domains[field_element.attrib['domain']] - - -def _reindent(s, indent, reformat=True): - """ - Remove the existing indentation from each line of a chunk of - text, s, and then prefix each line with a new indent string. - - Also removes trailing whitespace from each line, and leading and - trailing blank lines. - - """ - s = textwrap.dedent(s) - s = s.split('\n') - s = [x.rstrip() for x in s] - while s and (not s[0]): - s = s[1:] - while s and (not s[-1]): - s = s[:-1] - if reformat: - s = '\n'.join(s) - s = textwrap.wrap(s, initial_indent=indent, subsequent_indent=indent) - else: - s = [indent + x for x in s] - return '\n'.join(s) + '\n' - - -def generate_docstr(element, indent='', wrap=None): - print 'Generate objects' - """ - Generate a Python docstr for a given element in the AMQP - XML spec file. The element could be a class or method - - The 'wrap' parameter is an optional chunk of text that's - added to the beginning and end of the resulting docstring. - - """ - result = [] - - txt = element.text and element.text.rstrip() - if txt: - result.append(_reindent(txt, indent)) - result.append(indent) - extra_indent = '' - """ - rules = element.findall('rule') - if rules: - result.append(indent + 'RULES:') - for r in rules: - result.append(indent + 'RULE:') - result.append(indent) - extra_indent = ' ' - rule_docs = generate_docstr(r, indent + ' ') - if rule_docs: - result.append(extra_indent) - result.append(rule_docs) - result.append(indent) - """ - for d in element.findall('doc') + element.findall('rule'): - if d.tag == 'rule': - result.append(indent + 'RULE:') - result.append(indent) - extra_indent = ' ' - d = d.findall('doc')[0] - - docval = ''.join(d.textlist()).rstrip() - if not docval: - continue - reformat = True - if 'name' in d.attrib: - result.append(indent + d.attrib['name'].upper() + ':') - result.append(indent) - extra_indent = ' ' - if d.attrib['name'] == 'grammar': - reformat = False # Don't want re-indenting to mess this up - #else: - # extra_indent = '' - result.append(_reindent(docval, indent + extra_indent, reformat)) - result.append(indent) - - fields = element.findall('field') - if fields: - result.append(indent + 'PARAMETERS:') - for f in fields: - result.append(indent + ' ' + _fixup_field_name(f) + - ': ' + _field_type(f)) - field_docs = generate_docstr(f, indent + ' ') - if field_docs: - result.append(indent) - result.append(field_docs) - result.append(indent) - - if not result: - return None - - if wrap is not None: - result = [wrap] + result + [wrap] - - return '\n'.join(x.rstrip() for x in result) + '\n' - - -def generate_methods(class_element, out): - methods = class_element.findall('method') - methods.sort(key=lambda x: x.attrib['name']) - - for amqp_method in methods: - fields = amqp_method.findall('field') - fieldnames = [_fixup_field_name(x) for x in fields] - - # move any 'ticket' arguments to the end of the method declaration - # so that they can have a default value. - if 'ticket' in fieldnames: - fieldnames = [x for x in fieldnames if x != 'ticket'] + ['ticket'] - - chassis = [x.attrib['name'] for x in amqp_method.findall('chassis')] - if 'server' in chassis: - params = ['self'] - if 'content' in amqp_method.attrib: - params.append('msg') - - out.write(' def %s(%s):\n' % ( - _fixup_method_name(class_element, amqp_method), - ', '.join(params + fieldnames)), - ) - - s = generate_docstr(amqp_method, ' ', ' """') - if s: - out.write(s) - - if fields: - out.write(' args = AMQPWriter()\n') - smf_arg = ', args' - else: - smf_arg = '' - for f in fields: - out.write(' args.write_%s(%s)\n' % ( - _field_type(f), _fixup_field_name(f))) - - if class_element.attrib['name'] == 'connection': - smf_pattern = ' self.send_method_frame(0, (%s, %s)%s)\n' - else: - smf_pattern = ' self.send_method_frame((%s, %s)%s)\n' - - out.write(smf_pattern % (class_element.attrib['index'], - amqp_method.attrib['index'], smf_arg)) - - if 'synchronous' in amqp_method.attrib: - responses = [x.attrib['name'] - for x in amqp_method.findall('response')] - out.write(' return self.wait(allowed_methods=[\n') - for r in responses: - resp = method_name_map[(class_element.attrib['name'], r)] - out.write( - ' (%s, %s), # %s\n' % resp) - out.write(' ])\n') - - out.write('\n\n') - - if 'client' in chassis: - out.write(' def _%s(self, args):\n' % ( - _fixup_method_name(class_element, amqp_method), )) - s = generate_docstr(amqp_method, ' ', ' """') - if s: - out.write(s) - need_pass = True - for f in fields: - out.write(' %s = args.read_%s()\n' % ( - _fixup_field_name(f), _field_type(f))) - need_pass = False - if 'content' in amqp_method.attrib: - out.write(' msg = self.wait()\n') - need_pass = False - if need_pass: - out.write(' pass\n') - out.write('\n\n') - - -def generate_class(spec, class_element, out): - out.write('class %s(object):\n' % ( - class_element.attrib['name'].capitalize(), )) - s = generate_docstr(class_element, ' ', ' """') - if s: - out.write(s) - - generate_methods(class_element, out) - - # - # Generate methods for handled classes - # - for amqp_class in spec.findall('class'): - if (amqp_class.attrib['handler'] == class_element.attrib['name']) and \ - (amqp_class.attrib['name'] != class_element.attrib['name']): - out.write(' #############\n') - out.write(' #\n') - out.write(' # %s\n' % amqp_class.attrib['name'].capitalize()) - out.write(' #\n') - s = generate_docstr(amqp_class, ' # ', ' # ') - if s: - out.write(s) - out.write('\n') - - generate_methods(amqp_class, out) - - -def generate_module(spec, out): - """ - Given an AMQP spec parsed into an xml.etree.ElemenTree, - and a file-like 'out' object to write to, generate - the skeleton of a Python module. - - """ - # - # HACK THE SPEC so that 'access' is handled by - # 'channel' instead of 'connection' - # - for amqp_class in spec.findall('class'): - if amqp_class.attrib['name'] == 'access': - amqp_class.attrib['handler'] = 'channel' - - # - # Build up some helper dictionaries - # - for domain in spec.findall('domain'): - domains[domain.attrib['name']] = domain.attrib['type'] - - for amqp_class in spec.findall('class'): - for amqp_method in amqp_class.findall('method'): - method_name_map[(amqp_class.attrib['name'], - amqp_method.attrib['name'])] = \ - ( - amqp_class.attrib['index'], - amqp_method.attrib['index'], - (amqp_class.attrib['handler'].capitalize() + '.' + - _fixup_method_name(amqp_class, amqp_method)), - ) - - #### Actually generate output - - for amqp_class in spec.findall('class'): - if amqp_class.attrib['handler'] == amqp_class.attrib['name']: - generate_class(spec, amqp_class, out) - - out.write('_METHOD_MAP = {\n') - for amqp_class in spec.findall('class'): - print amqp_class.attrib -# for chassis in amqp_class.findall('chassis'): -# print ' ', chassis.attrib - for amqp_method in amqp_class.findall('method'): -# print ' ', amqp_method.attrib -# for chassis in amqp_method.findall('chassis'): -# print ' ', chassis.attrib - chassis = [x.attrib['name'] - for x in amqp_method.findall('chassis')] - if 'client' in chassis: - out.write(" (%s, %s): (%s, %s._%s),\n" % ( - amqp_class.attrib['index'], - amqp_method.attrib['index'], - amqp_class.attrib['handler'].capitalize(), - amqp_class.attrib['handler'].capitalize(), - _fixup_method_name(amqp_class, amqp_method))) - out.write('}\n\n') - - out.write('_METHOD_NAME_MAP = {\n') - for amqp_class in spec.findall('class'): - for amqp_method in amqp_class.findall('method'): - out.write(" (%s, %s): '%s.%s',\n" % ( - amqp_class.attrib['index'], - amqp_method.attrib['index'], - amqp_class.attrib['handler'].capitalize(), - _fixup_method_name(amqp_class, amqp_method))) - out.write('}\n') - - -def main(argv=None): - if argv is None: - argv = sys.argv - - if len(argv) < 2: - print('Usage: %s []' % argv[0]) - return 1 - - spec = ElementTree.parse(argv[1]) - if len(argv) < 3: - out = sys.stdout - else: - out = open(argv[2], 'w') - - generate_module(spec, out) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/extra/release/bump_version.py b/extra/release/bump_version.py deleted file mode 100755 index 87bb6bd..0000000 --- a/extra/release/bump_version.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python - -from __future__ import absolute_import - -import errno -import os -import re -import shlex -import subprocess -import sys - -from contextlib import contextmanager -from tempfile import NamedTemporaryFile - -rq = lambda s: s.strip("\"'") - - -def cmd(*args): - return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] - - -@contextmanager -def no_enoent(): - try: - yield - except OSError as exc: - if exc.errno != errno.ENOENT: - raise - - -class StringVersion(object): - - def decode(self, s): - s = rq(s) - text = "" - major, minor, release = s.split(".") - if not release.isdigit(): - pos = release.index(re.split("\d+", release)[1][0]) - release, text = release[:pos], release[pos:] - return int(major), int(minor), int(release), text - - def encode(self, v): - return ".".join(map(str, v[:3])) + v[3] -to_str = StringVersion().encode -from_str = StringVersion().decode - - -class TupleVersion(object): - - def decode(self, s): - v = list(map(rq, s.split(", "))) - return (tuple(map(int, v[0:3])) + - tuple(["".join(v[3:])])) - - def encode(self, v): - v = list(v) - - def quote(lit): - if isinstance(lit, basestring): - return '"{0}"'.format(lit) - return str(lit) - - if not v[-1]: - v.pop() - return ", ".join(map(quote, v)) - - -class VersionFile(object): - - def __init__(self, filename): - self.filename = filename - self._kept = None - - def _as_orig(self, version): - return self.wb.format(version=self.type.encode(version), - kept=self._kept) - - def write(self, version): - pattern = self.regex - with no_enoent(): - with NamedTemporaryFile() as dest: - with open(self.filename) as orig: - for line in orig: - if pattern.match(line): - dest.write(self._as_orig(version)) - else: - dest.write(line) - os.rename(dest.name, self.filename) - - def parse(self): - pattern = self.regex - gpos = 0 - with open(self.filename) as fh: - for line in fh: - m = pattern.match(line) - if m: - if "?P" in pattern.pattern: - self._kept, gpos = m.groupdict()["keep"], 1 - return self.type.decode(m.groups()[gpos]) - - -class PyVersion(VersionFile): - regex = re.compile(r'^VERSION\s*=\s*\((.+?)\)') - wb = "VERSION = ({version})\n" - type = TupleVersion() - - -class SphinxVersion(VersionFile): - regex = re.compile(r'^:[Vv]ersion:\s*(.+?)$') - wb = ':Version: {version}\n' - type = StringVersion() - - -class CPPVersion(VersionFile): - regex = re.compile(r'^\#\s*define\s*(?P\w*)VERSION\s+(.+)') - wb = '#define {kept}VERSION "{version}"\n' - type = StringVersion() - - -_filetype_to_type = {"py": PyVersion, - "rst": SphinxVersion, - "txt": SphinxVersion, - "c": CPPVersion, - "h": CPPVersion} - - -def filetype_to_type(filename): - _, _, suffix = filename.rpartition(".") - return _filetype_to_type[suffix](filename) - - -def bump(*files, **kwargs): - version = kwargs.get("version") - before_commit = kwargs.get("before_commit") - files = [filetype_to_type(f) for f in files] - versions = [v.parse() for v in files] - current = list(reversed(sorted(versions)))[0] # find highest - current = current.split()[0] # only first sentence - - if version: - next = from_str(version) - else: - major, minor, release, text = current - if text: - raise Exception("Can't bump alpha releases") - next = (major, minor, release + 1, text) - - print("Bump version from {0} -> {1}".format(to_str(current), to_str(next))) - - for v in files: - print(" writing {0.filename!r}...".format(v)) - v.write(next) - - if before_commit: - cmd(*shlex.split(before_commit)) - - print(cmd("git", "commit", "-m", "Bumps version to {0}".format( - to_str(next)), *[f.filename for f in files])) - print(cmd("git", "tag", "v{0}".format(to_str(next)))) - - -def main(argv=sys.argv, version=None, before_commit=None): - if not len(argv) > 1: - print("Usage: distdir [docfile] -- ") - sys.exit(0) - - args = [] - for arg in argv: - if arg.startswith("--before-commit="): - _, before_commit = arg.split('=') - else: - args.append(arg) - - if "--" in args: - c = args.index('--') - version = args[c + 1] - argv = args[:c] - bump(*args[1:], version=version, before_commit=before_commit) - -if __name__ == "__main__": - main() diff --git a/extra/release/sphinx-to-rst.py b/extra/release/sphinx-to-rst.py deleted file mode 100755 index e0c1fd0..0000000 --- a/extra/release/sphinx-to-rst.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -import os -import re -import sys - -dirname = "" - -RE_CODE_BLOCK = re.compile(r'.. code-block:: (.+?)\s*$') -RE_INCLUDE = re.compile(r'.. include:: (.+?)\s*$') -RE_REFERENCE = re.compile(r':(.+?):`(.+?)`') - - -def include_file(lines, pos, match): - global dirname - orig_filename = match.groups()[0] - filename = os.path.join(dirname, orig_filename) - fh = open(filename) - try: - old_dirname = dirname - dirname = os.path.dirname(orig_filename) - try: - lines[pos] = sphinx_to_rst(fh) - finally: - dirname = old_dirname - finally: - fh.close() - - -def replace_code_block(lines, pos, match): - lines[pos] = "" - curpos = pos - 1 - # Find the first previous line with text to append "::" to it. - while True: - prev_line = lines[curpos] - if not prev_line.isspace(): - prev_line_with_text = curpos - break - curpos -= 1 - - if lines[prev_line_with_text].endswith(":"): - lines[prev_line_with_text] += ":" - else: - lines[prev_line_with_text] += "::" - -TO_RST_MAP = {RE_CODE_BLOCK: replace_code_block, - RE_REFERENCE: r'``\2``', - RE_INCLUDE: include_file} - - -def _process(lines): - lines = list(lines) # non-destructive - for i, line in enumerate(lines): - for regex, alt in TO_RST_MAP.items(): - if callable(alt): - match = regex.match(line) - if match: - alt(lines, i, match) - line = lines[i] - else: - lines[i] = regex.sub(alt, line) - return lines - - -def sphinx_to_rst(fh): - return "".join(_process(fh)) - - -if __name__ == "__main__": - global dirname - dirname = os.path.dirname(sys.argv[1]) - fh = open(sys.argv[1]) - try: - print(sphinx_to_rst(fh)) - finally: - fh.close() diff --git a/extra/update_comments_from_spec.py b/extra/update_comments_from_spec.py deleted file mode 100644 index 677dd5c..0000000 --- a/extra/update_comments_from_spec.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import absolute_import - -import os -import sys -import re - -default_source_file = os.path.join( - os.path.dirname(__file__), - '../amqp/channel.py', -) - -RE_COMMENTS = re.compile( - '(?Pdef\s+(?P[a-zA-Z0-9_]+)\(.*?\)' - ':\n+\s+""")(?P.*?)(?=""")', - re.MULTILINE | re.DOTALL -) - -USAGE = """\ -Usage: %s []\ -""" - - -def update_comments(comments_file, impl_file, result_file): - text_file = open(impl_file, 'r') - source = text_file.read() - - comments = get_comments(comments_file) - for def_name, comment in comments.items(): - source = replace_comment_per_def( - source, result_file, def_name, comment - ) - - new_file = open(result_file, 'w+') - new_file.write(source) - - -def get_comments(filename): - text_file = open(filename, 'r') - whole_source = text_file.read() - comments = {} - - all_matches = RE_COMMENTS.finditer(whole_source) - for match in all_matches: - comments[match.group('mname')] = match.group('comment') - #print('method: %s \ncomment: %s' % ( - # match.group('mname'), match.group('comment'))) - - return comments - - -def replace_comment_per_def(source, result_file, def_name, new_comment): - regex = ('(?Pdef\s+' + - def_name + - '\(.*?\):\n+\s+""".*?\n).*?(?=""")') - #print('method and comment:' + def_name + new_comment) - result = re.sub(regex, '\g' + new_comment, source, 0, - re.MULTILINE | re.DOTALL) - return result - - -def main(argv=None): - if argv is None: - argv = sys.argv - - if len(argv) < 3: - print(USAGE % argv[0]) - return 1 - - impl_file = default_source_file - if len(argv) >= 4: - impl_file = argv[3] - - update_comments(argv[1], impl_file, argv[2]) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/funtests/run_all.py b/funtests/run_all.py deleted file mode 100755 index 8d3fb40..0000000 --- a/funtests/run_all.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -"""Run all the unittest modules for amqp""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import unittest - -import settings - -TEST_NAMES = [ - 'test_exceptions', - 'test_serialization', - 'test_basic_message', - 'test_connection', - 'test_channel', - 'test_with', -] - - -def main(): - suite = unittest.TestLoader().loadTestsFromNames(TEST_NAMES) - unittest.TextTestRunner(**settings.test_args).run(suite) - -if __name__ == '__main__': - main() diff --git a/funtests/settings.py b/funtests/settings.py deleted file mode 100644 index d935421..0000000 --- a/funtests/settings.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Parse commandline args for running unittests. - -Used by the overall run_all.py script, or the various -indivudial test modules that need settings for connecting -to a broker. - -""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import logging -from optparse import OptionParser - -connect_args = {} -test_args = {'verbosity': 1} - - -def parse_args(): - parser = OptionParser(usage='usage: %prog [options]') - parser.add_option( - '--host', dest='host', - help='AMQP server to connect to (default: %default)', - default='localhost', - ) - parser.add_option( - '-u', '--userid', dest='userid', - help='userid to authenticate as (default: %default)', - default='guest', - ) - parser.add_option( - '-p', '--password', dest='password', - help='password to authenticate with (default: %default)', - default='guest', - ) - parser.add_option( - '--ssl', dest='ssl', action='store_true', - help='Enable SSL (default: not enabled)', - default=False, - ) - parser.add_option( - '--debug', dest='debug', action='store_true', - help='Display debugging output', - default=False, - ) - parser.add_option( - '--port', dest='port', - help='port for the broker', - default=5672, - ) - - parser.add_option( - '-v', '--verbosity', dest='verbose', action='store_true', - help='Run unittests with increased verbosity', - default=False, - ) - - options, args = parser.parse_args() - - if options.debug: - console = logging.StreamHandler() - console.setLevel(logging.DEBUG) - formatter = logging.Formatter( - '%(name)-12s: %(levelname)-8s %(message)s', - ) - console.setFormatter(formatter) - amqp_logger = logging.getLogger('amqp') - amqp_logger.addHandler(console) - amqp_logger.setLevel(logging.DEBUG) - - connect_args['host'] = options.host - connect_args['userid'] = options.userid - connect_args['password'] = options.password - connect_args['ssl'] = options.ssl - - if options.verbose: - test_args['verbosity'] = 2 - -parse_args() diff --git a/funtests/test_basic_message.py b/funtests/test_basic_message.py deleted file mode 100755 index 268f360..0000000 --- a/funtests/test_basic_message.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -""" -Test the amqp.basic_message module. - -""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -from datetime import datetime -from decimal import Decimal - -import unittest - -try: - import cPickle as pickle -except ImportError: - import pickle # noqa - - -import settings - -from amqp.basic_message import Message - - -class TestBasicMessage(unittest.TestCase): - - def check_proplist(self, msg): - """Check roundtrip processing of a single object""" - raw_properties = msg._serialize_properties() - - new_msg = Message() - new_msg._load_properties(raw_properties) - new_msg.body = msg.body - - self.assertEqual(msg, new_msg) - - def test_eq(self): - msg = Message('hello', content_type='text/plain') - self.assertNotEqual(msg, None) - - # - # Make sure that something that looks vaguely - # like a Message doesn't raise an Attribute - # error when compared to a Message, and instead - # returns False - # - class FakeMsg(object): - pass - - fake_msg = FakeMsg() - fake_msg.properties = {'content_type': 'text/plain'} - - self.assertNotEqual(msg, fake_msg) - - def test_pickle(self): - msg = Message( - 'some body' * 200000, - content_type='text/plain', - content_encoding='utf-8', - application_headers={ - 'foo': 7, 'bar': 'baz', 'd2': {'foo2': 'xxx', 'foo3': -1}, - }, - delivery_mode=1, - priority=7, - ) - - msg2 = pickle.loads(pickle.dumps(msg)) - self.assertEqual(msg, msg2) - - def test_roundtrip(self): - """Check round-trip processing of content-properties.""" - self.check_proplist(Message()) - - self.check_proplist(Message(content_type='text/plain')) - - self.check_proplist(Message( - content_type='text/plain', - content_encoding='utf-8', - application_headers={ - 'foo': 7, 'bar': 'baz', 'd2': {'foo2': 'xxx', 'foo3': -1}, - }, - delivery_mode=1, - priority=7, - )) - - self.check_proplist(Message( - application_headers={ - 'regular': datetime(2007, 11, 12, 12, 34, 56), - 'dst': datetime(2007, 7, 12, 12, 34, 56), - }, - )) - - n = datetime.now() - # AMQP only does timestamps to 1-second resolution - n = n.replace(microsecond=0) - self.check_proplist(Message( - application_headers={'foo': n}), - ) - - self.check_proplist(Message( - application_headers={'foo': Decimal('10.1')}), - ) - - self.check_proplist(Message( - application_headers={'foo': Decimal('-1987654.193')}), - ) - - self.check_proplist(Message( - timestamp=datetime(1980, 1, 2, 3, 4, 6)), - ) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestBasicMessage) - unittest.TextTestRunner(**settings.test_args).run(suite) - - -if __name__ == '__main__': - main() diff --git a/funtests/test_channel.py b/funtests/test_channel.py deleted file mode 100755 index 10b860f..0000000 --- a/funtests/test_channel.py +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/bin/env python -""" -Test amqp.channel module - -""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import sys -import unittest - -import settings - -from amqp import ChannelError, Connection, Message, FrameSyntaxError - - -class TestChannel(unittest.TestCase): - - def setUp(self): - self.conn = Connection(**settings.connect_args) - self.ch = self.conn.channel() - - def tearDown(self): - self.ch.close() - self.conn.close() - - def test_defaults(self): - """Test how a queue defaults to being bound to an AMQP default - exchange, and how publishing defaults to the default exchange, and - basic_get defaults to getting from the most recently declared queue, - and queue_delete defaults to deleting the most recently declared - queue.""" - msg = Message( - 'funtest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}, - ) - - qname, _, _ = self.ch.queue_declare() - self.ch.basic_publish(msg, routing_key=qname) - - msg2 = self.ch.basic_get(no_ack=True) - self.assertEqual(msg, msg2) - - n = self.ch.queue_purge() - self.assertEqual(n, 0) - - n = self.ch.queue_delete() - self.assertEqual(n, 0) - - def test_encoding(self): - my_routing_key = 'funtest.test_queue' - - qname, _, _ = self.ch.queue_declare() - self.ch.queue_bind(qname, 'amq.direct', routing_key=my_routing_key) - - # - # No encoding, body passed through unchanged - # - msg = Message('hello world') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - if sys.version_info[0] < 3: - self.assertFalse(hasattr(msg2, 'content_encoding')) - self.assertTrue(isinstance(msg2.body, str)) - self.assertEqual(msg2.body, 'hello world') - - # - # Default UTF-8 encoding of unicode body, returned as unicode - # - msg = Message(u'hello world') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'UTF-8') - self.assertTrue(isinstance(msg2.body, unicode)) - self.assertEqual(msg2.body, u'hello world') - - # - # Explicit latin_1 encoding, still comes back as unicode - # - msg = Message(u'hello world', content_encoding='latin_1') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'latin_1') - self.assertTrue(isinstance(msg2.body, unicode)) - self.assertEqual(msg2.body, u'hello world') - - # - # Plain string with specified encoding comes back as unicode - # - msg = Message('hello w\xf6rld', content_encoding='latin_1') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'latin_1') - self.assertTrue(isinstance(msg2.body, unicode)) - self.assertEqual(msg2.body, u'hello w\u00f6rld') - - # - # Plain string (bytes in Python 3.x) with bogus encoding - # - - # don't really care about latin_1, just want bytes - test_bytes = u'hello w\xd6rld'.encode('latin_1') - msg = Message(test_bytes, content_encoding='I made this up') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'I made this up') - self.assertTrue(isinstance(msg2.body, bytes)) - self.assertEqual(msg2.body, test_bytes) - - # - # Turn off auto_decode for remaining tests - # - self.ch.auto_decode = False - - # - # Unicode body comes back as utf-8 encoded str - # - msg = Message(u'hello w\u00f6rld') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'UTF-8') - self.assertTrue(isinstance(msg2.body, bytes)) - self.assertEqual(msg2.body, u'hello w\xc3\xb6rld'.encode('latin_1')) - - # - # Plain string with specified encoding stays plain string - # - msg = Message('hello w\xf6rld', content_encoding='latin_1') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'latin_1') - self.assertTrue(isinstance(msg2.body, bytes)) - self.assertEqual(msg2.body, u'hello w\xf6rld'.encode('latin_1')) - - # - # Explicit latin_1 encoding, comes back as str - # - msg = Message(u'hello w\u00f6rld', content_encoding='latin_1') - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg2.content_encoding, 'latin_1') - self.assertTrue(isinstance(msg2.body, bytes)) - self.assertEqual(msg2.body, u'hello w\xf6rld'.encode('latin_1')) - - def test_queue_delete_empty(self): - self.assertFalse( - self.ch.queue_delete('bogus_queue_that_does_not_exist') - ) - - def test_survives_channel_error(self): - with self.assertRaises(ChannelError): - self.ch.queue_declare('krjqheewq_bogus', passive=True) - self.ch.queue_declare('funtest_survive') - self.ch.queue_declare('funtest_survive', passive=True) - self.assertEqual( - 0, self.ch.queue_delete('funtest_survive'), - ) - - def test_invalid_header(self): - """ - Test sending a message with an unserializable object in the header - - http://code.google.com/p/py-amqplib/issues/detail?id=17 - - """ - qname, _, _ = self.ch.queue_declare() - - msg = Message(application_headers={'test': object()}) - - self.assertRaises( - FrameSyntaxError, self.ch.basic_publish, msg, routing_key=qname, - ) - - def test_large(self): - """ - Test sending some extra large messages. - - """ - qname, _, _ = self.ch.queue_declare() - - for multiplier in [100, 1000, 10000]: - msg = Message( - 'funtest message' * multiplier, - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}, - ) - - self.ch.basic_publish(msg, routing_key=qname) - - msg2 = self.ch.basic_get(no_ack=True) - self.assertEqual(msg, msg2) - - def test_publish(self): - self.ch.exchange_declare('funtest.fanout', 'fanout', auto_delete=True) - - msg = Message( - 'funtest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}, - ) - - self.ch.basic_publish(msg, 'funtest.fanout') - - def test_queue(self): - my_routing_key = 'funtest.test_queue' - msg = Message( - 'funtest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}, - ) - - qname, _, _ = self.ch.queue_declare() - self.ch.queue_bind(qname, 'amq.direct', routing_key=my_routing_key) - - self.ch.basic_publish(msg, 'amq.direct', routing_key=my_routing_key) - - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg, msg2) - - def test_unbind(self): - my_routing_key = 'funtest.test_queue' - - qname, _, _ = self.ch.queue_declare() - self.ch.queue_bind(qname, 'amq.direct', routing_key=my_routing_key) - self.ch.queue_unbind(qname, 'amq.direct', routing_key=my_routing_key) - - def test_basic_return(self): - self.ch.exchange_declare('funtest.fanout', 'fanout', auto_delete=True) - - msg = Message( - 'funtest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}) - - self.ch.basic_publish(msg, 'funtest.fanout') - self.ch.basic_publish(msg, 'funtest.fanout', mandatory=True) - self.ch.basic_publish(msg, 'funtest.fanout', mandatory=True) - self.ch.basic_publish(msg, 'funtest.fanout', mandatory=True) - self.ch.close() - - # - # 3 of the 4 messages we sent should have been returned - # - self.assertEqual(self.ch.returned_messages.qsize(), 3) - - def test_exchange_bind(self): - """Test exchange binding. - Network configuration is as follows (-> is forwards to : - source_exchange -> dest_exchange -> queue - The test checks that once the message is publish to the - destination exchange(funtest.topic_dest) it is delivered to the queue. - """ - - test_routing_key = 'unit_test__key' - dest_exchange = 'funtest.topic_dest_bind' - source_exchange = 'funtest.topic_source_bind' - - self.ch.exchange_declare(dest_exchange, 'topic', auto_delete=True) - self.ch.exchange_declare(source_exchange, 'topic', auto_delete=True) - - qname, _, _ = self.ch.queue_declare() - self.ch.exchange_bind(destination=dest_exchange, - source=source_exchange, - routing_key=test_routing_key) - - self.ch.queue_bind(qname, dest_exchange, - routing_key=test_routing_key) - - msg = Message('funtest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}) - - self.ch.basic_publish(msg, source_exchange, - routing_key=test_routing_key) - - msg2 = self.ch.basic_get(qname, no_ack=True) - self.assertEqual(msg, msg2) - - def test_exchange_unbind(self): - dest_exchange = 'funtest.topic_dest_unbind' - source_exchange = 'funtest.topic_source_unbind' - test_routing_key = 'unit_test__key' - - self.ch.exchange_declare(dest_exchange, - 'topic', auto_delete=True) - self.ch.exchange_declare(source_exchange, - 'topic', auto_delete=True) - - self.ch.exchange_bind(destination=dest_exchange, - source=source_exchange, - routing_key=test_routing_key) - - self.ch.exchange_unbind(destination=dest_exchange, - source=source_exchange, - routing_key=test_routing_key) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestChannel) - unittest.TextTestRunner(**settings.test_args).run(suite) - -if __name__ == '__main__': - main() diff --git a/funtests/test_connection.py b/funtests/test_connection.py deleted file mode 100755 index 31938be..0000000 --- a/funtests/test_connection.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python -"""Test amqp.connection module""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import gc -import unittest - -import settings - - -from amqp import Connection - - -class TestConnection(unittest.TestCase): - - def setUp(self): - self.conn = Connection(**settings.connect_args) - - def tearDown(self): - if self.conn: - self.conn.close() - - def test_channel(self): - ch = self.conn.channel(1) - self.assertEqual(ch.channel_id, 1) - - ch2 = self.conn.channel() - self.assertNotEqual(ch2.channel_id, 1) - - ch.close() - ch2.close() - - def test_close(self): - """Make sure we've broken various references when closing - channels and connections, to help with GC.""" - # - # Create a channel and make sure it's linked as we'd expect - # - ch = self.conn.channel() - self.assertEqual(1 in self.conn.channels, True) - self.assertEqual(ch.connection, self.conn) - self.assertEqual(ch.is_open, True) - - # - # Close the channel and make sure the references are broken - # that we expect. - # - ch.close() - self.assertEqual(ch.connection, None) - self.assertEqual(1 in self.conn.channels, False) - self.assertEqual(ch.callbacks, {}) - self.assertEqual(ch.is_open, False) - - # - # Close the connection and make sure the references we expect - # are gone. - # - self.conn.close() - self.assertEqual(self.conn.connection, None) - self.assertEqual(self.conn.channels, None) - - def test_gc_closed(self): - """Make sure we've broken various references when closing - channels and connections, to help with GC. - - gc.garbage: http://docs.python.org/library/gc.html#gc.garbage - "A list of objects which the collector found to be - unreachable but could not be freed (uncollectable objects)." - - """ - unreachable_before = len(gc.garbage) - # - # Create a channel and make sure it's linked as we'd expect - # - self.conn.channel() - self.assertEqual(1 in self.conn.channels, True) - - # - # Close the connection and make sure the references we expect - # are gone. - # - self.conn.close() - - gc.collect() - gc.collect() - gc.collect() - self.assertEqual(unreachable_before, len(gc.garbage)) - - def test_gc_forget(self): - """Make sure the connection gets gc'ed when there is no more - references to it.""" - unreachable_before = len(gc.garbage) - - ch = self.conn.channel() - self.assertEqual(1 in self.conn.channels, True) - - # remove all the references - self.conn = None - del(ch) - - gc.collect() - gc.collect() - gc.collect() - self.assertEqual(unreachable_before, len(gc.garbage)) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestConnection) - unittest.TextTestRunner(**settings.test_args).run(suite) - - -if __name__ == '__main__': - main() diff --git a/funtests/test_exceptions.py b/funtests/test_exceptions.py deleted file mode 100755 index f418135..0000000 --- a/funtests/test_exceptions.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -""" -Test amqp.exceptions module - -""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import unittest - -import settings - -from amqp.exceptions import AMQPError - - -class TestException(unittest.TestCase): - def test_exception(self): - exc = AMQPError('My Error', (10, 10), reply_code=7) - self.assertEqual(exc.reply_code, 7) - self.assertEqual(exc.reply_text, 'My Error') - self.assertEqual(exc.method_sig, (10, 10)) - self.assertEqual( - exc.args, - (7, 'My Error', (10, 10), 'Connection.start'), - ) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestException) - unittest.TextTestRunner(**settings.test_args).run(suite) - - -if __name__ == '__main__': - main() diff --git a/funtests/test_serialization.py b/funtests/test_serialization.py deleted file mode 100755 index ce70438..0000000 --- a/funtests/test_serialization.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/bin/env python -""" -Test amqp.serialization, checking conversions -between byte streams and higher level objects. - -""" -# Copyright (C) 2007-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -from datetime import datetime -from decimal import Decimal -from random import randint -import sys -import unittest - -import settings - -from amqp.serialization import ( - AMQPReader, AMQPWriter, GenericContent, FrameSyntaxError, -) - - -class TestSerialization(unittest.TestCase): - - if sys.version_info[0] >= 3: - - def assertEqualBinary(self, b, s): - """ - Helper for Py3k Compatibility - - """ - self.assertEqual(b, s.encode('latin_1')) - else: - assertEqualBinary = unittest.TestCase.assertEqual - - def test_empty_writer(self): - w = AMQPWriter() - self.assertEqual(w.getvalue(), bytes()) - - # - # Bits - # - def test_single_bit(self): - for val, check in [(True, '\x01'), (False, '\x00')]: - w = AMQPWriter() - w.write_bit(val) - s = w.getvalue() - - self.assertEqualBinary(s, check) - - r = AMQPReader(s) - self.assertEqual(r.read_bit(), val) - - def test_multiple_bits(self): - w = AMQPWriter() - w.write_bit(True) - w.write_bit(True) - w.write_bit(False) - w.write_bit(True) - s = w.getvalue() - - self.assertEqualBinary(s, '\x0b') - - r = AMQPReader(s) - self.assertEqual(r.read_bit(), True) - self.assertEqual(r.read_bit(), True) - self.assertEqual(r.read_bit(), False) - self.assertEqual(r.read_bit(), True) - - def test_multiple_bits2(self): - """ - Check bits mixed with non-bits - """ - w = AMQPWriter() - w.write_bit(True) - w.write_bit(True) - w.write_bit(False) - w.write_octet(10) - w.write_bit(True) - s = w.getvalue() - - self.assertEqualBinary(s, '\x03\x0a\x01') - - r = AMQPReader(s) - self.assertEqual(r.read_bit(), True) - self.assertEqual(r.read_bit(), True) - self.assertEqual(r.read_bit(), False) - self.assertEqual(r.read_octet(), 10) - self.assertEqual(r.read_bit(), True) - - def test_multiple_bits3(self): - """ - Check bit groups that span multiple bytes - """ - w = AMQPWriter() - - # Spit out 20 bits - for i in range(10): - w.write_bit(True) - w.write_bit(False) - - s = w.getvalue() - - self.assertEqualBinary(s, '\x55\x55\x05') - - r = AMQPReader(s) - for i in range(10): - self.assertEqual(r.read_bit(), True) - self.assertEqual(r.read_bit(), False) - - # - # Octets - # - def test_octet(self): - for val in range(256): - w = AMQPWriter() - w.write_octet(val) - s = w.getvalue() - self.assertEqualBinary(s, chr(val)) - - r = AMQPReader(s) - self.assertEqual(r.read_octet(), val) - - def test_octet_invalid(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_octet, -1) - - def test_octet_invalid2(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_octet, 256) - - # - # Shorts - # - def test_short(self): - for i in range(256): - val = randint(0, 65535) - w = AMQPWriter() - w.write_short(val) - s = w.getvalue() - - r = AMQPReader(s) - self.assertEqual(r.read_short(), val) - - def test_short_invalid(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_short, -1) - - def test_short_invalid2(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_short, 65536) - - # - # Longs - # - def test_long(self): - for i in range(256): - val = randint(0, 4294967295) - w = AMQPWriter() - w.write_long(val) - s = w.getvalue() - - r = AMQPReader(s) - self.assertEqual(r.read_long(), val) - - def test_long_invalid(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_long, -1) - - def test_long_invalid2(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_long, 4294967296) - - # - # LongLongs - # - def test_longlong(self): - for i in range(256): - val = randint(0, (2 ** 64) - 1) - w = AMQPWriter() - w.write_longlong(val) - s = w.getvalue() - - r = AMQPReader(s) - self.assertEqual(r.read_longlong(), val) - - def test_longlong_invalid(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_longlong, -1) - - def test_longlong_invalid2(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_longlong, 2 ** 64) - - # - # Shortstr - # - def test_empty_shortstr(self): - w = AMQPWriter() - w.write_shortstr('') - s = w.getvalue() - - self.assertEqualBinary(s, '\x00') - - r = AMQPReader(s) - self.assertEqual(r.read_shortstr(), '') - - def test_shortstr(self): - w = AMQPWriter() - w.write_shortstr('hello') - s = w.getvalue() - self.assertEqualBinary(s, '\x05hello') - - r = AMQPReader(s) - self.assertEqual(r.read_shortstr(), 'hello') - - def test_shortstr_unicode(self): - w = AMQPWriter() - w.write_shortstr(u'hello') - s = w.getvalue() - self.assertEqualBinary(s, '\x05hello') - - r = AMQPReader(s) - self.assertEqual(r.read_shortstr(), u'hello') - - def test_long_shortstr(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_shortstr, 'x' * 256) - - def test_long_shortstr_unicode(self): - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_shortstr, u'\u0100' * 128) - - # - # Longstr - # - - def test_empty_longstr(self): - w = AMQPWriter() - w.write_longstr('') - s = w.getvalue() - - self.assertEqualBinary(s, '\x00\x00\x00\x00') - - r = AMQPReader(s) - self.assertEqual(r.read_longstr(), '') - - def test_longstr(self): - val = 'a' * 512 - w = AMQPWriter() - w.write_longstr(val) - s = w.getvalue() - - self.assertEqualBinary(s, '\x00\x00\x02\x00' + ('a' * 512)) - - r = AMQPReader(s) - self.assertEqual(r.read_longstr(), str(val)) - - def test_longstr_unicode(self): - val = u'a' * 512 - w = AMQPWriter() - w.write_longstr(val) - s = w.getvalue() - - self.assertEqualBinary(s, '\x00\x00\x02\x00' + ('a' * 512)) - - r = AMQPReader(s) - self.assertEqual(r.read_longstr(), val) - - # - # Table - # - def test_table_empty(self): - val = {} - w = AMQPWriter() - w.write_table(val) - s = w.getvalue() - - self.assertEqualBinary(s, '\x00\x00\x00\x00') - - r = AMQPReader(s) - self.assertEqual(r.read_table(), val) - - def test_table(self): - val = {'foo': 7} - w = AMQPWriter() - w.write_table(val) - s = w.getvalue() - - self.assertEqualBinary(s, '\x00\x00\x00\x09\x03fooI\x00\x00\x00\x07') - - r = AMQPReader(s) - self.assertEqual(r.read_table(), val) - - def test_table_invalid(self): - """ - Check that an un-serializable table entry raises a ValueError - - """ - val = {'test': object()} - w = AMQPWriter() - self.assertRaises(FrameSyntaxError, w.write_table, val) - - def test_table_multi(self): - val = { - 'foo': 7, - 'bar': Decimal('123345.1234'), - 'baz': 'this is some random string I typed', - 'ubaz': u'And something in unicode', - 'dday_aniv': datetime(1994, 6, 6), - 'nothing': None, - 'more': { - 'abc': -123, - 'def': 'hello world', - 'now': datetime(2007, 11, 11, 21, 14, 31), - 'qty': Decimal('-123.45'), - 'blank': {}, - 'extra': { - 'deeper': 'more strings', - 'nums': -12345678, - }, - } - } - - w = AMQPWriter() - w.write_table(val) - s = w.getvalue() - - r = AMQPReader(s) - self.assertEqual(r.read_table(), val) - - # - # Array - # - def test_array_from_list(self): - val = [1, 'foo', None] - w = AMQPWriter() - w.write_array(val) - s = w.getvalue() - - self.assertEqualBinary( - s, '\x00\x00\x00\x0EI\x00\x00\x00\x01S\x00\x00\x00\x03fooV', - ) - - r = AMQPReader(s) - self.assertEqual(r.read_array(), val) - - def test_array_from_tuple(self): - val = (1, 'foo', None) - w = AMQPWriter() - w.write_array(val) - s = w.getvalue() - - self.assertEqualBinary( - s, '\x00\x00\x00\x0EI\x00\x00\x00\x01S\x00\x00\x00\x03fooV', - ) - - r = AMQPReader(s) - self.assertEqual(r.read_array(), list(val)) - - def test_table_with_array(self): - val = { - 'foo': 7, - 'bar': Decimal('123345.1234'), - 'baz': 'this is some random string I typed', - 'blist': [1, 2, 3], - 'nlist': [1, [2, 3, 4]], - 'ndictl': {'nfoo': 8, 'nblist': [5, 6, 7]} - } - - w = AMQPWriter() - w.write_table(val) - s = w.getvalue() - - r = AMQPReader(s) - self.assertEqual(r.read_table(), val) - - # - # GenericContent - # - def test_generic_content_eq(self): - msg_1 = GenericContent(dummy='foo') - msg_2 = GenericContent(dummy='foo') - msg_3 = GenericContent(dummy='bar') - - self.assertEqual(msg_1, msg_1) - self.assertEqual(msg_1, msg_2) - self.assertNotEqual(msg_1, msg_3) - self.assertNotEqual(msg_1, None) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestSerialization) - unittest.TextTestRunner(**settings.test_args).run(suite) - - -if __name__ == '__main__': - main() diff --git a/funtests/test_with.py b/funtests/test_with.py deleted file mode 100644 index b479794..0000000 --- a/funtests/test_with.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -""" -Test support for 'with' statements - -""" -# Copyright (C) 2009 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import unittest - -import settings - -from amqp import Connection, Message - - -class TestChannel(unittest.TestCase): - - def test_with(self): - with Connection(**settings.connect_args) as conn: - self.assertEqual(conn.transport is None, False) - - with conn.channel(1) as ch: - self.assertEqual(1 in conn.channels, True) - - # - # Do something with the channel - # - ch.exchange_declare('unittest.fanout', 'fanout', - auto_delete=True) - - msg = Message( - 'unittest message', - content_type='text/plain', - application_headers={'foo': 7, 'bar': 'baz'}, - ) - - ch.basic_publish(msg, 'unittest.fanout') - - # - # check that the channel was closed - # - self.assertEqual(1 in conn.channels, False) - self.assertEqual(ch.is_open, False) - - # - # Check that the connection was closed - # - self.assertEqual(conn.transport, None) - - -def main(): - suite = unittest.TestLoader().loadTestsFromTestCase(TestChannel) - unittest.TextTestRunner(**settings.test_args).run(suite) - - -if __name__ == '__main__': - main() diff --git a/requirements/docs.txt b/requirements/docs.txt deleted file mode 100644 index 0f57c43..0000000 --- a/requirements/docs.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx -sphinxcontrib-issuetracker>=0.9 diff --git a/requirements/pkgutils.txt b/requirements/pkgutils.txt deleted file mode 100644 index 2e522fc..0000000 --- a/requirements/pkgutils.txt +++ /dev/null @@ -1,5 +0,0 @@ -paver -flake8 -flakeplus -tox -Sphinx-PyPI-upload diff --git a/requirements/test.txt b/requirements/test.txt deleted file mode 100644 index 7554730..0000000 --- a/requirements/test.txt +++ /dev/null @@ -1,5 +0,0 @@ -unittest2>=0.4.0 -nose -nose-cover3 -coverage>=3.0 -mock diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 861a9f5..0000000 --- a/setup.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - diff --git a/setup.py b/setup.py deleted file mode 100644 index a1cfed5..0000000 --- a/setup.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -try: - from setuptools import setup, find_packages - from setuptools.command.test import test -except ImportError: - raise - from ez_setup import use_setuptools - use_setuptools() - from setuptools import setup, find_packages # noqa - from setuptools.command.test import test # noqa - -import os -import sys -import codecs - -if sys.version_info < (2, 6): - raise Exception('amqp requires Python 2.6 or higher.') - -NAME = 'amqp' -entrypoints = {} -extra = {} - -# -*- Classifiers -*- - -classes = """ - Development Status :: 5 - Production/Stable - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.6 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.0 - Programming Language :: Python :: 3.1 - Programming Language :: Python :: 3.2 - Programming Language :: Python :: 3.3 - License :: OSI Approved :: GNU Library or \ -Lesser General Public License (LGPL) - Intended Audience :: Developers - License :: OSI Approved :: BSD License - Operating System :: OS Independent -""" -classifiers = [s.strip() for s in classes.split('\n') if s] - -# -*- Distribution Meta -*- - -import re -re_meta = re.compile(r'__(\w+?)__\s*=\s*(.*)') -re_vers = re.compile(r'VERSION\s*=\s*\((.*?)\)') -re_doc = re.compile(r'^"""(.+?)"""') -rq = lambda s: s.strip("\"'") - - -def add_default(m): - attr_name, attr_value = m.groups() - return ((attr_name, rq(attr_value)), ) - - -def add_version(m): - v = list(map(rq, m.groups()[0].split(', '))) - return (('VERSION', '.'.join(v[0:3]) + ''.join(v[3:])), ) - - -def add_doc(m): - return (('doc', m.groups()[0]), ) - -pats = {re_meta: add_default, - re_vers: add_version, - re_doc: add_doc} -here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'amqp/__init__.py')) as meta_fh: - meta = {} - for line in meta_fh: - if line.strip() == '# -eof meta-': - break - for pattern, handler in pats.items(): - m = pattern.match(line.strip()) - if m: - meta.update(handler(m)) - -# -*- Installation Requires -*- - -py_version = sys.version_info -is_jython = sys.platform.startswith('java') -is_pypy = hasattr(sys, 'pypy_version_info') - - -def strip_comments(l): - return l.split('#', 1)[0].strip() - - -def reqs(f): - return filter(None, [strip_comments(l) for l in open( - os.path.join(os.getcwd(), 'requirements', f)).readlines()]) - -install_requires = [] - -# -*- Tests Requires -*- - -tests_require = reqs('test.txt') - -# -*- Long Description -*- - -if os.path.exists('README.rst'): - long_description = codecs.open('README.rst', 'r', 'utf-8').read() -else: - long_description = 'See http://pypi.python.org/pypi/amqp' - -# -*- Entry Points -*- # - -# -*- %%% -*- - -setup( - name=NAME, - version=meta['VERSION'], - description=meta['doc'], - author=meta['author'], - author_email=meta['contact'], - maintainer=meta['maintainer'], - url=meta['homepage'], - platforms=['any'], - license='LGPL', - packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']), - zip_safe=False, - install_requires=install_requires, - tests_require=tests_require, - test_suite='nose.collector', - classifiers=classifiers, - entry_points=entrypoints, - long_description=long_description, - **extra)