From 673644124fd39ccc7e1c4b6de491132890254e5c Mon Sep 17 00:00:00 2001 From: MuffinSpawn Date: Fri, 12 Aug 2016 15:34:28 -0500 Subject: [PATCH] Revert "Branch merging" --- Dockerfile | 3 +- Server.py | 6 +- WaggleRouter.py | 4 +- config.py | 12 +- dataprocess.py | 23 +- install_dependencies.sh | 9 - packages_o/pika-0.10.0/LICENSE | 25 - packages_o/pika-0.10.0/MANIFEST.in | 2 - packages_o/pika-0.10.0/PKG-INFO | 122 - packages_o/pika-0.10.0/README.rst | 94 - .../pika-0.10.0/build/lib/pika/__init__.py | 27 - .../build/lib/pika/adapters/__init__.py | 41 - .../lib/pika/adapters/base_connection.py | 491 ---- .../lib/pika/adapters/blocking_connection.py | 2477 ----------------- .../lib/pika/adapters/libev_connection.py | 295 -- .../lib/pika/adapters/select_connection.py | 613 ---- .../lib/pika/adapters/tornado_connection.py | 95 - .../lib/pika/adapters/twisted_connection.py | 451 --- .../pika-0.10.0/build/lib/pika/amqp_object.py | 66 - .../pika-0.10.0/build/lib/pika/callback.py | 410 --- .../pika-0.10.0/build/lib/pika/channel.py | 1254 --------- .../pika-0.10.0/build/lib/pika/compat.py | 105 - .../pika-0.10.0/build/lib/pika/connection.py | 1634 ----------- .../pika-0.10.0/build/lib/pika/credentials.py | 104 - packages_o/pika-0.10.0/build/lib/pika/data.py | 291 -- .../pika-0.10.0/build/lib/pika/exceptions.py | 237 -- .../pika-0.10.0/build/lib/pika/frame.py | 265 -- .../pika-0.10.0/build/lib/pika/heartbeat.py | 171 -- packages_o/pika-0.10.0/build/lib/pika/spec.py | 2312 --------------- .../pika-0.10.0/build/lib/pika/utils.py | 16 - .../pika-0.10.0/dist/pika-0.10.0-py3.4.egg | Bin 212652 -> 0 bytes packages_o/pika-0.10.0/pika.egg-info/PKG-INFO | 122 - .../pika-0.10.0/pika.egg-info/SOURCES.txt | 31 - .../pika.egg-info/dependency_links.txt | 1 - .../pika-0.10.0/pika.egg-info/requires.txt | 10 - .../pika-0.10.0/pika.egg-info/top_level.txt | 1 - packages_o/pika-0.10.0/pika.egg-info/zip-safe | 1 - packages_o/pika-0.10.0/pika/__init__.py | 27 - .../pika-0.10.0/pika/adapters/__init__.py | 41 - .../pika/adapters/base_connection.py | 491 ---- .../pika/adapters/blocking_connection.py | 2477 ----------------- .../pika/adapters/libev_connection.py | 295 -- .../pika/adapters/select_connection.py | 613 ---- .../pika/adapters/tornado_connection.py | 95 - .../pika/adapters/twisted_connection.py | 451 --- packages_o/pika-0.10.0/pika/amqp_object.py | 66 - packages_o/pika-0.10.0/pika/callback.py | 410 --- packages_o/pika-0.10.0/pika/channel.py | 1254 --------- packages_o/pika-0.10.0/pika/compat.py | 105 - packages_o/pika-0.10.0/pika/connection.py | 1634 ----------- packages_o/pika-0.10.0/pika/credentials.py | 104 - packages_o/pika-0.10.0/pika/data.py | 291 -- packages_o/pika-0.10.0/pika/exceptions.py | 237 -- packages_o/pika-0.10.0/pika/frame.py | 265 -- packages_o/pika-0.10.0/pika/heartbeat.py | 171 -- packages_o/pika-0.10.0/pika/spec.py | 2312 --------------- packages_o/pika-0.10.0/pika/utils.py | 16 - packages_o/pika-0.10.0/setup.cfg | 8 - packages_o/pika-0.10.0/setup.py | 51 - registrationprocess.py | 10 +- systemd/beehive-server.service | 2 +- utilitiesprocess.py | 6 +- waggle_protocol | 2 +- 63 files changed, 22 insertions(+), 23233 deletions(-) delete mode 100644 packages_o/pika-0.10.0/LICENSE delete mode 100644 packages_o/pika-0.10.0/MANIFEST.in delete mode 100644 packages_o/pika-0.10.0/PKG-INFO delete mode 100644 packages_o/pika-0.10.0/README.rst delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/__init__.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/__init__.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/base_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/blocking_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/libev_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/select_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/tornado_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/adapters/twisted_connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/amqp_object.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/callback.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/channel.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/compat.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/connection.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/credentials.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/data.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/exceptions.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/frame.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/heartbeat.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/spec.py delete mode 100644 packages_o/pika-0.10.0/build/lib/pika/utils.py delete mode 100644 packages_o/pika-0.10.0/dist/pika-0.10.0-py3.4.egg delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/PKG-INFO delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/SOURCES.txt delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/dependency_links.txt delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/requires.txt delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/top_level.txt delete mode 100644 packages_o/pika-0.10.0/pika.egg-info/zip-safe delete mode 100644 packages_o/pika-0.10.0/pika/__init__.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/__init__.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/base_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/blocking_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/libev_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/select_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/tornado_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/adapters/twisted_connection.py delete mode 100644 packages_o/pika-0.10.0/pika/amqp_object.py delete mode 100644 packages_o/pika-0.10.0/pika/callback.py delete mode 100644 packages_o/pika-0.10.0/pika/channel.py delete mode 100644 packages_o/pika-0.10.0/pika/compat.py delete mode 100644 packages_o/pika-0.10.0/pika/connection.py delete mode 100644 packages_o/pika-0.10.0/pika/credentials.py delete mode 100644 packages_o/pika-0.10.0/pika/data.py delete mode 100644 packages_o/pika-0.10.0/pika/exceptions.py delete mode 100644 packages_o/pika-0.10.0/pika/frame.py delete mode 100644 packages_o/pika-0.10.0/pika/heartbeat.py delete mode 100644 packages_o/pika-0.10.0/pika/spec.py delete mode 100644 packages_o/pika-0.10.0/pika/utils.py delete mode 100644 packages_o/pika-0.10.0/setup.cfg delete mode 100644 packages_o/pika-0.10.0/setup.py diff --git a/Dockerfile b/Dockerfile index cd7ebcfd..4274a37c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,7 @@ FROM ubuntu:14.04 RUN apt-get update ; apt-get install -y git \ - python-dev python-pip \ - python3-dev python3-pip + python-dev python-pip ADD . /usr/lib/waggle/beehive-server/ diff --git a/Server.py b/Server.py index 50dbdc28..e40ac018 100755 --- a/Server.py +++ b/Server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """ This module sets up and runs the waggle server. """ @@ -173,12 +173,12 @@ def flush(self): #Declare all of the appropriate exchanges, queues, and bindings - for queueName in list(queue_bindings.keys()): + for queueName in queue_bindings.keys(): rabbitChannel.queue_declare(queueName) for exchName in exchage_list: rabbitChannel.exchange_declare(exchName) - for key in list(queue_bindings.keys()): + for key in queue_bindings.keys(): bind = queue_bindings[key] rabbitChannel.queue_bind(exchange=bind[0], queue=key, routing_key=bind[1]) diff --git a/WaggleRouter.py b/WaggleRouter.py index 07f93fdf..1b96580c 100644 --- a/WaggleRouter.py +++ b/WaggleRouter.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # WaggleRouter.py import sys sys.path.append("..") @@ -108,7 +106,7 @@ def gotPacket(self,ch,method,props,body): recipient = recipient_node['queue'] self.channel.basic_publish(exchange='internal', routing_key = recipient, body=body) except Exception as e: - print(str(e)) + print str(e) finally: ch.basic_ack(delivery_tag =method.delivery_tag) diff --git a/config.py b/config.py index d68c1d50..8617a6a2 100755 --- a/config.py +++ b/config.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python -import sys, os, io, configparser, logging, pika, ssl, re +import sys, os, StringIO, ConfigParser, logging, pika, ssl, re import time, datetime @@ -41,7 +41,7 @@ def read_value(key, defaultval): value=None try: value=my_config.get("root", key) - except configparser.NoOptionError: + except ConfigParser.NoOptionError: value="" if not value: @@ -56,8 +56,8 @@ def read_value(key, defaultval): if os.path.isfile(CONFIG_FILE): ini_str = ini_str + open(CONFIG_FILE, 'r').read() -ini_fp = io.StringIO(ini_str) -my_config = configparser.RawConfigParser() +ini_fp = StringIO.StringIO(ini_str) +my_config = ConfigParser.RawConfigParser() my_config.readfp(ini_fp) RABBITMQ_HOST=read_value("rabbitmq-host", "rabbitmq") @@ -171,7 +171,7 @@ def unix_time(dt): return delta.total_seconds() def unix_time_millis(dt): - return int(unix_time(dt) * 1000.0) + return long(unix_time(dt) * 1000.0) diff --git a/dataprocess.py b/dataprocess.py index 053e2c25..f802d7ea 100644 --- a/dataprocess.py +++ b/dataprocess.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # dataprocess.py import sys @@ -73,7 +71,7 @@ def __init__(self): def callback(self,ch,method,props,body): #TODO: this simply drops failed messages, might find a better solution!? Keeping them has the risk of spamming RabbitMQ try: - header, opt, data = unpack(body) + header,data = unpack(body) except Exception as e: logger.error("Error unpacking data: %s" % (str(e))) ch.basic_ack(delivery_tag=method.delivery_tag) @@ -139,23 +137,8 @@ def cassandra_insert(self,header,data): logger.error("Error preparing statement: (%s) %s" % (type(e).__name__, str(e)) ) raise - # TODO: Later we will fix this issue - idx = [0, 1, 3, 5, 6] - for i in idx: - if (type(data[i]) == bytes): - data[i] = data[i].decode('iso-8859-1') - - tmp = [] - for entity in data[7]: - if (type(entity) == bytes): - tmp.append(entity.decode('iso-8859-1')) - else: - tmp.append(entity) - data[7] = tmp - - # for entity in data[7]: - # if (type(entity) == bytes): - # entity = data[i].decode('iso-8859-1') + + if not data[3]: data[3] = 'default' diff --git a/install_dependencies.sh b/install_dependencies.sh index 2fb8c3e7..b407b917 100755 --- a/install_dependencies.sh +++ b/install_dependencies.sh @@ -32,15 +32,6 @@ pip install crcmod cd pika-0.9.14/ python setup.py install -cd ../../ -cd packages_o/ -pip3 install blist -pip3 install cassandra-driver -pip3 install crcmod - -cd pika-0.10.0/ -python3 setup.py install - cd ../../ # cqlshlib for the cassandra client diff --git a/packages_o/pika-0.10.0/LICENSE b/packages_o/pika-0.10.0/LICENSE deleted file mode 100644 index 6d671a44..00000000 --- a/packages_o/pika-0.10.0/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2009-2015, Tony Garnock-Jones, Gavin M. Roy, Pivotal and others. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the Pika project nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages_o/pika-0.10.0/MANIFEST.in b/packages_o/pika-0.10.0/MANIFEST.in deleted file mode 100644 index 9c8317c4..00000000 --- a/packages_o/pika-0.10.0/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include LICENSE -include README.rst \ No newline at end of file diff --git a/packages_o/pika-0.10.0/PKG-INFO b/packages_o/pika-0.10.0/PKG-INFO deleted file mode 100644 index fe14c42d..00000000 --- a/packages_o/pika-0.10.0/PKG-INFO +++ /dev/null @@ -1,122 +0,0 @@ -Metadata-Version: 1.1 -Name: pika -Version: 0.10.0 -Summary: Pika Python AMQP Client Library -Home-page: https://pika.readthedocs.org -Author: Gavin M. Roy -Author-email: gavinmroy@gmail.com -License: BSD -Description: Pika, an AMQP 0-9-1 client library for Python - ============================================= - - |Version| |Downloads| |Status| |Coverage| |License| - - Introduction - ------------- - Pika is a pure-Python implementation of the AMQP 0-9-1 protocol that tries - to stay fairly independent of the underlying network support library. - - - Python 2.6+ and 3.3+ are supported. - - - Since threads aren't appropriate to every situation, it doesn't - require threads. It takes care not to forbid them, either. The same - goes for greenlets, callbacks, continuations and generators. It is - not necessarily thread-safe however, and your mileage will vary. - - - People may be using direct sockets, plain old `select()`, - or any of the wide variety of ways of getting network events to and from a - python application. Pika tries to stay compatible with all of these, and to - make adapting it to a new environment as simple as possible. - - Documentation - ------------- - - Pika's documentation can be found at `https://pika.readthedocs.org `_ - - Example - ------- - Here is the most simple example of use, sending a message with the BlockingConnection adapter: - - .. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - channel.basic_publish(exchange='example', - routing_key='test', - body='Test Message') - connection.close() - - And an example of writing a blocking consumer: - - .. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - - for method_frame, properties, body in channel.consume('test'): - - # Display the message parts and ack the message - print method_frame, properties, body - channel.basic_ack(method_frame.delivery_tag) - - # Escape out of the loop after 10 messages - if method_frame.delivery_tag == 10: - break - - # Cancel the consumer and return any pending messages - requeued_messages = channel.cancel() - print 'Requeued %i messages' % requeued_messages - connection.close() - - Pika provides the following adapters - ------------------------------------ - - - BlockingConnection - enables blocking, synchronous operation on top of library for simple uses - - LibevConnection - adapter for use with the libev event loop http://libev.schmorp.de - - SelectConnection - fast asynchronous adapter - - TornadoConnection - adapter for use with the Tornado IO Loop http://tornadoweb.org - - TwistedConnection - adapter for use with the Twisted asynchronous package http://twistedmatrix.com/ - - Contributing - ------------ - To contribute to pika, please make sure that any new features or changes - to existing functionality include test coverage. Additionally, please format - your code using `yapf `_ with ``google`` style - prior to issuing your pull request. - - .. |Version| image:: https://img.shields.io/pypi/v/pika.svg? - :target: http://badge.fury.io/py/pika - - .. |Status| image:: https://img.shields.io/travis/pika/pika.svg? - :target: https://travis-ci.org/pika/pika - - .. |Coverage| image:: https://img.shields.io/codecov/c/github/pika/pika.svg? - :target: https://codecov.io/github/pika/pika?branch=master - - .. |Downloads| image:: https://img.shields.io/pypi/dm/pika.svg? - :target: https://pypi.python.org/pypi/pika - - .. |License| image:: https://img.shields.io/pypi/l/pika.svg? - :target: https://pika.readthedocs.org - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Natural Language :: English -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: Jython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Communications -Classifier: Topic :: Internet -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Networking diff --git a/packages_o/pika-0.10.0/README.rst b/packages_o/pika-0.10.0/README.rst deleted file mode 100644 index 94c35eb9..00000000 --- a/packages_o/pika-0.10.0/README.rst +++ /dev/null @@ -1,94 +0,0 @@ -Pika, an AMQP 0-9-1 client library for Python -============================================= - -|Version| |Downloads| |Status| |Coverage| |License| - -Introduction -------------- -Pika is a pure-Python implementation of the AMQP 0-9-1 protocol that tries -to stay fairly independent of the underlying network support library. - -- Python 2.6+ and 3.3+ are supported. - -- Since threads aren't appropriate to every situation, it doesn't - require threads. It takes care not to forbid them, either. The same - goes for greenlets, callbacks, continuations and generators. It is - not necessarily thread-safe however, and your mileage will vary. - -- People may be using direct sockets, plain old `select()`, - or any of the wide variety of ways of getting network events to and from a - python application. Pika tries to stay compatible with all of these, and to - make adapting it to a new environment as simple as possible. - -Documentation -------------- - -Pika's documentation can be found at `https://pika.readthedocs.org `_ - -Example -------- -Here is the most simple example of use, sending a message with the BlockingConnection adapter: - -.. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - channel.basic_publish(exchange='example', - routing_key='test', - body='Test Message') - connection.close() - -And an example of writing a blocking consumer: - -.. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - - for method_frame, properties, body in channel.consume('test'): - - # Display the message parts and ack the message - print method_frame, properties, body - channel.basic_ack(method_frame.delivery_tag) - - # Escape out of the loop after 10 messages - if method_frame.delivery_tag == 10: - break - - # Cancel the consumer and return any pending messages - requeued_messages = channel.cancel() - print 'Requeued %i messages' % requeued_messages - connection.close() - -Pika provides the following adapters ------------------------------------- - -- BlockingConnection - enables blocking, synchronous operation on top of library for simple uses -- LibevConnection - adapter for use with the libev event loop http://libev.schmorp.de -- SelectConnection - fast asynchronous adapter -- TornadoConnection - adapter for use with the Tornado IO Loop http://tornadoweb.org -- TwistedConnection - adapter for use with the Twisted asynchronous package http://twistedmatrix.com/ - -Contributing ------------- -To contribute to pika, please make sure that any new features or changes -to existing functionality include test coverage. Additionally, please format -your code using `yapf `_ with ``google`` style -prior to issuing your pull request. - -.. |Version| image:: https://img.shields.io/pypi/v/pika.svg? - :target: http://badge.fury.io/py/pika - -.. |Status| image:: https://img.shields.io/travis/pika/pika.svg? - :target: https://travis-ci.org/pika/pika - -.. |Coverage| image:: https://img.shields.io/codecov/c/github/pika/pika.svg? - :target: https://codecov.io/github/pika/pika?branch=master - -.. |Downloads| image:: https://img.shields.io/pypi/dm/pika.svg? - :target: https://pypi.python.org/pypi/pika - -.. |License| image:: https://img.shields.io/pypi/l/pika.svg? - :target: https://pika.readthedocs.org diff --git a/packages_o/pika-0.10.0/build/lib/pika/__init__.py b/packages_o/pika-0.10.0/build/lib/pika/__init__.py deleted file mode 100644 index aa00cb05..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -__version__ = '0.10.0' - -import logging -try: - # not available in python 2.6 - from logging import NullHandler -except ImportError: - - class NullHandler(logging.Handler): - - def emit(self, record): - pass - -# Add NullHandler to prevent logging warnings -logging.getLogger(__name__).addHandler(NullHandler()) - -from pika.connection import ConnectionParameters -from pika.connection import URLParameters -from pika.credentials import PlainCredentials -from pika.spec import BasicProperties - -from pika.adapters import BaseConnection -from pika.adapters import BlockingConnection -from pika.adapters import SelectConnection -from pika.adapters import TornadoConnection -from pika.adapters import TwistedConnection -from pika.adapters import LibevConnection diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/__init__.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/__init__.py deleted file mode 100644 index 1b8e8223..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# -# For copyright and licensing please refer to COPYING. -# -# ***** END LICENSE BLOCK ***** -"""Pika provides multiple adapters to connect to RabbitMQ: - -- adapters.select_connection.SelectConnection: A native event based connection - adapter that implements select, kqueue, poll and epoll. -- adapters.tornado_connection.TornadoConnection: Connection adapter for use - with the Tornado web framework. -- adapters.blocking_connection.BlockingConnection: Enables blocking, - synchronous operation on top of library for simple uses. -- adapters.twisted_connection.TwistedConnection: Connection adapter for use - with the Twisted framework -- adapters.libev_connection.LibevConnection: Connection adapter for use - with the libev event loop and employing nonblocking IO - -""" -from pika.adapters.base_connection import BaseConnection -from pika.adapters.blocking_connection import BlockingConnection -from pika.adapters.select_connection import SelectConnection -from pika.adapters.select_connection import IOLoop - -# Dynamically handle 3rd party library dependencies for optional imports -try: - from pika.adapters.tornado_connection import TornadoConnection -except ImportError: - TornadoConnection = None - -try: - from pika.adapters.twisted_connection import TwistedConnection - from pika.adapters.twisted_connection import TwistedProtocolConnection -except ImportError: - TwistedConnection = None - TwistedProtocolConnection = None - -try: - from pika.adapters.libev_connection import LibevConnection -except ImportError: - LibevConnection = None diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/base_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/base_connection.py deleted file mode 100644 index 8f2629d2..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/base_connection.py +++ /dev/null @@ -1,491 +0,0 @@ -"""Base class extended by connection adapters. This extends the -connection.Connection class to encapsulate connection behavior but still -isolate socket and low level communication. - -""" -import errno -import logging -import socket -import ssl - -import pika.compat -from pika import connection -from pika import exceptions - -try: - SOL_TCP = socket.SOL_TCP -except AttributeError: - SOL_TCP = 6 - - -if pika.compat.PY2: - _SOCKET_ERROR = socket.error -else: - # socket.error was deprecated and replaced by OSError in python 3.3 - _SOCKET_ERROR = OSError - - -LOGGER = logging.getLogger(__name__) - - -class BaseConnection(connection.Connection): - """BaseConnection class that should be extended by connection adapters""" - - # Use epoll's constants to keep life easy - READ = 0x0001 - WRITE = 0x0004 - ERROR = 0x0008 - - ERRORS_TO_ABORT = [errno.EBADF, errno.ECONNABORTED, errno.EPIPE] - ERRORS_TO_IGNORE = [errno.EWOULDBLOCK, errno.EAGAIN, errno.EINTR] - DO_HANDSHAKE = True - WARN_ABOUT_IOLOOP = False - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - ioloop=None, - stop_ioloop_on_close=True): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Method to call on connection open - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param method on_close_callback: Method to call on connection close - :param object ioloop: IOLoop object to use - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :raises: RuntimeError - :raises: ValueError - - """ - if parameters and not isinstance(parameters, connection.Parameters): - raise ValueError('Expected instance of Parameters, not %r' % - parameters) - - # Let the developer know we could not import SSL - if parameters and parameters.ssl and not ssl: - raise RuntimeError("SSL specified but it is not available") - self.base_events = self.READ | self.ERROR - self.event_state = self.base_events - self.ioloop = ioloop - self.socket = None - self.stop_ioloop_on_close = stop_ioloop_on_close - self.write_buffer = None - super(BaseConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback) - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - return self.ioloop.add_timeout(deadline, callback_method) - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - super(BaseConnection, self).close(reply_code, reply_text) - self._handle_ioloop_stop() - - def remove_timeout(self, timeout_id): - """Remove the timeout from the IOLoop by the ID returned from - add_timeout. - - :rtype: str - - """ - self.ioloop.remove_timeout(timeout_id) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker, returning True if connected. - - :returns: error string or exception instance on error; None on success - - """ - # Get the addresses for the socket, supporting IPv4 & IPv6 - while True: - try: - addresses = socket.getaddrinfo(self.params.host, self.params.port, - 0, socket.SOCK_STREAM, - socket.IPPROTO_TCP) - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - - LOGGER.critical('Could not get addresses to use: %s (%s)', error, - self.params.host) - return error - - # If the socket is created and connected, continue on - error = "No socket addresses available" - for sock_addr in addresses: - error = self._create_and_connect_to_socket(sock_addr) - if not error: - # Make the socket non-blocking after the connect - self.socket.setblocking(0) - return None - self._cleanup_socket() - - # Failed to connect - return error - - def _adapter_disconnect(self): - """Invoked if the connection is being told to disconnect""" - try: - self._remove_heartbeat() - self._cleanup_socket() - self._check_state_on_disconnect() - finally: - # Ensure proper cleanup since _check_state_on_disconnect may raise - # an exception - self._handle_ioloop_stop() - self._init_connection_state() - - def _check_state_on_disconnect(self): - """Checks to see if we were in opening a connection with RabbitMQ when - we were disconnected and raises exceptions for the anticipated - exception types. - - """ - if self.connection_state == self.CONNECTION_PROTOCOL: - LOGGER.error('Incompatible Protocol Versions') - raise exceptions.IncompatibleProtocolError - elif self.connection_state == self.CONNECTION_START: - LOGGER.error("Socket closed while authenticating indicating a " - "probable authentication error") - raise exceptions.ProbableAuthenticationError - elif self.connection_state == self.CONNECTION_TUNE: - LOGGER.error("Socket closed while tuning the connection indicating " - "a probable permission error when accessing a virtual " - "host") - raise exceptions.ProbableAccessDeniedError - elif self.is_open: - LOGGER.warning("Socket closed when connection was open") - elif not self.is_closed and not self.is_closing: - LOGGER.warning('Unknown state on disconnect: %i', - self.connection_state) - - def _cleanup_socket(self): - """Close the socket cleanly""" - if self.socket: - try: - self.socket.shutdown(socket.SHUT_RDWR) - except _SOCKET_ERROR: - pass - self.socket.close() - self.socket = None - - def _create_and_connect_to_socket(self, sock_addr_tuple): - """Create socket and connect to it, using SSL if enabled. - - :returns: error string on failure; None on success - """ - self.socket = socket.socket(sock_addr_tuple[0], socket.SOCK_STREAM, 0) - self.socket.setsockopt(SOL_TCP, socket.TCP_NODELAY, 1) - self.socket.settimeout(self.params.socket_timeout) - - # Wrap socket if using SSL - if self.params.ssl: - self.socket = self._wrap_socket(self.socket) - ssl_text = " with SSL" - else: - ssl_text = "" - - LOGGER.info('Connecting to %s:%s%s', sock_addr_tuple[4][0], - sock_addr_tuple[4][1], ssl_text) - - # Connect to the socket - try: - self.socket.connect(sock_addr_tuple[4]) - except socket.timeout: - error = 'Connection to %s:%s failed: timeout' % ( - sock_addr_tuple[4][0], sock_addr_tuple[4][1] - ) - LOGGER.error(error) - return error - except _SOCKET_ERROR as error: - error = 'Connection to %s:%s failed: %s' % (sock_addr_tuple[4][0], - sock_addr_tuple[4][1], - error) - LOGGER.warning(error) - return error - - # Handle SSL Connection Negotiation - if self.params.ssl and self.DO_HANDSHAKE: - try: - self._do_ssl_handshake() - except ssl.SSLError as error: - error = 'SSL connection to %s:%s failed: %s' % ( - sock_addr_tuple[4][0], sock_addr_tuple[4][1], error - ) - LOGGER.error(error) - return error - # Made it this far - return None - - def _do_ssl_handshake(self): - """Perform SSL handshaking, copied from python stdlib test_ssl.py. - - """ - if not self.DO_HANDSHAKE: - return - while True: - try: - self.socket.do_handshake() - break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - self.event_state = self.READ - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - self.event_state = self.WRITE - else: - raise - self._manage_event_state() - - @staticmethod - def _get_error_code(error_value): - """Get the error code from the error_value accounting for Python - version differences. - - :rtype: int - - """ - if not error_value: - return None - if hasattr(error_value, 'errno'): # Python >= 2.6 - return error_value.errno - elif error_value is not None: - return error_value[0] # Python <= 2.5 - return None - - def _flush_outbound(self): - """write early, if the socket will take the data why not get it out - there asap. - """ - self._handle_write() - self._manage_event_state() - - def _handle_disconnect(self): - """Called internally when the socket is disconnected already - """ - self._adapter_disconnect() - self._on_connection_closed(None, True) - - def _handle_ioloop_stop(self): - """Invoked when the connection is closed to determine if the IOLoop - should be stopped or not. - - """ - if self.stop_ioloop_on_close and self.ioloop: - self.ioloop.stop() - elif self.WARN_ABOUT_IOLOOP: - LOGGER.warning('Connection is closed but not stopping IOLoop') - - def _handle_error(self, error_value): - """Internal error handling method. Here we expect a socket.error - coming in and will handle different socket errors differently. - - :param int|object error_value: The inbound error - - """ - if 'timed out' in str(error_value): - raise socket.timeout - error_code = self._get_error_code(error_value) - if not error_code: - LOGGER.critical("Tried to handle an error where no error existed") - return - - # Ok errors, just continue what we were doing before - if error_code in self.ERRORS_TO_IGNORE: - LOGGER.debug("Ignoring %s", error_code) - return - - # Socket is no longer connected, abort - elif error_code in self.ERRORS_TO_ABORT: - LOGGER.error("Fatal Socket Error: %r", error_value) - - elif self.params.ssl and isinstance(error_value, ssl.SSLError): - - if error_value.args[0] == ssl.SSL_ERROR_WANT_READ: - self.event_state = self.READ - elif error_value.args[0] == ssl.SSL_ERROR_WANT_WRITE: - self.event_state = self.WRITE - else: - LOGGER.error("SSL Socket error: %r", error_value) - - else: - # Haven't run into this one yet, log it. - LOGGER.error("Socket Error: %s", error_code) - - # Disconnect from our IOLoop and let Connection know what's up - self._handle_disconnect() - - def _handle_timeout(self): - """Handle a socket timeout in read or write. - We don't do anything in the non-blocking handlers because we - only have the socket in a blocking state during connect.""" - pass - - def _handle_events(self, fd, events, error=None, write_only=False): - """Handle IO/Event loop events, processing them. - - :param int fd: The file descriptor for the events - :param int events: Events from the IO/Event loop - :param int error: Was an error specified - :param bool write_only: Only handle write events - - """ - if not self.socket: - LOGGER.error('Received events on closed socket: %r', fd) - return - - if self.socket and (events & self.WRITE): - self._handle_write() - self._manage_event_state() - - if self.socket and not write_only and (events & self.READ): - self._handle_read() - - if (self.socket and write_only and (events & self.READ) and - (events & self.ERROR)): - LOGGER.error('BAD libc: Write-Only but Read+Error. ' - 'Assume socket disconnected.') - self._handle_disconnect() - - if self.socket and (events & self.ERROR): - LOGGER.error('Error event %r, %r', events, error) - self._handle_error(error) - - def _handle_read(self): - """Read from the socket and call our on_data_available with the data.""" - try: - while True: - try: - if self.params.ssl: - data = self.socket.read(self._buffer_size) - else: - data = self.socket.recv(self._buffer_size) - - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - else: - raise - - except socket.timeout: - self._handle_timeout() - return 0 - - except ssl.SSLError as error: - if error.args[0] == ssl.SSL_ERROR_WANT_READ: - # ssl wants more data but there is nothing currently - # available in the socket, wait for it to become readable. - return 0 - return self._handle_error(error) - - except _SOCKET_ERROR as error: - if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): - return 0 - return self._handle_error(error) - - # Empty data, should disconnect - if not data or data == 0: - LOGGER.error('Read empty data, calling disconnect') - return self._handle_disconnect() - - # Pass the data into our top level frame dispatching method - self._on_data_available(data) - return len(data) - - def _handle_write(self): - """Try and write as much as we can, if we get blocked requeue - what's left""" - bytes_written = 0 - try: - while self.outbound_buffer: - frame = self.outbound_buffer.popleft() - while True: - try: - bw = self.socket.send(frame) - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - else: - raise - - bytes_written += bw - if bw < len(frame): - LOGGER.debug("Partial write, requeing remaining data") - self.outbound_buffer.appendleft(frame[bw:]) - break - - except socket.timeout: - # Will only come here if the socket is blocking - LOGGER.debug("socket timeout, requeuing frame") - self.outbound_buffer.appendleft(frame) - self._handle_timeout() - - except _SOCKET_ERROR as error: - if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): - LOGGER.debug("Would block, requeuing frame") - self.outbound_buffer.appendleft(frame) - else: - return self._handle_error(error) - - return bytes_written - - - def _init_connection_state(self): - """Initialize or reset all of our internal state variables for a given - connection. If we disconnect and reconnect, all of our state needs to - be wiped. - - """ - super(BaseConnection, self)._init_connection_state() - self.base_events = self.READ | self.ERROR - self.event_state = self.base_events - self.socket = None - - def _manage_event_state(self): - """Manage the bitmask for reading/writing/error which is used by the - io/event handler to specify when there is an event such as a read or - write. - - """ - if self.outbound_buffer: - if not self.event_state & self.WRITE: - self.event_state |= self.WRITE - self.ioloop.update_handler(self.socket.fileno(), - self.event_state) - elif self.event_state & self.WRITE: - self.event_state = self.base_events - self.ioloop.update_handler(self.socket.fileno(), self.event_state) - - def _wrap_socket(self, sock): - """Wrap the socket for connecting over SSL. - - :rtype: ssl.SSLSocket - - """ - return ssl.wrap_socket(sock, - do_handshake_on_connect=self.DO_HANDSHAKE, - **self.params.ssl_options) diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/blocking_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/blocking_connection.py deleted file mode 100644 index 09051dae..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/blocking_connection.py +++ /dev/null @@ -1,2477 +0,0 @@ -"""The blocking connection adapter module implements blocking semantics on top -of Pika's core AMQP driver. While most of the asynchronous expectations are -removed when using the blocking connection adapter, it attempts to remain true -to the asynchronous RPC nature of the AMQP protocol, supporting server sent -RPC commands. - -The user facing classes in the module consist of the -:py:class:`~pika.adapters.blocking_connection.BlockingConnection` -and the :class:`~pika.adapters.blocking_connection.BlockingChannel` -classes. - -""" -# Disable "access to protected member warnings: this wrapper implementation is -# a friend of those instances -# pylint: disable=W0212 - -from collections import namedtuple, deque -import contextlib -import functools -import logging -import time - -import pika.channel -from pika import compat -from pika import exceptions -import pika.spec -# NOTE: import SelectConnection after others to avoid circular depenency -from pika.adapters.select_connection import SelectConnection - -LOGGER = logging.getLogger(__name__) - - -class _CallbackResult(object): - """ CallbackResult is a non-thread-safe implementation for receiving - callback results; INTERNAL USE ONLY! - """ - __slots__ = ('_value_class', '_ready', '_values') - def __init__(self, value_class=None): - """ - :param callable value_class: only needed if the CallbackResult - instance will be used with - `set_value_once` and `append_element`. - *args and **kwargs of the value setter - methods will be passed to this class. - - """ - self._value_class = value_class - self._ready = None - self._values = None - self.reset() - - def reset(self): - """Reset value, but not _value_class""" - self._ready = False - self._values = None - - def __bool__(self): - """ Called by python runtime to implement truth value testing and the - built-in operation bool(); NOTE: python 3.x - """ - return self.is_ready() - - # python 2.x version of __bool__ - __nonzero__ = __bool__ - - def __enter__(self): - """ Entry into context manager that automatically resets the object - on exit; this usage pattern helps garbage-collection by eliminating - potential circular references. - """ - return self - - def __exit__(self, *args, **kwargs): - """Reset value""" - self.reset() - - def is_ready(self): - """ - :returns: True if the object is in a signaled state - """ - return self._ready - - @property - def ready(self): - """True if the object is in a signaled state""" - return self._ready - - def signal_once(self, *_args, **_kwargs): # pylint: disable=W0613 - """ Set as ready - - :raises AssertionError: if result was already signalled - """ - assert not self._ready, '_CallbackResult was already set' - self._ready = True - - def set_value_once(self, *args, **kwargs): - """ Set as ready with value; the value may be retrived via the `value` - property getter - - :raises AssertionError: if result was already set - """ - self.signal_once() - try: - self._values = (self._value_class(*args, **kwargs),) - except Exception: - LOGGER.error( - "set_value_once failed: value_class=%r; args=%r; kwargs=%r", - self._value_class, args, kwargs) - raise - - def append_element(self, *args, **kwargs): - """Append an element to values""" - assert not self._ready or isinstance(self._values, list), ( - '_CallbackResult state is incompatible with append_element: ' - 'ready=%r; values=%r' % (self._ready, self._values)) - - try: - value = self._value_class(*args, **kwargs) - except Exception: - LOGGER.error( - "append_element failed: value_class=%r; args=%r; kwargs=%r", - self._value_class, args, kwargs) - raise - - if self._values is None: - self._values = [value] - else: - self._values.append(value) - - self._ready = True - - - @property - def value(self): - """ - :returns: a reference to the value that was set via `set_value_once` - :raises AssertionError: if result was not set or value is incompatible - with `set_value_once` - """ - assert self._ready, '_CallbackResult was not set' - assert isinstance(self._values, tuple) and len(self._values) == 1, ( - '_CallbackResult value is incompatible with set_value_once: %r' - % (self._values,)) - - return self._values[0] - - - @property - def elements(self): - """ - :returns: a reference to the list containing one or more elements that - were added via `append_element` - :raises AssertionError: if result was not set or value is incompatible - with `append_element` - """ - assert self._ready, '_CallbackResult was not set' - assert isinstance(self._values, list) and len(self._values) > 0, ( - '_CallbackResult value is incompatible with append_element: %r' - % (self._values,)) - - return self._values - - -class _IoloopTimerContext(object): # pylint: disable=R0903 - """Context manager for registering and safely unregistering a - SelectConnection ioloop-based timer - """ - - def __init__(self, duration, connection): - """ - :param float duration: non-negative timer duration in seconds - :param SelectConnection connection: - """ - assert hasattr(connection, 'add_timeout'), connection - self._duration = duration - self._connection = connection - self._callback_result = _CallbackResult() - self._timer_id = None - - def __enter__(self): - """Register a timer""" - self._timer_id = self._connection.add_timeout( - self._duration, - self._callback_result.signal_once) - return self - - def __exit__(self, *_args, **_kwargs): - """Unregister timer if it hasn't fired yet""" - if not self._callback_result: - self._connection.remove_timeout(self._timer_id) - - def is_ready(self): - """ - :returns: True if timer has fired, False otherwise - """ - return self._callback_result.is_ready() - - -class _TimerEvt(object): # pylint: disable=R0903 - """Represents a timer created via `BlockingConnection.add_timeout`""" - __slots__ = ('timer_id', '_callback') - - def __init__(self, callback): - """ - :param callback: see callback_method in `BlockingConnection.add_timeout` - """ - self._callback = callback - - # Will be set to timer id returned from the underlying implementation's - # `add_timeout` method - self.timer_id = None - - def __repr__(self): - return '%s(timer_id=%s, callback=%s)' % (self.__class__.__name__, - self.timer_id, self._callback) - - def dispatch(self): - """Dispatch the user's callback method""" - self._callback() - - -class _ConnectionBlockedUnblockedEvtBase(object): # pylint: disable=R0903 - """Base class for `_ConnectionBlockedEvt` and `_ConnectionUnblockedEvt`""" - __slots__ = ('_callback', '_method_frame') - - def __init__(self, callback, method_frame): - """ - :param callback: see callback_method parameter in - `BlockingConnection.add_on_connection_blocked_callback` and - `BlockingConnection.add_on_connection_unblocked_callback` - :param pika.frame.Method method_frame: with method_frame.method of type - `pika.spec.Connection.Blocked` or `pika.spec.Connection.Unblocked` - """ - self._callback = callback - self._method_frame = method_frame - - def __repr__(self): - return '%s(callback=%s, frame=%s)' % (self.__class__.__name__, - self._callback, - self._method_frame) - - def dispatch(self): - """Dispatch the user's callback method""" - self._callback(self._method_frame) - - -class _ConnectionBlockedEvt( # pylint: disable=R0903 - _ConnectionBlockedUnblockedEvtBase): - """Represents a Connection.Blocked notification from RabbitMQ broker`""" - pass - - -class _ConnectionUnblockedEvt( # pylint: disable=R0903 - _ConnectionBlockedUnblockedEvtBase): - """Represents a Connection.Unblocked notification from RabbitMQ broker`""" - pass - - -class BlockingConnection(object): # pylint: disable=R0902 - """The BlockingConnection creates a layer on top of Pika's asynchronous core - providing methods that will block until their expected response has - returned. Due to the asynchronous nature of the `Basic.Deliver` and - `Basic.Return` calls from RabbitMQ to your application, you can still - implement continuation-passing style asynchronous methods if you'd like to - receive messages from RabbitMQ using - :meth:`basic_consume ` or if you want to be - notified of a delivery failure when using - :meth:`basic_publish ` . - - For more information about communicating with the blocking_connection - adapter, be sure to check out the - :class:`BlockingChannel ` class which implements the - :class:`Channel ` based communication for the - blocking_connection adapter. - - """ - # Connection-opened callback args - _OnOpenedArgs = namedtuple('BlockingConnection__OnOpenedArgs', - 'connection') - - # Connection-establishment error callback args - _OnOpenErrorArgs = namedtuple('BlockingConnection__OnOpenErrorArgs', - 'connection error_text') - - # Connection-closing callback args - _OnClosedArgs = namedtuple('BlockingConnection__OnClosedArgs', - 'connection reason_code reason_text') - - # Channel-opened callback args - _OnChannelOpenedArgs = namedtuple( - 'BlockingConnection__OnChannelOpenedArgs', - 'channel') - - def __init__(self, parameters=None, _impl_class=None): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param _impl_class: for tests/debugging only; implementation class; - None=default - - :raises RuntimeError: - - """ - # Used by the _acquire_event_dispatch decorator; when already greater - # than 0, event dispatch is already acquired higher up the call stack - self._event_dispatch_suspend_depth = 0 - - # Connection-specific events that are ready for dispatch: _TimerEvt, - # _ConnectionBlockedEvt, _ConnectionUnblockedEvt - self._ready_events = deque() - - # Channel numbers of channels that are requesting a call to their - # BlockingChannel._dispatch_events method; See - # `_request_channel_dispatch` - self._channels_pending_dispatch = set() - - # Receives on_open_callback args from Connection - self._opened_result = _CallbackResult(self._OnOpenedArgs) - - # Receives on_open_error_callback args from Connection - self._open_error_result = _CallbackResult(self._OnOpenErrorArgs) - - # Receives on_close_callback args from Connection - self._closed_result = _CallbackResult(self._OnClosedArgs) - - # Set to True when when user calls close() on the connection - # NOTE: this is a workaround to detect socket error because - # on_close_callback passes reason_code=0 when called due to socket error - self._user_initiated_close = False - - impl_class = _impl_class or SelectConnection - self._impl = impl_class( - parameters=parameters, - on_open_callback=self._opened_result.set_value_once, - on_open_error_callback=self._open_error_result.set_value_once, - on_close_callback=self._closed_result.set_value_once, - stop_ioloop_on_close=False) - - self._process_io_for_connection_setup() - - def _cleanup(self): - """Clean up members that might inhibit garbage collection""" - self._ready_events.clear() - self._opened_result.reset() - self._open_error_result.reset() - self._closed_result.reset() - - @contextlib.contextmanager - def _acquire_event_dispatch(self): - """ Context manager that controls access to event dispatcher for - preventing reentrancy. - - The "as" value is True if the managed code block owns the event - dispatcher and False if caller higher up in the call stack already owns - it. Only managed code that gets ownership (got True) is permitted to - dispatch - """ - try: - # __enter__ part - self._event_dispatch_suspend_depth += 1 - yield self._event_dispatch_suspend_depth == 1 - finally: - # __exit__ part - self._event_dispatch_suspend_depth -= 1 - - def _process_io_for_connection_setup(self): # pylint: disable=C0103 - """ Perform follow-up processing for connection setup request: flush - connection output and process input while waiting for connection-open - or connection-error. - - :raises AMQPConnectionError: on connection open error - """ - self._flush_output(self._opened_result.is_ready, - self._open_error_result.is_ready) - - if self._open_error_result.ready: - raise exceptions.AMQPConnectionError( - self._open_error_result.value.error_text) - - assert self._opened_result.ready - assert self._opened_result.value.connection is self._impl - - def _flush_output(self, *waiters): - """ Flush output and process input while waiting for any of the given - callbacks to return true. The wait is aborted upon connection-close. - Otherwise, processing continues until the output is flushed AND at least - one of the callbacks returns true. If there are no callbacks, then - processing ends when all output is flushed. - - :param waiters: sequence of zero or more callables taking no args and - returning true when it's time to stop processing. - Their results are OR'ed together. - """ - if self._impl.is_closed: - raise exceptions.ConnectionClosed() - - # Conditions for terminating the processing loop: - # connection closed - # OR - # empty outbound buffer and no waiters - # OR - # empty outbound buffer and any waiter is ready - is_done = (lambda: - self._closed_result.ready or - (not self._impl.outbound_buffer and - (not waiters or any(ready() for ready in waiters)))) - - # Process I/O until our completion condition is satisified - while not is_done(): - self._impl.ioloop.poll() - self._impl.ioloop.process_timeouts() - - if self._closed_result.ready: - try: - result = self._closed_result.value - if result.reason_code not in [0, 200]: - LOGGER.critical('Connection close detected; result=%r', - result) - raise exceptions.ConnectionClosed(result.reason_code, - result.reason_text) - elif not self._user_initiated_close: - # NOTE: unfortunately, upon socket error, on_close_callback - # presently passes reason_code=0, so we don't detect that as - # an error - LOGGER.critical('Connection close detected') - raise exceptions.ConnectionClosed() - else: - LOGGER.info('Connection closed; result=%r', result) - finally: - self._cleanup() - - def _request_channel_dispatch(self, channel_number): - """Called by BlockingChannel instances to request a call to their - _dispatch_events method or to terminate `process_data_events`; - BlockingConnection will honor these requests from a safe context. - - :param int channel_number: positive channel number to request a call - to the channel's `_dispatch_events`; a negative channel number to - request termination of `process_data_events` - """ - self._channels_pending_dispatch.add(channel_number) - - def _dispatch_channel_events(self): - """Invoke the `_dispatch_events` method on open channels that requested - it - """ - if not self._channels_pending_dispatch: - return - - with self._acquire_event_dispatch() as dispatch_acquired: - if not dispatch_acquired: - # Nested dispatch or dispatch blocked higher in call stack - return - - candidates = list(self._channels_pending_dispatch) - self._channels_pending_dispatch.clear() - - for channel_number in candidates: - if channel_number < 0: - # This was meant to terminate process_data_events - continue - - try: - impl_channel = self._impl._channels[channel_number] - except KeyError: - continue - - if impl_channel.is_open: - impl_channel._get_cookie()._dispatch_events() - - def _on_timer_ready(self, evt): - """Handle expiry of a timer that was registered via `add_timeout` - - :param _TimerEvt evt: - - """ - self._ready_events.append(evt) - - def _on_connection_blocked(self, user_callback, method_frame): - """Handle Connection.Blocked notification from RabbitMQ broker - - :param callable user_callback: callback_method passed to - `add_on_connection_blocked_callback` - :param pika.frame.Method method_frame: method frame having `method` - member of type `pika.spec.Connection.Blocked` - """ - self._ready_events.append( - _ConnectionBlockedEvt(user_callback, method_frame)) - - def _on_connection_unblocked(self, user_callback, method_frame): - """Handle Connection.Unblocked notification from RabbitMQ broker - - :param callable user_callback: callback_method passed to - `add_on_connection_unblocked_callback` - :param pika.frame.Method method_frame: method frame having `method` - member of type `pika.spec.Connection.Blocked` - """ - self._ready_events.append( - _ConnectionUnblockedEvt(user_callback, method_frame)) - - def _dispatch_connection_events(self): - """Dispatch ready connection events""" - if not self._ready_events: - return - - with self._acquire_event_dispatch() as dispatch_acquired: - if not dispatch_acquired: - # Nested dispatch or dispatch blocked higher in call stack - return - - # Limit dispatch to the number of currently ready events to avoid - # getting stuck in this loop - for _ in compat.xrange(len(self._ready_events)): - try: - evt = self._ready_events.popleft() - except IndexError: - # Some events (e.g., timers) must have been cancelled - break - - evt.dispatch() - - def add_on_connection_blocked_callback(self, # pylint: disable=C0103 - callback_method): - """Add a callback to be notified when RabbitMQ has sent a - `Connection.Blocked` frame indicating that RabbitMQ is low on - resources. Publishers can use this to voluntarily suspend publishing, - instead of relying on back pressure throttling. The callback - will be passed the `Connection.Blocked` method frame. - - :param method callback_method: Callback to call on `Connection.Blocked`, - having the signature callback_method(pika.frame.Method), where the - method frame's `method` member is of type - `pika.spec.Connection.Blocked` - - """ - self._impl.add_on_connection_blocked_callback( - functools.partial(self._on_connection_blocked, callback_method)) - - def add_on_connection_unblocked_callback(self, # pylint: disable=C0103 - callback_method): - """Add a callback to be notified when RabbitMQ has sent a - `Connection.Unblocked` frame letting publishers know it's ok - to start publishing again. The callback will be passed the - `Connection.Unblocked` method frame. - - :param method callback_method: Callback to call on - `Connection.Unblocked`, having the signature - callback_method(pika.frame.Method), where the method frame's - `method` member is of type `pika.spec.Connection.Unblocked` - - """ - self._impl.add_on_connection_unblocked_callback( - functools.partial(self._on_connection_unblocked, callback_method)) - - def add_timeout(self, deadline, callback_method): - """Create a single-shot timer to fire after deadline seconds. Do not - confuse with Tornado's timeout where you pass in the time you want to - have your callback called. Only pass in the seconds until it's to be - called. - - NOTE: the timer callbacks are dispatched only in the scope of - specially-designated methods: see - `BlockingConnection.process_data_events` and - `BlockingChannel.start_consuming`. - - :param float deadline: The number of seconds to wait to call callback - :param callable callback_method: The callback method with the signature - callback_method() - - :returns: opaque timer id - - """ - if not callable(callback_method): - raise ValueError( - 'callback_method parameter must be callable, but got %r' - % (callback_method,)) - - evt = _TimerEvt(callback=callback_method) - timer_id = self._impl.add_timeout( - deadline, - functools.partial(self._on_timer_ready, evt)) - evt.timer_id = timer_id - - return timer_id - - def remove_timeout(self, timeout_id): - """Remove a timer if it's still in the timeout stack - - :param timeout_id: The opaque timer id to remove - - """ - # Remove from the impl's timeout stack - self._impl.remove_timeout(timeout_id) - - # Remove from ready events, if the timer fired already - for i, evt in enumerate(self._ready_events): - if isinstance(evt, _TimerEvt) and evt.timer_id == timeout_id: - index_to_remove = i - break - else: - # Not found - return - - del self._ready_events[index_to_remove] - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - LOGGER.info('Closing connection (%s): %s', reply_code, reply_text) - - self._user_initiated_close = True - - # Close channels that remain opened - for impl_channel in pika.compat.dictvalues(self._impl._channels): - channel = impl_channel._get_cookie() - if channel.is_open: - channel.close(reply_code, reply_text) - - # Close the connection - self._impl.close(reply_code, reply_text) - - self._flush_output(self._closed_result.is_ready) - - def process_data_events(self, time_limit=0): - """Will make sure that data events are processed. Dispatches timer and - channel callbacks if not called from the scope of BlockingConnection or - BlockingChannel callback. Your app can block on this method. - - :param float time_limit: suggested upper bound on processing time in - seconds. The actual blocking time depends on the granularity of the - underlying ioloop. Zero means return as soon as possible. None means - there is no limit on processing time and the function will block - until I/O produces actionalable events. Defaults to 0 for backward - compatibility. This parameter is NEW in pika 0.10.0. - """ - common_terminator = lambda: bool( - self._channels_pending_dispatch or self._ready_events) - - if time_limit is None: - self._flush_output(common_terminator) - else: - with _IoloopTimerContext(time_limit, self._impl) as timer: - self._flush_output(timer.is_ready, common_terminator) - - if self._ready_events: - self._dispatch_connection_events() - - if self._channels_pending_dispatch: - self._dispatch_channel_events() - - def sleep(self, duration): - """A safer way to sleep than calling time.sleep() directly that would - keep the adapter from ignoring frames sent from the broker. The - connection will "sleep" or block the number of seconds specified in - duration in small intervals. - - :param float duration: The time to sleep in seconds - - """ - assert duration >= 0, duration - - deadline = time.time() + duration - time_limit = duration - # Process events at least once - while True: - self.process_data_events(time_limit) - time_limit = deadline - time.time() - if time_limit <= 0: - break - - def channel(self, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - :rtype: pika.synchronous_connection.BlockingChannel - """ - with _CallbackResult(self._OnChannelOpenedArgs) as opened_args: - impl_channel = self._impl.channel( - on_open_callback=opened_args.set_value_once, - channel_number=channel_number) - - # Create our proxy channel - channel = BlockingChannel(impl_channel, self) - - # Link implementation channel with our proxy channel - impl_channel._set_cookie(channel) - - # Drive I/O until Channel.Open-ok - channel._flush_output(opened_args.is_ready) - - - return channel - - def __enter__(self): - # Prepare `with` context - return self - - def __exit__(self, tp, value, traceback): - # Close connection after `with` context - self.close() - - # - # Connections state properties - # - - @property - def is_closed(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_closed - - @property - def is_closing(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_closing - - @property - def is_open(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_open - - # - # Properties that reflect server capabilities for the current connection - # - - @property - def basic_nack_supported(self): - """Specifies if the server supports basic.nack on the active connection. - - :rtype: bool - - """ - return self._impl.basic_nack - - @property - def consumer_cancel_notify_supported(self): # pylint: disable=C0103 - """Specifies if the server supports consumer cancel notification on the - active connection. - - :rtype: bool - - """ - return self._impl.consumer_cancel_notify - - @property - def exchange_exchange_bindings_supported(self): # pylint: disable=C0103 - """Specifies if the active connection supports exchange to exchange - bindings. - - :rtype: bool - - """ - return self._impl.exchange_exchange_bindings - - @property - def publisher_confirms_supported(self): - """Specifies if the active connection can use publisher confirmations. - - :rtype: bool - - """ - return self._impl.publisher_confirms - - # Legacy property names for backward compatibility - basic_nack = basic_nack_supported - consumer_cancel_notify = consumer_cancel_notify_supported - exchange_exchange_bindings = exchange_exchange_bindings_supported - publisher_confirms = publisher_confirms_supported - - -class _ChannelPendingEvt(object): # pylint: disable=R0903 - """Base class for BlockingChannel pending events""" - pass - - -class _ConsumerDeliveryEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents consumer message delivery `Basic.Deliver`; it - contains method, properties, and body of the delivered message. - """ - - __slots__ = ('method', 'properties', 'body') - - def __init__(self, method, properties, body): - """ - :param spec.Basic.Deliver method: NOTE: consumer_tag and delivery_tag - are valid only within source channel - :param spec.BasicProperties properties: message properties - :param body: message body; empty string if no body - :type body: str or unicode - """ - self.method = method - self.properties = properties - self.body = body - - -class _ConsumerCancellationEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents server-initiated consumer cancellation delivered to - client via Basic.Cancel. After receiving Basic.Cancel, there will be no - further deliveries for the consumer identified by `consumer_tag` in - `Basic.Cancel` - """ - - __slots__ = ('method_frame') - - def __init__(self, method_frame): - """ - :param pika.frame.Method method_frame: method frame with method of type - `spec.Basic.Cancel` - """ - self.method_frame = method_frame - - def __repr__(self): - return '%s(method_frame=%r)' % (self.__class__.__name__, - self.method_frame) - - @property - def method(self): - """method of type spec.Basic.Cancel""" - return self.method_frame.method - - -class _ReturnedMessageEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents a message returned by broker via `Basic.Return`""" - - __slots__ = ('callback', 'channel', 'method', 'properties', 'body') - - def __init__(self, callback, channel, method, properties, body): # pylint: disable=R0913 - """ - :param callable callback: user's callback, having the signature - callback(channel, method, properties, body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - :param pika.Channel channel: - :param pika.spec.Basic.Return method: - :param pika.spec.BasicProperties properties: - :param body: str, unicode, or bytes (python 3.x) - """ - self.callback = callback - self.channel = channel - self.method = method - self.properties = properties - self.body = body - - def __repr__(self): - return ('%s(callback=%r, channel=%r, method=%r, properties=%r, ' - 'body=%.300r') % (self.__class__.__name__, self.callback, - self.channel, self.method, self.properties, - self.body) - - def dispatch(self): - """Dispatch user's callback""" - self.callback(self.channel, self.method, self.properties, self.body) - - -class ReturnedMessage(object): # pylint: disable=R0903 - """Represents a message returned via Basic.Return in publish-acknowledgments - mode - """ - - __slots__ = ('method', 'properties', 'body') - - def __init__(self, method, properties, body): - """ - :param spec.Basic.Return method: - :param spec.BasicProperties properties: message properties - :param body: message body; empty string if no body - :type body: str or unicode - """ - self.method = method - self.properties = properties - self.body = body - - -class _ConsumerInfo(object): - """Information about an active consumer""" - - __slots__ = ('consumer_tag', 'no_ack', 'consumer_cb', - 'alternate_event_sink', 'state') - - # Consumer states - SETTING_UP = 1 - ACTIVE = 2 - TEARING_DOWN = 3 - CANCELLED_BY_BROKER = 4 - - def __init__(self, consumer_tag, no_ack, consumer_cb=None, - alternate_event_sink=None): - """ - NOTE: exactly one of consumer_cb/alternate_event_sink musts be non-None. - - :param str consumer_tag: - :param bool no_ack: the no-ack value for the consumer - :param callable consumer_cb: The function for dispatching messages to - user, having the signature: - consumer_callback(channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - :param callable alternate_event_sink: if specified, _ConsumerDeliveryEvt - and _ConsumerCancellationEvt objects will be diverted to this - callback instead of being deposited in the channel's - `_pending_events` container. Signature: - alternate_event_sink(evt) - """ - assert (consumer_cb is None) != (alternate_event_sink is None), ( - 'exactly one of consumer_cb/alternate_event_sink must be non-None', - consumer_cb, alternate_event_sink) - self.consumer_tag = consumer_tag - self.no_ack = no_ack - self.consumer_cb = consumer_cb - self.alternate_event_sink = alternate_event_sink - self.state = self.SETTING_UP - - @property - def setting_up(self): - """True if in SETTING_UP state""" - return self.state == self.SETTING_UP - - @property - def active(self): - """True if in ACTIVE state""" - return self.state == self.ACTIVE - - @property - def tearing_down(self): - """True if in TEARING_DOWN state""" - return self.state == self.TEARING_DOWN - - @property - def cancelled_by_broker(self): - """True if in CANCELLED_BY_BROKER state""" - return self.state == self.CANCELLED_BY_BROKER - - -class _QueueConsumerGeneratorInfo(object): # pylint: disable=R0903 - """Container for information about the active queue consumer generator """ - __slots__ = ('params', 'consumer_tag', 'pending_events') - - def __init__(self, params, consumer_tag): - """ - :params tuple params: a three-tuple (queue, no_ack, exclusive) that were - used to create the queue consumer - :param str consumer_tag: consumer tag - """ - self.params = params - self.consumer_tag = consumer_tag - #self.messages = deque() - - # Holds pending events of types _ConsumerDeliveryEvt and - # _ConsumerCancellationEvt - self.pending_events = deque() - - def __repr__(self): - return '%s(params=%r, consumer_tag=%r)' % ( - self.__class__.__name__, self.params, self.consumer_tag) - - -class BlockingChannel(object): # pylint: disable=R0904,R0902 - """The BlockingChannel implements blocking semantics for most things that - one would use callback-passing-style for with the - :py:class:`~pika.channel.Channel` class. In addition, - the `BlockingChannel` class implements a :term:`generator` that allows - you to :doc:`consume messages ` - without using callbacks. - - Example of creating a BlockingChannel:: - - import pika - - # Create our connection object - connection = pika.BlockingConnection() - - # The returned object will be a synchronous channel - channel = connection.channel() - - """ - - # Used as value_class with _CallbackResult for receiving Basic.GetOk args - _RxMessageArgs = namedtuple( - 'BlockingChannel__RxMessageArgs', - [ - 'channel', # implementation pika.Channel instance - 'method', # Basic.GetOk - 'properties', # pika.spec.BasicProperties - 'body' # str, unicode, or bytes (python 3.x) - ]) - - - # For use as value_class with any _CallbackResult that expects method_frame - # as the only arg - _MethodFrameCallbackResultArgs = namedtuple( - 'BlockingChannel__MethodFrameCallbackResultArgs', - 'method_frame') - - # Broker's basic-ack/basic-nack args when delivery confirmation is enabled; - # may concern a single or multiple messages - _OnMessageConfirmationReportArgs = namedtuple( # pylint: disable=C0103 - 'BlockingChannel__OnMessageConfirmationReportArgs', - 'method_frame') - - # Parameters for broker-inititated Channel.Close request: reply_code - # holds the broker's non-zero error code and reply_text holds the - # corresponding error message text. - _OnChannelClosedByBrokerArgs = namedtuple( - 'BlockingChannel__OnChannelClosedByBrokerArgs', - 'method_frame') - - # For use as value_class with _CallbackResult expecting Channel.Flow - # confirmation. - _FlowOkCallbackResultArgs = namedtuple( - 'BlockingChannel__FlowOkCallbackResultArgs', - 'active' # True if broker will start or continue sending; False if not - ) - - _CONSUMER_CANCELLED_CB_KEY = 'blocking_channel_consumer_cancelled' - - def __init__(self, channel_impl, connection): - """Create a new instance of the Channel - - :param channel_impl: Channel implementation object as returned from - SelectConnection.channel() - :param BlockingConnection connection: The connection object - - """ - self._impl = channel_impl - self._connection = connection - - # A mapping of consumer tags to _ConsumerInfo for active consumers - self._consumer_infos = dict() - - # Queue consumer generator generator info of type - # _QueueConsumerGeneratorInfo created by BlockingChannel.consume - self._queue_consumer_generator = None - - # Whether RabbitMQ delivery confirmation has been enabled - self._delivery_confirmation = False - - # Receives message delivery confirmation report (Basic.ack or - # Basic.nack) from broker when delivery confirmations are enabled - self._message_confirmation_result = _CallbackResult( - self._OnMessageConfirmationReportArgs) - - # deque of pending events: _ConsumerDeliveryEvt and - # _ConsumerCancellationEvt objects that will be returned by - # `BlockingChannel.get_event()` - self._pending_events = deque() - - # Holds a ReturnedMessage object representing a message received via - # Basic.Return in publisher-acknowledgments mode. - self._puback_return = None - - # Receives Basic.ConsumeOk reply from server - self._basic_consume_ok_result = _CallbackResult() - - # Receives the broker-inititated Channel.Close parameters - self._channel_closed_by_broker_result = _CallbackResult( # pylint: disable=C0103 - self._OnChannelClosedByBrokerArgs) - - # Receives args from Basic.GetEmpty response - # http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.get - self._basic_getempty_result = _CallbackResult( - self._MethodFrameCallbackResultArgs) - - self._impl.add_on_cancel_callback(self._on_consumer_cancelled_by_broker) - - self._impl.add_callback( - self._basic_consume_ok_result.signal_once, - replies=[pika.spec.Basic.ConsumeOk], - one_shot=False) - - self._impl.add_callback( - self._channel_closed_by_broker_result.set_value_once, - replies=[pika.spec.Channel.Close], - one_shot=True) - - self._impl.add_callback( - self._basic_getempty_result.set_value_once, - replies=[pika.spec.Basic.GetEmpty], - one_shot=False) - - LOGGER.info("Created channel=%s", self.channel_number) - - def _cleanup(self): - """Clean up members that might inhibit garbage collection""" - self._message_confirmation_result.reset() - self._pending_events = deque() - self._consumer_infos = dict() - - def __int__(self): - """Return the channel object as its channel number - - :rtype: int - - """ - return self.channel_number - - @property - def channel_number(self): - """Channel number""" - return self._impl.channel_number - - @property - def connection(self): - """The channel's BlockingConnection instance""" - return self._connection - - @property - def is_closed(self): - """Returns True if the channel is closed. - - :rtype: bool - - """ - return self._impl.is_closed - - @property - def is_closing(self): - """Returns True if the channel is closing. - - :rtype: bool - - """ - return self._impl.is_closing - - @property - def is_open(self): - """Returns True if the channel is open. - - :rtype: bool - - """ - return self._impl.is_open - - _ALWAYS_READY_WAITERS = ((lambda: True), ) - - def _flush_output(self, *waiters): - """ Flush output and process input while waiting for any of the given - callbacks to return true. The wait is aborted upon channel-close or - connection-close. - Otherwise, processing continues until the output is flushed AND at least - one of the callbacks returns true. If there are no callbacks, then - processing ends when all output is flushed. - - :param waiters: sequence of zero or more callables taking no args and - returning true when it's time to stop processing. - Their results are OR'ed together. - """ - if self._impl.is_closed: - raise exceptions.ChannelClosed() - - if not waiters: - waiters = self._ALWAYS_READY_WAITERS - - self._connection._flush_output( - self._channel_closed_by_broker_result.is_ready, - *waiters) - - if self._channel_closed_by_broker_result: - # Channel was force-closed by broker - self._cleanup() - method = ( - self._channel_closed_by_broker_result.value.method_frame.method) - raise exceptions.ChannelClosed(method.reply_code, method.reply_text) - - def _on_puback_message_returned(self, channel, method, properties, body): - """Called as the result of Basic.Return from broker in - publisher-acknowledgements mode. Saves the info as a ReturnedMessage - instance in self._puback_return. - - :param pika.Channel channel: our self._impl channel - :param pika.spec.Basic.Return method: - :param pika.spec.BasicProperties properties: message properties - :param body: returned message body; empty string if no body - :type body: str, unicode - - """ - assert channel is self._impl, ( - channel.channel_number, self.channel_number) - - assert isinstance(method, pika.spec.Basic.Return), method - assert isinstance(properties, pika.spec.BasicProperties), ( - properties) - - LOGGER.warn( - "Published message was returned: _delivery_confirmation=%s; " - "channel=%s; method=%r; properties=%r; body_size=%d; " - "body_prefix=%.255r", self._delivery_confirmation, - channel.channel_number, method, properties, - len(body) if body is not None else None, body) - - self._puback_return = ReturnedMessage(method, properties, body) - - - def _add_pending_event(self, evt): - """Append an event to the channel's list of events that are ready for - dispatch to user and signal our connection that this channel is ready - for event dispatch - - :param _ChannelPendingEvt evt: an event derived from _ChannelPendingEvt - """ - self._pending_events.append(evt) - self.connection._request_channel_dispatch(self.channel_number) - - - def _on_consumer_cancelled_by_broker(self, # pylint: disable=C0103 - method_frame): - """Called by impl when broker cancels consumer via Basic.Cancel. - - This is a RabbitMQ-specific feature. The circumstances include deletion - of queue being consumed as well as failure of a HA node responsible for - the queue being consumed. - - :param pika.frame.Method method_frame: method frame with the - `spec.Basic.Cancel` method - - """ - evt = _ConsumerCancellationEvt(method_frame) - - consumer = self._consumer_infos[method_frame.method.consumer_tag] - - # Don't interfere with client-initiated cancellation flow - if not consumer.tearing_down: - consumer.state = _ConsumerInfo.CANCELLED_BY_BROKER - - if consumer.alternate_event_sink is not None: - consumer.alternate_event_sink(evt) - else: - self._add_pending_event(evt) - - def _on_consumer_message_delivery(self, channel, # pylint: disable=W0613 - method, properties, body): - """Called by impl when a message is delivered for a consumer - - :param Channel channel: The implementation channel object - :param spec.Basic.Deliver method: - :param pika.spec.BasicProperties properties: message properties - :param body: delivered message body; empty string if no body - :type body: str, unicode, or bytes (python 3.x) - - """ - evt = _ConsumerDeliveryEvt(method, properties, body) - - consumer = self._consumer_infos[method.consumer_tag] - - if consumer.alternate_event_sink is not None: - consumer.alternate_event_sink(evt) - else: - self._add_pending_event(evt) - - def _on_consumer_generator_event(self, evt): - """Sink for the queue consumer generator's consumer events; append the - event to queue consumer generator's pending events buffer. - - :param evt: an object of type _ConsumerDeliveryEvt or - _ConsumerCancellationEvt - """ - self._queue_consumer_generator.pending_events.append(evt) - # Schedule termination of connection.process_data_events using a - # negative channel number - self.connection._request_channel_dispatch(-self.channel_number) - - def _cancel_all_consumers(self): - """Cancel all consumers. - - NOTE: pending non-ackable messages will be lost; pending ackable - messages will be rejected. - - """ - if self._consumer_infos: - LOGGER.debug('Cancelling %i consumers', len(self._consumer_infos)) - - if self._queue_consumer_generator is not None: - # Cancel queue consumer generator - self.cancel() - - # Cancel consumers created via basic_consume - for consumer_tag in pika.compat.dictkeys(self._consumer_infos): - self.basic_cancel(consumer_tag) - - def _dispatch_events(self): - """Called by BlockingConnection to dispatch pending events. - - `BlockingChannel` schedules this callback via - `BlockingConnection._request_channel_dispatch` - """ - while self._pending_events: - evt = self._pending_events.popleft() - - if type(evt) is _ConsumerDeliveryEvt: - consumer_info = self._consumer_infos[evt.method.consumer_tag] - consumer_info.consumer_cb(self, evt.method, evt.properties, - evt.body) - - elif type(evt) is _ConsumerCancellationEvt: - del self._consumer_infos[evt.method_frame.method.consumer_tag] - - self._impl.callbacks.process(self.channel_number, - self._CONSUMER_CANCELLED_CB_KEY, - self, - evt.method_frame) - else: - evt.dispatch() - - - def close(self, reply_code=0, reply_text="Normal Shutdown"): - """Will invoke a clean shutdown of the channel with the AMQP Broker. - - :param int reply_code: The reply code to close the channel with - :param str reply_text: The reply text to close the channel with - - """ - LOGGER.info('Channel.close(%s, %s)', reply_code, reply_text) - - # Cancel remaining consumers - self._cancel_all_consumers() - - # Close the channel - try: - with _CallbackResult() as close_ok_result: - self._impl.add_callback(callback=close_ok_result.signal_once, - replies=[pika.spec.Channel.CloseOk], - one_shot=True) - - self._impl.close(reply_code=reply_code, reply_text=reply_text) - self._flush_output(close_ok_result.is_ready) - finally: - self._cleanup() - - def flow(self, active): - """Turn Channel flow control off and on. - - NOTE: RabbitMQ doesn't support active=False; per - https://www.rabbitmq.com/specification.html: "active=false is not - supported by the server. Limiting prefetch with basic.qos provides much - better control" - - For more information, please reference: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#channel.flow - - :param bool active: Turn flow on (True) or off (False) - - :returns: True if broker will start or continue sending; False if not - :rtype: bool - - """ - with _CallbackResult(self._FlowOkCallbackResultArgs) as flow_ok_result: - self._impl.flow(callback=flow_ok_result.set_value_once, - active=active) - self._flush_output(flow_ok_result.is_ready) - return flow_ok_result.value.active - - def add_on_cancel_callback(self, callback): - """Pass a callback function that will be called when Basic.Cancel - is sent by the broker. The callback function should receive a method - frame parameter. - - :param callable callback: a callable for handling broker's Basic.Cancel - notification with the call signature: callback(method_frame) - where method_frame is of type `pika.frame.Method` with method of - type `spec.Basic.Cancel` - - - """ - self._impl.callbacks.add(self.channel_number, - self._CONSUMER_CANCELLED_CB_KEY, - callback, - one_shot=False) - - def add_on_return_callback(self, callback): - """Pass a callback function that will be called when a published - message is rejected and returned by the server via `Basic.Return`. - - :param callable callback: The method to call on callback with the - signature callback(channel, method, properties, body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - """ - self._impl.add_on_return_callback( - lambda _channel, method, properties, body: ( - self._add_pending_event( - _ReturnedMessageEvt( - callback, self, method, properties, body)))) - - def basic_consume(self, # pylint: disable=R0913 - consumer_callback, - queue, - no_ack=False, - exclusive=False, - consumer_tag=None, - arguments=None): - """Sends the AMQP command Basic.Consume to the broker and binds messages - for the consumer_tag to the consumer callback. If you do not pass in - a consumer_tag, one will be automatically generated for you. Returns - the consumer tag. - - NOTE: the consumer callbacks are dispatched only in the scope of - specially-designated methods: see - `BlockingConnection.process_data_events` and - `BlockingChannel.start_consuming`. - - For more information about Basic.Consume, see: - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.consume - - :param callable consumer_callback: The function for dispatching messages - to user, having the signature: - consumer_callback(channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - :param queue: The queue to consume from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a response (i.e., - no ack/nack) - :param bool exclusive: Don't allow other consumers on the queue - :param consumer_tag: You may specify your own consumer tag; if left - empty, a consumer tag will be generated automatically - :type consumer_tag: str or unicode - :param dict arguments: Custom key/value pair arguments for the consumer - :returns: consumer tag - :rtype: str - - :raises pika.exceptions.DuplicateConsumerTag: if consumer with given - consumer_tag is already present. - - """ - if not callable(consumer_callback): - raise ValueError('consumer callback must be callable; got %r' - % consumer_callback) - - return self._basic_consume_impl( - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments, - consumer_callback=consumer_callback) - - def _basic_consume_impl(self, # pylint: disable=R0913 - queue, - no_ack, - exclusive, - consumer_tag, - arguments=None, - consumer_callback=None, - alternate_event_sink=None): - """The low-level implementation used by `basic_consume` and `consume`. - See `basic_consume` docstring for more info. - - NOTE: exactly one of consumer_callback/alternate_event_sink musts be - non-None. - - This method has one additional parameter alternate_event_sink over the - args described in `basic_consume`. - - :param callable alternate_event_sink: if specified, _ConsumerDeliveryEvt - and _ConsumerCancellationEvt objects will be diverted to this - callback instead of being deposited in the channel's - `_pending_events` container. Signature: - alternate_event_sink(evt) - - :raises pika.exceptions.DuplicateConsumerTag: if consumer with given - consumer_tag is already present. - - """ - if (consumer_callback is None) == (alternate_event_sink is None): - raise ValueError( - ('exactly one of consumer_callback/alternate_event_sink must ' - 'be non-None', consumer_callback, alternate_event_sink)) - - if not consumer_tag: - # Need a consumer tag to register consumer info before sending - # request to broker, because I/O might dispatch incoming messages - # immediately following Basic.Consume-ok before _flush_output - # returns - consumer_tag = self._impl._generate_consumer_tag() - - if consumer_tag in self._consumer_infos: - raise exceptions.DuplicateConsumerTag(consumer_tag) - - # Create new consumer - self._consumer_infos[consumer_tag] = _ConsumerInfo( - consumer_tag, - no_ack=no_ack, - consumer_cb=consumer_callback, - alternate_event_sink=alternate_event_sink) - - try: - with self._basic_consume_ok_result as ok_result: - tag = self._impl.basic_consume( - consumer_callback=self._on_consumer_message_delivery, - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments) - - assert tag == consumer_tag, (tag, consumer_tag) - - self._flush_output(ok_result.is_ready) - except Exception: - # If channel was closed, self._consumer_infos will be empty - if consumer_tag in self._consumer_infos: - del self._consumer_infos[consumer_tag] - raise - - # NOTE: Consumer could get cancelled by broker immediately after opening - # (e.g., queue getting deleted externally) - if self._consumer_infos[consumer_tag].setting_up: - self._consumer_infos[consumer_tag].state = _ConsumerInfo.ACTIVE - - return consumer_tag - - def basic_cancel(self, consumer_tag): - """This method cancels a consumer. This does not affect already - delivered messages, but it does mean the server will not send any more - messages for that consumer. The client may receive an arbitrary number - of messages in between sending the cancel method and receiving the - cancel-ok reply. - - NOTE: When cancelling a no_ack=False consumer, this implementation - automatically Nacks and suppresses any incoming messages that have not - yet been dispatched to the consumer's callback. However, when cancelling - a no_ack=True consumer, this method will return any pending messages - that arrived before broker confirmed the cancellation. - - :param str consumer_tag: Identifier for the consumer; the result of - passing a consumer_tag that was created on another channel is - undefined (bad things will happen) - - :returns: (NEW IN pika 0.10.0) empty sequence for a no_ack=False - consumer; for a no_ack=True consumer, returns a (possibly empty) - sequence of pending messages that arrived before broker confirmed - the cancellation (this is done instead of via consumer's callback in - order to prevent reentrancy/recursion. Each message is four-tuple: - (channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - """ - try: - consumer_info = self._consumer_infos[consumer_tag] - except KeyError: - LOGGER.warn("User is attempting to cancel an unknown consumer=%s; " - "already cancelled by user or broker?", consumer_tag) - return [] - - try: - # Assertion failure here is most likely due to reentrance - assert consumer_info.active or consumer_info.cancelled_by_broker, ( - consumer_info.state) - - # Assertion failure here signals disconnect between consumer state - # in BlockingConnection and Connection - assert (consumer_info.cancelled_by_broker or - consumer_tag in self._impl._consumers), consumer_tag - - no_ack = consumer_info.no_ack - - consumer_info.state = _ConsumerInfo.TEARING_DOWN - - with _CallbackResult() as cancel_ok_result: - # Nack pending messages for no_ack=False consumer - if not no_ack: - pending_messages = self._remove_pending_deliveries( - consumer_tag) - if pending_messages: - # NOTE: we use impl's basic_reject to avoid the - # possibility of redelivery before basic_cancel takes - # control of nacking. - # NOTE: we can't use basic_nack with the multiple option - # to avoid nacking messages already held by our client. - for message in pending_messages: - self._impl.basic_reject(message.method.delivery_tag, - requeue=True) - - # Cancel the consumer; impl takes care of rejecting any - # additional deliveries that arrive for a no_ack=False - # consumer - self._impl.basic_cancel( - callback=cancel_ok_result.signal_once, - consumer_tag=consumer_tag, - nowait=False) - - # Flush output and wait for Basic.Cancel-ok or - # broker-initiated Basic.Cancel - self._flush_output( - cancel_ok_result.is_ready, - lambda: consumer_tag not in self._impl._consumers) - - if no_ack: - # Return pending messages for no_ack=True consumer - return [ - (evt.method, evt.properties, evt.body) - for evt in self._remove_pending_deliveries(consumer_tag)] - else: - # impl takes care of rejecting any incoming deliveries during - # cancellation - messages = self._remove_pending_deliveries(consumer_tag) - assert not messages, messages - - return [] - finally: - # NOTE: The entry could be purged if channel or connection closes - if consumer_tag in self._consumer_infos: - del self._consumer_infos[consumer_tag] - - def _remove_pending_deliveries(self, consumer_tag): - """Extract _ConsumerDeliveryEvt objects destined for the given consumer - from pending events, discarding the _ConsumerCancellationEvt, if any - - :param str consumer_tag: - - :returns: a (possibly empty) sequence of _ConsumerDeliveryEvt destined - for the given consumer tag - """ - remaining_events = deque() - unprocessed_messages = [] - while self._pending_events: - evt = self._pending_events.popleft() - if type(evt) is _ConsumerDeliveryEvt: - if evt.method.consumer_tag == consumer_tag: - unprocessed_messages.append(evt) - continue - if type(evt) is _ConsumerCancellationEvt: - if evt.method_frame.method.consumer_tag == consumer_tag: - # A broker-initiated Basic.Cancel must have arrived - # before our cancel request completed - continue - - remaining_events.append(evt) - - self._pending_events = remaining_events - - return unprocessed_messages - - def start_consuming(self): - """Processes I/O events and dispatches timers and `basic_consume` - callbacks until all consumers are cancelled. - - NOTE: this blocking function may not be called from the scope of a - pika callback, because dispatching `basic_consume` callbacks from this - context would constitute recursion. - - :raises pika.exceptions.RecursionError: if called from the scope of a - `BlockingConnection` or `BlockingChannel` callback - - """ - # Check if called from the scope of an event dispatch callback - with self.connection._acquire_event_dispatch() as dispatch_allowed: - if not dispatch_allowed: - raise exceptions.RecursionError( - 'start_consuming may not be called from the scope of ' - 'another BlockingConnection or BlockingChannel callback') - - # Process events as long as consumers exist on this channel - while self._consumer_infos: - self.connection.process_data_events(time_limit=None) - - def stop_consuming(self, consumer_tag=None): - """ Cancels all consumers, signalling the `start_consuming` loop to - exit. - - NOTE: pending non-ackable messages will be lost; pending ackable - messages will be rejected. - - """ - if consumer_tag: - self.basic_cancel(consumer_tag) - else: - self._cancel_all_consumers() - - def consume(self, queue, no_ack=False, # pylint: disable=R0913 - exclusive=False, arguments=None, - inactivity_timeout=None): - """Blocking consumption of a queue instead of via a callback. This - method is a generator that yields each message as a tuple of method, - properties, and body. The active generator iterator terminates when the - consumer is cancelled by client or broker. - - Example: - - for method, properties, body in channel.consume('queue'): - print body - channel.basic_ack(method.delivery_tag) - - You should call `BlockingChannel.cancel()` when you escape out of the - generator loop. - - If you don't cancel this consumer, then next call on the same channel - to `consume()` with the exact same (queue, no_ack, exclusive) parameters - will resume the existing consumer generator; however, calling with - different parameters will result in an exception. - - :param queue: The queue name to consume - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a ack/nack response - :param bool exclusive: Don't allow other consumers on the queue - :param dict arguments: Custom key/value pair arguments for the consumer - :param float inactivity_timeout: if a number is given (in - seconds), will cause the method to yield None after the given period - of inactivity; this permits for pseudo-regular maintenance - activities to be carried out by the user while waiting for messages - to arrive. If None is given (default), then the method blocks until - the next event arrives. NOTE that timing granularity is limited by - the timer resolution of the underlying implementation. - NEW in pika 0.10.0. - - :yields: tuple(spec.Basic.Deliver, spec.BasicProperties, str or unicode) - - :raises ValueError: if consumer-creation parameters don't match those - of the existing queue consumer generator, if any. - NEW in pika 0.10.0 - """ - params = (queue, no_ack, exclusive) - - if self._queue_consumer_generator is not None: - if params != self._queue_consumer_generator.params: - raise ValueError( - 'Consume with different params not allowed on existing ' - 'queue consumer generator; previous params: %r; ' - 'new params: %r' - % (self._queue_consumer_generator.params, - (queue, no_ack, exclusive))) - else: - LOGGER.debug('Creating new queue consumer generator; params: %r', - params) - # Need a consumer tag to register consumer info before sending - # request to broker, because I/O might pick up incoming messages - # in addition to Basic.Consume-ok - consumer_tag = self._impl._generate_consumer_tag() - - self._queue_consumer_generator = _QueueConsumerGeneratorInfo( - params, - consumer_tag) - - try: - self._basic_consume_impl( - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments, - alternate_event_sink=self._on_consumer_generator_event) - except Exception: - self._queue_consumer_generator = None - raise - - LOGGER.info('Created new queue consumer generator %r', - self._queue_consumer_generator) - - while self._queue_consumer_generator is not None: - if self._queue_consumer_generator.pending_events: - evt = self._queue_consumer_generator.pending_events.popleft() - if type(evt) is _ConsumerCancellationEvt: - # Consumer was cancelled by broker - self._queue_consumer_generator = None - break - else: - yield (evt.method, evt.properties, evt.body) - continue - - # Wait for a message to arrive - if inactivity_timeout is None: - self.connection.process_data_events(time_limit=None) - continue - - # Wait with inactivity timeout - wait_start_time = time.time() - wait_deadline = wait_start_time + inactivity_timeout - delta = inactivity_timeout - - while (self._queue_consumer_generator is not None and - not self._queue_consumer_generator.pending_events): - self.connection.process_data_events(time_limit=delta) - - if not self._queue_consumer_generator: - # Consumer was cancelled by client - break - - if self._queue_consumer_generator.pending_events: - # Got message(s) - break - - delta = wait_deadline - time.time() - if delta <= 0.0: - # Signal inactivity timeout - yield None - break - - def get_waiting_message_count(self): - """Returns the number of messages that may be retrieved from the current - queue consumer generator via `BasicChannel.consume` without blocking. - NEW in pika 0.10.0 - - :rtype: int - """ - if self._queue_consumer_generator is not None: - pending_events = self._queue_consumer_generator.pending_events - count = len(pending_events) - if count and type(pending_events[-1]) is _ConsumerCancellationEvt: - count -= 1 - else: - count = 0 - - return count - - def cancel(self): - """Cancel the queue consumer created by `BlockingChannel.consume`, - rejecting all pending ackable messages. - - NOTE: If you're looking to cancel a consumer issued with - BlockingChannel.basic_consume then you should call - BlockingChannel.basic_cancel. - - :return int: The number of messages requeued by Basic.Nack. - NEW in 0.10.0: returns 0 - - """ - if self._queue_consumer_generator is None: - LOGGER.warning('cancel: queue consumer generator is inactive ' - '(already cancelled by client or broker?)') - return 0 - - try: - _, no_ack, _ = self._queue_consumer_generator.params - if not no_ack: - # Reject messages held by queue consumer generator; NOTE: we - # can't use basic_nack with the multiple option to avoid nacking - # messages already held by our client. - pending_events = self._queue_consumer_generator.pending_events - for _ in compat.xrange(self.get_waiting_message_count()): - evt = pending_events.popleft() - self._impl.basic_reject(evt.method.delivery_tag, - requeue=True) - - self.basic_cancel(self._queue_consumer_generator.consumer_tag) - finally: - self._queue_consumer_generator = None - - # Return 0 for compatibility with legacy implementation; the number of - # nacked messages is not meaningful since only messages consumed with - # no_ack=False may be nacked, and those arriving after calling - # basic_cancel will be rejected automatically by impl channel, so we'll - # never know how many of those were nacked. - return 0 - - def basic_ack(self, delivery_tag=0, multiple=False): - """Acknowledge one or more messages. When sent by the client, this - method acknowledges one or more messages delivered via the Deliver or - Get-Ok methods. When sent by server, this method acknowledges one or - more messages published with the Publish method on a channel in - confirm mode. The acknowledgement can be for a single message or a - set of messages up to and including a specific message. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - """ - self._impl.basic_ack(delivery_tag=delivery_tag, multiple=multiple) - self._flush_output() - - def basic_nack(self, delivery_tag=None, multiple=False, requeue=True): - """This method allows a client to reject one or more incoming messages. - It can be used to interrupt and cancel large incoming messages, or - return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - self._impl.basic_nack(delivery_tag=delivery_tag, multiple=multiple, - requeue=requeue) - self._flush_output() - - def basic_get(self, queue=None, no_ack=False): - """Get a single message from the AMQP broker. Returns a sequence with - the method frame, message properties, and body. - - :param queue: Name of queue to get a message from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a reply - :returns: a three-tuple; (None, None, None) if the queue was empty; - otherwise (method, properties, body); NOTE: body may be None - :rtype: (None, None, None)|(spec.Basic.GetOk, - spec.BasicProperties, - str or unicode or None) - """ - assert not self._basic_getempty_result - # NOTE: nested with for python 2.6 compatibility - with _CallbackResult(self._RxMessageArgs) as get_ok_result: - with self._basic_getempty_result: - self._impl.basic_get(callback=get_ok_result.set_value_once, - queue=queue, - no_ack=no_ack) - self._flush_output(get_ok_result.is_ready, - self._basic_getempty_result.is_ready) - if get_ok_result: - evt = get_ok_result.value - return (evt.method, evt.properties, evt.body) - else: - assert self._basic_getempty_result, ( - "wait completed without GetOk and GetEmpty") - return None, None, None - - def basic_publish(self, exchange, routing_key, body, # pylint: disable=R0913 - properties=None, mandatory=False, immediate=False): - """Publish to the channel with the given exchange, routing key and body. - Returns a boolean value indicating the success of the operation. - - This is the legacy BlockingChannel method for publishing. See also - `BasicChannel.publish` that provides more information about failures. - - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - NOTE: mandatory and immediate may be enabled even without delivery - confirmation, but in the absence of delivery confirmation the - synchronous implementation has no way to know how long to wait for - the Basic.Return or lack thereof. - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body; empty string if no body - :type body: str or unicode - :param pika.spec.BasicProperties properties: message properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - :returns: True if delivery confirmation is not enabled (NEW in pika - 0.10.0); otherwise returns False if the message could not be - deliveved (Basic.nack and/or Basic.Return) and True if the message - was delivered (Basic.ack and no Basic.Return) - """ - try: - self.publish(exchange, routing_key, body, properties, - mandatory, immediate) - except (exceptions.NackError, exceptions.UnroutableError): - return False - else: - return True - - def publish(self, exchange, routing_key, body, # pylint: disable=R0913 - properties=None, mandatory=False, immediate=False): - """Publish to the channel with the given exchange, routing key, and - body. Unlike the legacy `BlockingChannel.basic_publish`, this method - provides more information about failures via exceptions. - - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - NOTE: mandatory and immediate may be enabled even without delivery - confirmation, but in the absence of delivery confirmation the - synchronous implementation has no way to know how long to wait for - the Basic.Return. - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body; empty string if no body - :type body: str or unicode - :param pika.spec.BasicProperties properties: message properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - :raises UnroutableError: raised when a message published in - publisher-acknowledgments mode (see - `BlockingChannel.confirm_delivery`) is returned via `Basic.Return` - followed by `Basic.Ack`. - :raises NackError: raised when a message published in - publisher-acknowledgements mode is Nack'ed by the broker. See - `BlockingChannel.confirm_delivery`. - - """ - if self._delivery_confirmation: - # In publisher-acknowledgments mode - with self._message_confirmation_result: - self._impl.basic_publish(exchange=exchange, - routing_key=routing_key, - body=body, - properties=properties, - mandatory=mandatory, - immediate=immediate) - - self._flush_output(self._message_confirmation_result.is_ready) - conf_method = (self._message_confirmation_result.value - .method_frame - .method) - - if isinstance(conf_method, pika.spec.Basic.Nack): - # Broker was unable to process the message due to internal - # error - LOGGER.warn( - "Message was Nack'ed by broker: nack=%r; channel=%s; " - "exchange=%s; routing_key=%s; mandatory=%r; " - "immediate=%r", conf_method, self.channel_number, - exchange, routing_key, mandatory, immediate) - if self._puback_return is not None: - returned_messages = [self._puback_return] - self._puback_return = None - else: - returned_messages = [] - raise exceptions.NackError(returned_messages) - - else: - assert isinstance(conf_method, pika.spec.Basic.Ack), ( - conf_method) - - if self._puback_return is not None: - # Unroutable message was returned - messages = [self._puback_return] - self._puback_return = None - raise exceptions.UnroutableError(messages) - else: - # In non-publisher-acknowledgments mode - self._impl.basic_publish(exchange=exchange, - routing_key=routing_key, - body=body, - properties=properties, - mandatory=mandatory, - immediate=immediate) - self._flush_output() - - def basic_qos(self, prefetch_size=0, prefetch_count=0, all_channels=False): - """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 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. - - :param int prefetch_size: This field specifies the prefetch window - size. 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 in the consumer. - :param int prefetch_count: 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 in the consumer. - :param bool all_channels: Should the QoS apply to all channels - - """ - with _CallbackResult() as qos_ok_result: - self._impl.basic_qos(callback=qos_ok_result.signal_once, - prefetch_size=prefetch_size, - prefetch_count=prefetch_count, - all_channels=all_channels) - self._flush_output(qos_ok_result.is_ready) - - def basic_recover(self, requeue=False): - """This method asks the server to redeliver all unacknowledged messages - on a specified channel. Zero or more messages may be redelivered. This - method replaces the asynchronous Recover. - - :param bool requeue: If False, the message will be redelivered to the - original recipient. If True, the server will - attempt to requeue the message, potentially then - delivering it to an alternative subscriber. - - """ - with _CallbackResult() as recover_ok_result: - self._impl.basic_recover(callback=recover_ok_result.signal_once, - requeue=requeue) - self._flush_output(recover_ok_result.is_ready) - - def basic_reject(self, delivery_tag=None, requeue=True): - """Reject an incoming message. This method allows a client to reject a - message. It can be used to interrupt and cancel large incoming messages, - or return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - self._impl.basic_reject(delivery_tag=delivery_tag, requeue=requeue) - self._flush_output() - - def confirm_delivery(self): - """Turn on RabbitMQ-proprietary Confirm mode in the channel. - - For more information see: - http://www.rabbitmq.com/extensions.html#confirms - """ - if self._delivery_confirmation: - LOGGER.error('confirm_delivery: confirmation was already enabled ' - 'on channel=%s', self.channel_number) - return - - with _CallbackResult() as select_ok_result: - self._impl.add_callback(callback=select_ok_result.signal_once, - replies=[pika.spec.Confirm.SelectOk], - one_shot=True) - - self._impl.confirm_delivery( - callback=self._message_confirmation_result.set_value_once, - nowait=False) - - self._flush_output(select_ok_result.is_ready) - - self._delivery_confirmation = True - - # Unroutable messages returned after this point will be in the context - # of publisher acknowledgments - self._impl.add_on_return_callback(self._on_puback_message_returned) - - def exchange_declare(self, exchange=None, # pylint: disable=R0913 - exchange_type='direct', passive=False, durable=False, - auto_delete=False, internal=False, - arguments=None, **kwargs): - """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. - - If passive set, the server will reply with Declare-Ok if the exchange - already exists with the same name, and raise an error if not and if the - exchange does not already exist, the server MUST raise a channel - exception with reply code 404 (not found). - - :param exchange: The exchange name consists of a non-empty sequence of - these characters: letters, digits, hyphen, underscore, - period, or colon. - :type exchange: str or unicode - :param str exchange_type: The exchange type to use - :param bool passive: Perform a declare or just check to see if it exists - :param bool durable: Survive a reboot of RabbitMQ - :param bool auto_delete: Remove when no more queues are bound to it - :param bool internal: Can only be published to by other exchanges - :param dict arguments: Custom key/value pair arguments for the exchange - :param str type: via kwargs: the deprecated exchange type parameter - - :returns: Method frame from the Exchange.Declare-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.DeclareOk` - - """ - assert len(kwargs) <= 1, kwargs - - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as declare_ok_result: - self._impl.exchange_declare( - callback=declare_ok_result.set_value_once, - exchange=exchange, - exchange_type=exchange_type, - passive=passive, - durable=durable, - auto_delete=auto_delete, - internal=internal, - nowait=False, - arguments=arguments, - type=kwargs["type"] if kwargs else None) - - self._flush_output(declare_ok_result.is_ready) - return declare_ok_result.value.method_frame - - def exchange_delete(self, exchange=None, if_unused=False): - """Delete the exchange. - - :param exchange: The exchange name - :type exchange: str or unicode - :param bool if_unused: only delete if the exchange is unused - - :returns: Method frame from the Exchange.Delete-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.DeleteOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as delete_ok_result: - self._impl.exchange_delete( - callback=delete_ok_result.set_value_once, - exchange=exchange, - if_unused=if_unused, - nowait=False) - - self._flush_output(delete_ok_result.is_ready) - return delete_ok_result.value.method_frame - - def exchange_bind(self, destination=None, source=None, routing_key='', - arguments=None): - """Bind an exchange to another exchange. - - :param destination: The destination exchange to bind - :type destination: str or unicode - :param source: The source exchange to bind to - :type source: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Exchange.Bind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.BindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as bind_ok_result: - self._impl.exchange_bind( - callback=bind_ok_result.set_value_once, - destination=destination, - source=source, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(bind_ok_result.is_ready) - return bind_ok_result.value.method_frame - - def exchange_unbind(self, destination=None, source=None, routing_key='', - arguments=None): - """Unbind an exchange from another exchange. - - :param destination: The destination exchange to unbind - :type destination: str or unicode - :param source: The source exchange to unbind from - :type source: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Exchange.Unbind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.UnbindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as unbind_ok_result: - self._impl.exchange_unbind( - callback=unbind_ok_result.set_value_once, - destination=destination, - source=source, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(unbind_ok_result.is_ready) - return unbind_ok_result.value.method_frame - - def queue_declare(self, queue='', passive=False, durable=False, # pylint: disable=R0913 - exclusive=False, auto_delete=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. - - Leave the queue name empty for a auto-named queue in RabbitMQ - - :param queue: The queue name - :type queue: str or unicode; if empty string, the broker will create a - unique queue name; - :param bool passive: Only check to see if the queue exists - :param bool durable: Survive reboots of the broker - :param bool exclusive: Only allow access by the current connection - :param bool auto_delete: Delete after consumer cancels or disconnects - :param dict arguments: Custom key/value arguments for the queue - - :returns: Method frame from the Queue.Declare-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.DeclareOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as declare_ok_result: - self._impl.queue_declare( - callback=declare_ok_result.set_value_once, - queue=queue, - passive=passive, - durable=durable, - exclusive=exclusive, - auto_delete=auto_delete, - nowait=False, - arguments=arguments) - - self._flush_output(declare_ok_result.is_ready) - return declare_ok_result.value.method_frame - - def queue_delete(self, queue='', if_unused=False, if_empty=False): - """Delete a queue from the broker. - - :param queue: The queue to delete - :type queue: str or unicode - :param bool if_unused: only delete if it's unused - :param bool if_empty: only delete if the queue is empty - - :returns: Method frame from the Queue.Delete-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.DeleteOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as delete_ok_result: - self._impl.queue_delete(callback=delete_ok_result.set_value_once, - queue=queue, - if_unused=if_unused, - if_empty=if_empty, - nowait=False) - - self._flush_output(delete_ok_result.is_ready) - return delete_ok_result.value.method_frame - - def queue_purge(self, queue=''): - """Purge all of the messages from the specified queue - - :param queue: The queue to purge - :type queue: str or unicode - - :returns: Method frame from the Queue.Purge-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.PurgeOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as purge_ok_result: - self._impl.queue_purge(callback=purge_ok_result.set_value_once, - queue=queue, - nowait=False) - - self._flush_output(purge_ok_result.is_ready) - return purge_ok_result.value.method_frame - - def queue_bind(self, queue, exchange, routing_key=None, - arguments=None): - """Bind the queue to the specified exchange - - :param queue: The queue to bind to the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Queue.Bind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.BindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as bind_ok_result: - self._impl.queue_bind(callback=bind_ok_result.set_value_once, - queue=queue, - exchange=exchange, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(bind_ok_result.is_ready) - return bind_ok_result.value.method_frame - - def queue_unbind(self, queue='', exchange=None, routing_key=None, - arguments=None): - """Unbind a queue from an exchange. - - :param queue: The queue to unbind from the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind from - :type exchange: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Queue.Unbind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.UnbindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as unbind_ok_result: - self._impl.queue_unbind(callback=unbind_ok_result.set_value_once, - queue=queue, - exchange=exchange, - routing_key=routing_key, - arguments=arguments) - self._flush_output(unbind_ok_result.is_ready) - return unbind_ok_result.value.method_frame - - 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. - - :returns: Method frame from the Tx.Select-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.SelectOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as select_ok_result: - self._impl.tx_select(select_ok_result.set_value_once) - - self._flush_output(select_ok_result.is_ready) - return select_ok_result.value.method_frame - - def tx_commit(self): - """Commit a transaction. - - :returns: Method frame from the Tx.Commit-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.CommitOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as commit_ok_result: - self._impl.tx_commit(commit_ok_result.set_value_once) - - self._flush_output(commit_ok_result.is_ready) - return commit_ok_result.value.method_frame - - def tx_rollback(self): - """Rollback a transaction. - - :returns: Method frame from the Tx.Commit-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.CommitOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as rollback_ok_result: - self._impl.tx_rollback(rollback_ok_result.set_value_once) - - self._flush_output(rollback_ok_result.is_ready) - return rollback_ok_result.value.method_frame diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/libev_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/libev_connection.py deleted file mode 100644 index 232fd572..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/libev_connection.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Use pika with the libev IOLoop via pyev""" -import pyev -import signal -import array -import logging -import warnings -from collections import deque - -from pika.adapters.base_connection import BaseConnection - -LOGGER = logging.getLogger(__name__) - -global_sigint_watcher, global_sigterm_watcher = None, None - - -class LibevConnection(BaseConnection): - """The LibevConnection runs on the libev IOLoop. If you're running the - connection in a web app, make sure you set stop_ioloop_on_close to False, - which is the default behavior for this adapter, otherwise the web app - will stop taking requests. - - You should be familiar with pyev and libev to use this adapter, esp. - with regard to the use of libev ioloops. - - If an on_signal_callback method is provided, the adapter creates signal - watchers the first time; subsequent instantiations with a provided method - reuse the same watchers but will call the new method upon receiving a - signal. See pyev/libev signal handling to understand why this is done. - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection can't - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the default_loop in libev - :param on_signal_callback: Method to call if SIGINT or SIGTERM occur - :type on_signal_callback: method - - """ - WARN_ABOUT_IOLOOP = True - - # use static arrays to translate masks between pika and libev - _PIKA_TO_LIBEV_ARRAY = array.array('i', [0] * ( - (BaseConnection.READ | BaseConnection.WRITE | BaseConnection.ERROR) + 1 - )) - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ] = pyev.EV_READ - _PIKA_TO_LIBEV_ARRAY[BaseConnection.WRITE] = pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | - BaseConnection.WRITE] = pyev.EV_READ | pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | - BaseConnection.ERROR] = pyev.EV_READ - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.WRITE | - BaseConnection.ERROR] = pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | BaseConnection.WRITE | - BaseConnection.ERROR] = pyev.EV_READ | pyev.EV_WRITE - - _LIBEV_TO_PIKA_ARRAY = array.array('i', [0] * - ((pyev.EV_READ | pyev.EV_WRITE) + 1)) - - _LIBEV_TO_PIKA_ARRAY[pyev.EV_READ] = BaseConnection.READ - _LIBEV_TO_PIKA_ARRAY[pyev.EV_WRITE] = BaseConnection.WRITE - - _LIBEV_TO_PIKA_ARRAY[pyev.EV_READ | pyev.EV_WRITE] = \ - BaseConnection.READ | BaseConnection.WRITE - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False, - custom_ioloop=None, - on_signal_callback=None): - """Create a new instance of the LibevConnection class, connecting - to RabbitMQ automatically - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cannot - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the default IOLoop in libev - :param on_signal_callback: Method to call if SIGINT or SIGTERM occur - :type on_signal_callback: method - - """ - if custom_ioloop: - self.ioloop = custom_ioloop - else: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", RuntimeWarning) - self.ioloop = pyev.default_loop() - - self.async = None - self._on_signal_callback = on_signal_callback - self._io_watcher = None - self._active_timers = {} - self._stopped_timers = deque() - - super(LibevConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, self.ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the remote socket, adding the socket to the IOLoop if - connected - - :rtype: bool - - """ - LOGGER.debug('init io and signal watchers if any') - # reuse existing signal watchers, can only be declared for 1 ioloop - global global_sigint_watcher, global_sigterm_watcher - error = super(LibevConnection, self)._adapter_connect() - - if not error: - if self._on_signal_callback and not global_sigterm_watcher: - global_sigterm_watcher = \ - self.ioloop.signal(signal.SIGTERM, - self._handle_sigterm) - - if self._on_signal_callback and not global_sigint_watcher: - global_sigint_watcher = self.ioloop.signal(signal.SIGINT, - self._handle_sigint) - - if not self._io_watcher: - self._io_watcher = \ - self.ioloop.io(self.socket.fileno(), - self._PIKA_TO_LIBEV_ARRAY[self.event_state], - self._handle_events) - - self.async = pyev.Async(self.ioloop, self._noop_callable) - self.async.start() - if self._on_signal_callback: - global_sigterm_watcher.start() - if self._on_signal_callback: - global_sigint_watcher.start() - self._io_watcher.start() - - return error - - def _noop_callable(self, *args, **kwargs): - pass - - def _init_connection_state(self): - """Initialize or reset all of our internal state variables for a given - connection. If we disconnect and reconnect, all of our state needs to - be wiped. - - """ - for timer in self._active_timers.keys(): - self.remove_timeout(timer) - if global_sigint_watcher: - global_sigint_watcher.stop() - if global_sigterm_watcher: - global_sigterm_watcher.stop() - if self._io_watcher: - self._io_watcher.stop() - super(LibevConnection, self)._init_connection_state() - - def _handle_sigint(self, signal_watcher, libev_events): - """If an on_signal_callback has been defined, call it returning the - string 'SIGINT'. - - """ - LOGGER.debug('SIGINT') - self._on_signal_callback('SIGINT') - - def _handle_sigterm(self, signal_watcher, libev_events): - """If an on_signal_callback has been defined, call it returning the - string 'SIGTERM'. - - """ - LOGGER.debug('SIGTERM') - self._on_signal_callback('SIGTERM') - - def _handle_events(self, io_watcher, libev_events, **kwargs): - """Handle IO events by efficiently translating to BaseConnection - events and calling super. - - """ - super(LibevConnection, - self)._handle_events(io_watcher.fd, - self._LIBEV_TO_PIKA_ARRAY[libev_events], - **kwargs) - - def _reset_io_watcher(self): - """Reset the IO watcher; retry as necessary - - """ - self._io_watcher.stop() - - retries = 0 - while True: - try: - self._io_watcher.set( - self._io_watcher.fd, - self._PIKA_TO_LIBEV_ARRAY[self.event_state]) - - break - except: # sometimes the stop() doesn't complete in time - if retries > 5: raise - self._io_watcher.stop() # so try it again - retries += 1 - - self._io_watcher.start() - - def _manage_event_state(self): - """Manage the bitmask for reading/writing/error which is used by the - io/event handler to specify when there is an event such as a read or - write. - - """ - if self.outbound_buffer: - if not self.event_state & self.WRITE: - self.event_state |= self.WRITE - self._reset_io_watcher() - elif self.event_state & self.WRITE: - self.event_state = self.base_events - self._reset_io_watcher() - - def _timer_callback(self, timer, libev_events): - """Manage timer callbacks indirectly.""" - if timer in self._active_timers: - (callback_method, callback_timeout, - kwargs) = self._active_timers[timer] - - if callback_timeout: - callback_method(timeout=timer, **kwargs) - else: - callback_method(**kwargs) - - self.remove_timeout(timer) - else: - LOGGER.warning('Timer callback_method not found') - - def _get_timer(self, deadline): - """Get a timer from the pool or allocate a new one.""" - if self._stopped_timers: - timer = self._stopped_timers.pop() - timer.set(deadline, 0.0) - else: - timer = self.ioloop.timer(deadline, 0.0, self._timer_callback) - - return timer - - def add_timeout(self, deadline, callback_method, - callback_timeout=False, **callback_kwargs): - """Add the callback_method indirectly to the IOLoop timer to fire - after deadline seconds. Returns the timer handle. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :param callback_timeout: Whether timeout kwarg is passed on callback - :type callback_timeout: boolean - :param kwargs callback_kwargs: additional kwargs to pass on callback - :rtype: timer instance handle. - - """ - LOGGER.debug('deadline: {0}'.format(deadline)) - timer = self._get_timer(deadline) - self._active_timers[timer] = (callback_method, callback_timeout, - callback_kwargs) - timer.start() - return timer - - def remove_timeout(self, timer): - """Remove the timer from the IOLoop using the handle returned from - add_timeout. - - param: timer instance handle - - """ - LOGGER.debug('stop') - self._active_timers.pop(timer, None) - timer.stop() - self._stopped_timers.append(timer) - - def _create_and_connect_to_socket(self, sock_addr_tuple): - """Call super and then set the socket to nonblocking.""" - result = super(LibevConnection, - self)._create_and_connect_to_socket(sock_addr_tuple) - if result: - self.socket.setblocking(0) - return result diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/select_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/select_connection.py deleted file mode 100644 index f217b234..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/select_connection.py +++ /dev/null @@ -1,613 +0,0 @@ -"""A connection adapter that tries to use the best polling method for the -platform pika is running on. - -""" -import os -import logging -import socket -import select -import errno -import time -from collections import defaultdict -import threading - -import pika.compat -from pika.compat import dictkeys - -from pika.adapters.base_connection import BaseConnection - -LOGGER = logging.getLogger(__name__) - -# One of select, epoll, kqueue or poll -SELECT_TYPE = None - -# Use epoll's constants to keep life easy -READ = 0x0001 -WRITE = 0x0004 -ERROR = 0x0008 - - -if pika.compat.PY2: - _SELECT_ERROR = select.error -else: - # select.error was deprecated and replaced by OSError in python 3.3 - _SELECT_ERROR = OSError - - -def _get_select_errno(error): - if pika.compat.PY2: - assert isinstance(error, select.error), repr(error) - return error.args[0] - else: - assert isinstance(error, OSError), repr(error) - return error.errno - - -class SelectConnection(BaseConnection): - """An asynchronous connection adapter that attempts to use the fastest - event loop adapter for the given platform. - - """ - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=True, - custom_ioloop=None): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Method to call on connection open - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param method on_close_callback: Method to call on connection close - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - :raises: RuntimeError - - """ - ioloop = custom_ioloop or IOLoop() - super(SelectConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker, returning True on success, False - on failure. - - :rtype: bool - - """ - error = super(SelectConnection, self)._adapter_connect() - if not error: - self.ioloop.add_handler(self.socket.fileno(), self._handle_events, - self.event_state) - return error - - def _adapter_disconnect(self): - """Disconnect from the RabbitMQ broker""" - if self.socket: - self.ioloop.remove_handler(self.socket.fileno()) - super(SelectConnection, self)._adapter_disconnect() - - -class IOLoop(object): - """Singlton wrapper that decides which type of poller to use, creates an - instance of it in start_poller and keeps the invoking application in a - blocking state by calling the pollers start method. Poller should keep - looping until IOLoop.instance().stop() is called or there is a socket - error. - - Passes through all operations to the loaded poller object. - - """ - - def __init__(self): - self._poller = self._get_poller() - - def __getattr__(self, attr): - return getattr(self._poller, attr) - - def _get_poller(self): - """Determine the best poller to use for this enviroment.""" - - poller = None - - if hasattr(select, 'epoll'): - if not SELECT_TYPE or SELECT_TYPE == 'epoll': - LOGGER.debug('Using EPollPoller') - poller = EPollPoller() - - if not poller and hasattr(select, 'kqueue'): - if not SELECT_TYPE or SELECT_TYPE == 'kqueue': - LOGGER.debug('Using KQueuePoller') - poller = KQueuePoller() - - if (not poller and hasattr(select, 'poll') and - hasattr(select.poll(), 'modify')): # pylint: disable=E1101 - if not SELECT_TYPE or SELECT_TYPE == 'poll': - LOGGER.debug('Using PollPoller') - poller = PollPoller() - - if not poller: - LOGGER.debug('Using SelectPoller') - poller = SelectPoller() - - return poller - - -class SelectPoller(object): - """Default behavior is to use Select since it's the widest supported and has - all of the methods we need for child classes as well. One should only need - to override the update_handler and start methods for additional types. - - """ - # Drop out of the poll loop every POLL_TIMEOUT secs as a worst case, this - # is only a backstop value. We will run timeouts when they are scheduled. - POLL_TIMEOUT = 5 - # if the poller uses MS specify 1000 - POLL_TIMEOUT_MULT = 1 - - def __init__(self): - """Create an instance of the SelectPoller - - """ - # fd-to-handler function mappings - self._fd_handlers = dict() - - # event-to-fdset mappings - self._fd_events = {READ: set(), WRITE: set(), ERROR: set()} - - self._stopping = False - self._timeouts = {} - self._next_timeout = None - self._processing_fd_event_map = {} - - # Mutex for controlling critical sections where ioloop-interrupt sockets - # are created, used, and destroyed. Needed in case `stop()` is called - # from a thread. - self._mutex = threading.Lock() - - # ioloop-interrupt socket pair; initialized in start() - self._r_interrupt = None - self._w_interrupt = None - - def get_interrupt_pair(self): - """ Use a socketpair to be able to interrupt the ioloop if called - from another thread. Socketpair() is not supported on some OS (Win) - so use a pair of simple UDP sockets instead. The sockets will be - closed and garbage collected by python when the ioloop itself is. - """ - try: - read_sock, write_sock = socket.socketpair() - - except AttributeError: - LOGGER.debug("Using custom socketpair for interrupt") - read_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - read_sock.bind(('localhost', 0)) - write_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - write_sock.connect(read_sock.getsockname()) - - read_sock.setblocking(0) - write_sock.setblocking(0) - return read_sock, write_sock - - def read_interrupt(self, interrupt_sock, - events, write_only): # pylint: disable=W0613 - """ Read the interrupt byte(s). We ignore the event mask and write_only - flag as we can ony get here if there's data to be read on our fd. - - :param int interrupt_sock: The file descriptor to read from - :param int events: (unused) The events generated for this fd - :param bool write_only: (unused) True if poll was called to trigger a - write - """ - try: - os.read(interrupt_sock, 512) - except OSError as err: - if err.errno != errno.EAGAIN: - raise - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - timeout_at = time.time() + deadline - value = {'deadline': timeout_at, 'callback': callback_method} - timeout_id = hash(frozenset(value.items())) - self._timeouts[timeout_id] = value - - if not self._next_timeout or timeout_at < self._next_timeout: - self._next_timeout = timeout_at - - return timeout_id - - def remove_timeout(self, timeout_id): - """Remove a timeout if it's still in the timeout stack - - :param str timeout_id: The timeout id to remove - - """ - try: - timeout = self._timeouts.pop(timeout_id) - if timeout['deadline'] == self._next_timeout: - self._next_timeout = None - except KeyError: - pass - - def get_next_deadline(self): - """Get the interval to the next timeout event, or a default interval - """ - if self._next_timeout: - timeout = max((self._next_timeout - time.time(), 0)) - - elif self._timeouts: - deadlines = [t['deadline'] for t in self._timeouts.values()] - self._next_timeout = min(deadlines) - timeout = max((self._next_timeout - time.time(), 0)) - - else: - timeout = SelectPoller.POLL_TIMEOUT - - timeout = min((timeout, SelectPoller.POLL_TIMEOUT)) - return timeout * SelectPoller.POLL_TIMEOUT_MULT - - - def process_timeouts(self): - """Process the self._timeouts event stack""" - - now = time.time() - # Run the timeouts in order of deadlines. Although this shouldn't - # be strictly necessary it preserves old behaviour when timeouts - # were only run periodically. - to_run = sorted([(k, timer) for (k, timer) in self._timeouts.items() - if timer['deadline'] <= now], - key=lambda item: item[1]['deadline']) - - for k, timer in to_run: - if k not in self._timeouts: - # Previous invocation(s) should have deleted the timer. - continue - try: - timer['callback']() - finally: - # Don't do 'del self._timeout[k]' as the key might - # have been deleted just now. - if self._timeouts.pop(k, None) is not None: - self._next_timeout = None - - - def add_handler(self, fileno, handler, events): - """Add a new fileno to the set to be monitored - - :param int fileno: The file descriptor - :param method handler: What is called when an event happens - :param int events: The event mask - - """ - self._fd_handlers[fileno] = handler - self.update_handler(fileno, events) - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - - for ev in (READ, WRITE, ERROR): - if events & ev: - self._fd_events[ev].add(fileno) - else: - self._fd_events[ev].discard(fileno) - - - def remove_handler(self, fileno): - """Remove a file descriptor from the set - - :param int fileno: The file descriptor - - """ - try: - del self._processing_fd_event_map[fileno] - except KeyError: - pass - - self.update_handler(fileno, 0) - del self._fd_handlers[fileno] - - def start(self): - """Start the main poller loop. It will loop here until self._stopping""" - - LOGGER.debug('Starting IOLoop') - self._stopping = False - - with self._mutex: - # Watch out for reentry - if self._r_interrupt is None: - # Create ioloop-interrupt socket pair and register read handler. - # NOTE: we defer their creation because some users (e.g., - # BlockingConnection adapter) don't use the event loop and these - # sockets would get reported as leaks - self._r_interrupt, self._w_interrupt = self.get_interrupt_pair() - self.add_handler(self._r_interrupt.fileno(), - self.read_interrupt, - READ) - interrupt_sockets_created = True - else: - interrupt_sockets_created = False - try: - # Run event loop - while not self._stopping: - self.poll() - self.process_timeouts() - finally: - # Unregister and close ioloop-interrupt socket pair - if interrupt_sockets_created: - with self._mutex: - self.remove_handler(self._r_interrupt.fileno()) - self._r_interrupt.close() - self._r_interrupt = None - self._w_interrupt.close() - self._w_interrupt = None - - def stop(self): - """Request exit from the ioloop.""" - - LOGGER.debug('Stopping IOLoop') - self._stopping = True - - with self._mutex: - if self._w_interrupt is None: - return - - try: - # Send byte to interrupt the poll loop, use write() for - # consitency. - os.write(self._w_interrupt.fileno(), b'X') - except OSError as err: - if err.errno != errno.EWOULDBLOCK: - raise - except Exception as err: - # There's nothing sensible to do here, we'll exit the interrupt - # loop after POLL_TIMEOUT secs in worst case anyway. - LOGGER.warning("Failed to send ioloop interrupt: %s", err) - raise - - def poll(self, write_only=False): - """Wait for events on interested filedescriptors. - - :param bool write_only: Passed through to the hadnlers to indicate - that they should only process write events. - """ - while True: - try: - read, write, error = select.select(self._fd_events[READ], - self._fd_events[WRITE], - self._fd_events[ERROR], - self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - # Build an event bit mask for each fileno we've recieved an event for - - fd_event_map = defaultdict(int) - for fd_set, ev in zip((read, write, error), (READ, WRITE, ERROR)): - for fileno in fd_set: - fd_event_map[fileno] |= ev - - self._process_fd_events(fd_event_map, write_only) - - def _process_fd_events(self, fd_event_map, write_only): - """ Processes the callbacks for each fileno we've recieved events. - Before doing so we re-calculate the event mask based on what is - currently set in case it has been changed under our feet by a - previous callback. We also take a store a refernce to the - fd_event_map in the class so that we can detect removal of an - fileno during processing of another callback and not generate - spurious callbacks on it. - - :param dict fd_event_map: Map of fds to events recieved on them. - """ - - self._processing_fd_event_map = fd_event_map - - for fileno in dictkeys(fd_event_map): - if fileno not in fd_event_map: - # the fileno has been removed from the map under our feet. - continue - - events = fd_event_map[fileno] - for ev in [READ, WRITE, ERROR]: - if fileno not in self._fd_events[ev]: - events &= ~ev - - if events: - handler = self._fd_handlers[fileno] - handler(fileno, events, write_only=write_only) - -class KQueuePoller(SelectPoller): - """KQueuePoller works on BSD based systems and is faster than select""" - - def __init__(self): - """Create an instance of the KQueuePoller - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._kqueue = select.kqueue() - super(KQueuePoller, self).__init__() - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - - kevents = list() - if not events & READ: - if fileno in self._fd_events[READ]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_READ, - flags=select.KQ_EV_DELETE)) - else: - if fileno not in self._fd_events[READ]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_READ, - flags=select.KQ_EV_ADD)) - if not events & WRITE: - if fileno in self._fd_events[WRITE]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_WRITE, - flags=select.KQ_EV_DELETE)) - else: - if fileno not in self._fd_events[WRITE]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_WRITE, - flags=select.KQ_EV_ADD)) - for event in kevents: - self._kqueue.control([event], 0) - super(KQueuePoller, self).update_handler(fileno, events) - - def _map_event(self, kevent): - """return the event type associated with a kevent object - - :param kevent kevent: a kevent object as returned by kqueue.control() - - """ - if kevent.filter == select.KQ_FILTER_READ: - return READ - elif kevent.filter == select.KQ_FILTER_WRITE: - return WRITE - elif kevent.flags & select.KQ_EV_ERROR: - return ERROR - - def poll(self, write_only=False): - """Check to see if the events that are cared about have fired. - - :param bool write_only: Don't look at self.events, just look to see if - the adapter can write. - - """ - while True: - try: - kevents = self._kqueue.control(None, 1000, - self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - fd_event_map = defaultdict(int) - for event in kevents: - fileno = event.ident - fd_event_map[fileno] |= self._map_event(event) - - self._process_fd_events(fd_event_map, write_only) - - -class PollPoller(SelectPoller): - """Poll works on Linux and can have better performance than EPoll in - certain scenarios. Both are faster than select. - - """ - POLL_TIMEOUT_MULT = 1000 - - def __init__(self): - """Create an instance of the KQueuePoller - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._poll = self.create_poller() - super(PollPoller, self).__init__() - - def create_poller(self): - return select.poll() # pylint: disable=E1101 - - def add_handler(self, fileno, handler, events): - """Add a file descriptor to the poll set - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._poll.register(fileno, events) - super(PollPoller, self).add_handler(fileno, handler, events) - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - super(PollPoller, self).update_handler(fileno, events) - self._poll.modify(fileno, events) - - def remove_handler(self, fileno): - """Remove a fileno to the set - - :param int fileno: The file descriptor - - """ - super(PollPoller, self).remove_handler(fileno) - self._poll.unregister(fileno) - - def poll(self, write_only=False): - """Poll until the next timeout waiting for an event - - :param bool write_only: Only process write events - - """ - while True: - try: - events = self._poll.poll(self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - fd_event_map = defaultdict(int) - for fileno, event in events: - fd_event_map[fileno] |= event - - self._process_fd_events(fd_event_map, write_only) - - -class EPollPoller(PollPoller): - """EPoll works on Linux and can have better performance than Poll in - certain scenarios. Both are faster than select. - - """ - POLL_TIMEOUT_MULT = 1 - - def create_poller(self): - return select.epoll() # pylint: disable=E1101 diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/tornado_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/tornado_connection.py deleted file mode 100644 index 1c5c6078..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/tornado_connection.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Use pika with the Tornado IOLoop""" -from tornado import ioloop -import logging -import time - -from pika.adapters import base_connection - -LOGGER = logging.getLogger(__name__) - - -class TornadoConnection(base_connection.BaseConnection): - """The TornadoConnection runs on the Tornado IOLoop. If you're running the - connection in a web app, make sure you set stop_ioloop_on_close to False, - which is the default behavior for this adapter, otherwise the web app - will stop taking requests. - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - - """ - WARN_ABOUT_IOLOOP = True - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False, - custom_ioloop=None): - """Create a new instance of the TornadoConnection class, connecting - to RabbitMQ automatically - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - - """ - self.sleep_counter = 0 - self.ioloop = custom_ioloop or ioloop.IOLoop.instance() - super(TornadoConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, self.ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the remote socket, adding the socket to the IOLoop if - connected. - - :rtype: bool - - """ - error = super(TornadoConnection, self)._adapter_connect() - if not error: - self.ioloop.add_handler(self.socket.fileno(), self._handle_events, - self.event_state) - return error - - def _adapter_disconnect(self): - """Disconnect from the RabbitMQ broker""" - if self.socket: - self.ioloop.remove_handler(self.socket.fileno()) - super(TornadoConnection, self)._adapter_disconnect() - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - return self.ioloop.add_timeout(time.time() + deadline, callback_method) - - def remove_timeout(self, timeout_id): - """Remove the timeout from the IOLoop by the ID returned from - add_timeout. - - :rtype: str - - """ - return self.ioloop.remove_timeout(timeout_id) diff --git a/packages_o/pika-0.10.0/build/lib/pika/adapters/twisted_connection.py b/packages_o/pika-0.10.0/build/lib/pika/adapters/twisted_connection.py deleted file mode 100644 index 2ee65b26..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/adapters/twisted_connection.py +++ /dev/null @@ -1,451 +0,0 @@ -"""Using Pika with a Twisted reactor. - -Supports two methods of establishing the connection, using TwistedConnection -or TwistedProtocolConnection. For details about each method, see the docstrings -of the corresponding classes. - -The interfaces in this module are Deferred-based when possible. This means that -the connection.channel() method and most of the channel methods return -Deferreds instead of taking a callback argument and that basic_consume() -returns a Twisted DeferredQueue where messages from the server will be -stored. Refer to the docstrings for TwistedConnection.channel() and the -TwistedChannel class for details. - -""" -import functools -from twisted.internet import defer, error, reactor -from twisted.python import log - -from pika import exceptions -from pika.adapters import base_connection - - -class ClosableDeferredQueue(defer.DeferredQueue): - """ - Like the normal Twisted DeferredQueue, but after close() is called with an - Exception instance all pending Deferreds are errbacked and further attempts - to call get() or put() return a Failure wrapping that exception. - """ - - def __init__(self, size=None, backlog=None): - self.closed = None - super(ClosableDeferredQueue, self).__init__(size, backlog) - - def put(self, obj): - if self.closed: - return defer.fail(self.closed) - return defer.DeferredQueue.put(self, obj) - - def get(self): - if self.closed: - return defer.fail(self.closed) - return defer.DeferredQueue.get(self) - - def close(self, reason): - self.closed = reason - while self.waiting: - self.waiting.pop().errback(reason) - self.pending = [] - - -class TwistedChannel(object): - """A wrapper wround Pika's Channel. - - Channel methods that normally take a callback argument are wrapped to - return a Deferred that fires with whatever would be passed to the callback. - If the channel gets closed, all pending Deferreds are errbacked with a - ChannelClosed exception. The returned Deferreds fire with whatever - arguments the callback to the original method would receive. - - The basic_consume method is wrapped in a special way, see its docstring for - details. - """ - - WRAPPED_METHODS = ('exchange_declare', 'exchange_delete', 'queue_declare', - 'queue_bind', 'queue_purge', 'queue_unbind', 'basic_qos', - 'basic_get', 'basic_recover', 'tx_select', 'tx_commit', - 'tx_rollback', 'flow', 'basic_cancel') - - def __init__(self, channel): - self.__channel = channel - self.__closed = None - self.__calls = set() - self.__consumers = {} - - channel.add_on_close_callback(self.channel_closed) - - def channel_closed(self, channel, reply_code, reply_text): - # enter the closed state - self.__closed = exceptions.ChannelClosed(reply_code, reply_text) - # errback all pending calls - for d in self.__calls: - d.errback(self.__closed) - # close all open queues - for consumers in self.__consumers.values(): - for c in consumers: - c.close(self.__closed) - # release references to stored objects - self.__calls = set() - self.__consumers = {} - - def basic_consume(self, *args, **kwargs): - """Consume from a server queue. Returns a Deferred that fires with a - tuple: (queue_object, consumer_tag). The queue object is an instance of - ClosableDeferredQueue, where data received from the queue will be - stored. Clients should use its get() method to fetch individual - message. - """ - if self.__closed: - return defer.fail(self.__closed) - - queue = ClosableDeferredQueue() - queue_name = kwargs['queue'] - kwargs['consumer_callback'] = lambda *args: queue.put(args) - self.__consumers.setdefault(queue_name, set()).add(queue) - - try: - consumer_tag = self.__channel.basic_consume(*args, **kwargs) - except: - return defer.fail() - - return defer.succeed((queue, consumer_tag)) - - def queue_delete(self, *args, **kwargs): - """Wraps the method the same way all the others are wrapped, but removes - the reference to the queue object after it gets deleted on the server. - - """ - wrapped = self.__wrap_channel_method('queue_delete') - queue_name = kwargs['queue'] - - d = wrapped(*args, **kwargs) - return d.addCallback(self.__clear_consumer, queue_name) - - def basic_publish(self, *args, **kwargs): - """Make sure the channel is not closed and then publish. Return a - Deferred that fires with the result of the channel's basic_publish. - - """ - if self.__closed: - return defer.fail(self.__closed) - return defer.succeed(self.__channel.basic_publish(*args, **kwargs)) - - def __wrap_channel_method(self, name): - """Wrap Pika's Channel method to make it return a Deferred that fires - when the method completes and errbacks if the channel gets closed. If - the original method's callback would receive more than one argument, the - Deferred fires with a tuple of argument values. - - """ - method = getattr(self.__channel, name) - - @functools.wraps(method) - def wrapped(*args, **kwargs): - if self.__closed: - return defer.fail(self.__closed) - - d = defer.Deferred() - self.__calls.add(d) - d.addCallback(self.__clear_call, d) - - def single_argument(*args): - """ - Make sure that the deferred is called with a single argument. - In case the original callback fires with more than one, convert - to a tuple. - """ - if len(args) > 1: - d.callback(tuple(args)) - else: - d.callback(*args) - - kwargs['callback'] = single_argument - - try: - method(*args, **kwargs) - except: - return defer.fail() - return d - - return wrapped - - def __clear_consumer(self, ret, queue_name): - self.__consumers.pop(queue_name, None) - return ret - - def __clear_call(self, ret, d): - self.__calls.discard(d) - return ret - - def __getattr__(self, name): - # Wrap methods defined in WRAPPED_METHODS, forward the rest of accesses - # to the channel. - if name in self.WRAPPED_METHODS: - return self.__wrap_channel_method(name) - return getattr(self.__channel, name) - - -class IOLoopReactorAdapter(object): - """An adapter providing Pika's IOLoop interface using a Twisted reactor. - - Accepts a TwistedConnection object and a Twisted reactor object. - - """ - - def __init__(self, connection, reactor): - self.connection = connection - self.reactor = reactor - self.started = False - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: twisted.internet.interfaces.IDelayedCall - - """ - return self.reactor.callLater(deadline, callback_method) - - def remove_timeout(self, call): - """Remove a call - - :param twisted.internet.interfaces.IDelayedCall call: The call to cancel - - """ - call.cancel() - - def stop(self): - # Guard against stopping the reactor multiple times - if not self.started: - return - self.started = False - self.reactor.stop() - - def start(self): - # Guard against starting the reactor multiple times - if self.started: - return - self.started = True - self.reactor.run() - - def remove_handler(self, _): - # The fileno is irrelevant, as it's the connection's job to provide it - # to the reactor when asked to do so. Removing the handler from the - # ioloop is removing it from the reactor in Twisted's parlance. - self.reactor.removeReader(self.connection) - self.reactor.removeWriter(self.connection) - - def update_handler(self, _, event_state): - # Same as in remove_handler, the fileno is irrelevant. First remove the - # connection entirely from the reactor, then add it back depending on - # the event state. - self.reactor.removeReader(self.connection) - self.reactor.removeWriter(self.connection) - - if event_state & self.connection.READ: - self.reactor.addReader(self.connection) - - if event_state & self.connection.WRITE: - self.reactor.addWriter(self.connection) - - -class TwistedConnection(base_connection.BaseConnection): - """A standard Pika connection adapter. You instantiate the class passing the - connection parameters and the connected callback and when it gets called - you can start using it. - - The problem is that connection establishing is done using the blocking - socket module. For instance, if the host you are connecting to is behind a - misconfigured firewall that just drops packets, the whole process will - freeze until the connection timeout passes. To work around that problem, - use TwistedProtocolConnection, but read its docstring first. - - Objects of this class get put in the Twisted reactor which will notify them - when the socket connection becomes readable or writable, so apart from - implementing the BaseConnection interface, they also provide Twisted's - IReadWriteDescriptor interface. - - """ - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False): - super(TwistedConnection, self).__init__( - parameters=parameters, - on_open_callback=on_open_callback, - on_open_error_callback=on_open_error_callback, - on_close_callback=on_close_callback, - ioloop=IOLoopReactorAdapter(self, reactor), - stop_ioloop_on_close=stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker""" - # Connect (blockignly!) to the server - error = super(TwistedConnection, self)._adapter_connect() - if not error: - # Set the I/O events we're waiting for (see IOLoopReactorAdapter - # docstrings for why it's OK to pass None as the file descriptor) - self.ioloop.update_handler(None, self.event_state) - return error - - def _adapter_disconnect(self): - """Called when the adapter should disconnect""" - self.ioloop.remove_handler(None) - self._cleanup_socket() - - def _handle_disconnect(self): - """Do not stop the reactor, this would cause the entire process to exit, - just fire the disconnect callbacks - - """ - self._on_connection_closed(None, True) - - def _on_connected(self): - """Call superclass and then update the event state to flush the outgoing - frame out. Commit 50d842526d9f12d32ad9f3c4910ef60b8c301f59 removed a - self._flush_outbound call that was in _send_frame which previously - made this step unnecessary. - - """ - super(TwistedConnection, self)._on_connected() - self._manage_event_state() - - def channel(self, channel_number=None): - """Return a Deferred that fires with an instance of a wrapper around the - Pika Channel class. - - """ - d = defer.Deferred() - base_connection.BaseConnection.channel(self, d.callback, channel_number) - return d.addCallback(TwistedChannel) - - # IReadWriteDescriptor methods - - def fileno(self): - return self.socket.fileno() - - def logPrefix(self): - return "twisted-pika" - - def connectionLost(self, reason): - # If the connection was not closed cleanly, log the error - if not reason.check(error.ConnectionDone): - log.err(reason) - - self._handle_disconnect() - - def doRead(self): - self._handle_read() - - def doWrite(self): - self._handle_write() - self._manage_event_state() - - -class TwistedProtocolConnection(base_connection.BaseConnection): - """A hybrid between a Pika Connection and a Twisted Protocol. Allows using - Twisted's non-blocking connectTCP/connectSSL methods for connecting to the - server. - - It has one caveat: TwistedProtocolConnection objects have a ready - instance variable that's a Deferred which fires when the connection is - ready to be used (the initial AMQP handshaking has been done). You *have* - to wait for this Deferred to fire before requesting a channel. - - Since it's Twisted handling connection establishing it does not accept - connect callbacks, you have to implement that within Twisted. Also remember - that the host, port and ssl values of the connection parameters are ignored - because, yet again, it's Twisted who manages the connection. - - """ - - def __init__(self, parameters): - self.ready = defer.Deferred() - super(TwistedProtocolConnection, self).__init__( - parameters=parameters, - on_open_callback=self.connectionReady, - on_open_error_callback=self.connectionFailed, - on_close_callback=None, - ioloop=IOLoopReactorAdapter(self, reactor), - stop_ioloop_on_close=False) - - def connect(self): - # The connection is open asynchronously by Twisted, so skip the whole - # connect() part, except for setting the connection state - self._set_connection_state(self.CONNECTION_INIT) - - def _adapter_connect(self): - # Should never be called, as we override connect() and leave the - # building of a TCP connection to Twisted, but implement anyway to keep - # the interface - return False - - def _adapter_disconnect(self): - # Disconnect from the server - self.transport.loseConnection() - - def _flush_outbound(self): - """Override BaseConnection._flush_outbound to send all bufferred data - the Twisted way, by writing to the transport. No need for buffering, - Twisted handles that for us. - """ - while self.outbound_buffer: - self.transport.write(self.outbound_buffer.popleft()) - - def channel(self, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - Return a Deferred that fires with an instance of a wrapper around the - Pika Channel class. - - :param int channel_number: The channel number to use, defaults to the - next available. - - """ - d = defer.Deferred() - base_connection.BaseConnection.channel(self, d.callback, channel_number) - return d.addCallback(TwistedChannel) - - # IProtocol methods - - def dataReceived(self, data): - # Pass the bytes to Pika for parsing - self._on_data_available(data) - - def connectionLost(self, reason): - # Let the caller know there's been an error - d, self.ready = self.ready, None - if d: - d.errback(reason) - - def makeConnection(self, transport): - self.transport = transport - self.connectionMade() - - def connectionMade(self): - # Tell everyone we're connected - self._on_connected() - - # Our own methods - - def connectionReady(self, res): - d, self.ready = self.ready, None - if d: - d.callback(res) - - def connectionFailed(self, connection_unused, error_message=None): - d, self.ready = self.ready, None - if d: - attempts = self.params.connection_attempts - exc = exceptions.AMQPConnectionError(attempts) - d.errback(exc) diff --git a/packages_o/pika-0.10.0/build/lib/pika/amqp_object.py b/packages_o/pika-0.10.0/build/lib/pika/amqp_object.py deleted file mode 100644 index 576a2c41..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/amqp_object.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Base classes that are extended by low level AMQP frames and higher level -AMQP classes and methods. - -""" - - -class AMQPObject(object): - """Base object that is extended by AMQP low level frames and AMQP classes - and methods. - - """ - NAME = 'AMQPObject' - INDEX = None - - def __repr__(self): - items = list() - for key, value in self.__dict__.items(): - if getattr(self.__class__, key, None) != value: - items.append('%s=%s' % (key, value)) - if not items: - return "<%s>" % self.NAME - return "<%s(%s)>" % (self.NAME, sorted(items)) - - -class Class(AMQPObject): - """Is extended by AMQP classes""" - NAME = 'Unextended Class' - - -class Method(AMQPObject): - """Is extended by AMQP methods""" - NAME = 'Unextended Method' - synchronous = False - - def _set_content(self, properties, body): - """If the method is a content frame, set the properties and body to - be carried as attributes of the class. - - :param pika.frame.Properties properties: AMQP Basic Properties - :param body: The message body - :type body: str or unicode - - """ - self._properties = properties - self._body = body - - def get_properties(self): - """Return the properties if they are set. - - :rtype: pika.frame.Properties - - """ - return self._properties - - def get_body(self): - """Return the message body if it is set. - - :rtype: str|unicode - - """ - return self._body - - -class Properties(AMQPObject): - """Class to encompass message properties (AMQP Basic.Properties)""" - NAME = 'Unextended Properties' diff --git a/packages_o/pika-0.10.0/build/lib/pika/callback.py b/packages_o/pika-0.10.0/build/lib/pika/callback.py deleted file mode 100644 index 6ac58bd9..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/callback.py +++ /dev/null @@ -1,410 +0,0 @@ -"""Callback management class, common area for keeping track of all callbacks in -the Pika stack. - -""" -import functools -import logging - -from pika import frame -from pika import amqp_object -from pika.compat import xrange, canonical_str - -LOGGER = logging.getLogger(__name__) - - -def name_or_value(value): - """Will take Frame objects, classes, etc and attempt to return a valid - string identifier for them. - - :param value: The value to sanitize - :type value: pika.amqp_object.AMQPObject|pika.frame.Frame|int|unicode|str - :rtype: str - - """ - # Is it subclass of AMQPObject - try: - if issubclass(value, amqp_object.AMQPObject): - return value.NAME - except TypeError: - pass - - # Is it a Pika frame object? - if isinstance(value, frame.Method): - return value.method.NAME - - # Is it a Pika frame object (go after Method since Method extends this) - if isinstance(value, amqp_object.AMQPObject): - return value.NAME - - # Cast the value to a str (python 2 and python 3); encoding as UTF-8 on Python 2 - return canonical_str(value) - - -def sanitize_prefix(function): - """Automatically call name_or_value on the prefix passed in.""" - - @functools.wraps(function) - def wrapper(*args, **kwargs): - args = list(args) - offset = 1 - if 'prefix' in kwargs: - kwargs['prefix'] = name_or_value(kwargs['prefix']) - elif len(args) - 1 >= offset: - args[offset] = name_or_value(args[offset]) - offset += 1 - if 'key' in kwargs: - kwargs['key'] = name_or_value(kwargs['key']) - elif len(args) - 1 >= offset: - args[offset] = name_or_value(args[offset]) - - return function(*tuple(args), **kwargs) - - return wrapper - - -def check_for_prefix_and_key(function): - """Automatically return false if the key or prefix is not in the callbacks - for the instance. - - """ - - @functools.wraps(function) - def wrapper(*args, **kwargs): - offset = 1 - # Sanitize the prefix - if 'prefix' in kwargs: - prefix = name_or_value(kwargs['prefix']) - else: - prefix = name_or_value(args[offset]) - offset += 1 - - # Make sure to sanitize the key as well - if 'key' in kwargs: - key = name_or_value(kwargs['key']) - else: - key = name_or_value(args[offset]) - - # Make sure prefix and key are in the stack - if prefix not in args[0]._stack or key not in args[0]._stack[prefix]: - return False - - # Execute the method - return function(*args, **kwargs) - - return wrapper - - -class CallbackManager(object): - """CallbackManager is a global callback system designed to be a single place - where Pika can manage callbacks and process them. It should be referenced - by the CallbackManager.instance() method instead of constructing new - instances of it. - - """ - CALLS = 'calls' - ARGUMENTS = 'arguments' - DUPLICATE_WARNING = 'Duplicate callback found for "%s:%s"' - CALLBACK = 'callback' - ONE_SHOT = 'one_shot' - ONLY_CALLER = 'only' - - def __init__(self): - """Create an instance of the CallbackManager""" - self._stack = dict() - - @sanitize_prefix - def add(self, prefix, key, callback, - one_shot=True, - only_caller=None, - arguments=None): - """Add a callback to the stack for the specified key. If the call is - specified as one_shot, it will be removed after being fired - - The prefix is usually the channel number but the class is generic - and prefix and key may be any value. If you pass in only_caller - CallbackManager will restrict processing of the callback to only - the calling function/object that you specify. - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :param method callback: The callback to call - :param bool one_shot: Remove this callback after it is called - :param object only_caller: Only allow one_caller value to call the - event that fires the callback. - :param dict arguments: Arguments to validate when processing - :rtype: tuple(prefix, key) - - """ - # Prep the stack - if prefix not in self._stack: - self._stack[prefix] = dict() - - if key not in self._stack[prefix]: - self._stack[prefix][key] = list() - - # Check for a duplicate - for callback_dict in self._stack[prefix][key]: - if (callback_dict[self.CALLBACK] == callback and - callback_dict[self.ARGUMENTS] == arguments and - callback_dict[self.ONLY_CALLER] == only_caller): - if callback_dict[self.ONE_SHOT] is True: - callback_dict[self.CALLS] += 1 - LOGGER.debug('Incremented callback reference counter: %r', - callback_dict) - else: - LOGGER.warning(self.DUPLICATE_WARNING, prefix, key) - return prefix, key - - # Create the callback dictionary - callback_dict = self._callback_dict(callback, one_shot, only_caller, - arguments) - self._stack[prefix][key].append(callback_dict) - LOGGER.debug('Added: %r', callback_dict) - return prefix, key - - def clear(self): - """Clear all the callbacks if there are any defined.""" - self._stack = dict() - LOGGER.debug('Callbacks cleared') - - @sanitize_prefix - def cleanup(self, prefix): - """Remove all callbacks from the stack by a prefix. Returns True - if keys were there to be removed - - :param str or int prefix: The prefix for keeping track of callbacks with - :rtype: bool - - """ - LOGGER.debug('Clearing out %r from the stack', prefix) - if prefix not in self._stack or not self._stack[prefix]: - return False - del self._stack[prefix] - return True - - @sanitize_prefix - def pending(self, prefix, key): - """Return count of callbacks for a given prefix or key or None - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :rtype: None or int - - """ - if not prefix in self._stack or not key in self._stack[prefix]: - return None - return len(self._stack[prefix][key]) - - @sanitize_prefix - @check_for_prefix_and_key - def process(self, prefix, key, caller, *args, **keywords): - """Run through and process all the callbacks for the specified keys. - Caller should be specified at all times so that callbacks which - require a specific function to call CallbackManager.process will - not be processed. - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :param object caller: Who is firing the event - :param list args: Any optional arguments - :param dict keywords: Optional keyword arguments - :rtype: bool - - """ - LOGGER.debug('Processing %s:%s', prefix, key) - if prefix not in self._stack or key not in self._stack[prefix]: - return False - - callbacks = list() - # Check each callback, append it to the list if it should be called - for callback_dict in list(self._stack[prefix][key]): - if self._should_process_callback(callback_dict, caller, list(args)): - callbacks.append(callback_dict[self.CALLBACK]) - if callback_dict[self.ONE_SHOT]: - self._use_one_shot_callback(prefix, key, callback_dict) - - # Call each callback - for callback in callbacks: - LOGGER.debug('Calling %s for "%s:%s"', callback, prefix, key) - try: - callback(*args, **keywords) - except: - LOGGER.exception('Calling %s for "%s:%s" failed', callback, - prefix, key) - raise - return True - - @sanitize_prefix - @check_for_prefix_and_key - def remove(self, prefix, key, callback_value=None, arguments=None): - """Remove a callback from the stack by prefix, key and optionally - the callback itself. If you only pass in prefix and key, all - callbacks for that prefix and key will be removed. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - :param method callback_value: The method defined to call on callback - :param dict arguments: Optional arguments to check - :rtype: bool - - """ - if callback_value: - offsets_to_remove = list() - for offset in xrange(len(self._stack[prefix][key]), 0, -1): - callback_dict = self._stack[prefix][key][offset - 1] - - if (callback_dict[self.CALLBACK] == callback_value and - self._arguments_match(callback_dict, [arguments])): - offsets_to_remove.append(offset - 1) - - for offset in offsets_to_remove: - try: - LOGGER.debug('Removing callback #%i: %r', offset, - self._stack[prefix][key][offset]) - del self._stack[prefix][key][offset] - except KeyError: - pass - - self._cleanup_callback_dict(prefix, key) - return True - - @sanitize_prefix - @check_for_prefix_and_key - def remove_all(self, prefix, key): - """Remove all callbacks for the specified prefix and key. - - :param str prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - - """ - del self._stack[prefix][key] - self._cleanup_callback_dict(prefix, key) - - def _arguments_match(self, callback_dict, args): - """Validate if the arguments passed in match the expected arguments in - the callback_dict. We expect this to be a frame passed in to *args for - process or passed in as a list from remove. - - :param dict callback_dict: The callback dictionary to evaluate against - :param list args: The arguments passed in as a list - - """ - if callback_dict[self.ARGUMENTS] is None: - return True - if not args: - return False - if isinstance(args[0], dict): - return self._dict_arguments_match(args[0], - callback_dict[self.ARGUMENTS]) - return self._obj_arguments_match(args[0].method - if hasattr(args[0], 'method') else - args[0], callback_dict[self.ARGUMENTS]) - - def _callback_dict(self, callback, one_shot, only_caller, arguments): - """Return the callback dictionary. - - :param method callback: The callback to call - :param bool one_shot: Remove this callback after it is called - :param object only_caller: Only allow one_caller value to call the - event that fires the callback. - :rtype: dict - - """ - value = { - self.CALLBACK: callback, - self.ONE_SHOT: one_shot, - self.ONLY_CALLER: only_caller, - self.ARGUMENTS: arguments - } - if one_shot: - value[self.CALLS] = 1 - return value - - def _cleanup_callback_dict(self, prefix, key=None): - """Remove empty dict nodes in the callback stack. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - - """ - if key and key in self._stack[prefix] and not self._stack[prefix][key]: - del self._stack[prefix][key] - if prefix in self._stack and not self._stack[prefix]: - del self._stack[prefix] - - @staticmethod - def _dict_arguments_match(value, expectation): - """Checks an dict to see if it has attributes that meet the expectation. - - :param dict value: The dict to evaluate - :param dict expectation: The values to check against - :rtype: bool - - """ - LOGGER.debug('Comparing %r to %r', value, expectation) - for key in expectation: - if value.get(key) != expectation[key]: - LOGGER.debug('Values in dict do not match for %s', key) - return False - return True - - @staticmethod - def _obj_arguments_match(value, expectation): - """Checks an object to see if it has attributes that meet the - expectation. - - :param object value: The object to evaluate - :param dict expectation: The values to check against - :rtype: bool - - """ - for key in expectation: - if not hasattr(value, key): - LOGGER.debug('%r does not have required attribute: %s', - type(value), key) - return False - if getattr(value, key) != expectation[key]: - LOGGER.debug('Values in %s do not match for %s', type(value), - key) - return False - return True - - def _should_process_callback(self, callback_dict, caller, args): - """Returns True if the callback should be processed. - - :param dict callback_dict: The callback configuration - :param object caller: Who is firing the event - :param list args: Any optional arguments - :rtype: bool - - """ - if not self._arguments_match(callback_dict, args): - LOGGER.debug('Arguments do not match for %r, %r', callback_dict, - args) - return False - return (callback_dict[self.ONLY_CALLER] is None or - (callback_dict[self.ONLY_CALLER] and - callback_dict[self.ONLY_CALLER] == caller)) - - def _use_one_shot_callback(self, prefix, key, callback_dict): - """Process the one-shot callback, decrementing the use counter and - removing it from the stack if it's now been fully used. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - :param dict callback_dict: The callback dict to process - - """ - LOGGER.debug('Processing use of oneshot callback') - callback_dict[self.CALLS] -= 1 - LOGGER.debug('%i registered uses left', callback_dict[self.CALLS]) - - if callback_dict[self.CALLS] <= 0: - self.remove(prefix, key, callback_dict[self.CALLBACK], - callback_dict[self.ARGUMENTS]) diff --git a/packages_o/pika-0.10.0/build/lib/pika/channel.py b/packages_o/pika-0.10.0/build/lib/pika/channel.py deleted file mode 100644 index 2bf3b0f0..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/channel.py +++ /dev/null @@ -1,1254 +0,0 @@ -"""The Channel class provides a wrapper for interacting with RabbitMQ -implementing the methods and behaviors for an AMQP Channel. - -""" -import collections -import logging -import warnings -import uuid - -import pika.frame as frame -import pika.exceptions as exceptions -import pika.spec as spec -from pika.utils import is_callable -from pika.compat import unicode_type, dictkeys, as_bytes - - -LOGGER = logging.getLogger(__name__) -MAX_CHANNELS = 32768 - - -class Channel(object): - """A Channel is the primary communication method for interacting with - RabbitMQ. It is recommended that you do not directly invoke - the creation of a channel object in your application code but rather - construct the a channel by calling the active connection's channel() - method. - - """ - CLOSED = 0 - OPENING = 1 - OPEN = 2 - CLOSING = 3 - - _ON_CHANNEL_CLEANUP_CB_KEY = '_on_channel_cleanup' - - def __init__(self, connection, channel_number, on_open_callback=None): - """Create a new instance of the Channel - - :param pika.connection.Connection connection: The connection - :param int channel_number: The channel number for this instance - :param method on_open_callback: The method to call on channel open - - """ - if not isinstance(channel_number, int): - raise exceptions.InvalidChannelNumber - self.channel_number = channel_number - self.callbacks = connection.callbacks - self.connection = connection - - # The frame-handler changes depending on the type of frame processed - self.frame_dispatcher = ContentFrameDispatcher() - - self._blocked = collections.deque(list()) - self._blocking = None - self._has_on_flow_callback = False - self._cancelled = set() - self._consumers = dict() - self._consumers_with_noack = set() - self._on_flowok_callback = None - self._on_getok_callback = None - self._on_openok_callback = on_open_callback - self._pending = dict() - self._state = self.CLOSED - - # opaque cookie value set by wrapper layer (e.g., BlockingConnection) - # via _set_cookie - self._cookie = None - - def __int__(self): - """Return the channel object as its channel number - - :rtype: int - - """ - return self.channel_number - - def add_callback(self, callback, replies, one_shot=True): - """Pass in a callback handler and a list replies from the - RabbitMQ broker which you'd like the callback notified of. Callbacks - should allow for the frame parameter to be passed in. - - :param method callback: The method to call - :param list replies: The replies to get a callback for - :param bool one_shot: Only handle the first type callback - - """ - for reply in replies: - self.callbacks.add(self.channel_number, reply, callback, one_shot) - - def add_on_cancel_callback(self, callback): - """Pass a callback function that will be called when the basic_cancel - is sent by the server. The callback function should receive a frame - parameter. - - :param method callback: The method to call on callback - - """ - self.callbacks.add(self.channel_number, spec.Basic.Cancel, callback, - False) - - def add_on_close_callback(self, callback): - """Pass a callback function that will be called when the channel is - closed. The callback function will receive the channel, the - reply_code (int) and the reply_text (int) sent by the server describing - why the channel was closed. - - :param method callback: The method to call on callback - - """ - self.callbacks.add(self.channel_number, '_on_channel_close', callback, - False, self) - - def add_on_flow_callback(self, callback): - """Pass a callback function that will be called when Channel.Flow is - called by the remote server. Note that newer versions of RabbitMQ - will not issue this but instead use TCP backpressure - - :param method callback: The method to call on callback - - """ - self._has_on_flow_callback = True - self.callbacks.add(self.channel_number, spec.Channel.Flow, callback, - False) - - def add_on_return_callback(self, callback): - """Pass a callback function that will be called when basic_publish as - sent a message that has been rejected and returned by the server. - - :param method callback: The method to call on callback with the - signature callback(channel, method, properties, - body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - """ - self.callbacks.add(self.channel_number, '_on_return', callback, False) - - def basic_ack(self, delivery_tag=0, multiple=False): - """Acknowledge one or more messages. When sent by the client, this - method acknowledges one or more messages delivered via the Deliver or - Get-Ok methods. When sent by server, this method acknowledges one or - more messages published with the Publish method on a channel in - confirm mode. The acknowledgement can be for a single message or a - set of messages up to and including a specific message. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - """ - if not self.is_open: - raise exceptions.ChannelClosed() - return self._send_method(spec.Basic.Ack(delivery_tag, multiple)) - - def basic_cancel(self, callback=None, consumer_tag='', nowait=False): - """This method cancels a consumer. This does not affect already - delivered messages, but it does mean the server will not send any more - messages for that consumer. The client may receive an arbitrary number - of messages in between sending the cancel method and receiving the - cancel-ok reply. It may also be sent from the server to the client in - the event of the consumer being unexpectedly cancelled (i.e. cancelled - for any reason other than the server receiving the corresponding - basic.cancel from the client). This allows clients to be notified of - the loss of consumers due to events such as queue deletion. - - :param method callback: Method to call for a Basic.CancelOk response - :param str consumer_tag: Identifier for the consumer - :param bool nowait: Do not expect a Basic.CancelOk response - :raises: ValueError - - """ - self._validate_channel_and_callback(callback) - if consumer_tag not in self.consumer_tags: - return - if callback: - if nowait is True: - raise ValueError('Can not pass a callback if nowait is True') - self.callbacks.add(self.channel_number, spec.Basic.CancelOk, - callback) - self._cancelled.add(consumer_tag) - self._rpc(spec.Basic.Cancel(consumer_tag=consumer_tag, - nowait=nowait), self._on_cancelok, - [(spec.Basic.CancelOk, {'consumer_tag': consumer_tag})] if - nowait is False else []) - - def basic_consume(self, consumer_callback, - queue='', - no_ack=False, - exclusive=False, - consumer_tag=None, - arguments=None): - """Sends the AMQP command Basic.Consume to the broker and binds messages - for the consumer_tag to the consumer callback. If you do not pass in - a consumer_tag, one will be automatically generated for you. Returns - the consumer tag. - - For more information on basic_consume, see: - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.consume - - :param method consumer_callback: The method to callback when consuming - with the signature consumer_callback(channel, method, properties, - body), where - channel: pika.Channel - method: pika.spec.Basic.Deliver - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - :param queue: The queue to consume from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a response - :param bool exclusive: Don't allow other consumers on the queue - :param consumer_tag: Specify your own consumer tag - :type consumer_tag: str or unicode - :param dict arguments: Custom key/value pair arguments for the consume - :rtype: str - - """ - self._validate_channel_and_callback(consumer_callback) - - # If a consumer tag was not passed, create one - if not consumer_tag: - consumer_tag = self._generate_consumer_tag() - - if consumer_tag in self._consumers or consumer_tag in self._cancelled: - raise exceptions.DuplicateConsumerTag(consumer_tag) - - if no_ack: - self._consumers_with_noack.add(consumer_tag) - - self._consumers[consumer_tag] = consumer_callback - self._pending[consumer_tag] = list() - self._rpc(spec.Basic.Consume(queue=queue, - consumer_tag=consumer_tag, - no_ack=no_ack, - exclusive=exclusive, - arguments=arguments or dict()), - self._on_eventok, [(spec.Basic.ConsumeOk, - {'consumer_tag': consumer_tag})]) - - return consumer_tag - - def _generate_consumer_tag(self): - """Generate a consumer tag - - NOTE: this protected method may be called by derived classes - - :returns: consumer tag - :rtype: str - """ - return 'ctag%i.%s' % (self.channel_number, - uuid.uuid4().hex) - - def basic_get(self, callback=None, queue='', no_ack=False): - """Get a single message from the AMQP broker. If you want to - be notified of Basic.GetEmpty, use the Channel.add_callback method - adding your Basic.GetEmpty callback which should expect only one - parameter, frame. For more information on basic_get and its - parameters, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.get - - :param method callback: The method to callback with a message that has - the signature callback(channel, method, properties, body), where: - channel: pika.Channel - method: pika.spec.Basic.GetOk - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - :param queue: The queue to get a message from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a reply - - """ - self._validate_channel_and_callback(callback) - self._on_getok_callback = callback - self._send_method(spec.Basic.Get(queue=queue, no_ack=no_ack)) - - def basic_nack(self, delivery_tag=None, multiple=False, requeue=True): - """This method allows a client to reject one or more incoming messages. - It can be used to interrupt and cancel large incoming messages, or - return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - return self._send_method(spec.Basic.Nack(delivery_tag, multiple, - requeue)) - - def basic_publish(self, exchange, routing_key, body, - properties=None, - mandatory=False, - immediate=False): - """Publish to the channel with the given exchange, routing key and body. - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body - :type body: str or unicode - :param pika.spec.BasicProperties properties: Basic.properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - if immediate: - LOGGER.warning('The immediate flag is deprecated in RabbitMQ') - if isinstance(body, unicode_type): - body = body.encode('utf-8') - properties = properties or spec.BasicProperties() - self._send_method(spec.Basic.Publish(exchange=exchange, - routing_key=routing_key, - mandatory=mandatory, - immediate=immediate), - (properties, body)) - - def basic_qos(self, - callback=None, - prefetch_size=0, - prefetch_count=0, - all_channels=False): - """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 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. - - :param method callback: The method to callback for Basic.QosOk response - :param int prefetch_size: This field specifies the prefetch window - size. 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. - :param int prefetch_count: 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. - :param bool all_channels: Should the QoS apply to all channels - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Basic.Qos(prefetch_size, prefetch_count, - all_channels), callback, - [spec.Basic.QosOk]) - - def basic_reject(self, delivery_tag, requeue=True): - """Reject an incoming message. This method allows a client to reject a - message. It can be used to interrupt and cancel large incoming messages, - or return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - :raises: TypeError - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - if not isinstance(delivery_tag, int): - raise TypeError('delivery_tag must be an integer') - return self._send_method(spec.Basic.Reject(delivery_tag, requeue)) - - def basic_recover(self, callback=None, requeue=False): - """This method asks the server to redeliver all unacknowledged messages - on a specified channel. Zero or more messages may be redelivered. This - method replaces the asynchronous Recover. - - :param method callback: Method to call when receiving Basic.RecoverOk - :param bool requeue: If False, the message will be redelivered to the - original recipient. If True, the server will - attempt to requeue the message, potentially then - delivering it to an alternative subscriber. - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Basic.Recover(requeue), callback, - [spec.Basic.RecoverOk]) - - def close(self, reply_code=0, reply_text="Normal Shutdown"): - """Will invoke a clean shutdown of the channel with the AMQP Broker. - - :param int reply_code: The reply code to close the channel with - :param str reply_text: The reply text to close the channel with - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - LOGGER.info('Channel.close(%s, %s)', reply_code, reply_text) - if self._consumers: - LOGGER.debug('Cancelling %i consumers', len(self._consumers)) - for consumer_tag in dictkeys(self._consumers): - self.basic_cancel(consumer_tag=consumer_tag) - self._set_state(self.CLOSING) - self._rpc(spec.Channel.Close(reply_code, reply_text, 0, 0), - self._on_closeok, [spec.Channel.CloseOk]) - - def confirm_delivery(self, callback=None, nowait=False): - """Turn on Confirm mode in the channel. Pass in a callback to be - notified by the Broker when a message has been confirmed as received or - rejected (Basic.Ack, Basic.Nack) from the broker to the publisher. - - For more information see: - http://www.rabbitmq.com/extensions.html#confirms - - :param method callback: The callback for delivery confirmations - :param bool nowait: Do not send a reply frame (Confirm.SelectOk) - - """ - self._validate_channel_and_callback(callback) - if (self.connection.publisher_confirms is False or - self.connection.basic_nack is False): - raise exceptions.MethodNotImplemented('Not Supported on Server') - - # Add the ack and nack callbacks - if callback is not None: - self.callbacks.add(self.channel_number, spec.Basic.Ack, callback, - False) - self.callbacks.add(self.channel_number, spec.Basic.Nack, callback, - False) - - # Send the RPC command - self._rpc(spec.Confirm.Select(nowait), self._on_selectok, - [spec.Confirm.SelectOk] if nowait is False else []) - - @property - def consumer_tags(self): - """Property method that returns a list of currently active consumers - - :rtype: list - - """ - return dictkeys(self._consumers) - - def exchange_bind(self, - callback=None, - destination=None, - source=None, - routing_key='', - nowait=False, - arguments=None): - """Bind an exchange to another exchange. - - :param method callback: The method to call on Exchange.BindOk - :param destination: The destination exchange to bind - :type destination: str or unicode - :param source: The source exchange to bind to - :type source: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param bool nowait: Do not wait for an Exchange.BindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Bind(0, destination, source, routing_key, - nowait, arguments or dict()), - callback, [spec.Exchange.BindOk] if nowait is False - else []) - - def exchange_declare(self, - callback=None, - exchange=None, - exchange_type='direct', - passive=False, - durable=False, - auto_delete=False, - internal=False, - nowait=False, - arguments=None, - type=None): - """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. - - If passive set, the server will reply with Declare-Ok if the exchange - already exists with the same name, and raise an error if not and if the - exchange does not already exist, the server MUST raise a channel - exception with reply code 404 (not found). - - :param method callback: Call this method on Exchange.DeclareOk - :param exchange: The exchange name consists of a non-empty - :type exchange: str or unicode - sequence of these characters: letters, - digits, hyphen, underscore, period, or - colon. - :param str exchange_type: The exchange type to use - :param bool passive: Perform a declare or just check to see if it exists - :param bool durable: Survive a reboot of RabbitMQ - :param bool auto_delete: Remove when no more queues are bound to it - :param bool internal: Can only be published to by other exchanges - :param bool nowait: Do not expect an Exchange.DeclareOk response - :param dict arguments: Custom key/value pair arguments for the exchange - :param str type: The deprecated exchange type parameter - - """ - self._validate_channel_and_callback(callback) - if type is not None: - warnings.warn('type is deprecated, use exchange_type instead', - DeprecationWarning) - if exchange_type == 'direct' and type != exchange_type: - exchange_type = type - return self._rpc(spec.Exchange.Declare(0, exchange, exchange_type, - passive, durable, auto_delete, - internal, nowait, - arguments or dict()), callback, - [spec.Exchange.DeclareOk] if nowait is False else []) - - def exchange_delete(self, - callback=None, - exchange=None, - if_unused=False, - nowait=False): - """Delete the exchange. - - :param method callback: The method to call on Exchange.DeleteOk - :param exchange: The exchange name - :type exchange: str or unicode - :param bool if_unused: only delete if the exchange is unused - :param bool nowait: Do not wait for an Exchange.DeleteOk - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Delete(0, exchange, if_unused, nowait), - callback, [spec.Exchange.DeleteOk] if nowait is False - else []) - - def exchange_unbind(self, - callback=None, - destination=None, - source=None, - routing_key='', - nowait=False, - arguments=None): - """Unbind an exchange from another exchange. - - :param method callback: The method to call on Exchange.UnbindOk - :param destination: The destination exchange to unbind - :type destination: str or unicode - :param source: The source exchange to unbind from - :type source: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param bool nowait: Do not wait for an Exchange.UnbindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Unbind(0, destination, source, - routing_key, nowait, arguments), - callback, [spec.Exchange.UnbindOk] if nowait is False - else []) - - def flow(self, callback, active): - """Turn Channel flow control off and on. Pass a callback to be notified - of the response from the server. active is a bool. Callback should - expect a bool in response indicating channel flow state. For more - information, please reference: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#channel.flow - - :param method callback: The callback method - :param bool active: Turn flow on or off - - """ - self._validate_channel_and_callback(callback) - self._on_flowok_callback = callback - self._rpc(spec.Channel.Flow(active), self._on_flowok, - [spec.Channel.FlowOk]) - - @property - def is_closed(self): - """Returns True if the channel is closed. - - :rtype: bool - - """ - return self._state == self.CLOSED - - @property - def is_closing(self): - """Returns True if the channel is closing. - - :rtype: bool - - """ - return self._state == self.CLOSING - - @property - def is_open(self): - """Returns True if the channel is open. - - :rtype: bool - - """ - return self._state == self.OPEN - - def open(self): - """Open the channel""" - self._set_state(self.OPENING) - self._add_callbacks() - self._rpc(spec.Channel.Open(), self._on_openok, [spec.Channel.OpenOk]) - - def queue_bind(self, callback, queue, exchange, - routing_key=None, - nowait=False, - arguments=None): - """Bind the queue to the specified exchange - - :param method callback: The method to call on Queue.BindOk - :param queue: The queue to bind to the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param bool nowait: Do not wait for a Queue.BindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - replies = [spec.Queue.BindOk] if nowait is False else [] - if routing_key is None: - routing_key = queue - return self._rpc(spec.Queue.Bind(0, queue, exchange, routing_key, - nowait, arguments or dict()), callback, - replies) - - def queue_declare(self, callback, - queue='', - passive=False, - durable=False, - exclusive=False, - auto_delete=False, - 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. - - Leave the queue name empty for a auto-named queue in RabbitMQ - - :param method callback: The method to call on Queue.DeclareOk - :param queue: The queue name - :type queue: str or unicode - :param bool passive: Only check to see if the queue exists - :param bool durable: Survive reboots of the broker - :param bool exclusive: Only allow access by the current connection - :param bool auto_delete: Delete after consumer cancels or disconnects - :param bool nowait: Do not wait for a Queue.DeclareOk - :param dict arguments: Custom key/value arguments for the queue - - """ - if queue: - condition = (spec.Queue.DeclareOk, - {'queue': queue}) - else: - condition = spec.Queue.DeclareOk - replies = [condition] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Declare(0, queue, passive, durable, - exclusive, auto_delete, nowait, - arguments or dict()), callback, - replies) - - def queue_delete(self, - callback=None, - queue='', - if_unused=False, - if_empty=False, - nowait=False): - """Delete a queue from the broker. - - :param method callback: The method to call on Queue.DeleteOk - :param queue: The queue to delete - :type queue: str or unicode - :param bool if_unused: only delete if it's unused - :param bool if_empty: only delete if the queue is empty - :param bool nowait: Do not wait for a Queue.DeleteOk - - """ - replies = [spec.Queue.DeleteOk] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Delete(0, queue, if_unused, if_empty, - nowait), callback, replies) - - def queue_purge(self, callback=None, queue='', nowait=False): - """Purge all of the messages from the specified queue - - :param method callback: The method to call on Queue.PurgeOk - :param queue: The queue to purge - :type queue: str or unicode - :param bool nowait: Do not expect a Queue.PurgeOk response - - """ - replies = [spec.Queue.PurgeOk] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Purge(0, queue, nowait), callback, replies) - - def queue_unbind(self, - callback=None, - queue='', - exchange=None, - routing_key=None, - arguments=None): - """Unbind a queue from an exchange. - - :param method callback: The method to call on Queue.UnbindOk - :param queue: The queue to unbind from the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind from - :type exchange: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - if routing_key is None: - routing_key = queue - return self._rpc(spec.Queue.Unbind(0, queue, exchange, routing_key, - arguments or dict()), callback, - [spec.Queue.UnbindOk]) - - def tx_commit(self, callback=None): - """Commit a transaction - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Commit(), callback, [spec.Tx.CommitOk]) - - def tx_rollback(self, callback=None): - """Rollback a transaction. - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Rollback(), callback, [spec.Tx.RollbackOk]) - - def tx_select(self, callback=None): - """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. - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Select(), callback, [spec.Tx.SelectOk]) - - # Internal methods - - def _add_callbacks(self): - """Callbacks that add the required behavior for a channel when - connecting and connected to a server. - - """ - # Add a callback for Basic.GetEmpty - self.callbacks.add(self.channel_number, spec.Basic.GetEmpty, - self._on_getempty, False) - - # Add a callback for Basic.Cancel - self.callbacks.add(self.channel_number, spec.Basic.Cancel, - self._on_cancel, False) - - # Deprecated in newer versions of RabbitMQ but still register for it - self.callbacks.add(self.channel_number, spec.Channel.Flow, - self._on_flow, False) - - # Add a callback for when the server closes our channel - self.callbacks.add(self.channel_number, spec.Channel.Close, - self._on_close, True) - - def _add_on_cleanup_callback(self, callback): - """For internal use only (e.g., Connection needs to remove closed - channels from its channel container). Pass a callback function that will - be called when the channel is being cleaned up after all channel-close - callbacks callbacks. - - :param method callback: The method to call on callback with the - signature: callback(channel) - - """ - self.callbacks.add(self.channel_number, self._ON_CHANNEL_CLEANUP_CB_KEY, - callback, one_shot=True, only_caller=self) - - def _add_pending_msg(self, consumer_tag, method_frame, header_frame, body): - """Add the received message to the pending message stack. - - :param str consumer_tag: The consumer tag for the message - :param pika.frame.Method method_frame: The received method frame - :param pika.frame.Header header_frame: The received header frame - :param body: The message body - :type body: str or unicode - - """ - self._pending[consumer_tag].append((self, method_frame.method, - header_frame.properties, body)) - - def _cleanup(self): - """Remove all consumers and any callbacks for the channel.""" - self.callbacks.process(self.channel_number, - self._ON_CHANNEL_CLEANUP_CB_KEY, self, - self) - self._consumers = dict() - self.callbacks.cleanup(str(self.channel_number)) - self._cookie = None - - def _cleanup_consumer_ref(self, consumer_tag): - """Remove any references to the consumer tag in internal structures - for consumer state. - - :param str consumer_tag: The consumer tag to cleanup - - """ - if consumer_tag in self._consumers_with_noack: - self._consumers_with_noack.remove(consumer_tag) - if consumer_tag in self._consumers: - del self._consumers[consumer_tag] - if consumer_tag in self._pending: - del self._pending[consumer_tag] - self._cancelled.discard(consumer_tag) - - def _get_cookie(self): - """Used by the wrapper implementation (e.g., `BlockingChannel`) to - retrieve the cookie that it set via `_set_cookie` - - :returns: opaque cookie value that was set via `_set_cookie` - """ - return self._cookie - - def _get_pending_msg(self, consumer_tag): - """Get a pending message for the consumer tag from the stack. - - :param str consumer_tag: The consumer tag to get a message from - :rtype: tuple(pika.frame.Header, pika.frame.Method, str|unicode) - - """ - return self._pending[consumer_tag].pop(0) - - def _handle_content_frame(self, frame_value): - """This is invoked by the connection when frames that are not registered - with the CallbackManager have been found. This should only be the case - when the frames are related to content delivery. - - The frame_dispatcher will be invoked which will return the fully formed - message in three parts when all of the body frames have been received. - - :param pika.amqp_object.Frame frame_value: The frame to deliver - - """ - try: - response = self.frame_dispatcher.process(frame_value) - except exceptions.UnexpectedFrameError: - return self._unexpected_frame(frame_value) - - if response: - if isinstance(response[0].method, spec.Basic.Deliver): - self._on_deliver(*response) - elif isinstance(response[0].method, spec.Basic.GetOk): - self._on_getok(*response) - elif isinstance(response[0].method, spec.Basic.Return): - self._on_return(*response) - - def _has_content(self, method_frame): - """Return a bool if it's a content method as defined by the spec - - :param pika.amqp_object.Method method_frame: The method frame received - - """ - return spec.has_content(method_frame.INDEX) - - def _on_cancel(self, method_frame): - """When the broker cancels a consumer, delete it from our internal - dictionary. - - :param pika.frame.Method method_frame: The method frame received - - """ - if method_frame.method.consumer_tag in self._cancelled: - # User-initiated cancel is waiting for Cancel-ok - return - - self._cleanup_consumer_ref(method_frame.method.consumer_tag) - - def _on_cancelok(self, method_frame): - """Called in response to a frame from the Broker when the - client sends Basic.Cancel - - :param pika.frame.Method method_frame: The method frame received - - """ - self._cleanup_consumer_ref(method_frame.method.consumer_tag) - - def _on_close(self, method_frame): - """Handle the case where our channel has been closed for us - - :param pika.frame.Method method_frame: The close frame - - """ - LOGGER.info('%s', method_frame) - LOGGER.warning('Received remote Channel.Close (%s): %s', - method_frame.method.reply_code, - method_frame.method.reply_text) - if self.connection.is_open: - self._send_method(spec.Channel.CloseOk()) - self._set_state(self.CLOSED) - self.callbacks.process(self.channel_number, '_on_channel_close', self, - self, method_frame.method.reply_code, - method_frame.method.reply_text) - self._cleanup() - - def _on_closeok(self, method_frame): - """Invoked when RabbitMQ replies to a Channel.Close method - - :param pika.frame.Method method_frame: The CloseOk frame - - """ - self._set_state(self.CLOSED) - self.callbacks.process(self.channel_number, '_on_channel_close', self, - self, 0, '') - self._cleanup() - - def _on_deliver(self, method_frame, header_frame, body): - """Cope with reentrancy. If a particular consumer is still active when - another delivery appears for it, queue the deliveries up until it - finally exits. - - :param pika.frame.Method method_frame: The method frame received - :param pika.frame.Header header_frame: The header frame received - :param body: The body received - :type body: str or unicode - - """ - consumer_tag = method_frame.method.consumer_tag - if consumer_tag in self._cancelled: - if self.is_open and consumer_tag not in self._consumers_with_noack: - self.basic_reject(method_frame.method.delivery_tag) - return - if consumer_tag not in self._consumers: - return self._add_pending_msg(consumer_tag, method_frame, - header_frame, body) - while self._pending[consumer_tag]: - self._consumers[consumer_tag](*self._get_pending_msg(consumer_tag)) - self._consumers[consumer_tag](self, method_frame.method, - header_frame.properties, body) - - def _on_eventok(self, method_frame): - """Generic events that returned ok that may have internal callbacks. - We keep a list of what we've yet to implement so that we don't silently - drain events that we don't support. - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug('Discarding frame %r', method_frame) - - def _on_flow(self, method_frame_unused): - """Called if the server sends a Channel.Flow frame. - - :param pika.frame.Method method_frame_unused: The Channel.Flow frame - - """ - if self._has_on_flow_callback is False: - LOGGER.warning('Channel.Flow received from server') - - def _on_flowok(self, method_frame): - """Called in response to us asking the server to toggle on Channel.Flow - - :param pika.frame.Method method_frame: The method frame received - - """ - self.flow_active = method_frame.method.active - if self._on_flowok_callback: - self._on_flowok_callback(method_frame.method.active) - self._on_flowok_callback = None - else: - LOGGER.warning('Channel.FlowOk received with no active callbacks') - - def _on_getempty(self, method_frame): - """When we receive an empty reply do nothing but log it - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug('Received Basic.GetEmpty: %r', method_frame) - - def _on_getok(self, method_frame, header_frame, body): - """Called in reply to a Basic.Get when there is a message. - - :param pika.frame.Method method_frame: The method frame received - :param pika.frame.Header header_frame: The header frame received - :param body: The body received - :type body: str or unicode - - """ - if self._on_getok_callback is not None: - callback = self._on_getok_callback - self._on_getok_callback = None - callback(self, method_frame.method, header_frame.properties, body) - else: - LOGGER.error('Basic.GetOk received with no active callback') - - def _on_openok(self, frame_unused): - """Called by our callback handler when we receive a Channel.OpenOk and - subsequently calls our _on_openok_callback which was passed into the - Channel constructor. The reason we do this is because we want to make - sure that the on_open_callback parameter passed into the Channel - constructor is not the first callback we make. - - :param pika.frame.Method frame_unused: Unused Channel.OpenOk frame - - """ - self._set_state(self.OPEN) - if self._on_openok_callback is not None: - self._on_openok_callback(self) - - def _on_return(self, method_frame, header_frame, body): - """Called if the server sends a Basic.Return frame. - - :param pika.frame.Method method_frame: The Basic.Return frame - :param pika.frame.Header header_frame: The content header frame - :param body: The message body - :type body: str or unicode - - """ - if not self.callbacks.process(self.channel_number, '_on_return', self, - self, - method_frame.method, - header_frame.properties, - body): - LOGGER.warning('Basic.Return received from server (%r, %r)', - method_frame.method, header_frame.properties) - - def _on_selectok(self, method_frame): - """Called when the broker sends a Confirm.SelectOk frame - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug("Confirm.SelectOk Received: %r", method_frame) - - def _on_synchronous_complete(self, method_frame_unused): - """This is called when a synchronous command is completed. It will undo - the blocking state and send all the frames that stacked up while we - were in the blocking state. - - :param pika.frame.Method method_frame_unused: The method frame received - - """ - LOGGER.debug('%i blocked frames', len(self._blocked)) - self._blocking = None - while len(self._blocked) > 0 and self._blocking is None: - self._rpc(*self._blocked.popleft()) - - def _rpc(self, method_frame, callback=None, acceptable_replies=None): - """Shortcut wrapper to the Connection's rpc command using its callback - stack, passing in our channel number. - - :param pika.amqp_object.Method method_frame: The method frame to call - :param method callback: The callback for the RPC response - :param list acceptable_replies: The replies this RPC call expects - - """ - # Make sure the channel is open - if self.is_closed: - raise exceptions.ChannelClosed - - # If the channel is blocking, add subsequent commands to our stack - if self._blocking: - return self._blocked.append([method_frame, callback, - acceptable_replies]) - - # Validate we got None or a list of acceptable_replies - if acceptable_replies and not isinstance(acceptable_replies, list): - raise TypeError("acceptable_replies should be list or None") - - # Validate the callback is callable - if callback and not is_callable(callback): - raise TypeError("callback should be None, a function or method.") - - # Block until a response frame is received for synchronous frames - if method_frame.synchronous: - self._blocking = method_frame.NAME - - # If acceptable replies are set, add callbacks - if acceptable_replies: - for reply in acceptable_replies or list(): - if isinstance(reply, tuple): - reply, arguments = reply - else: - arguments = None - LOGGER.debug('Adding in on_synchronous_complete callback') - self.callbacks.add(self.channel_number, reply, - self._on_synchronous_complete, - arguments=arguments) - if callback: - LOGGER.debug('Adding passed in callback') - self.callbacks.add(self.channel_number, reply, callback, - arguments=arguments) - - self._send_method(method_frame) - - def _send_method(self, method_frame, content=None): - """Shortcut wrapper to send a method through our connection, passing in - the channel number - - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - self.connection._send_method(self.channel_number, method_frame, content) - - def _set_cookie(self, cookie): - """Used by wrapper layer (e.g., `BlockingConnection`) to link the - channel implementation back to the proxy. See `_get_cookie`. - - :param cookie: an opaque value; typically a proxy channel implementation - instance (e.g., `BlockingChannel` instance) - """ - self._cookie = cookie - - def _set_state(self, connection_state): - """Set the channel connection state to the specified state value. - - :param int connection_state: The connection_state value - - """ - self._state = connection_state - - def _unexpected_frame(self, frame_value): - """Invoked when a frame is received that is not setup to be processed. - - :param pika.frame.Frame frame_value: The frame received - - """ - LOGGER.warning('Unexpected frame: %r', frame_value) - - def _validate_channel_and_callback(self, callback): - if not self.is_open: - raise exceptions.ChannelClosed() - if callback is not None and not is_callable(callback): - raise ValueError('callback must be a function or method') - - -class ContentFrameDispatcher(object): - """Handle content related frames, building a message and return the message - back in three parts upon receipt. - - """ - - def __init__(self): - """Create a new instance of the Dispatcher passing in the callback - manager. - - """ - self._method_frame = None - self._header_frame = None - self._seen_so_far = 0 - self._body_fragments = list() - - def process(self, frame_value): - """Invoked by the Channel object when passed frames that are not - setup in the rpc process and that don't have explicit reply types - defined. This includes Basic.Publish, Basic.GetOk and Basic.Return - - :param Method|Header|Body frame_value: The frame to process - - """ - if (isinstance(frame_value, frame.Method) and - spec.has_content(frame_value.method.INDEX)): - self._method_frame = frame_value - elif isinstance(frame_value, frame.Header): - self._header_frame = frame_value - if frame_value.body_size == 0: - return self._finish() - elif isinstance(frame_value, frame.Body): - return self._handle_body_frame(frame_value) - else: - raise exceptions.UnexpectedFrameError(frame_value) - - def _finish(self): - """Invoked when all of the message has been received - - :rtype: tuple(pika.frame.Method, pika.frame.Header, str) - - """ - content = (self._method_frame, self._header_frame, - b''.join(self._body_fragments)) - self._reset() - return content - - def _handle_body_frame(self, body_frame): - """Receive body frames and append them to the stack. When the body size - matches, call the finish method. - - :param Body body_frame: The body frame - :raises: pika.exceptions.BodyTooLongError - :rtype: tuple(pika.frame.Method, pika.frame.Header, str)|None - - """ - self._seen_so_far += len(body_frame.fragment) - self._body_fragments.append(body_frame.fragment) - if self._seen_so_far == self._header_frame.body_size: - return self._finish() - elif self._seen_so_far > self._header_frame.body_size: - raise exceptions.BodyTooLongError(self._seen_so_far, - self._header_frame.body_size) - return None - - def _reset(self): - """Reset the values for processing frames""" - self._method_frame = None - self._header_frame = None - self._seen_so_far = 0 - self._body_fragments = list() diff --git a/packages_o/pika-0.10.0/build/lib/pika/compat.py b/packages_o/pika-0.10.0/build/lib/pika/compat.py deleted file mode 100644 index 73f3879f..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/compat.py +++ /dev/null @@ -1,105 +0,0 @@ -import sys as _sys - - -PY2 = _sys.version_info < (3,) -PY3 = not PY2 - - -if not PY2: - # these were moved around for Python 3 - from urllib.parse import unquote as url_unquote, urlencode - - # Python 3 does not have basestring anymore; we include - # *only* the str here as this is used for textual data. - basestring = (str,) - - # for assertions that the data is either encoded or non-encoded text - str_or_bytes = (str, bytes) - - # xrange is gone, replace it with range - xrange = range - - # the unicode type is str - unicode_type = str - - - def dictkeys(dct): - """ - Returns a list of keys of dictionary - - dict.keys returns a view that works like .keys in Python 2 - *except* any modifications in the dictionary will be visible - (and will cause errors if the view is being iterated over while - it is modified). - """ - - return list(dct.keys()) - - def dictvalues(dct): - """ - Returns a list of values of a dictionary - - dict.values returns a view that works like .values in Python 2 - *except* any modifications in the dictionary will be visible - (and will cause errors if the view is being iterated over while - it is modified). - """ - return list(dct.values()) - - def byte(*args): - """ - This is the same as Python 2 `chr(n)` for bytes in Python 3 - - Returns a single byte `bytes` for the given int argument (we - optimize it a bit here by passing the positional argument tuple - directly to the bytes constructor. - """ - return bytes(args) - - class long(int): - """ - A marker class that signifies that the integer value should be - serialized as `l` instead of `I` - """ - - def __repr__(self): - return str(self) + 'L' - - def canonical_str(value): - """ - Return the canonical str value for the string. - In both Python 3 and Python 2 this is str. - """ - - return str(value) - -else: - from urllib import unquote as url_unquote, urlencode - - basestring = basestring - str_or_bytes = basestring - xrange = xrange - unicode_type = unicode - dictkeys = dict.keys - dictvalues = dict.values - byte = chr - long = long - - def canonical_str(value): - """ - Returns the canonical string value of the given string. - In Python 2 this is the value unchanged if it is an str, otherwise - it is the unicode value encoded as UTF-8. - """ - - try: - return str(value) - except UnicodeEncodeError: - return str(value.encode('utf-8')) - - -def as_bytes(value): - if not isinstance(value, bytes): - return value.encode('UTF-8') - return value - diff --git a/packages_o/pika-0.10.0/build/lib/pika/connection.py b/packages_o/pika-0.10.0/build/lib/pika/connection.py deleted file mode 100644 index d15581f8..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/connection.py +++ /dev/null @@ -1,1634 +0,0 @@ -"""Core connection objects""" -import ast -import sys -import collections -import logging -import math -import platform -import threading -import urllib -import warnings - -if sys.version_info > (3,): - import urllib.parse as urlparse -else: - import urlparse - -from pika import __version__ -from pika import callback -from pika import channel -from pika import credentials as pika_credentials -from pika import exceptions -from pika import frame -from pika import heartbeat -from pika import utils - -from pika import spec - -from pika.compat import basestring, url_unquote, dictkeys - - -BACKPRESSURE_WARNING = ("Pika: Write buffer exceeded warning threshold at " - "%i bytes and an estimated %i frames behind") -PRODUCT = "Pika Python Client Library" - -LOGGER = logging.getLogger(__name__) - - -class Parameters(object): - """Base connection parameters class definition - - :param str DEFAULT_HOST: 'localhost' - :param int DEFAULT_PORT: 5672 - :param str DEFAULT_VIRTUAL_HOST: '/' - :param str DEFAULT_USERNAME: 'guest' - :param str DEFAULT_PASSWORD: 'guest' - :param int DEFAULT_HEARTBEAT_INTERVAL: None - :param int DEFAULT_CHANNEL_MAX: 0 - :param int DEFAULT_FRAME_MAX: pika.spec.FRAME_MAX_SIZE - :param str DEFAULT_LOCALE: 'en_US' - :param int DEFAULT_CONNECTION_ATTEMPTS: 1 - :param int|float DEFAULT_RETRY_DELAY: 2.0 - :param int|float DEFAULT_SOCKET_TIMEOUT: 0.25 - :param bool DEFAULT_SSL: False - :param dict DEFAULT_SSL_OPTIONS: {} - :param int DEFAULT_SSL_PORT: 5671 - :param bool DEFAULT_BACKPRESSURE_DETECTION: False - - """ - DEFAULT_BACKPRESSURE_DETECTION = False - DEFAULT_CONNECTION_ATTEMPTS = 1 - DEFAULT_CHANNEL_MAX = 0 - DEFAULT_FRAME_MAX = spec.FRAME_MAX_SIZE - DEFAULT_HEARTBEAT_INTERVAL = None # accept server's proposal - DEFAULT_HOST = 'localhost' - DEFAULT_LOCALE = 'en_US' - DEFAULT_PASSWORD = 'guest' - DEFAULT_PORT = 5672 - DEFAULT_RETRY_DELAY = 2.0 - DEFAULT_SOCKET_TIMEOUT = 0.25 - DEFAULT_SSL = False - DEFAULT_SSL_OPTIONS = {} - DEFAULT_SSL_PORT = 5671 - DEFAULT_USERNAME = 'guest' - DEFAULT_VIRTUAL_HOST = '/' - - def __init__(self): - self.virtual_host = self.DEFAULT_VIRTUAL_HOST - self.backpressure_detection = self.DEFAULT_BACKPRESSURE_DETECTION - self.channel_max = self.DEFAULT_CHANNEL_MAX - self.connection_attempts = self.DEFAULT_CONNECTION_ATTEMPTS - self.credentials = self._credentials(self.DEFAULT_USERNAME, - self.DEFAULT_PASSWORD) - self.frame_max = self.DEFAULT_FRAME_MAX - self.heartbeat = self.DEFAULT_HEARTBEAT_INTERVAL - self.host = self.DEFAULT_HOST - self.locale = self.DEFAULT_LOCALE - self.port = self.DEFAULT_PORT - self.retry_delay = self.DEFAULT_RETRY_DELAY - self.ssl = self.DEFAULT_SSL - self.ssl_options = self.DEFAULT_SSL_OPTIONS - self.socket_timeout = self.DEFAULT_SOCKET_TIMEOUT - - def __repr__(self): - """Represent the info about the instance. - - :rtype: str - - """ - return ('<%s host=%s port=%s virtual_host=%s ssl=%s>' % - (self.__class__.__name__, self.host, self.port, - self.virtual_host, self.ssl)) - - def _credentials(self, username, password): - """Return a plain credentials object for the specified username and - password. - - :param str username: The username to use - :param str password: The password to use - :rtype: pika_credentials.PlainCredentials - - """ - return pika_credentials.PlainCredentials(username, password) - - def _validate_backpressure(self, backpressure_detection): - """Validate that the backpressure detection option is a bool. - - :param bool backpressure_detection: The backpressure detection value - :rtype: bool - :raises: TypeError - - """ - if not isinstance(backpressure_detection, bool): - raise TypeError('backpressure detection must be a bool') - return True - - def _validate_channel_max(self, channel_max): - """Validate that the channel_max value is an int - - :param int channel_max: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(channel_max, int): - raise TypeError('channel_max must be an int') - if channel_max < 1 or channel_max > 65535: - raise ValueError('channel_max must be <= 65535 and > 0') - return True - - def _validate_connection_attempts(self, connection_attempts): - """Validate that the connection_attempts value is an int - - :param int connection_attempts: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(connection_attempts, int): - raise TypeError('connection_attempts must be an int') - if connection_attempts < 1: - raise ValueError('connection_attempts must be None or > 0') - return True - - def _validate_credentials(self, credentials): - """Validate the credentials passed in are using a valid object type. - - :param pika.credentials.Credentials credentials: Credentials to validate - :rtype: bool - :raises: TypeError - - """ - for credential_type in pika_credentials.VALID_TYPES: - if isinstance(credentials, credential_type): - return True - raise TypeError('Credentials must be an object of type: %r' % - pika_credentials.VALID_TYPES) - - def _validate_frame_max(self, frame_max): - """Validate that the frame_max value is an int and does not exceed - the maximum frame size and is not less than the frame min size. - - :param int frame_max: The value to validate - :rtype: bool - :raises: TypeError - :raises: InvalidMinimumFrameSize - - """ - if not isinstance(frame_max, int): - raise TypeError('frame_max must be an int') - if frame_max < spec.FRAME_MIN_SIZE: - raise exceptions.InvalidMinimumFrameSize - elif frame_max > spec.FRAME_MAX_SIZE: - raise exceptions.InvalidMaximumFrameSize - return True - - def _validate_heartbeat_interval(self, heartbeat_interval): - """Validate that the heartbeat_interval value is an int - - :param int heartbeat_interval: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(heartbeat_interval, int): - raise TypeError('heartbeat must be an int') - if heartbeat_interval < 0: - raise ValueError('heartbeat_interval must >= 0') - return True - - def _validate_host(self, host): - """Validate that the host value is an str - - :param str|unicode host: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(host, basestring): - raise TypeError('host must be a str or unicode str') - return True - - def _validate_locale(self, locale): - """Validate that the locale value is an str - - :param str locale: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(locale, basestring): - raise TypeError('locale must be a str') - return True - - def _validate_port(self, port): - """Validate that the port value is an int - - :param int port: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(port, int): - raise TypeError('port must be an int') - return True - - def _validate_retry_delay(self, retry_delay): - """Validate that the retry_delay value is an int or float - - :param int|float retry_delay: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not any([isinstance(retry_delay, int), - isinstance(retry_delay, float)]): - raise TypeError('retry_delay must be a float or int') - return True - - def _validate_socket_timeout(self, socket_timeout): - """Validate that the socket_timeout value is an int or float - - :param int|float socket_timeout: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not any([isinstance(socket_timeout, int), - isinstance(socket_timeout, float)]): - raise TypeError('socket_timeout must be a float or int') - if not socket_timeout > 0: - raise ValueError('socket_timeout must be > 0') - return True - - def _validate_ssl(self, ssl): - """Validate the SSL toggle is a bool - - :param bool ssl: The SSL enabled/disabled value - :rtype: bool - :raises: TypeError - - """ - if not isinstance(ssl, bool): - raise TypeError('ssl must be a bool') - return True - - def _validate_ssl_options(self, ssl_options): - """Validate the SSL options value is a dictionary. - - :param dict|None ssl_options: SSL Options to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(ssl_options, dict) and ssl_options is not None: - raise TypeError('ssl_options must be either None or dict') - return True - - def _validate_virtual_host(self, virtual_host): - """Validate that the virtual_host value is an str - - :param str virtual_host: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(virtual_host, basestring): - raise TypeError('virtual_host must be a str') - return True - - -class ConnectionParameters(Parameters): - """Connection parameters object that is passed into the connection adapter - upon construction. - - :param str host: Hostname or IP Address to connect to - :param int port: TCP port to connect to - :param str virtual_host: RabbitMQ virtual host to use - :param pika.credentials.Credentials credentials: auth credentials - :param int channel_max: Maximum number of channels to allow - :param int frame_max: The maximum byte size for an AMQP frame - :param int heartbeat_interval: How often to send heartbeats - :param bool ssl: Enable SSL - :param dict ssl_options: Arguments passed to ssl.wrap_socket as - :param int connection_attempts: Maximum number of retry attempts - :param int|float retry_delay: Time to wait in seconds, before the next - :param int|float socket_timeout: Use for high latency networks - :param str locale: Set the locale value - :param bool backpressure_detection: Toggle backpressure detection - - """ - - def __init__(self, - host=None, - port=None, - virtual_host=None, - credentials=None, - channel_max=None, - frame_max=None, - heartbeat_interval=None, - ssl=None, - ssl_options=None, - connection_attempts=None, - retry_delay=None, - socket_timeout=None, - locale=None, - backpressure_detection=None): - """Create a new ConnectionParameters instance. - - :param str host: Hostname or IP Address to connect to - :param int port: TCP port to connect to - :param str virtual_host: RabbitMQ virtual host to use - :param pika.credentials.Credentials credentials: auth credentials - :param int channel_max: Maximum number of channels to allow - :param int frame_max: The maximum byte size for an AMQP frame - :param int heartbeat_interval: How often to send heartbeats. - Min between this value and server's proposal - will be used. Use 0 to deactivate heartbeats - and None to accept server's proposal. - :param bool ssl: Enable SSL - :param dict ssl_options: Arguments passed to ssl.wrap_socket - :param int connection_attempts: Maximum number of retry attempts - :param int|float retry_delay: Time to wait in seconds, before the next - :param int|float socket_timeout: Use for high latency networks - :param str locale: Set the locale value - :param bool backpressure_detection: Toggle backpressure detection - - """ - super(ConnectionParameters, self).__init__() - - # Create the default credentials object - if not credentials: - credentials = self._credentials(self.DEFAULT_USERNAME, - self.DEFAULT_PASSWORD) - - # Assign the values - if host and self._validate_host(host): - self.host = host - if port is not None and self._validate_port(port): - self.port = port - if virtual_host and self._validate_virtual_host(virtual_host): - self.virtual_host = virtual_host - if credentials and self._validate_credentials(credentials): - self.credentials = credentials - if channel_max is not None and self._validate_channel_max(channel_max): - self.channel_max = channel_max - if frame_max is not None and self._validate_frame_max(frame_max): - self.frame_max = frame_max - if locale and self._validate_locale(locale): - self.locale = locale - if (heartbeat_interval is not None and - self._validate_heartbeat_interval(heartbeat_interval)): - self.heartbeat = heartbeat_interval - if ssl is not None and self._validate_ssl(ssl): - self.ssl = ssl - if ssl_options and self._validate_ssl_options(ssl_options): - self.ssl_options = ssl_options or dict() - if (connection_attempts is not None and - self._validate_connection_attempts(connection_attempts)): - self.connection_attempts = connection_attempts - if retry_delay is not None and self._validate_retry_delay(retry_delay): - self.retry_delay = retry_delay - if (socket_timeout is not None and - self._validate_socket_timeout(socket_timeout)): - self.socket_timeout = socket_timeout - if (backpressure_detection is not None and - self._validate_backpressure(backpressure_detection)): - self.backpressure_detection = backpressure_detection - - -class URLParameters(Parameters): - """Connect to RabbitMQ via an AMQP URL in the format:: - - amqp://username:password@host:port/[?query-string] - - Ensure that the virtual host is URI encoded when specified. For example if - you are using the default "/" virtual host, the value should be `%2f`. - - Valid query string values are: - - - backpressure_detection: - Toggle backpressure detection, possible values are `t` or `f` - - channel_max: - Override the default maximum channel count value - - connection_attempts: - Specify how many times pika should try and reconnect before it gives up - - frame_max: - Override the default maximum frame size for communication - - heartbeat_interval: - Specify the number of seconds between heartbeat frames to ensure that - the link between RabbitMQ and your application is up - - locale: - Override the default `en_US` locale value - - ssl: - Toggle SSL, possible values are `t`, `f` - - ssl_options: - Arguments passed to :meth:`ssl.wrap_socket` - - retry_delay: - The number of seconds to sleep before attempting to connect on - connection failure. - - socket_timeout: - Override low level socket timeout value - - :param str url: The AMQP URL to connect to - - """ - - def __init__(self, url): - """Create a new URLParameters instance. - - :param str url: The URL value - - """ - super(URLParameters, self).__init__() - self._process_url(url) - - def _process_url(self, url): - """Take an AMQP URL and break it up into the various parameters. - - :param str url: The URL to parse - - """ - if url[0:4] == 'amqp': - url = 'http' + url[4:] - - parts = urlparse.urlparse(url) - - # Handle the Protocol scheme, changing to HTTPS so urlparse doesnt barf - if parts.scheme == 'https': - self.ssl = True - - if self._validate_host(parts.hostname): - self.host = parts.hostname - if not parts.port: - if self.ssl: - self.port = self.DEFAULT_SSL_PORT if \ - self.ssl else self.DEFAULT_PORT - elif self._validate_port(parts.port): - self.port = parts.port - - if parts.username is not None: - self.credentials = pika_credentials.PlainCredentials(parts.username, - parts.password) - - # Get the Virtual Host - if len(parts.path) <= 1: - self.virtual_host = self.DEFAULT_VIRTUAL_HOST - else: - path_parts = parts.path.split('/') - virtual_host = url_unquote(path_parts[1]) - if self._validate_virtual_host(virtual_host): - self.virtual_host = virtual_host - - # Handle query string values, validating and assigning them - values = urlparse.parse_qs(parts.query) - - # Cast the various numeric values to the appropriate values - for key in dictkeys(values): - # Always reassign the first list item in query values - values[key] = values[key].pop(0) - if values[key].isdigit(): - values[key] = int(values[key]) - else: - try: - values[key] = float(values[key]) - except ValueError: - pass - - if 'backpressure_detection' in values: - if values['backpressure_detection'] == 't': - self.backpressure_detection = True - elif values['backpressure_detection'] == 'f': - self.backpressure_detection = False - else: - raise ValueError('Invalid backpressure_detection value: %s' % - values['backpressure_detection']) - - if ('channel_max' in values and - self._validate_channel_max(values['channel_max'])): - self.channel_max = values['channel_max'] - - if ('connection_attempts' in values and - self._validate_connection_attempts(values['connection_attempts'])): - self.connection_attempts = values['connection_attempts'] - - if ('frame_max' in values and - self._validate_frame_max(values['frame_max'])): - self.frame_max = values['frame_max'] - - if ('heartbeat_interval' in values and - self._validate_heartbeat_interval(values['heartbeat_interval'])): - self.heartbeat = values['heartbeat_interval'] - - if ('locale' in values and self._validate_locale(values['locale'])): - self.locale = values['locale'] - - if ('retry_delay' in values and - self._validate_retry_delay(values['retry_delay'])): - self.retry_delay = values['retry_delay'] - - if ('socket_timeout' in values and - self._validate_socket_timeout(values['socket_timeout'])): - self.socket_timeout = values['socket_timeout'] - - if 'ssl_options' in values: - options = ast.literal_eval(values['ssl_options']) - if self._validate_ssl_options(options): - self.ssl_options = options - - -class Connection(object): - """This is the core class that implements communication with RabbitMQ. This - class should not be invoked directly but rather through the use of an - adapter such as SelectConnection or BlockingConnection. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Called when the connection is opened - :param method on_open_error_callback: Called if the connection cant - be opened - :param method on_close_callback: Called when the connection is closed - - """ - ON_CONNECTION_BACKPRESSURE = '_on_connection_backpressure' - ON_CONNECTION_BLOCKED = '_on_connection_blocked' - ON_CONNECTION_CLOSED = '_on_connection_closed' - ON_CONNECTION_ERROR = '_on_connection_error' - ON_CONNECTION_OPEN = '_on_connection_open' - ON_CONNECTION_UNBLOCKED = '_on_connection_unblocked' - CONNECTION_CLOSED = 0 - CONNECTION_INIT = 1 - CONNECTION_PROTOCOL = 2 - CONNECTION_START = 3 - CONNECTION_TUNE = 4 - CONNECTION_OPEN = 5 - CONNECTION_CLOSING = 6 - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None): - """Connection initialization expects an object that has implemented the - Parameters class and a callback function to notify when we have - successfully connected to the AMQP Broker. - - Available Parameters classes are the ConnectionParameters class and - URLParameters class. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Called when the connection is opened - :param method on_open_error_callback: Called if the connection cant - be opened - :param method on_close_callback: Called when the connection is closed - - """ - self._write_lock = threading.Lock() - - # Define our callback dictionary - self.callbacks = callback.CallbackManager() - - # Add the on connection error callback - self.callbacks.add(0, self.ON_CONNECTION_ERROR, - on_open_error_callback or self._on_connection_error, - False) - - self.heartbeat = None - - # On connection callback - if on_open_callback: - self.add_on_open_callback(on_open_callback) - - # On connection callback - if on_close_callback: - self.add_on_close_callback(on_close_callback) - - # Set our configuration options - self.params = parameters or ConnectionParameters() - - # Initialize the connection state and connect - self._init_connection_state() - self.connect() - - def add_backpressure_callback(self, callback_method): - """Call method "callback" when pika believes backpressure is being - applied. - - :param method callback_method: The method to call - - """ - self.callbacks.add(0, self.ON_CONNECTION_BACKPRESSURE, callback_method, - False) - - def add_on_close_callback(self, callback_method): - """Add a callback notification when the connection has closed. The - callback will be passed the connection, the reply_code (int) and the - reply_text (str), if sent by the remote server. - - :param method callback_method: Callback to call on close - - """ - self.callbacks.add(0, self.ON_CONNECTION_CLOSED, callback_method, False) - - def add_on_connection_blocked_callback(self, callback_method): - """Add a callback to be notified when RabbitMQ has sent a - ``Connection.Blocked`` frame indicating that RabbitMQ is low on - resources. Publishers can use this to voluntarily suspend publishing, - instead of relying on back pressure throttling. The callback - will be passed the ``Connection.Blocked`` method frame. - - :param method callback_method: Callback to call on `Connection.Blocked` - - """ - self.callbacks.add(0, spec.Connection.Blocked, callback_method, False) - - def add_on_connection_unblocked_callback(self, callback_method): - """Add a callback to be notified when RabbitMQ has sent a - ``Connection.Unblocked`` frame letting publishers know it's ok - to start publishing again. The callback will be passed the - ``Connection.Unblocked`` method frame. - - :param method callback_method: Callback to call on - `Connection.Unblocked` - - """ - self.callbacks.add(0, spec.Connection.Unblocked, callback_method, False) - - def add_on_open_callback(self, callback_method): - """Add a callback notification when the connection has opened. - - :param method callback_method: Callback to call when open - - """ - self.callbacks.add(0, self.ON_CONNECTION_OPEN, callback_method, False) - - def add_on_open_error_callback(self, callback_method, remove_default=True): - """Add a callback notification when the connection can not be opened. - - The callback method should accept the connection object that could not - connect, and an optional error message. - - :param method callback_method: Callback to call when can't connect - :param bool remove_default: Remove default exception raising callback - - """ - if remove_default: - self.callbacks.remove(0, self.ON_CONNECTION_ERROR, - self._on_connection_error) - self.callbacks.add(0, self.ON_CONNECTION_ERROR, callback_method, False) - - def add_timeout(self, deadline, callback_method): - """Adapters should override to call the callback after the - specified number of seconds have elapsed, using a timer, or a - thread, or similar. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - - """ - raise NotImplementedError - - def channel(self, on_open_callback, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - :param method on_open_callback: The callback when the channel is opened - :param int channel_number: The channel number to use, defaults to the - next available. - :rtype: pika.channel.Channel - - """ - if not channel_number: - channel_number = self._next_channel_number() - self._channels[channel_number] = self._create_channel(channel_number, - on_open_callback) - self._add_channel_callbacks(channel_number) - self._channels[channel_number].open() - return self._channels[channel_number] - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - if self.is_closing or self.is_closed: - return - - if self._has_open_channels: - self._close_channels(reply_code, reply_text) - - # Set our connection state - self._set_connection_state(self.CONNECTION_CLOSING) - LOGGER.info("Closing connection (%s): %s", reply_code, reply_text) - self.closing = reply_code, reply_text - - if not self._has_open_channels: - # if there are open channels then _on_close_ready will finally be - # called in _on_channel_cleanup once all channels have been closed - self._on_close_ready() - - def connect(self): - """Invoke if trying to reconnect to a RabbitMQ server. Constructing the - Connection object should connect on its own. - - """ - self._set_connection_state(self.CONNECTION_INIT) - error = self._adapter_connect() - if not error: - return self._on_connected() - self.remaining_connection_attempts -= 1 - LOGGER.warning('Could not connect, %i attempts left', - self.remaining_connection_attempts) - if self.remaining_connection_attempts: - LOGGER.info('Retrying in %i seconds', self.params.retry_delay) - self.add_timeout(self.params.retry_delay, self.connect) - else: - self.callbacks.process(0, self.ON_CONNECTION_ERROR, self, self, - error) - self.remaining_connection_attempts = self.params.connection_attempts - self._set_connection_state(self.CONNECTION_CLOSED) - - def remove_timeout(self, callback_method): - """Adapters should override to call the callback after the - specified number of seconds have elapsed, using a timer, or a - thread, or similar. - - :param method callback_method: The callback to remove a timeout for - - """ - raise NotImplementedError - - def set_backpressure_multiplier(self, value=10): - """Alter the backpressure multiplier value. We set this to 10 by default. - This value is used to raise warnings and trigger the backpressure - callback. - - :param int value: The multiplier value to set - - """ - self._backpressure = value - - # - # Connections state properties - # - - @property - def is_closed(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_CLOSED - - @property - def is_closing(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_CLOSING - - @property - def is_open(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_OPEN - - # - # Properties that reflect server capabilities for the current connection - # - - @property - def basic_nack(self): - """Specifies if the server supports basic.nack on the active connection. - - :rtype: bool - - """ - return self.server_capabilities.get('basic.nack', False) - - @property - def consumer_cancel_notify(self): - """Specifies if the server supports consumer cancel notification on the - active connection. - - :rtype: bool - - """ - return self.server_capabilities.get('consumer_cancel_notify', False) - - @property - def exchange_exchange_bindings(self): - """Specifies if the active connection supports exchange to exchange - bindings. - - :rtype: bool - - """ - return self.server_capabilities.get('exchange_exchange_bindings', False) - - @property - def publisher_confirms(self): - """Specifies if the active connection can use publisher confirmations. - - :rtype: bool - - """ - return self.server_capabilities.get('publisher_confirms', False) - - # - # Internal methods for managing the communication process - # - - def _adapter_connect(self): - """Subclasses should override to set up the outbound socket connection. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _adapter_disconnect(self): - """Subclasses should override this to cause the underlying transport - (socket) to close. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _add_channel_callbacks(self, channel_number): - """Add the appropriate callbacks for the specified channel number. - - :param int channel_number: The channel number for the callbacks - - """ - # This permits us to garbage-collect our reference to the channel - # regardless of whether it was closed by client or broker, and do so - # after all channel-close callbacks. - self._channels[channel_number]._add_on_cleanup_callback( - self._on_channel_cleanup) - - def _add_connection_start_callback(self): - """Add a callback for when a Connection.Start frame is received from - the broker. - - """ - self.callbacks.add(0, spec.Connection.Start, self._on_connection_start) - - def _add_connection_tune_callback(self): - """Add a callback for when a Connection.Tune frame is received.""" - self.callbacks.add(0, spec.Connection.Tune, self._on_connection_tune) - - def _append_frame_buffer(self, value): - """Append the bytes to the frame buffer. - - :param str value: The bytes to append to the frame buffer - - """ - self._frame_buffer += value - - @property - def _buffer_size(self): - """Return the suggested buffer size from the connection state/tune or - the default if that is None. - - :rtype: int - - """ - return self.params.frame_max or spec.FRAME_MAX_SIZE - - def _check_for_protocol_mismatch(self, value): - """Invoked when starting a connection to make sure it's a supported - protocol. - - :param pika.frame.Method value: The frame to check - :raises: ProtocolVersionMismatch - - """ - if (value.method.version_major, - value.method.version_minor) != spec.PROTOCOL_VERSION[0:2]: - raise exceptions.ProtocolVersionMismatch(frame.ProtocolHeader(), - value) - - @property - def _client_properties(self): - """Return the client properties dictionary. - - :rtype: dict - - """ - return { - 'product': PRODUCT, - 'platform': 'Python %s' % platform.python_version(), - 'capabilities': { - 'authentication_failure_close': True, - 'basic.nack': True, - 'connection.blocked': True, - 'consumer_cancel_notify': True, - 'publisher_confirms': True - }, - 'information': 'See http://pika.rtfd.org', - 'version': __version__ - } - - def _close_channels(self, reply_code, reply_text): - """Close the open channels with the specified reply_code and reply_text. - - :param int reply_code: The code for why the channels are being closed - :param str reply_text: The text reason for why the channels are closing - - """ - if self.is_open: - for channel_number in dictkeys(self._channels): - if self._channels[channel_number].is_open: - self._channels[channel_number].close(reply_code, reply_text) - else: - del self._channels[channel_number] - # Force any lingering callbacks to be removed - # moved inside else block since channel's _cleanup removes - # callbacks - self.callbacks.cleanup(channel_number) - else: - self._channels = dict() - - def _combine(self, a, b): - """Pass in two values, if a is 0, return b otherwise if b is 0, - return a. If neither case matches return the smallest value. - - :param int a: The first value - :param int b: The second value - :rtype: int - - """ - return min(a, b) or (a or b) - - def _connect(self): - """Attempt to connect to RabbitMQ - - :rtype: bool - - """ - warnings.warn('This method is deprecated, use Connection.connect', - DeprecationWarning) - - def _create_channel(self, channel_number, on_open_callback): - """Create a new channel using the specified channel number and calling - back the method specified by on_open_callback - - :param int channel_number: The channel number to use - :param method on_open_callback: The callback when the channel is opened - - """ - LOGGER.debug('Creating channel %s', channel_number) - return channel.Channel(self, channel_number, on_open_callback) - - def _create_heartbeat_checker(self): - """Create a heartbeat checker instance if there is a heartbeat interval - set. - - :rtype: pika.heartbeat.Heartbeat - - """ - if self.params.heartbeat is not None and self.params.heartbeat > 0: - LOGGER.debug('Creating a HeartbeatChecker: %r', - self.params.heartbeat) - return heartbeat.HeartbeatChecker(self, self.params.heartbeat) - - def _remove_heartbeat(self): - """Stop the heartbeat checker if it exists - - """ - if self.heartbeat: - self.heartbeat.stop() - self.heartbeat = None - - def _deliver_frame_to_channel(self, value): - """Deliver the frame to the channel specified in the frame. - - :param pika.frame.Method value: The frame to deliver - - """ - if not value.channel_number in self._channels: - if self._is_basic_deliver_frame(value): - self._reject_out_of_band_delivery(value.channel_number, - value.method.delivery_tag) - else: - LOGGER.warning("Received %r for non-existing channel %i", value, - value.channel_number) - return - return self._channels[value.channel_number]._handle_content_frame(value) - - def _detect_backpressure(self): - """Attempt to calculate if TCP backpressure is being applied due to - our outbound buffer being larger than the average frame size over - a window of frames. - - """ - avg_frame_size = self.bytes_sent / self.frames_sent - buffer_size = sum([len(frame) for frame in self.outbound_buffer]) - if buffer_size > (avg_frame_size * self._backpressure): - LOGGER.warning(BACKPRESSURE_WARNING, buffer_size, - int(buffer_size / avg_frame_size)) - self.callbacks.process(0, self.ON_CONNECTION_BACKPRESSURE, self) - - def _ensure_closed(self): - """If the connection is not closed, close it.""" - if self.is_open: - self.close() - - def _flush_outbound(self): - """Adapters should override to flush the contents of outbound_buffer - out along the socket. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _get_body_frame_max_length(self): - """Calculate the maximum amount of bytes that can be in a body frame. - - :rtype: int - - """ - return ( - self.params.frame_max - spec.FRAME_HEADER_SIZE - spec.FRAME_END_SIZE - ) - - def _get_credentials(self, method_frame): - """Get credentials for authentication. - - :param pika.frame.MethodFrame method_frame: The Connection.Start frame - :rtype: tuple(str, str) - - """ - (auth_type, - response) = self.params.credentials.response_for(method_frame.method) - if not auth_type: - raise exceptions.AuthenticationError(self.params.credentials.TYPE) - self.params.credentials.erase_credentials() - return auth_type, response - - @property - def _has_open_channels(self): - """Returns true if channels are open. - - :rtype: bool - - """ - return any([self._channels[num].is_open - for num in dictkeys(self._channels)]) - - def _has_pending_callbacks(self, value): - """Return true if there are any callbacks pending for the specified - frame. - - :param pika.frame.Method value: The frame to check - :rtype: bool - - """ - return self.callbacks.pending(value.channel_number, value.method) - - def _init_connection_state(self): - """Initialize or reset all of the internal state variables for a given - connection. On disconnect or reconnect all of the state needs to - be wiped. - - """ - # Connection state - self._set_connection_state(self.CONNECTION_CLOSED) - - # Negotiated server properties - self.server_properties = None - - # Outbound buffer for buffering writes until we're able to send them - self.outbound_buffer = collections.deque([]) - - # Inbound buffer for decoding frames - self._frame_buffer = bytes() - - # Dict of open channels - self._channels = dict() - - # Remaining connection attempts - self.remaining_connection_attempts = self.params.connection_attempts - - # Data used for Heartbeat checking and back-pressure detection - self.bytes_sent = 0 - self.bytes_received = 0 - self.frames_sent = 0 - self.frames_received = 0 - self.heartbeat = None - - # Default back-pressure multiplier value - self._backpressure = 10 - - # When closing, hold reason why - self.closing = 0, 'Not specified' - - # Our starting point once connected, first frame received - self._add_connection_start_callback() - - def _is_basic_deliver_frame(self, frame_value): - """Returns true if the frame is a Basic.Deliver - - :param pika.frame.Method frame_value: The frame to check - :rtype: bool - - """ - return isinstance(frame_value, spec.Basic.Deliver) - - def _is_connection_close_frame(self, value): - """Returns true if the frame is a Connection.Close frame. - - :param pika.frame.Method value: The frame to check - :rtype: bool - - """ - if not value: - return False - return isinstance(value.method, spec.Connection.Close) - - def _is_method_frame(self, value): - """Returns true if the frame is a method frame. - - :param pika.frame.Frame value: The frame to evaluate - :rtype: bool - - """ - return isinstance(value, frame.Method) - - def _is_protocol_header_frame(self, value): - """Returns True if it's a protocol header frame. - - :rtype: bool - - """ - return isinstance(value, frame.ProtocolHeader) - - def _next_channel_number(self): - """Return the next available channel number or raise an exception. - - :rtype: int - - """ - limit = self.params.channel_max or channel.MAX_CHANNELS - if len(self._channels) == limit: - raise exceptions.NoFreeChannels() - - ckeys = set(self._channels.keys()) - if not ckeys: - return 1 - return [x + 1 for x in sorted(ckeys) if x + 1 not in ckeys][0] - - def _on_channel_cleanup(self, channel): - """Remove the channel from the dict of channels when Channel.CloseOk is - sent. If connection is closing and no more channels remain, proceed to - `_on_close_ready`. - - :param pika.channel.Channel channel: channel instance - - """ - try: - del self._channels[channel.channel_number] - LOGGER.debug('Removed channel %s', channel.channel_number) - except KeyError: - LOGGER.error('Channel %r not in channels', - channel.channel_number) - if self.is_closing and not self._has_open_channels: - self._on_close_ready() - - def _on_close_ready(self): - """Called when the Connection is in a state that it can close after - a close has been requested. This happens, for example, when all of the - channels are closed that were open when the close request was made. - - """ - if self.is_closed: - LOGGER.warning('Invoked while already closed') - return - self._send_connection_close(self.closing[0], self.closing[1]) - - def _on_connected(self): - """Invoked when the socket is connected and it's time to start speaking - AMQP with the broker. - - """ - self._set_connection_state(self.CONNECTION_PROTOCOL) - - # Start the communication with the RabbitMQ Broker - self._send_frame(frame.ProtocolHeader()) - - def _on_connection_closed(self, method_frame, from_adapter=False): - """Called when the connection is closed remotely. The from_adapter value - will be true if the connection adapter has been disconnected from - the broker and the method was invoked directly instead of by receiving - a Connection.Close frame. - - :param pika.frame.Method: The Connection.Close frame - :param bool from_adapter: Called by the connection adapter - - """ - if method_frame and self._is_connection_close_frame(method_frame): - self.closing = (method_frame.method.reply_code, - method_frame.method.reply_text) - - # Save the codes because self.closing gets reset by _adapter_disconnect - reply_code, reply_text = self.closing - - # Stop the heartbeat checker if it exists - self._remove_heartbeat() - - # If this did not come from the connection adapter, close the socket - if not from_adapter: - self._adapter_disconnect() - - # Invoke a method frame neutral close - self._on_disconnect(reply_code, reply_text) - - def _on_connection_error(self, connection_unused, error_message=None): - """Default behavior when the connecting connection can not connect. - - :raises: exceptions.AMQPConnectionError - - """ - raise exceptions.AMQPConnectionError(error_message or - self.params.connection_attempts) - - def _on_connection_open(self, method_frame): - """ - This is called once we have tuned the connection with the server and - called the Connection.Open on the server and it has replied with - Connection.Ok. - """ - self.known_hosts = method_frame.method.known_hosts - - # Add a callback handler for the Broker telling us to disconnect - self.callbacks.add(0, spec.Connection.Close, self._on_connection_closed) - - # We're now connected at the AMQP level - self._set_connection_state(self.CONNECTION_OPEN) - - # Call our initial callback that we're open - self.callbacks.process(0, self.ON_CONNECTION_OPEN, self, self) - - def _on_connection_start(self, method_frame): - """This is called as a callback once we have received a Connection.Start - from the server. - - :param pika.frame.Method method_frame: The frame received - :raises: UnexpectedFrameError - - """ - self._set_connection_state(self.CONNECTION_START) - if self._is_protocol_header_frame(method_frame): - raise exceptions.UnexpectedFrameError - self._check_for_protocol_mismatch(method_frame) - self._set_server_information(method_frame) - self._add_connection_tune_callback() - self._send_connection_start_ok(*self._get_credentials(method_frame)) - - def _on_connection_tune(self, method_frame): - """Once the Broker sends back a Connection.Tune, we will set our tuning - variables that have been returned to us and kick off the Heartbeat - monitor if required, send our TuneOk and then the Connection. Open rpc - call on channel 0. - - :param pika.frame.Method method_frame: The frame received - - """ - self._set_connection_state(self.CONNECTION_TUNE) - - # Get our max channels, frames and heartbeat interval - self.params.channel_max = self._combine(self.params.channel_max, - method_frame.method.channel_max) - self.params.frame_max = self._combine(self.params.frame_max, - method_frame.method.frame_max) - if self.params.heartbeat is None: - self.params.heartbeat = method_frame.method.heartbeat - elif self.params.heartbeat != 0: - self.params.heartbeat = self._combine(self.params.heartbeat, - method_frame.method.heartbeat) - - # Calculate the maximum pieces for body frames - self._body_max_length = self._get_body_frame_max_length() - - # Create a new heartbeat checker if needed - self.heartbeat = self._create_heartbeat_checker() - - # Send the TuneOk response with what we've agreed upon - self._send_connection_tune_ok() - - # Send the Connection.Open RPC call for the vhost - self._send_connection_open() - - def _on_data_available(self, data_in): - """This is called by our Adapter, passing in the data from the socket. - As long as we have buffer try and map out frame data. - - :param str data_in: The data that is available to read - - """ - self._append_frame_buffer(data_in) - while self._frame_buffer: - consumed_count, frame_value = self._read_frame() - if not frame_value: - return - self._trim_frame_buffer(consumed_count) - self._process_frame(frame_value) - - def _on_disconnect(self, reply_code, reply_text): - """Invoke passing in the reply_code and reply_text from internal - methods to the adapter. Called from on_connection_closed and Heartbeat - timeouts. - - :param str reply_code: The numeric close code - :param str reply_text: The text close reason - - """ - LOGGER.warning('Disconnected from RabbitMQ at %s:%i (%s): %s', - self.params.host, self.params.port, reply_code, - reply_text) - self._set_connection_state(self.CONNECTION_CLOSED) - for channel in dictkeys(self._channels): - if channel not in self._channels: - continue - method_frame = frame.Method(channel, spec.Channel.Close(reply_code, - reply_text)) - self._channels[channel]._on_close(method_frame) - self._process_connection_closed_callbacks(reply_code, reply_text) - self._remove_connection_callbacks() - - def _process_callbacks(self, frame_value): - """Process the callbacks for the frame if the frame is a method frame - and if it has any callbacks pending. - - :param pika.frame.Method frame_value: The frame to process - :rtype: bool - - """ - if (self._is_method_frame(frame_value) and - self._has_pending_callbacks(frame_value)): - self.callbacks.process(frame_value.channel_number, # Prefix - frame_value.method, # Key - self, # Caller - frame_value) # Args - return True - return False - - def _process_connection_closed_callbacks(self, reason_code, reason_text): - """Process any callbacks that should be called when the connection is - closed. - - :param str reason_code: The numeric code from RabbitMQ for the close - :param str reason_text: The text reason fro closing - - """ - self.callbacks.process(0, self.ON_CONNECTION_CLOSED, self, self, - reason_code, reason_text) - - def _process_frame(self, frame_value): - """Process an inbound frame from the socket. - - :param frame_value: The frame to process - :type frame_value: pika.frame.Frame | pika.frame.Method - - """ - # Will receive a frame type of -1 if protocol version mismatch - if frame_value.frame_type < 0: - return - - # Keep track of how many frames have been read - self.frames_received += 1 - - # Process any callbacks, if True, exit method - if self._process_callbacks(frame_value): - return - - # If a heartbeat is received, update the checker - if isinstance(frame_value, frame.Heartbeat): - if self.heartbeat: - self.heartbeat.received() - else: - LOGGER.warning('Received heartbeat frame without a heartbeat ' - 'checker') - - # If the frame has a channel number beyond the base channel, deliver it - elif frame_value.channel_number > 0: - self._deliver_frame_to_channel(frame_value) - - def _read_frame(self): - """Try and read from the frame buffer and decode a frame. - - :rtype tuple: (int, pika.frame.Frame) - - """ - return frame.decode_frame(self._frame_buffer) - - def _reject_out_of_band_delivery(self, channel_number, delivery_tag): - """Reject a delivery on the specified channel number and delivery tag - because said channel no longer exists. - - :param int channel_number: The channel number - :param int delivery_tag: The delivery tag - - """ - LOGGER.warning('Rejected out-of-band delivery on channel %i (%s)', - channel_number, delivery_tag) - self._send_method(channel_number, spec.Basic.Reject(delivery_tag)) - - def _remove_callback(self, channel_number, method_frame): - """Remove the specified method_frame callback if it is set for the - specified channel number. - - :param int channel_number: The channel number to remove the callback on - :param pika.object.Method: The method frame for the callback - - """ - self.callbacks.remove(str(channel_number), method_frame) - - def _remove_callbacks(self, channel_number, method_frames): - """Remove the callbacks for the specified channel number and list of - method frames. - - :param int channel_number: The channel number to remove the callback on - :param list method_frames: The method frames for the callback - - """ - for method_frame in method_frames: - self._remove_callback(channel_number, method_frame) - - def _remove_connection_callbacks(self): - """Remove all callbacks for the connection""" - self._remove_callbacks(0, [spec.Connection.Close, spec.Connection.Start, - spec.Connection.Open]) - - def _rpc(self, channel_number, method_frame, - callback_method=None, - acceptable_replies=None): - """Make an RPC call for the given callback, channel number and method. - acceptable_replies lists out what responses we'll process from the - server with the specified callback. - - :param int channel_number: The channel number for the RPC call - :param pika.object.Method method_frame: The method frame to call - :param method callback_method: The callback for the RPC response - :param list acceptable_replies: The replies this RPC call expects - - """ - # Validate that acceptable_replies is a list or None - if acceptable_replies and not isinstance(acceptable_replies, list): - raise TypeError('acceptable_replies should be list or None') - - # Validate the callback is callable - if callback_method: - if not utils.is_callable(callback_method): - raise TypeError('callback should be None, function or method.') - - for reply in acceptable_replies: - self.callbacks.add(channel_number, reply, callback_method) - - # Send the rpc call to RabbitMQ - self._send_method(channel_number, method_frame) - - def _send_connection_close(self, reply_code, reply_text): - """Send a Connection.Close method frame. - - :param int reply_code: The reason for the close - :param str reply_text: The text reason for the close - - """ - self._rpc(0, spec.Connection.Close(reply_code, reply_text, 0, 0), - self._on_connection_closed, [spec.Connection.CloseOk]) - - def _send_connection_open(self): - """Send a Connection.Open frame""" - self._rpc(0, spec.Connection.Open(self.params.virtual_host, - insist=True), - self._on_connection_open, [spec.Connection.OpenOk]) - - def _send_connection_start_ok(self, authentication_type, response): - """Send a Connection.StartOk frame - - :param str authentication_type: The auth type value - :param str response: The encoded value to send - - """ - self._send_method(0, - spec.Connection.StartOk(self._client_properties, - authentication_type, response, - self.params.locale)) - - def _send_connection_tune_ok(self): - """Send a Connection.TuneOk frame""" - self._send_method(0, spec.Connection.TuneOk(self.params.channel_max, - self.params.frame_max, - self.params.heartbeat)) - - def _send_frame(self, frame_value): - """This appends the fully generated frame to send to the broker to the - output buffer which will be then sent via the connection adapter. - - :param frame_value: The frame to write - :type frame_value: pika.frame.Frame|pika.frame.ProtocolHeader - :raises: exceptions.ConnectionClosed - - """ - if self.is_closed: - LOGGER.critical('Attempted to send frame when closed') - raise exceptions.ConnectionClosed - - marshaled_frame = frame_value.marshal() - self.bytes_sent += len(marshaled_frame) - self.frames_sent += 1 - self.outbound_buffer.append(marshaled_frame) - self._flush_outbound() - if self.params.backpressure_detection: - self._detect_backpressure() - - def _send_method(self, channel_number, method_frame, content=None): - """Constructs a RPC method frame and then sends it to the broker. - - :param int channel_number: The channel number for the frame - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - if not content: - with self._write_lock: - self._send_frame(frame.Method(channel_number, method_frame)) - return - self._send_message(channel_number, method_frame, content) - - def _send_message(self, channel_number, method_frame, content=None): - """Send the message directly, bypassing the single _send_frame - invocation by directly appending to the output buffer and flushing - within a lock. - - :param int channel_number: The channel number for the frame - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - length = len(content[1]) - write_buffer = [frame.Method(channel_number, method_frame).marshal(), - frame.Header(channel_number, length, - content[0]).marshal()] - if content[1]: - chunks = int(math.ceil(float(length) / self._body_max_length)) - for chunk in range(0, chunks): - s = chunk * self._body_max_length - e = s + self._body_max_length - if e > length: - e = length - write_buffer.append(frame.Body(channel_number, - content[1][s:e]).marshal()) - - with self._write_lock: - self.outbound_buffer += write_buffer - self.frames_sent += len(write_buffer) - self._flush_outbound() - if self.params.backpressure_detection: - self._detect_backpressure() - - def _set_connection_state(self, connection_state): - """Set the connection state. - - :param int connection_state: The connection state to set - - """ - self.connection_state = connection_state - - def _set_server_information(self, method_frame): - """Set the server properties and capabilities - - :param spec.connection.Start method_frame: The Connection.Start frame - - """ - self.server_properties = method_frame.method.server_properties - self.server_capabilities = self.server_properties.get('capabilities', - dict()) - if hasattr(self.server_properties, 'capabilities'): - del self.server_properties['capabilities'] - - def _trim_frame_buffer(self, byte_count): - """Trim the leading N bytes off the frame buffer and increment the - counter that keeps track of how many bytes have been read/used from the - socket. - - :param int byte_count: The number of bytes consumed - - """ - self._frame_buffer = self._frame_buffer[byte_count:] - self.bytes_received += byte_count diff --git a/packages_o/pika-0.10.0/build/lib/pika/credentials.py b/packages_o/pika-0.10.0/build/lib/pika/credentials.py deleted file mode 100644 index 4bb3801b..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/credentials.py +++ /dev/null @@ -1,104 +0,0 @@ -"""The credentials classes are used to encapsulate all authentication -information for the :class:`~pika.connection.ConnectionParameters` class. - -The :class:`~pika.credentials.PlainCredentials` class returns the properly -formatted username and password to the :class:`~pika.connection.Connection`. - -To authenticate with Pika, create a :class:`~pika.credentials.PlainCredentials` -object passing in the username and password and pass it as the credentials -argument value to the :class:`~pika.connection.ConnectionParameters` object. - -If you are using :class:`~pika.connection.URLParameters` you do not need a -credentials object, one will automatically be created for you. - -If you are looking to implement SSL certificate style authentication, you would -extend the :class:`~pika.credentials.ExternalCredentials` class implementing -the required behavior. - -""" -from .compat import as_bytes -import logging - -LOGGER = logging.getLogger(__name__) - - -class PlainCredentials(object): - """A credentials object for the default authentication methodology with - RabbitMQ. - - If you do not pass in credentials to the ConnectionParameters object, it - will create credentials for 'guest' with the password of 'guest'. - - If you pass True to erase_on_connect the credentials will not be stored - in memory after the Connection attempt has been made. - - :param str username: The username to authenticate with - :param str password: The password to authenticate with - :param bool erase_on_connect: erase credentials on connect. - - """ - TYPE = 'PLAIN' - - def __init__(self, username, password, erase_on_connect=False): - """Create a new instance of PlainCredentials - - :param str username: The username to authenticate with - :param str password: The password to authenticate with - :param bool erase_on_connect: erase credentials on connect. - - """ - self.username = username - self.password = password - self.erase_on_connect = erase_on_connect - - def response_for(self, start): - """Validate that this type of authentication is supported - - :param spec.Connection.Start start: Connection.Start method - :rtype: tuple(str|None, str|None) - - """ - if as_bytes(PlainCredentials.TYPE) not in\ - as_bytes(start.mechanisms).split(): - return None, None - return (PlainCredentials.TYPE, - b'\0' + as_bytes(self.username) + - b'\0' + as_bytes(self.password)) - - def erase_credentials(self): - """Called by Connection when it no longer needs the credentials""" - if self.erase_on_connect: - LOGGER.info("Erasing stored credential values") - self.username = None - self.password = None - - -class ExternalCredentials(object): - """The ExternalCredentials class allows the connection to use EXTERNAL - authentication, generally with a client SSL certificate. - - """ - TYPE = 'EXTERNAL' - - def __init__(self): - """Create a new instance of ExternalCredentials""" - self.erase_on_connect = False - - def response_for(self, start): - """Validate that this type of authentication is supported - - :param spec.Connection.Start start: Connection.Start method - :rtype: tuple(str or None, str or None) - - """ - if as_bytes(ExternalCredentials.TYPE) not in\ - as_bytes(start.mechanisms).split(): - return None, None - return ExternalCredentials.TYPE, b'' - - def erase_credentials(self): - """Called by Connection when it no longer needs the credentials""" - LOGGER.debug('Not supported by this Credentials type') - -# Append custom credential types to this list for validation support -VALID_TYPES = [PlainCredentials, ExternalCredentials] diff --git a/packages_o/pika-0.10.0/build/lib/pika/data.py b/packages_o/pika-0.10.0/build/lib/pika/data.py deleted file mode 100644 index fe35e371..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/data.py +++ /dev/null @@ -1,291 +0,0 @@ -"""AMQP Table Encoding/Decoding""" -import struct -import decimal -import calendar -from datetime import datetime - -from pika import exceptions -from pika.compat import unicode_type, PY2, long, as_bytes - - -def encode_short_string(pieces, value): - """Encode a string value as short string and append it to pieces list - returning the size of the encoded value. - - :param list pieces: Already encoded values - :param value: String value to encode - :type value: str or unicode - :rtype: int - - """ - encoded_value = as_bytes(value) - length = len(encoded_value) - - # 4.2.5.3 - # Short strings, stored as an 8-bit unsigned integer length followed by zero - # or more octets of data. Short strings can carry up to 255 octets of UTF-8 - # data, but may not contain binary zero octets. - # ... - # 4.2.5.5 - # The server SHOULD validate field names and upon receiving an invalid field - # name, it SHOULD signal a connection exception with reply code 503 (syntax - # error). - # -> validate length (avoid truncated utf-8 / corrupted data), but skip null - # byte check. - if length > 255: - raise exceptions.ShortStringTooLong(encoded_value) - - pieces.append(struct.pack('B', length)) - pieces.append(encoded_value) - return 1 + length - - -if PY2: - def decode_short_string(encoded, offset): - """Decode a short string value from ``encoded`` data at ``offset``. - """ - length = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - # Purely for compatibility with original python2 code. No idea what - # and why this does. - value = encoded[offset:offset + length] - try: - value = bytes(value) - except UnicodeEncodeError: - pass - offset += length - return value, offset - -else: - def decode_short_string(encoded, offset): - """Decode a short string value from ``encoded`` data at ``offset``. - """ - length = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - value = encoded[offset:offset + length].decode('utf8') - offset += length - return value, offset - - -def encode_table(pieces, table): - """Encode a dict as an AMQP table appending the encded table to the - pieces list passed in. - - :param list pieces: Already encoded frame pieces - :param dict table: The dict to encode - :rtype: int - - """ - table = table or {} - length_index = len(pieces) - pieces.append(None) # placeholder - tablesize = 0 - for (key, value) in table.items(): - tablesize += encode_short_string(pieces, key) - tablesize += encode_value(pieces, value) - - pieces[length_index] = struct.pack('>I', tablesize) - return tablesize + 4 - - -def encode_value(pieces, value): - """Encode the value passed in and append it to the pieces list returning - the the size of the encoded value. - - :param list pieces: Already encoded values - :param any value: The value to encode - :rtype: int - - """ - - if PY2: - if isinstance(value, basestring): - if isinstance(value, unicode_type): - value = value.encode('utf-8') - pieces.append(struct.pack('>cI', b'S', len(value))) - pieces.append(value) - return 5 + len(value) - else: - # support only str on Python 3 - if isinstance(value, str): - value = value.encode('utf-8') - pieces.append(struct.pack('>cI', b'S', len(value))) - pieces.append(value) - return 5 + len(value) - if isinstance(value, bool): - pieces.append(struct.pack('>cB', b't', int(value))) - return 2 - if isinstance(value, long): - pieces.append(struct.pack('>cq', b'l', value)) - return 9 - elif isinstance(value, int): - pieces.append(struct.pack('>ci', b'I', value)) - return 5 - elif isinstance(value, decimal.Decimal): - value = value.normalize() - if value.as_tuple().exponent < 0: - decimals = -value.as_tuple().exponent - raw = int(value * (decimal.Decimal(10) ** decimals)) - pieces.append(struct.pack('>cBi', b'D', decimals, raw)) - else: - # per spec, the "decimals" octet is unsigned (!) - pieces.append(struct.pack('>cBi', b'D', 0, int(value))) - return 6 - elif isinstance(value, datetime): - pieces.append(struct.pack('>cQ', b'T', - calendar.timegm(value.utctimetuple()))) - return 9 - elif isinstance(value, dict): - pieces.append(struct.pack('>c', b'F')) - return 1 + encode_table(pieces, value) - elif isinstance(value, list): - p = [] - for v in value: - encode_value(p, v) - piece = b''.join(p) - pieces.append(struct.pack('>cI', b'A', len(piece))) - pieces.append(piece) - return 5 + len(piece) - elif value is None: - pieces.append(struct.pack('>c', b'V')) - return 1 - else: - raise exceptions.UnsupportedAMQPFieldException(pieces, value) - - -def decode_table(encoded, offset): - """Decode the AMQP table passed in from the encoded value returning the - decoded result and the number of bytes read plus the offset. - - :param str encoded: The binary encoded data to decode - :param int offset: The starting byte offset - :rtype: tuple - - """ - result = {} - tablesize = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - limit = offset + tablesize - while offset < limit: - key, offset = decode_short_string(encoded, offset) - value, offset = decode_value(encoded, offset) - result[key] = value - return result, offset - - -def decode_value(encoded, offset): - """Decode the value passed in returning the decoded value and the number - of bytes read in addition to the starting offset. - - :param str encoded: The binary encoded data to decode - :param int offset: The starting byte offset - :rtype: tuple - :raises: pika.exceptions.InvalidFieldTypeException - - """ - # slice to get bytes in Python 3 and str in Python 2 - kind = encoded[offset:offset + 1] - offset += 1 - - # Bool - if kind == b't': - value = struct.unpack_from('>B', encoded, offset)[0] - value = bool(value) - offset += 1 - - # Short-Short Int - elif kind == b'b': - value = struct.unpack_from('>B', encoded, offset)[0] - offset += 1 - - # Short-Short Unsigned Int - elif kind == b'B': - value = struct.unpack_from('>b', encoded, offset)[0] - offset += 1 - - # Short Int - elif kind == b'U': - value = struct.unpack_from('>h', encoded, offset)[0] - offset += 2 - - # Short Unsigned Int - elif kind == b'u': - value = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - - # Long Int - elif kind == b'I': - value = struct.unpack_from('>i', encoded, offset)[0] - offset += 4 - - # Long Unsigned Int - elif kind == b'i': - value = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - - # Long-Long Int - elif kind == b'L': - value = long(struct.unpack_from('>q', encoded, offset)[0]) - offset += 8 - - # Long-Long Unsigned Int - elif kind == b'l': - value = long(struct.unpack_from('>Q', encoded, offset)[0]) - offset += 8 - - # Float - elif kind == b'f': - value = long(struct.unpack_from('>f', encoded, offset)[0]) - offset += 4 - - # Double - elif kind == b'd': - value = long(struct.unpack_from('>d', encoded, offset)[0]) - offset += 8 - - # Decimal - elif kind == b'D': - decimals = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - raw = struct.unpack_from('>i', encoded, offset)[0] - offset += 4 - value = decimal.Decimal(raw) * (decimal.Decimal(10) ** -decimals) - - # Short String - elif kind == b's': - value, offset = decode_short_string(encoded, offset) - - # Long String - elif kind == b'S': - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - value = encoded[offset:offset + length].decode('utf8') - offset += length - - # Field Array - elif kind == b'A': - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - offset_end = offset + length - value = [] - while offset < offset_end: - v, offset = decode_value(encoded, offset) - value.append(v) - - # Timestamp - elif kind == b'T': - value = datetime.utcfromtimestamp(struct.unpack_from('>Q', encoded, - offset)[0]) - offset += 8 - - # Field Table - elif kind == b'F': - (value, offset) = decode_table(encoded, offset) - - # Null / Void - elif kind == b'V': - value = None - else: - raise exceptions.InvalidFieldTypeException(kind) - - return value, offset diff --git a/packages_o/pika-0.10.0/build/lib/pika/exceptions.py b/packages_o/pika-0.10.0/build/lib/pika/exceptions.py deleted file mode 100644 index c56f6a09..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/exceptions.py +++ /dev/null @@ -1,237 +0,0 @@ -"""Pika specific exceptions""" - - -class AMQPError(Exception): - - def __repr__(self): - return 'An unspecified AMQP error has occurred' - - -class AMQPConnectionError(AMQPError): - - def __repr__(self): - if len(self.args) == 1: - if self.args[0] == 1: - return ('No connection could be opened after 1 ' - 'connection attempt') - elif isinstance(self.args[0], int): - return ('No connection could be opened after %s ' - 'connection attempts' % self.args[0]) - else: - return ('No connection could be opened: %s' % self.args[0]) - elif len(self.args) == 2: - return '%s: %s' % (self.args[0], self.args[1]) - - -class IncompatibleProtocolError(AMQPConnectionError): - - def __repr__(self): - return 'The protocol returned by the server is not supported' - - -class AuthenticationError(AMQPConnectionError): - - def __repr__(self): - return ('Server and client could not negotiate use of the %s ' - 'authentication mechanism' % self.args[0]) - - -class ProbableAuthenticationError(AMQPConnectionError): - - def __repr__(self): - return ('Client was disconnected at a connection stage indicating a ' - 'probable authentication error') - - -class ProbableAccessDeniedError(AMQPConnectionError): - - def __repr__(self): - return ('Client was disconnected at a connection stage indicating a ' - 'probable denial of access to the specified virtual host') - - -class NoFreeChannels(AMQPConnectionError): - - def __repr__(self): - return 'The connection has run out of free channels' - - -class ConnectionClosed(AMQPConnectionError): - - def __repr__(self): - if len(self.args) == 2: - return 'The AMQP connection was closed (%s) %s' % (self.args[0], - self.args[1]) - else: - return 'The AMQP connection was closed: %s' % (self.args,) - - -class AMQPChannelError(AMQPError): - - def __repr__(self): - return 'An unspecified AMQP channel error has occurred' - - -class ChannelClosed(AMQPChannelError): - - def __repr__(self): - if len(self.args) == 2: - return 'The channel was closed (%s) %s' % (self.args[0], - self.args[1]) - else: - return 'The channel was closed: %s' % (self.args,) - - -class DuplicateConsumerTag(AMQPChannelError): - - def __repr__(self): - return ('The consumer tag specified already exists for this ' - 'channel: %s' % self.args[0]) - - -class ConsumerCancelled(AMQPChannelError): - - def __repr__(self): - return 'Server cancelled consumer' - - -class UnroutableError(AMQPChannelError): - """Exception containing one or more unroutable messages returned by broker - via Basic.Return. - - Used by BlockingChannel. - - In publisher-acknowledgements mode, this is raised upon receipt of Basic.Ack - from broker; in the event of Basic.Nack from broker, `NackError` is raised - instead - """ - - def __init__(self, messages): - """ - :param messages: sequence of returned unroutable messages - :type messages: sequence of `blocking_connection.ReturnedMessage` - objects - """ - super(UnroutableError, self).__init__( - "%s unroutable message(s) returned" % (len(messages))) - - self.messages = messages - - def __repr__(self): - return '%s: %i unroutable messages returned by broker' % ( - self.__class__.__name__, len(self.messages)) - - -class NackError(AMQPChannelError): - """This exception is raised when a message published in - publisher-acknowledgements mode is Nack'ed by the broker. - - Used by BlockingChannel. - """ - - def __init__(self, messages): - """ - :param messages: sequence of returned unroutable messages - :type messages: sequence of `blocking_connection.ReturnedMessage` - objects - """ - super(NackError, self).__init__( - "%s message(s) NACKed" % (len(messages))) - - self.messages = messages - - def __repr__(self): - return '%s: %i unroutable messages returned by broker' % ( - self.__class__.__name__, len(self.messages)) - - -class InvalidChannelNumber(AMQPError): - - def __repr__(self): - return 'An invalid channel number has been specified: %s' % self.args[0] - - -class ProtocolSyntaxError(AMQPError): - - def __repr__(self): - return 'An unspecified protocol syntax error occurred' - - -class UnexpectedFrameError(ProtocolSyntaxError): - - def __repr__(self): - return 'Received a frame out of sequence: %r' % self.args[0] - - -class ProtocolVersionMismatch(ProtocolSyntaxError): - - def __repr__(self): - return 'Protocol versions did not match: %r vs %r' % (self.args[0], - self.args[1]) - - -class BodyTooLongError(ProtocolSyntaxError): - - def __repr__(self): - return ('Received too many bytes for a message delivery: ' - 'Received %i, expected %i' % (self.args[0], self.args[1])) - - -class InvalidFrameError(ProtocolSyntaxError): - - def __repr__(self): - return 'Invalid frame received: %r' % self.args[0] - - -class InvalidFieldTypeException(ProtocolSyntaxError): - - def __repr__(self): - return 'Unsupported field kind %s' % self.args[0] - - -class UnsupportedAMQPFieldException(ProtocolSyntaxError): - - def __repr__(self): - return 'Unsupported field kind %s' % type(self.args[1]) - - -class UnspportedAMQPFieldException(UnsupportedAMQPFieldException): - """Deprecated version of UnsupportedAMQPFieldException""" - - -class MethodNotImplemented(AMQPError): - pass - - -class ChannelError(Exception): - - def __repr__(self): - return 'An unspecified error occurred with the Channel' - - -class InvalidMinimumFrameSize(ProtocolSyntaxError): - - def __repr__(self): - return 'AMQP Minimum Frame Size is 4096 Bytes' - - -class InvalidMaximumFrameSize(ProtocolSyntaxError): - - def __repr__(self): - return 'AMQP Maximum Frame Size is 131072 Bytes' - - -class RecursionError(Exception): - """The requested operation would result in unsupported recursion or - reentrancy. - - Used by BlockingConnection/BlockingChannel - - """ - - -class ShortStringTooLong(AMQPError): - - def __repr__(self): - return ('AMQP Short String can contain up to 255 bytes: ' - '%.300s' % self.args[0]) diff --git a/packages_o/pika-0.10.0/build/lib/pika/frame.py b/packages_o/pika-0.10.0/build/lib/pika/frame.py deleted file mode 100644 index 9a07ec36..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/frame.py +++ /dev/null @@ -1,265 +0,0 @@ -"""Frame objects that do the frame demarshaling and marshaling.""" -import logging -import struct - -from pika import amqp_object -from pika import exceptions -from pika import spec -from pika.compat import byte - - -LOGGER = logging.getLogger(__name__) - - -class Frame(amqp_object.AMQPObject): - """Base Frame object mapping. Defines a behavior for all child classes for - assignment of core attributes and implementation of the a core _marshal - method which child classes use to create the binary AMQP frame. - - """ - NAME = 'Frame' - - def __init__(self, frame_type, channel_number): - """Create a new instance of a frame - - :param int frame_type: The frame type - :param int channel_number: The channel number for the frame - - """ - self.frame_type = frame_type - self.channel_number = channel_number - - def _marshal(self, pieces): - """Create the full AMQP wire protocol frame data representation - - :rtype: bytes - - """ - payload = b''.join(pieces) - return struct.pack('>BHI', self.frame_type, self.channel_number, - len(payload)) + payload + byte(spec.FRAME_END) - - def marshal(self): - """To be ended by child classes - - :raises NotImplementedError - - """ - raise NotImplementedError - - -class Method(Frame): - """Base Method frame object mapping. AMQP method frames are mapped on top - of this class for creating or accessing their data and attributes. - - """ - NAME = 'METHOD' - - def __init__(self, channel_number, method): - """Create a new instance of a frame - - :param int channel_number: The frame type - :param pika.Spec.Class.Method method: The AMQP Class.Method - - """ - Frame.__init__(self, spec.FRAME_METHOD, channel_number) - self.method = method - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - pieces = self.method.encode() - pieces.insert(0, struct.pack('>I', self.method.INDEX)) - return self._marshal(pieces) - - -class Header(Frame): - """Header frame object mapping. AMQP content header frames are mapped - on top of this class for creating or accessing their data and attributes. - - """ - NAME = 'Header' - - def __init__(self, channel_number, body_size, props): - """Create a new instance of a AMQP ContentHeader object - - :param int channel_number: The channel number for the frame - :param int body_size: The number of bytes for the body - :param pika.spec.BasicProperties props: Basic.Properties object - - """ - Frame.__init__(self, spec.FRAME_HEADER, channel_number) - self.body_size = body_size - self.properties = props - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - pieces = self.properties.encode() - pieces.insert(0, struct.pack('>HxxQ', self.properties.INDEX, - self.body_size)) - return self._marshal(pieces) - - -class Body(Frame): - """Body frame object mapping class. AMQP content body frames are mapped on - to this base class for getting/setting of attributes/data. - - """ - NAME = 'Body' - - def __init__(self, channel_number, fragment): - """ - Parameters: - - - channel_number: int - - fragment: unicode or str - """ - Frame.__init__(self, spec.FRAME_BODY, channel_number) - self.fragment = fragment - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - return self._marshal([self.fragment]) - - -class Heartbeat(Frame): - """Heartbeat frame object mapping class. AMQP Heartbeat frames are mapped - on to this class for a common access structure to the attributes/data - values. - - """ - NAME = 'Heartbeat' - - def __init__(self): - """Create a new instance of the Heartbeat frame""" - Frame.__init__(self, spec.FRAME_HEARTBEAT, 0) - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - return self._marshal(list()) - - -class ProtocolHeader(amqp_object.AMQPObject): - """AMQP Protocol header frame class which provides a pythonic interface - for creating AMQP Protocol headers - - """ - NAME = 'ProtocolHeader' - - def __init__(self, major=None, minor=None, revision=None): - """Construct a Protocol Header frame object for the specified AMQP - version - - :param int major: Major version number - :param int minor: Minor version number - :param int revision: Revision - - """ - self.frame_type = -1 - self.major = major or spec.PROTOCOL_VERSION[0] - self.minor = minor or spec.PROTOCOL_VERSION[1] - self.revision = revision or spec.PROTOCOL_VERSION[2] - - def marshal(self): - """Return the full AMQP wire protocol frame data representation of the - ProtocolHeader frame - - :rtype: str - - """ - return b'AMQP' + struct.pack('BBBB', 0, self.major, self.minor, - self.revision) - - -def decode_frame(data_in): - """Receives raw socket data and attempts to turn it into a frame. - Returns bytes used to make the frame and the frame - - :param str data_in: The raw data stream - :rtype: tuple(bytes consumed, frame) - :raises: pika.exceptions.InvalidFrameError - - """ - # Look to see if it's a protocol header frame - try: - if data_in[0:4] == b'AMQP': - major, minor, revision = struct.unpack_from('BBB', data_in, 5) - return 8, ProtocolHeader(major, minor, revision) - except (IndexError, struct.error): - return 0, None - - # Get the Frame Type, Channel Number and Frame Size - try: - (frame_type, channel_number, - frame_size) = struct.unpack('>BHL', data_in[0:7]) - except struct.error: - return 0, None - - # Get the frame data - frame_end = spec.FRAME_HEADER_SIZE + frame_size + spec.FRAME_END_SIZE - - # We don't have all of the frame yet - if frame_end > len(data_in): - return 0, None - - # The Frame termination chr is wrong - if data_in[frame_end - 1:frame_end] != byte(spec.FRAME_END): - raise exceptions.InvalidFrameError("Invalid FRAME_END marker") - - # Get the raw frame data - frame_data = data_in[spec.FRAME_HEADER_SIZE:frame_end - 1] - - if frame_type == spec.FRAME_METHOD: - - # Get the Method ID from the frame data - method_id = struct.unpack_from('>I', frame_data)[0] - - # Get a Method object for this method_id - method = spec.methods[method_id]() - - # Decode the content - method.decode(frame_data, 4) - - # Return the amount of data consumed and the Method object - return frame_end, Method(channel_number, method) - - elif frame_type == spec.FRAME_HEADER: - - # Return the header class and body size - class_id, weight, body_size = struct.unpack_from('>HHQ', frame_data) - - # Get the Properties type - properties = spec.props[class_id]() - - # Decode the properties - out = properties.decode(frame_data[12:]) - - # Return a Header frame - return frame_end, Header(channel_number, body_size, properties) - - elif frame_type == spec.FRAME_BODY: - - # Return the amount of data consumed and the Body frame w/ data - return frame_end, Body(channel_number, frame_data) - - elif frame_type == spec.FRAME_HEARTBEAT: - - # Return the amount of data and a Heartbeat frame - return frame_end, Heartbeat() - - raise exceptions.InvalidFrameError("Unknown frame type: %i" % frame_type) diff --git a/packages_o/pika-0.10.0/build/lib/pika/heartbeat.py b/packages_o/pika-0.10.0/build/lib/pika/heartbeat.py deleted file mode 100644 index 2d5d28e2..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/heartbeat.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Handle AMQP Heartbeats""" -import logging - -from pika import frame - -LOGGER = logging.getLogger(__name__) - - -class HeartbeatChecker(object): - """Checks to make sure that our heartbeat is received at the expected - intervals. - - """ - MAX_IDLE_COUNT = 2 - _CONNECTION_FORCED = 320 - _STALE_CONNECTION = "Too Many Missed Heartbeats, No reply in %i seconds" - - def __init__(self, connection, interval, idle_count=MAX_IDLE_COUNT): - """Create a heartbeat on connection sending a heartbeat frame every - interval seconds. - - :param pika.connection.Connection: Connection object - :param int interval: Heartbeat check interval - :param int idle_count: Number of heartbeat intervals missed until the - connection is considered idle and disconnects - - """ - self._connection = connection - self._interval = interval - self._max_idle_count = idle_count - - # Initialize counters - self._bytes_received = 0 - self._bytes_sent = 0 - self._heartbeat_frames_received = 0 - self._heartbeat_frames_sent = 0 - self._idle_byte_intervals = 0 - - # The handle for the last timer - self._timer = None - - # Setup the timer to fire in _interval seconds - self._setup_timer() - - @property - def active(self): - """Return True if the connection's heartbeat attribute is set to this - instance. - - :rtype True - - """ - return self._connection.heartbeat is self - - @property - def bytes_received_on_connection(self): - """Return the number of bytes received by the connection bytes object. - - :rtype int - - """ - return self._connection.bytes_received - - @property - def connection_is_idle(self): - """Returns true if the byte count hasn't changed in enough intervals - to trip the max idle threshold. - - """ - return self._idle_byte_intervals >= self._max_idle_count - - def received(self): - """Called when a heartbeat is received""" - LOGGER.debug('Received heartbeat frame') - self._heartbeat_frames_received += 1 - - def send_and_check(self): - """Invoked by a timer to send a heartbeat when we need to, check to see - if we've missed any heartbeats and disconnect our connection if it's - been idle too long. - - """ - LOGGER.debug('Received %i heartbeat frames, sent %i', - self._heartbeat_frames_received, - self._heartbeat_frames_sent) - - if self.connection_is_idle: - return self._close_connection() - - # Connection has not received any data, increment the counter - if not self._has_received_data: - self._idle_byte_intervals += 1 - else: - self._idle_byte_intervals = 0 - - # Update the counters of bytes sent/received and the frames received - self._update_counters() - - # Send a heartbeat frame - self._send_heartbeat_frame() - - # Update the timer to fire again - self._start_timer() - - def stop(self): - """Stop the heartbeat checker""" - if self._timer: - LOGGER.debug('Removing timeout for next heartbeat interval') - self._connection.remove_timeout(self._timer) - self._timer = None - - def _close_connection(self): - """Close the connection with the AMQP Connection-Forced value.""" - LOGGER.info('Connection is idle, %i stale byte intervals', - self._idle_byte_intervals) - duration = self._max_idle_count * self._interval - text = HeartbeatChecker._STALE_CONNECTION % duration - self._connection.close(HeartbeatChecker._CONNECTION_FORCED, text) - self._connection._adapter_disconnect() - self._connection._on_disconnect(HeartbeatChecker._CONNECTION_FORCED, - text) - - @property - def _has_received_data(self): - """Returns True if the connection has received data on the connection. - - :rtype: bool - - """ - return not self._bytes_received == self.bytes_received_on_connection - - def _new_heartbeat_frame(self): - """Return a new heartbeat frame. - - :rtype pika.frame.Heartbeat - - """ - return frame.Heartbeat() - - def _send_heartbeat_frame(self): - """Send a heartbeat frame on the connection. - - """ - LOGGER.debug('Sending heartbeat frame') - self._connection._send_frame(self._new_heartbeat_frame()) - self._heartbeat_frames_sent += 1 - - def _setup_timer(self): - """Use the connection objects delayed_call function which is - implemented by the Adapter for calling the check_heartbeats function - every interval seconds. - - """ - self._timer = self._connection.add_timeout(self._interval, - self.send_and_check) - - def _start_timer(self): - """If the connection still has this object set for heartbeats, add a - new timer. - - """ - if self.active: - self._setup_timer() - - def _update_counters(self): - """Update the internal counters for bytes sent and received and the - number of frames received - - """ - self._bytes_sent = self._connection.bytes_sent - self._bytes_received = self._connection.bytes_received diff --git a/packages_o/pika-0.10.0/build/lib/pika/spec.py b/packages_o/pika-0.10.0/build/lib/pika/spec.py deleted file mode 100644 index b3d7cc94..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/spec.py +++ /dev/null @@ -1,2312 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# -# For copyright and licensing please refer to COPYING. -# -# ***** END LICENSE BLOCK ***** - -# NOTE: Autogenerated code by codegen.py, do not edit - -import struct -from pika import amqp_object -from pika import data -from pika.compat import str_or_bytes, unicode_type - -str = bytes - - -PROTOCOL_VERSION = (0, 9, 1) -PORT = 5672 - -ACCESS_REFUSED = 403 -CHANNEL_ERROR = 504 -COMMAND_INVALID = 503 -CONNECTION_FORCED = 320 -CONTENT_TOO_LARGE = 311 -FRAME_BODY = 3 -FRAME_END = 206 -FRAME_END_SIZE = 1 -FRAME_ERROR = 501 -FRAME_HEADER = 2 -FRAME_HEADER_SIZE = 7 -FRAME_HEARTBEAT = 8 -FRAME_MAX_SIZE = 131072 -FRAME_METHOD = 1 -FRAME_MIN_SIZE = 4096 -INTERNAL_ERROR = 541 -INVALID_PATH = 402 -NOT_ALLOWED = 530 -NOT_FOUND = 404 -NOT_IMPLEMENTED = 540 -NO_CONSUMERS = 313 -NO_ROUTE = 312 -PRECONDITION_FAILED = 406 -REPLY_SUCCESS = 200 -RESOURCE_ERROR = 506 -RESOURCE_LOCKED = 405 -SYNTAX_ERROR = 502 -UNEXPECTED_FRAME = 505 - - -class Connection(amqp_object.Class): - - INDEX = 0x000A # 10 - NAME = 'Connection' - - class Start(amqp_object.Method): - - INDEX = 0x000A000A # 10, 10; 655370 - NAME = 'Connection.Start' - - def __init__(self, version_major=0, version_minor=9, server_properties=None, mechanisms='PLAIN', locales='en_US'): - self.version_major = version_major - self.version_minor = version_minor - self.server_properties = server_properties - self.mechanisms = mechanisms - self.locales = locales - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.version_major = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.version_minor = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - (self.server_properties, offset) = data.decode_table(encoded, offset) - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.mechanisms = encoded[offset:offset + length] - try: - self.mechanisms = str(self.mechanisms) - except UnicodeEncodeError: - pass - offset += length - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.locales = encoded[offset:offset + length] - try: - self.locales = str(self.locales) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('B', self.version_major)) - pieces.append(struct.pack('B', self.version_minor)) - data.encode_table(pieces, self.server_properties) - assert isinstance(self.mechanisms, str_or_bytes),\ - 'A non-string value was supplied for self.mechanisms' - value = self.mechanisms.encode('utf-8') if isinstance(self.mechanisms, unicode_type) else self.mechanisms - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - assert isinstance(self.locales, str_or_bytes),\ - 'A non-string value was supplied for self.locales' - value = self.locales.encode('utf-8') if isinstance(self.locales, unicode_type) else self.locales - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class StartOk(amqp_object.Method): - - INDEX = 0x000A000B # 10, 11; 655371 - NAME = 'Connection.StartOk' - - def __init__(self, client_properties=None, mechanism='PLAIN', response=None, locale='en_US'): - self.client_properties = client_properties - self.mechanism = mechanism - self.response = response - self.locale = locale - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - (self.client_properties, offset) = data.decode_table(encoded, offset) - self.mechanism, offset = data.decode_short_string(encoded, offset) - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.response = encoded[offset:offset + length] - try: - self.response = str(self.response) - except UnicodeEncodeError: - pass - offset += length - self.locale, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - data.encode_table(pieces, self.client_properties) - assert isinstance(self.mechanism, str_or_bytes),\ - 'A non-string value was supplied for self.mechanism' - data.encode_short_string(pieces, self.mechanism) - assert isinstance(self.response, str_or_bytes),\ - 'A non-string value was supplied for self.response' - value = self.response.encode('utf-8') if isinstance(self.response, unicode_type) else self.response - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - assert isinstance(self.locale, str_or_bytes),\ - 'A non-string value was supplied for self.locale' - data.encode_short_string(pieces, self.locale) - return pieces - - class Secure(amqp_object.Method): - - INDEX = 0x000A0014 # 10, 20; 655380 - NAME = 'Connection.Secure' - - def __init__(self, challenge=None): - self.challenge = challenge - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.challenge = encoded[offset:offset + length] - try: - self.challenge = str(self.challenge) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.challenge, str_or_bytes),\ - 'A non-string value was supplied for self.challenge' - value = self.challenge.encode('utf-8') if isinstance(self.challenge, unicode_type) else self.challenge - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class SecureOk(amqp_object.Method): - - INDEX = 0x000A0015 # 10, 21; 655381 - NAME = 'Connection.SecureOk' - - def __init__(self, response=None): - self.response = response - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.response = encoded[offset:offset + length] - try: - self.response = str(self.response) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.response, str_or_bytes),\ - 'A non-string value was supplied for self.response' - value = self.response.encode('utf-8') if isinstance(self.response, unicode_type) else self.response - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class Tune(amqp_object.Method): - - INDEX = 0x000A001E # 10, 30; 655390 - NAME = 'Connection.Tune' - - def __init__(self, channel_max=0, frame_max=0, heartbeat=0): - self.channel_max = channel_max - self.frame_max = frame_max - self.heartbeat = heartbeat - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.channel_max = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.frame_max = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.heartbeat = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.channel_max)) - pieces.append(struct.pack('>I', self.frame_max)) - pieces.append(struct.pack('>H', self.heartbeat)) - return pieces - - class TuneOk(amqp_object.Method): - - INDEX = 0x000A001F # 10, 31; 655391 - NAME = 'Connection.TuneOk' - - def __init__(self, channel_max=0, frame_max=0, heartbeat=0): - self.channel_max = channel_max - self.frame_max = frame_max - self.heartbeat = heartbeat - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.channel_max = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.frame_max = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.heartbeat = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.channel_max)) - pieces.append(struct.pack('>I', self.frame_max)) - pieces.append(struct.pack('>H', self.heartbeat)) - return pieces - - class Open(amqp_object.Method): - - INDEX = 0x000A0028 # 10, 40; 655400 - NAME = 'Connection.Open' - - def __init__(self, virtual_host='/', capabilities='', insist=False): - self.virtual_host = virtual_host - self.capabilities = capabilities - self.insist = insist - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.virtual_host, offset = data.decode_short_string(encoded, offset) - self.capabilities, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.insist = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.virtual_host, str_or_bytes),\ - 'A non-string value was supplied for self.virtual_host' - data.encode_short_string(pieces, self.virtual_host) - assert isinstance(self.capabilities, str_or_bytes),\ - 'A non-string value was supplied for self.capabilities' - data.encode_short_string(pieces, self.capabilities) - bit_buffer = 0 - if self.insist: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class OpenOk(amqp_object.Method): - - INDEX = 0x000A0029 # 10, 41; 655401 - NAME = 'Connection.OpenOk' - - def __init__(self, known_hosts=''): - self.known_hosts = known_hosts - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.known_hosts, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.known_hosts, str_or_bytes),\ - 'A non-string value was supplied for self.known_hosts' - data.encode_short_string(pieces, self.known_hosts) - return pieces - - class Close(amqp_object.Method): - - INDEX = 0x000A0032 # 10, 50; 655410 - NAME = 'Connection.Close' - - def __init__(self, reply_code=None, reply_text='', class_id=None, method_id=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.class_id = class_id - self.method_id = method_id - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.class_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.method_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - pieces.append(struct.pack('>H', self.class_id)) - pieces.append(struct.pack('>H', self.method_id)) - return pieces - - class CloseOk(amqp_object.Method): - - INDEX = 0x000A0033 # 10, 51; 655411 - NAME = 'Connection.CloseOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Blocked(amqp_object.Method): - - INDEX = 0x000A003C # 10, 60; 655420 - NAME = 'Connection.Blocked' - - def __init__(self, reason=''): - self.reason = reason - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.reason, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.reason, str_or_bytes),\ - 'A non-string value was supplied for self.reason' - data.encode_short_string(pieces, self.reason) - return pieces - - class Unblocked(amqp_object.Method): - - INDEX = 0x000A003D # 10, 61; 655421 - NAME = 'Connection.Unblocked' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Channel(amqp_object.Class): - - INDEX = 0x0014 # 20 - NAME = 'Channel' - - class Open(amqp_object.Method): - - INDEX = 0x0014000A # 20, 10; 1310730 - NAME = 'Channel.Open' - - def __init__(self, out_of_band=''): - self.out_of_band = out_of_band - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.out_of_band, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.out_of_band, str_or_bytes),\ - 'A non-string value was supplied for self.out_of_band' - data.encode_short_string(pieces, self.out_of_band) - return pieces - - class OpenOk(amqp_object.Method): - - INDEX = 0x0014000B # 20, 11; 1310731 - NAME = 'Channel.OpenOk' - - def __init__(self, channel_id=''): - self.channel_id = channel_id - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.channel_id = encoded[offset:offset + length] - try: - self.channel_id = str(self.channel_id) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.channel_id, str_or_bytes),\ - 'A non-string value was supplied for self.channel_id' - value = self.channel_id.encode('utf-8') if isinstance(self.channel_id, unicode_type) else self.channel_id - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class Flow(amqp_object.Method): - - INDEX = 0x00140014 # 20, 20; 1310740 - NAME = 'Channel.Flow' - - def __init__(self, active=None): - self.active = active - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.active = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.active: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class FlowOk(amqp_object.Method): - - INDEX = 0x00140015 # 20, 21; 1310741 - NAME = 'Channel.FlowOk' - - def __init__(self, active=None): - self.active = active - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.active = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.active: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Close(amqp_object.Method): - - INDEX = 0x00140028 # 20, 40; 1310760 - NAME = 'Channel.Close' - - def __init__(self, reply_code=None, reply_text='', class_id=None, method_id=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.class_id = class_id - self.method_id = method_id - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.class_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.method_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - pieces.append(struct.pack('>H', self.class_id)) - pieces.append(struct.pack('>H', self.method_id)) - return pieces - - class CloseOk(amqp_object.Method): - - INDEX = 0x00140029 # 20, 41; 1310761 - NAME = 'Channel.CloseOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Access(amqp_object.Class): - - INDEX = 0x001E # 30 - NAME = 'Access' - - class Request(amqp_object.Method): - - INDEX = 0x001E000A # 30, 10; 1966090 - NAME = 'Access.Request' - - def __init__(self, realm='/data', exclusive=False, passive=True, active=True, write=True, read=True): - self.realm = realm - self.exclusive = exclusive - self.passive = passive - self.active = active - self.write = write - self.read = read - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.realm, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.exclusive = (bit_buffer & (1 << 0)) != 0 - self.passive = (bit_buffer & (1 << 1)) != 0 - self.active = (bit_buffer & (1 << 2)) != 0 - self.write = (bit_buffer & (1 << 3)) != 0 - self.read = (bit_buffer & (1 << 4)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.realm, str_or_bytes),\ - 'A non-string value was supplied for self.realm' - data.encode_short_string(pieces, self.realm) - bit_buffer = 0 - if self.exclusive: - bit_buffer = bit_buffer | (1 << 0) - if self.passive: - bit_buffer = bit_buffer | (1 << 1) - if self.active: - bit_buffer = bit_buffer | (1 << 2) - if self.write: - bit_buffer = bit_buffer | (1 << 3) - if self.read: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RequestOk(amqp_object.Method): - - INDEX = 0x001E000B # 30, 11; 1966091 - NAME = 'Access.RequestOk' - - def __init__(self, ticket=1): - self.ticket = ticket - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - return pieces - - -class Exchange(amqp_object.Class): - - INDEX = 0x0028 # 40 - NAME = 'Exchange' - - class Declare(amqp_object.Method): - - INDEX = 0x0028000A # 40, 10; 2621450 - NAME = 'Exchange.Declare' - - def __init__(self, ticket=0, exchange=None, type='direct', passive=False, durable=False, auto_delete=False, internal=False, nowait=False, arguments={}): - self.ticket = ticket - self.exchange = exchange - self.type = type - self.passive = passive - self.durable = durable - self.auto_delete = auto_delete - self.internal = internal - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.type, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.passive = (bit_buffer & (1 << 0)) != 0 - self.durable = (bit_buffer & (1 << 1)) != 0 - self.auto_delete = (bit_buffer & (1 << 2)) != 0 - self.internal = (bit_buffer & (1 << 3)) != 0 - self.nowait = (bit_buffer & (1 << 4)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.type, str_or_bytes),\ - 'A non-string value was supplied for self.type' - data.encode_short_string(pieces, self.type) - bit_buffer = 0 - if self.passive: - bit_buffer = bit_buffer | (1 << 0) - if self.durable: - bit_buffer = bit_buffer | (1 << 1) - if self.auto_delete: - bit_buffer = bit_buffer | (1 << 2) - if self.internal: - bit_buffer = bit_buffer | (1 << 3) - if self.nowait: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class DeclareOk(amqp_object.Method): - - INDEX = 0x0028000B # 40, 11; 2621451 - NAME = 'Exchange.DeclareOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Delete(amqp_object.Method): - - INDEX = 0x00280014 # 40, 20; 2621460 - NAME = 'Exchange.Delete' - - def __init__(self, ticket=0, exchange=None, if_unused=False, nowait=False): - self.ticket = ticket - self.exchange = exchange - self.if_unused = if_unused - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.if_unused = (bit_buffer & (1 << 0)) != 0 - self.nowait = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - bit_buffer = 0 - if self.if_unused: - bit_buffer = bit_buffer | (1 << 0) - if self.nowait: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class DeleteOk(amqp_object.Method): - - INDEX = 0x00280015 # 40, 21; 2621461 - NAME = 'Exchange.DeleteOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Bind(amqp_object.Method): - - INDEX = 0x0028001E # 40, 30; 2621470 - NAME = 'Exchange.Bind' - - def __init__(self, ticket=0, destination=None, source=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.destination = destination - self.source = source - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.destination, offset = data.decode_short_string(encoded, offset) - self.source, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.destination, str_or_bytes),\ - 'A non-string value was supplied for self.destination' - data.encode_short_string(pieces, self.destination) - assert isinstance(self.source, str_or_bytes),\ - 'A non-string value was supplied for self.source' - data.encode_short_string(pieces, self.source) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class BindOk(amqp_object.Method): - - INDEX = 0x0028001F # 40, 31; 2621471 - NAME = 'Exchange.BindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Unbind(amqp_object.Method): - - INDEX = 0x00280028 # 40, 40; 2621480 - NAME = 'Exchange.Unbind' - - def __init__(self, ticket=0, destination=None, source=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.destination = destination - self.source = source - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.destination, offset = data.decode_short_string(encoded, offset) - self.source, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.destination, str_or_bytes),\ - 'A non-string value was supplied for self.destination' - data.encode_short_string(pieces, self.destination) - assert isinstance(self.source, str_or_bytes),\ - 'A non-string value was supplied for self.source' - data.encode_short_string(pieces, self.source) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class UnbindOk(amqp_object.Method): - - INDEX = 0x00280033 # 40, 51; 2621491 - NAME = 'Exchange.UnbindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Queue(amqp_object.Class): - - INDEX = 0x0032 # 50 - NAME = 'Queue' - - class Declare(amqp_object.Method): - - INDEX = 0x0032000A # 50, 10; 3276810 - NAME = 'Queue.Declare' - - def __init__(self, ticket=0, queue='', passive=False, durable=False, exclusive=False, auto_delete=False, nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.passive = passive - self.durable = durable - self.exclusive = exclusive - self.auto_delete = auto_delete - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.passive = (bit_buffer & (1 << 0)) != 0 - self.durable = (bit_buffer & (1 << 1)) != 0 - self.exclusive = (bit_buffer & (1 << 2)) != 0 - self.auto_delete = (bit_buffer & (1 << 3)) != 0 - self.nowait = (bit_buffer & (1 << 4)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.passive: - bit_buffer = bit_buffer | (1 << 0) - if self.durable: - bit_buffer = bit_buffer | (1 << 1) - if self.exclusive: - bit_buffer = bit_buffer | (1 << 2) - if self.auto_delete: - bit_buffer = bit_buffer | (1 << 3) - if self.nowait: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class DeclareOk(amqp_object.Method): - - INDEX = 0x0032000B # 50, 11; 3276811 - NAME = 'Queue.DeclareOk' - - def __init__(self, queue=None, message_count=None, consumer_count=None): - self.queue = queue - self.message_count = message_count - self.consumer_count = consumer_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.queue, offset = data.decode_short_string(encoded, offset) - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.consumer_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - pieces.append(struct.pack('>I', self.message_count)) - pieces.append(struct.pack('>I', self.consumer_count)) - return pieces - - class Bind(amqp_object.Method): - - INDEX = 0x00320014 # 50, 20; 3276820 - NAME = 'Queue.Bind' - - def __init__(self, ticket=0, queue='', exchange=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.exchange = exchange - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class BindOk(amqp_object.Method): - - INDEX = 0x00320015 # 50, 21; 3276821 - NAME = 'Queue.BindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Purge(amqp_object.Method): - - INDEX = 0x0032001E # 50, 30; 3276830 - NAME = 'Queue.Purge' - - def __init__(self, ticket=0, queue='', nowait=False): - self.ticket = ticket - self.queue = queue - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class PurgeOk(amqp_object.Method): - - INDEX = 0x0032001F # 50, 31; 3276831 - NAME = 'Queue.PurgeOk' - - def __init__(self, message_count=None): - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class Delete(amqp_object.Method): - - INDEX = 0x00320028 # 50, 40; 3276840 - NAME = 'Queue.Delete' - - def __init__(self, ticket=0, queue='', if_unused=False, if_empty=False, nowait=False): - self.ticket = ticket - self.queue = queue - self.if_unused = if_unused - self.if_empty = if_empty - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.if_unused = (bit_buffer & (1 << 0)) != 0 - self.if_empty = (bit_buffer & (1 << 1)) != 0 - self.nowait = (bit_buffer & (1 << 2)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.if_unused: - bit_buffer = bit_buffer | (1 << 0) - if self.if_empty: - bit_buffer = bit_buffer | (1 << 1) - if self.nowait: - bit_buffer = bit_buffer | (1 << 2) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class DeleteOk(amqp_object.Method): - - INDEX = 0x00320029 # 50, 41; 3276841 - NAME = 'Queue.DeleteOk' - - def __init__(self, message_count=None): - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class Unbind(amqp_object.Method): - - INDEX = 0x00320032 # 50, 50; 3276850 - NAME = 'Queue.Unbind' - - def __init__(self, ticket=0, queue='', exchange=None, routing_key='', arguments={}): - self.ticket = ticket - self.queue = queue - self.exchange = exchange - self.routing_key = routing_key - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - data.encode_table(pieces, self.arguments) - return pieces - - class UnbindOk(amqp_object.Method): - - INDEX = 0x00320033 # 50, 51; 3276851 - NAME = 'Queue.UnbindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Basic(amqp_object.Class): - - INDEX = 0x003C # 60 - NAME = 'Basic' - - class Qos(amqp_object.Method): - - INDEX = 0x003C000A # 60, 10; 3932170 - NAME = 'Basic.Qos' - - def __init__(self, prefetch_size=0, prefetch_count=0, global_=False): - self.prefetch_size = prefetch_size - self.prefetch_count = prefetch_count - self.global_ = global_ - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.prefetch_size = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.prefetch_count = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.global_ = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.prefetch_size)) - pieces.append(struct.pack('>H', self.prefetch_count)) - bit_buffer = 0 - if self.global_: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class QosOk(amqp_object.Method): - - INDEX = 0x003C000B # 60, 11; 3932171 - NAME = 'Basic.QosOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Consume(amqp_object.Method): - - INDEX = 0x003C0014 # 60, 20; 3932180 - NAME = 'Basic.Consume' - - def __init__(self, ticket=0, queue='', consumer_tag='', no_local=False, no_ack=False, exclusive=False, nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.consumer_tag = consumer_tag - self.no_local = no_local - self.no_ack = no_ack - self.exclusive = exclusive - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.no_local = (bit_buffer & (1 << 0)) != 0 - self.no_ack = (bit_buffer & (1 << 1)) != 0 - self.exclusive = (bit_buffer & (1 << 2)) != 0 - self.nowait = (bit_buffer & (1 << 3)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - bit_buffer = 0 - if self.no_local: - bit_buffer = bit_buffer | (1 << 0) - if self.no_ack: - bit_buffer = bit_buffer | (1 << 1) - if self.exclusive: - bit_buffer = bit_buffer | (1 << 2) - if self.nowait: - bit_buffer = bit_buffer | (1 << 3) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class ConsumeOk(amqp_object.Method): - - INDEX = 0x003C0015 # 60, 21; 3932181 - NAME = 'Basic.ConsumeOk' - - def __init__(self, consumer_tag=None): - self.consumer_tag = consumer_tag - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - return pieces - - class Cancel(amqp_object.Method): - - INDEX = 0x003C001E # 60, 30; 3932190 - NAME = 'Basic.Cancel' - - def __init__(self, consumer_tag=None, nowait=False): - self.consumer_tag = consumer_tag - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class CancelOk(amqp_object.Method): - - INDEX = 0x003C001F # 60, 31; 3932191 - NAME = 'Basic.CancelOk' - - def __init__(self, consumer_tag=None): - self.consumer_tag = consumer_tag - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - return pieces - - class Publish(amqp_object.Method): - - INDEX = 0x003C0028 # 60, 40; 3932200 - NAME = 'Basic.Publish' - - def __init__(self, ticket=0, exchange='', routing_key='', mandatory=False, immediate=False): - self.ticket = ticket - self.exchange = exchange - self.routing_key = routing_key - self.mandatory = mandatory - self.immediate = immediate - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.mandatory = (bit_buffer & (1 << 0)) != 0 - self.immediate = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.mandatory: - bit_buffer = bit_buffer | (1 << 0) - if self.immediate: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Return(amqp_object.Method): - - INDEX = 0x003C0032 # 60, 50; 3932210 - NAME = 'Basic.Return' - - def __init__(self, reply_code=None, reply_text='', exchange=None, routing_key=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.exchange = exchange - self.routing_key = routing_key - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - return pieces - - class Deliver(amqp_object.Method): - - INDEX = 0x003C003C # 60, 60; 3932220 - NAME = 'Basic.Deliver' - - def __init__(self, consumer_tag=None, delivery_tag=None, redelivered=False, exchange=None, routing_key=None): - self.consumer_tag = consumer_tag - self.delivery_tag = delivery_tag - self.redelivered = redelivered - self.exchange = exchange - self.routing_key = routing_key - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.redelivered = (bit_buffer & (1 << 0)) != 0 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.redelivered: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - return pieces - - class Get(amqp_object.Method): - - INDEX = 0x003C0046 # 60, 70; 3932230 - NAME = 'Basic.Get' - - def __init__(self, ticket=0, queue='', no_ack=False): - self.ticket = ticket - self.queue = queue - self.no_ack = no_ack - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.no_ack = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.no_ack: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class GetOk(amqp_object.Method): - - INDEX = 0x003C0047 # 60, 71; 3932231 - NAME = 'Basic.GetOk' - - def __init__(self, delivery_tag=None, redelivered=False, exchange=None, routing_key=None, message_count=None): - self.delivery_tag = delivery_tag - self.redelivered = redelivered - self.exchange = exchange - self.routing_key = routing_key - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.redelivered = (bit_buffer & (1 << 0)) != 0 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.redelivered: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class GetEmpty(amqp_object.Method): - - INDEX = 0x003C0048 # 60, 72; 3932232 - NAME = 'Basic.GetEmpty' - - def __init__(self, cluster_id=''): - self.cluster_id = cluster_id - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.cluster_id, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.cluster_id, str_or_bytes),\ - 'A non-string value was supplied for self.cluster_id' - data.encode_short_string(pieces, self.cluster_id) - return pieces - - class Ack(amqp_object.Method): - - INDEX = 0x003C0050 # 60, 80; 3932240 - NAME = 'Basic.Ack' - - def __init__(self, delivery_tag=0, multiple=False): - self.delivery_tag = delivery_tag - self.multiple = multiple - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.multiple = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.multiple: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Reject(amqp_object.Method): - - INDEX = 0x003C005A # 60, 90; 3932250 - NAME = 'Basic.Reject' - - def __init__(self, delivery_tag=None, requeue=True): - self.delivery_tag = delivery_tag - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RecoverAsync(amqp_object.Method): - - INDEX = 0x003C0064 # 60, 100; 3932260 - NAME = 'Basic.RecoverAsync' - - def __init__(self, requeue=False): - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Recover(amqp_object.Method): - - INDEX = 0x003C006E # 60, 110; 3932270 - NAME = 'Basic.Recover' - - def __init__(self, requeue=False): - self.requeue = requeue - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RecoverOk(amqp_object.Method): - - INDEX = 0x003C006F # 60, 111; 3932271 - NAME = 'Basic.RecoverOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Nack(amqp_object.Method): - - INDEX = 0x003C0078 # 60, 120; 3932280 - NAME = 'Basic.Nack' - - def __init__(self, delivery_tag=0, multiple=False, requeue=True): - self.delivery_tag = delivery_tag - self.multiple = multiple - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.multiple = (bit_buffer & (1 << 0)) != 0 - self.requeue = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.multiple: - bit_buffer = bit_buffer | (1 << 0) - if self.requeue: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - -class Tx(amqp_object.Class): - - INDEX = 0x005A # 90 - NAME = 'Tx' - - class Select(amqp_object.Method): - - INDEX = 0x005A000A # 90, 10; 5898250 - NAME = 'Tx.Select' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class SelectOk(amqp_object.Method): - - INDEX = 0x005A000B # 90, 11; 5898251 - NAME = 'Tx.SelectOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Commit(amqp_object.Method): - - INDEX = 0x005A0014 # 90, 20; 5898260 - NAME = 'Tx.Commit' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class CommitOk(amqp_object.Method): - - INDEX = 0x005A0015 # 90, 21; 5898261 - NAME = 'Tx.CommitOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Rollback(amqp_object.Method): - - INDEX = 0x005A001E # 90, 30; 5898270 - NAME = 'Tx.Rollback' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class RollbackOk(amqp_object.Method): - - INDEX = 0x005A001F # 90, 31; 5898271 - NAME = 'Tx.RollbackOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Confirm(amqp_object.Class): - - INDEX = 0x0055 # 85 - NAME = 'Confirm' - - class Select(amqp_object.Method): - - INDEX = 0x0055000A # 85, 10; 5570570 - NAME = 'Confirm.Select' - - def __init__(self, nowait=False): - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class SelectOk(amqp_object.Method): - - INDEX = 0x0055000B # 85, 11; 5570571 - NAME = 'Confirm.SelectOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class BasicProperties(amqp_object.Properties): - - CLASS = Basic - INDEX = 0x003C # 60 - NAME = 'BasicProperties' - - FLAG_CONTENT_TYPE = (1 << 15) - FLAG_CONTENT_ENCODING = (1 << 14) - FLAG_HEADERS = (1 << 13) - FLAG_DELIVERY_MODE = (1 << 12) - FLAG_PRIORITY = (1 << 11) - FLAG_CORRELATION_ID = (1 << 10) - FLAG_REPLY_TO = (1 << 9) - FLAG_EXPIRATION = (1 << 8) - FLAG_MESSAGE_ID = (1 << 7) - FLAG_TIMESTAMP = (1 << 6) - FLAG_TYPE = (1 << 5) - FLAG_USER_ID = (1 << 4) - FLAG_APP_ID = (1 << 3) - FLAG_CLUSTER_ID = (1 << 2) - - def __init__(self, content_type=None, content_encoding=None, headers=None, delivery_mode=None, priority=None, correlation_id=None, reply_to=None, expiration=None, message_id=None, timestamp=None, type=None, user_id=None, app_id=None, cluster_id=None): - self.content_type = content_type - self.content_encoding = content_encoding - self.headers = headers - self.delivery_mode = delivery_mode - self.priority = priority - self.correlation_id = correlation_id - self.reply_to = reply_to - self.expiration = expiration - self.message_id = message_id - self.timestamp = timestamp - self.type = type - self.user_id = user_id - self.app_id = app_id - self.cluster_id = cluster_id - - def decode(self, encoded, offset=0): - flags = 0 - flagword_index = 0 - while True: - partial_flags = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - flags = flags | (partial_flags << (flagword_index * 16)) - if not (partial_flags & 1): - break - flagword_index += 1 - if flags & BasicProperties.FLAG_CONTENT_TYPE: - self.content_type, offset = data.decode_short_string(encoded, offset) - else: - self.content_type = None - if flags & BasicProperties.FLAG_CONTENT_ENCODING: - self.content_encoding, offset = data.decode_short_string(encoded, offset) - else: - self.content_encoding = None - if flags & BasicProperties.FLAG_HEADERS: - (self.headers, offset) = data.decode_table(encoded, offset) - else: - self.headers = None - if flags & BasicProperties.FLAG_DELIVERY_MODE: - self.delivery_mode = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - else: - self.delivery_mode = None - if flags & BasicProperties.FLAG_PRIORITY: - self.priority = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - else: - self.priority = None - if flags & BasicProperties.FLAG_CORRELATION_ID: - self.correlation_id, offset = data.decode_short_string(encoded, offset) - else: - self.correlation_id = None - if flags & BasicProperties.FLAG_REPLY_TO: - self.reply_to, offset = data.decode_short_string(encoded, offset) - else: - self.reply_to = None - if flags & BasicProperties.FLAG_EXPIRATION: - self.expiration, offset = data.decode_short_string(encoded, offset) - else: - self.expiration = None - if flags & BasicProperties.FLAG_MESSAGE_ID: - self.message_id, offset = data.decode_short_string(encoded, offset) - else: - self.message_id = None - if flags & BasicProperties.FLAG_TIMESTAMP: - self.timestamp = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - else: - self.timestamp = None - if flags & BasicProperties.FLAG_TYPE: - self.type, offset = data.decode_short_string(encoded, offset) - else: - self.type = None - if flags & BasicProperties.FLAG_USER_ID: - self.user_id, offset = data.decode_short_string(encoded, offset) - else: - self.user_id = None - if flags & BasicProperties.FLAG_APP_ID: - self.app_id, offset = data.decode_short_string(encoded, offset) - else: - self.app_id = None - if flags & BasicProperties.FLAG_CLUSTER_ID: - self.cluster_id, offset = data.decode_short_string(encoded, offset) - else: - self.cluster_id = None - return self - - def encode(self): - pieces = list() - flags = 0 - if self.content_type is not None: - flags = flags | BasicProperties.FLAG_CONTENT_TYPE - assert isinstance(self.content_type, str_or_bytes),\ - 'A non-string value was supplied for self.content_type' - data.encode_short_string(pieces, self.content_type) - if self.content_encoding is not None: - flags = flags | BasicProperties.FLAG_CONTENT_ENCODING - assert isinstance(self.content_encoding, str_or_bytes),\ - 'A non-string value was supplied for self.content_encoding' - data.encode_short_string(pieces, self.content_encoding) - if self.headers is not None: - flags = flags | BasicProperties.FLAG_HEADERS - data.encode_table(pieces, self.headers) - if self.delivery_mode is not None: - flags = flags | BasicProperties.FLAG_DELIVERY_MODE - pieces.append(struct.pack('B', self.delivery_mode)) - if self.priority is not None: - flags = flags | BasicProperties.FLAG_PRIORITY - pieces.append(struct.pack('B', self.priority)) - if self.correlation_id is not None: - flags = flags | BasicProperties.FLAG_CORRELATION_ID - assert isinstance(self.correlation_id, str_or_bytes),\ - 'A non-string value was supplied for self.correlation_id' - data.encode_short_string(pieces, self.correlation_id) - if self.reply_to is not None: - flags = flags | BasicProperties.FLAG_REPLY_TO - assert isinstance(self.reply_to, str_or_bytes),\ - 'A non-string value was supplied for self.reply_to' - data.encode_short_string(pieces, self.reply_to) - if self.expiration is not None: - flags = flags | BasicProperties.FLAG_EXPIRATION - assert isinstance(self.expiration, str_or_bytes),\ - 'A non-string value was supplied for self.expiration' - data.encode_short_string(pieces, self.expiration) - if self.message_id is not None: - flags = flags | BasicProperties.FLAG_MESSAGE_ID - assert isinstance(self.message_id, str_or_bytes),\ - 'A non-string value was supplied for self.message_id' - data.encode_short_string(pieces, self.message_id) - if self.timestamp is not None: - flags = flags | BasicProperties.FLAG_TIMESTAMP - pieces.append(struct.pack('>Q', self.timestamp)) - if self.type is not None: - flags = flags | BasicProperties.FLAG_TYPE - assert isinstance(self.type, str_or_bytes),\ - 'A non-string value was supplied for self.type' - data.encode_short_string(pieces, self.type) - if self.user_id is not None: - flags = flags | BasicProperties.FLAG_USER_ID - assert isinstance(self.user_id, str_or_bytes),\ - 'A non-string value was supplied for self.user_id' - data.encode_short_string(pieces, self.user_id) - if self.app_id is not None: - flags = flags | BasicProperties.FLAG_APP_ID - assert isinstance(self.app_id, str_or_bytes),\ - 'A non-string value was supplied for self.app_id' - data.encode_short_string(pieces, self.app_id) - if self.cluster_id is not None: - flags = flags | BasicProperties.FLAG_CLUSTER_ID - assert isinstance(self.cluster_id, str_or_bytes),\ - 'A non-string value was supplied for self.cluster_id' - data.encode_short_string(pieces, self.cluster_id) - flag_pieces = list() - while True: - remainder = flags >> 16 - partial_flags = flags & 0xFFFE - if remainder != 0: - partial_flags |= 1 - flag_pieces.append(struct.pack('>H', partial_flags)) - flags = remainder - if not flags: - break - return flag_pieces + pieces - -methods = { - 0x000A000A: Connection.Start, - 0x000A000B: Connection.StartOk, - 0x000A0014: Connection.Secure, - 0x000A0015: Connection.SecureOk, - 0x000A001E: Connection.Tune, - 0x000A001F: Connection.TuneOk, - 0x000A0028: Connection.Open, - 0x000A0029: Connection.OpenOk, - 0x000A0032: Connection.Close, - 0x000A0033: Connection.CloseOk, - 0x000A003C: Connection.Blocked, - 0x000A003D: Connection.Unblocked, - 0x0014000A: Channel.Open, - 0x0014000B: Channel.OpenOk, - 0x00140014: Channel.Flow, - 0x00140015: Channel.FlowOk, - 0x00140028: Channel.Close, - 0x00140029: Channel.CloseOk, - 0x001E000A: Access.Request, - 0x001E000B: Access.RequestOk, - 0x0028000A: Exchange.Declare, - 0x0028000B: Exchange.DeclareOk, - 0x00280014: Exchange.Delete, - 0x00280015: Exchange.DeleteOk, - 0x0028001E: Exchange.Bind, - 0x0028001F: Exchange.BindOk, - 0x00280028: Exchange.Unbind, - 0x00280033: Exchange.UnbindOk, - 0x0032000A: Queue.Declare, - 0x0032000B: Queue.DeclareOk, - 0x00320014: Queue.Bind, - 0x00320015: Queue.BindOk, - 0x0032001E: Queue.Purge, - 0x0032001F: Queue.PurgeOk, - 0x00320028: Queue.Delete, - 0x00320029: Queue.DeleteOk, - 0x00320032: Queue.Unbind, - 0x00320033: Queue.UnbindOk, - 0x003C000A: Basic.Qos, - 0x003C000B: Basic.QosOk, - 0x003C0014: Basic.Consume, - 0x003C0015: Basic.ConsumeOk, - 0x003C001E: Basic.Cancel, - 0x003C001F: Basic.CancelOk, - 0x003C0028: Basic.Publish, - 0x003C0032: Basic.Return, - 0x003C003C: Basic.Deliver, - 0x003C0046: Basic.Get, - 0x003C0047: Basic.GetOk, - 0x003C0048: Basic.GetEmpty, - 0x003C0050: Basic.Ack, - 0x003C005A: Basic.Reject, - 0x003C0064: Basic.RecoverAsync, - 0x003C006E: Basic.Recover, - 0x003C006F: Basic.RecoverOk, - 0x003C0078: Basic.Nack, - 0x005A000A: Tx.Select, - 0x005A000B: Tx.SelectOk, - 0x005A0014: Tx.Commit, - 0x005A0015: Tx.CommitOk, - 0x005A001E: Tx.Rollback, - 0x005A001F: Tx.RollbackOk, - 0x0055000A: Confirm.Select, - 0x0055000B: Confirm.SelectOk -} - -props = { - 0x003C: BasicProperties -} - - -def has_content(methodNumber): - return methodNumber in ( - Basic.Publish.INDEX, - Basic.Return.INDEX, - Basic.Deliver.INDEX, - Basic.GetOk.INDEX, - ) diff --git a/packages_o/pika-0.10.0/build/lib/pika/utils.py b/packages_o/pika-0.10.0/build/lib/pika/utils.py deleted file mode 100644 index 57f93b0a..00000000 --- a/packages_o/pika-0.10.0/build/lib/pika/utils.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Non-module specific functions shared by modules in the pika package - -""" -import collections - - -def is_callable(handle): - """Returns a bool value if the handle passed in is a callable - method/function - - :param any handle: The object to check - :rtype: bool - - """ - return isinstance(handle, collections.Callable) diff --git a/packages_o/pika-0.10.0/dist/pika-0.10.0-py3.4.egg b/packages_o/pika-0.10.0/dist/pika-0.10.0-py3.4.egg deleted file mode 100644 index 6becf324b33e2d1af439145cba99dde9b1960e60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212652 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�l$#S*n~e%KLTi?hHeD$`s7eY*XN@PIa|Lsac(E( z&0Nsr-fZHf%(ZT&Pu()N@$pqCNRhDYP*3>3O4F@>9H!vhlf@BOe_!n>!hdKTiPwKP zTr2!2Po@VFbBeUune(VZVXnALE&1+sk_G@mE*~b7Ib6B4;V26d>`?T z6~^2(_eMYZ)vZUmd9cVMU`{?@yIQjx0Wu3;;#iuiY&f9I5%?kCOrn694y==wE!`|PQa!VQ>m619j-Qj7D#RGcY=O&dtPM9NI_3)vUL_9(947}hn7 z{N$F94%Bw(HrdMx+MQ6YIj($_`nFXuCg}yi8`%JN`vfv~W51Ux<-mqV=&YA5%g9&D z{cm2d)pI0xw5KVEZX3x}kfjm`1$iR6Wd{_c&lG9LV25O4;wFM~iw=n*KutABNs_3K z5D&;GgTBjRvC2|(<(;4KpQr~PFI~Oaq~>2IKe{&bZFq{%0{am8%A!Lm{+qpZ$I>6a zJ5VH63#9OFvaYZ5W+YLJ9o~Fy-uOy`t z*PSn>Gu<3}c`Ma=#nc;?{IKrX!|TvQp}eW{xy97I1I1i2so;#*B%azOd_}(UDUozn z5FM#cQZ|blVHT#wb%kp+&tKzjE}#ZVIyx@aH^U8b4(WCDAFn#!bjvNE$l%N5`WmVm zp)yc-S%1Gx>w#tIQC~b>4^Px4Ur`L-GW0Cf8aUA&+=Jf3Xc{a$ zw#MH52%;5mL(VOfNKT=u`)LA{#qZJ#lt0H0Y;3pLRC+8-Jlzgvb!OPnPc!78M?6Cg zahXoLH0z8oYO@mLgYT3Al6CneT34!ToWPwvK%7;LkO9240-iT8!(Z+Bo*FFC2|QeG z_xJseM-BukX~{aw9`?Do9$96*7lMh(tTB(UEFwKR_wtg@yMs*GWga_a3nNOTBV^}c z4-602Vso~q?Awz%-}RA$1GCZ8*5JlHZJ~AFk3&)lJ1FKHz`2t|xud*uBO!U^gYn%3 z6pkKG%LFp+g<%oe;r(;h~h2DV0KOd;j~Gg%?9&n;%|kDR#bk=u$^p?L79N1Gn3 zxov#(n?vZqexHP>ojnFE*QPdAyA3*B5ouSQR<%tl8|T(5nW_#h_k(6LH@~3&GgN~a z;=5S00s!Rx0|X%Xze2T>y@}EP5^isNAf0i>JWOYN4tc`>1msaj`H>_bJ540X!~;o4 zko*DU$J7_i*>_05ai;lsfFpt@)LW0KBaXBhaJL!`DKt%T)?;tF&Bk=`t+;wycn!Id zhQF?@b7y2+-#n7oc@N#M{;I0FI=f8gfu9kI)j~BYFD^U(j;i{8D}VPUQs4Drf9}t3 zuY8X0BJTSLe%iu*sryn#Q9k|fDG?G)9r!5U#|!g{e}=*PfZw_J7Xe?n#GwQlF^Db@ zG+>gP)8CgNB*Yl)!YK?KvwN|Lp?omNC6kMhg(DK26Db@LZ3!gIhqqU)UBp$2-F-a7uKhQRtBDgxx(Omt++y311Li$OvDMUH}$8 zCp;4KIE$!v+{k|0$2HrkVV1iBgplg*>I84bD;8YGix3nWuB9!L@%P-QX*CM!r|pd!_l zLaHI2SW35RinoW7X$vLerq>IJuL~f%BAK9*T_Yj6E(TjgIO39NlapK*LK+Eo;33zJ z1zRy5a3<~vEh9GaE7=jS30!*?ZjT$XA)Jt*btO=+HLc4CcZ7`Cl1Ijgb8(FGS%IOd6nZi6;}&xQ0_G z(fpH0w!rEFo60g^)ZE>n5q+9%#F*h4Gju~T$sM^Rnp8k5df6Q^Y~J2k4E5w z-L$Y~mYL{-Y;+O4rbWOGUavvu0u3z7_owG?%^R9)6TC-;@D&ZpR^G=eq@=oOfFCAJ zsc+s>t`+1K_gI5pe3eMOwv3b?251uKTk-~ycjGSzZO4FO47U# zY>TiJKTk~3ycDb(e-J-UPU1Wt?2B+7ACH8@DbPC?1E+lSB{@P=lHgt8&MRvjgm(}m z8`gH3-^6GJJT008x06)9v&;~Uhs&V(gUp)_;m!frc_o2w*^M@@!{5(HVPM;AQ?g?{ zKieUxT6KIxBFmC5MGG^m13QC*msk9!5S3Bn)KR~y`c5Bz$ZLM*!oq$77Arqk1>RwW2hrqPz4EZDQEt3r>p$eO#NgX|zY^Ydp!af>zke&*%H;c3mHC+mzQ zdv`Q8#naQ}?K*s>W-+nt)JyR=7_KS)D!ZzANX*=5b1KD{GJP2KCQe+3;tgse>Pi1; zNoMH)DM^G75TD1H_b;O=Ydg2zpKX61iw@JJ_2N& zFfc+xXhO4(IfgN?Nd^3Yi|}ce#+$*oNr8$7wUqt@JPCPMe7e!d6PrjdMmt?Bb z(U~}vF^#n>7V?KPaI6#;G7aSMsLIQ1R)d-ETAnGCqLD>nSZYcL72?7Y#V8X~kx{JJ zU%WpwYF30oTRsS%6z8}PRwo8AeEkP)g!?>vx9d+UG{~dhzj|qT7i6*h>sdU@XHd-) zn46D7Ag=|EZqFq+#z}=qzEzIcUmfWKZeIbtqna&I=mOq+erE_*B*i?i?BhR4@YnGO}Tv!MThzJ@GihDdo zAy2Sp$*nhwK9yq4SjNN%y)AEpOP?knn-&=zEhL^s<6 zP=l}yHV~C96rI!v6B7v)>2A`$!TJ{Y&W|e+-Sn#{00=K(iKNV62^NJXdymB@)sEMC zCCj P45kc7ay`SQr2Kv}0!(_0j(>CzfJp_qQKk1~!~3d~k?DRhtz`$SpodZAhC z-B_%`B#qSI0gATTiox8C8aVZ?edQ^;1vYSkV4XG%QNKT>&;RA12u>nt!w)ol&b^{! z%tstZR3(3KeFjC@kD@5QrlG76VA>v|OvFIiX?h=U$Vq+9noBs!oIMB=3dXE>R4%Do z_opqct|Ad#l}PfR!JfrYC6R?iJ^qS9^#;CvGrl_KW?fe2tvZ^u)>XCEDJRUSyS&_$ zwY*%EtC_lkY_XQ8^}Rsd-EB{>8j;ZQIX0H5*P0VOa843(**_$F{)6B1DH zb`5X_=8`DNT99 z5+}Wj=ic&XSY$G38B&Q=+699%Yz3Zr36svC+KKimi5llnPG!4^%(QD@#rXSk$Xj@x zzGhgSejD1oic!)G6oA9B0$*k#x>N~p$R#`sAr-MAMRk+V7#ByT4 z98x8WsdrT#0IeP*qY_G+2c@LeHSPDGGl^aTzHegU4acff0&VHGePk z#5N7|ga+}<&8`&I-3mgRqQmR@uJDHdKEh=7Lzi&m1EkY@6_OaeI4*xI$I;o;HR&7s zK+dsjkcn(o1CW!{NK2nEttlq~e^l7^#^5C#F((O>GBN;t^kqR)2G0&Gt{Q&_pu#^Y zP%km%>riIt-3a{q8$Vb`m*DbF>bX5>DiZvKJRo9xC?qR>-B{__oO!VbD^KP!0xE;D zxK$>laMgy>a1oBNn1BCW+-KMT1@lGTjw3k9xsv57olhFC_G|tC$JRg4o2>hJwsw@H zhm%IF5gEN~*IY085FFgpJ`WHB7?B8p{D;-w;Ic;#0T-$Ko=$j;8gq!xtTw$V?Lk@_ zB!8*5F(M)d0Tq?O#?VMaB((YxCC~a^RDJDqxLF?n4;^%nObnR^go!ZMYr9D8>9^QE z?EPqM_V;cT~ zKt}T82dv z>e+AuPQM_?SwD{7gQzOGJ{!@GB55q2-AF?r7-e?;WI&#J!+ZNs=Gg@l4nfpsb33JC z08g-)w{O|tnkfbKg>j__F26Bwrj4e=;;XTwBeXA+CN@|=-Zofj;?Rt9j$st?k=(2b zv>`jB^ai@v=JxdFnYF<)y_sMVr3pZuaX~^W6m(;SGD*KIh>4JxnIeO8xryhZ3W14~ zsMK_Ii6PZ=tzTkTV4SkYwdY*ynKCnuzUt{OnD2Oe!4GqJK62AH%@ zqL&yzXB(bAdB7FrEhWuTkQQzrI`&s{OK`r@ph1967a~}q5G)az%M+U(50x99*ApR$ zreBs*jBEFWU9$+sp%T=Cxk1E{SptxB7i28Z=KV4;3*uV1uJlEf5M&V1&O@~!8pQI? zu^08=6k@=38tD+7P-71!V{1FrvkxNr888he4=4P2-LuxWTEZx3_9xB1Yp2 z*lf&%!E&P5=K?L}`*j*$;8;)_V~8=n3M}4i7&>5XsgWlye_?bW6Nq^nNg%8)6%VAt zd_3js~yaS0|K^NuO_%man@=r(gn{{S49!TEx&82QS zT%|*2*2`Zw!5DS_UC0cZ{HsV>RO?F#p+521mqPWCLo?Lw1?BW^Oh>I?W{{jjQ?=#k zoi0tkL8KEtjd5^ugLNRA=C1;>Tfp&FCu>vyuVyvV|GM^qJ)n46-2ngEe7Mo2^MXlm zbXL&-FN~4sy2YlG>bk!i#h(2_s|4Z5lkE042sqK`*%&x#Tm#SqhMnpj!L&;{PEaTz z<6?qPD%xzgrhWhLPmnApNHq>025QTio7!%D=K?5b@5B%dBjhefHSRpL&y-|`g#0Gp zwYkiK4WkcQxvO1%5?3+h`?eL(oPo@Aij)ZN!|PJs#p-IFWw#@I`G*WkS;p!T^OIMs z)`9OtK#o!ZD=R*4=WFZ2shaQ%`?wYV%xxTjnZ_%RnPB(cZj#}trxl|C)3aFX+->2o z5hwi4O)%IyvHvRTWW{~lW)5-Rn60h6^ zjeaJm!$LQjlDb8E0tN0Y&LkS@3;?`%30B!lBGBn`*k8{!@t-IH%@3Nt%re13t~wGq zvs%h-sl|WM6R)MR6cqvwyJ`dieI*L|tGi#-uky)1(v^ECMZ7&;DZ}KUBt^9Xc*Dc$FwxJ#5SNC+^TzfWdf_Yl@+m*SYx3N>C52}u} z-tts}s&-rN#7dxphFj-&*L0;Y`Z)Ab#nXZoD|Wmf2eZ;(|N7*~dr{Zpzh7^y8eOiEPWomRf0^CpteUy0*F*M3f#bJ4?t$(q+wOOpaZM2SMe5#=Pr8I+ zZ?X4G6p4EuwyiFh!>gP(196pBC3n@CXgihr1op0^IJEwS(OaV<+qpsCHHhi5>0M)e z*nwhc(y(onY=YgUv~7Gs(^>9VwL9h`;t%uDg2gio!xT7!S*x@tu<_MB)2;2^AxnS0 zeU3Es7OU2!PaNL(eI|(OgZb-G=wKr~n^};?$zwn5>Xl+#}vf7Ghg|Ee=u2 zJlkcJPR=|ny{HpEJaM03bt;UvbmLA{nmm3Kda*3*+CctW!Sa0_{mayauUmQOO=UGZ zqwySX(hT#u0~?<_N88qZ-{(bJz(y+wvIL0Hp6_`x6Y|$)!a=+ZYhlUSpdL#}?i6a$ zq{F#ZG^N2Lj3B2f1aI*HwHd3Z)}Ii)93wCedLUGq97w~CWl?>6r1ZhA_A+yv6KwQWTrC-e9UI7K4bV!VWY$YK^ z1+EVRmK;=jT3q5xv{Dsvk10k0z?4|ULnSypVip!Dc z>3FyU_;t!e%lUZUs_VPCA`7L zCWr>tS=s3J!yV;;i#xeqYq)7P=szgcjf^!D6UV@(D|UEUJt-Rg`54poS5j*VmZc>b zzQqmI@2zQl4dbJm6YH{lHB{jVn}jtZ&LsjsF{)i4qR;WI2L&}i;uO$!a!z5N){9as z@V5{yQ#We{D_Re*+F7ySyh8Mk?4ZjU4N5E(7t@ts`z}t(PRn5pRsG5N%fc@0iWB6P0>yKz8FZ@3)oC=!gTo6( z>t4lJla_x{(3cI1rQ63*8b~4Z+tL@jK!9L@*1T6J5vZlku-k3|$%rD>e^eD;;-3&8 ze6>+#VmvH$Hmo7q^^oYMToy=H*;qCioQA^F3uUKpoD5Icc_kxak=ELZ3LR)dGG!xW zmLb|IX(vqStCmYrLFTRl(n2^y+QoKq1Kv$nDd`gnaed+Cm-q7mJutp3^%}2YJwiZc z<@;d(H9|mup9J8}5`{f$k&9Xpt_x-3b|jllZ3wb?ZHPDc_7>6ZEx6`={N=Pn`0bw9 z$pAde^}y})O3z7_M_>wX>=X|IEKNc$smbS8+8EC&AO+Se0nZg%G|S{F)i9ebO#hj} z;?7w**m07wXkBWsI*fxZpJ`G&+HV_ya;T6|?wTy@-$#Sl_FXo(qAx?@pS2u*CDif% z>#X*{cfKH`%p~RI;VoOfXy^h76`95}-{HeqRF!K^O|~fCLghNI3|#lD4TY2&Y*yQ! z*R|b+SbfKKneubL$JtI)uuu!6oB|pL^bUEkv56ePSUTJnDBD z!8j`ym1RNBE}`>0ZAE73NiA%crLJP_swVG*)DBi0#XBn3I%`An__c6}4SAR9rhFs= zlINocDW5U9$5S5XT+_6)>4yY)oww%HKzW;Go3`ePqa{mza?1H2a=Ow5=A!Z-8?!=B z|0_#VBSIFjF{w-;oo`*cK+dYQb6O2U8KIRlcl8HDY zJ@SEmNwE2DgH3IOPWW0CS#?(y>!Kx+k@(2>J){39HoL-1kO1&YBm6e9P^7B|g3WopoDhCR@>Hmx z`b+xlO7k_yc7$it-J|ClTOTO-N1=m0UrOA(5OQ1iygvw!&>wA4YP_ZvtHV=6M|C!K zU5su<%r2hU8HOlKJk1786nmH2+5k=1I%!XnwS=pk2?J+w6N4BC1`KpBSql?xZM=Z!?eK zoV9Gb^Kx79wgz6RyR*mm@dk+tq1pe+Wv2%Y*|?Q4?)s+-3>netY2rc~6pOfgEYCsa z!2*WF7oYqTO6P(7)d;snot5+9!;1(YSWaVpKqFuebB1c4WIhR>>H4^vb!|W|6jX8`@sgLr-mpHde;M2Bxucn4jJ}ts^M#r~AdA z+_gyfw_^?2s;6MKD|xauSF=%j+(ZI7_T$ju2aFVVXM)rxxTGYk0g}Z@T}|VR#6Wmh ztNn@iD~93u4S|nZ-p3G?_e0DKN7fk@4;8LzQx-?q8OMy+FY|I6&76DL8ku2+krQy4 z0ddtsf~=M`-HUi{o3=3&FJOBT#DAG;=`O&a4REl|+A$QC(LP$G7Gi}7#ucLRF+gUm zTc%BcuqVU_3VjAoTd8oZtd^mS3(eUM9&LQihz8iDHiw|yoOLGARBis&-ujztj|X9j%jXu2bH${*NrH9vJ?EYM}YA<~hn}6<(Gb&pN{DZ-?wp{4-X8ba1A2<-p(TRXs+9?CqGO zhUvi7O=Uvrm!3SQeY8bg8P+q|RkZxkvnbY;z2urY&6AD&LPbnudtL1ziH+}Nu}Nml)0+|5vxg_o;{<65m5XCk`=}KUYd#o0Wqm=-t&so zPerQEGd0hykr`UV8QKVJm-;Mpm}-^nKkez@@X#@9Pu}Ww8tO;+m*Rn{a5H9v0;{

SS`RnZW5j}<-o8x0rAJXm2=ftJcg;>LK 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 - - Example - ------- - Here is the most simple example of use, sending a message with the BlockingConnection adapter: - - .. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - channel.basic_publish(exchange='example', - routing_key='test', - body='Test Message') - connection.close() - - And an example of writing a blocking consumer: - - .. code :: python - - import pika - connection = pika.BlockingConnection() - channel = connection.channel() - - for method_frame, properties, body in channel.consume('test'): - - # Display the message parts and ack the message - print method_frame, properties, body - channel.basic_ack(method_frame.delivery_tag) - - # Escape out of the loop after 10 messages - if method_frame.delivery_tag == 10: - break - - # Cancel the consumer and return any pending messages - requeued_messages = channel.cancel() - print 'Requeued %i messages' % requeued_messages - connection.close() - - Pika provides the following adapters - ------------------------------------ - - - BlockingConnection - enables blocking, synchronous operation on top of library for simple uses - - LibevConnection - adapter for use with the libev event loop http://libev.schmorp.de - - SelectConnection - fast asynchronous adapter - - TornadoConnection - adapter for use with the Tornado IO Loop http://tornadoweb.org - - TwistedConnection - adapter for use with the Twisted asynchronous package http://twistedmatrix.com/ - - Contributing - ------------ - To contribute to pika, please make sure that any new features or changes - to existing functionality include test coverage. Additionally, please format - your code using `yapf `_ with ``google`` style - prior to issuing your pull request. - - .. |Version| image:: https://img.shields.io/pypi/v/pika.svg? - :target: http://badge.fury.io/py/pika - - .. |Status| image:: https://img.shields.io/travis/pika/pika.svg? - :target: https://travis-ci.org/pika/pika - - .. |Coverage| image:: https://img.shields.io/codecov/c/github/pika/pika.svg? - :target: https://codecov.io/github/pika/pika?branch=master - - .. |Downloads| image:: https://img.shields.io/pypi/dm/pika.svg? - :target: https://pypi.python.org/pypi/pika - - .. |License| image:: https://img.shields.io/pypi/l/pika.svg? - :target: https://pika.readthedocs.org - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Natural Language :: English -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: Jython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Communications -Classifier: Topic :: Internet -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Networking diff --git a/packages_o/pika-0.10.0/pika.egg-info/SOURCES.txt b/packages_o/pika-0.10.0/pika.egg-info/SOURCES.txt deleted file mode 100644 index b5dd7fd8..00000000 --- a/packages_o/pika-0.10.0/pika.egg-info/SOURCES.txt +++ /dev/null @@ -1,31 +0,0 @@ -LICENSE -MANIFEST.in -README.rst -setup.cfg -setup.py -pika/__init__.py -pika/amqp_object.py -pika/callback.py -pika/channel.py -pika/compat.py -pika/connection.py -pika/credentials.py -pika/data.py -pika/exceptions.py -pika/frame.py -pika/heartbeat.py -pika/spec.py -pika/utils.py -pika.egg-info/PKG-INFO -pika.egg-info/SOURCES.txt -pika.egg-info/dependency_links.txt -pika.egg-info/requires.txt -pika.egg-info/top_level.txt -pika.egg-info/zip-safe -pika/adapters/__init__.py -pika/adapters/base_connection.py -pika/adapters/blocking_connection.py -pika/adapters/libev_connection.py -pika/adapters/select_connection.py -pika/adapters/tornado_connection.py -pika/adapters/twisted_connection.py \ No newline at end of file diff --git a/packages_o/pika-0.10.0/pika.egg-info/dependency_links.txt b/packages_o/pika-0.10.0/pika.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/packages_o/pika-0.10.0/pika.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages_o/pika-0.10.0/pika.egg-info/requires.txt b/packages_o/pika-0.10.0/pika.egg-info/requires.txt deleted file mode 100644 index b777ed6f..00000000 --- a/packages_o/pika-0.10.0/pika.egg-info/requires.txt +++ /dev/null @@ -1,10 +0,0 @@ - - -[twisted] -twisted - -[libev] -pyev - -[tornado] -tornado \ No newline at end of file diff --git a/packages_o/pika-0.10.0/pika.egg-info/top_level.txt b/packages_o/pika-0.10.0/pika.egg-info/top_level.txt deleted file mode 100644 index df7f4230..00000000 --- a/packages_o/pika-0.10.0/pika.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pika diff --git a/packages_o/pika-0.10.0/pika.egg-info/zip-safe b/packages_o/pika-0.10.0/pika.egg-info/zip-safe deleted file mode 100644 index 8b137891..00000000 --- a/packages_o/pika-0.10.0/pika.egg-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages_o/pika-0.10.0/pika/__init__.py b/packages_o/pika-0.10.0/pika/__init__.py deleted file mode 100644 index aa00cb05..00000000 --- a/packages_o/pika-0.10.0/pika/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -__version__ = '0.10.0' - -import logging -try: - # not available in python 2.6 - from logging import NullHandler -except ImportError: - - class NullHandler(logging.Handler): - - def emit(self, record): - pass - -# Add NullHandler to prevent logging warnings -logging.getLogger(__name__).addHandler(NullHandler()) - -from pika.connection import ConnectionParameters -from pika.connection import URLParameters -from pika.credentials import PlainCredentials -from pika.spec import BasicProperties - -from pika.adapters import BaseConnection -from pika.adapters import BlockingConnection -from pika.adapters import SelectConnection -from pika.adapters import TornadoConnection -from pika.adapters import TwistedConnection -from pika.adapters import LibevConnection diff --git a/packages_o/pika-0.10.0/pika/adapters/__init__.py b/packages_o/pika-0.10.0/pika/adapters/__init__.py deleted file mode 100644 index 1b8e8223..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# -# For copyright and licensing please refer to COPYING. -# -# ***** END LICENSE BLOCK ***** -"""Pika provides multiple adapters to connect to RabbitMQ: - -- adapters.select_connection.SelectConnection: A native event based connection - adapter that implements select, kqueue, poll and epoll. -- adapters.tornado_connection.TornadoConnection: Connection adapter for use - with the Tornado web framework. -- adapters.blocking_connection.BlockingConnection: Enables blocking, - synchronous operation on top of library for simple uses. -- adapters.twisted_connection.TwistedConnection: Connection adapter for use - with the Twisted framework -- adapters.libev_connection.LibevConnection: Connection adapter for use - with the libev event loop and employing nonblocking IO - -""" -from pika.adapters.base_connection import BaseConnection -from pika.adapters.blocking_connection import BlockingConnection -from pika.adapters.select_connection import SelectConnection -from pika.adapters.select_connection import IOLoop - -# Dynamically handle 3rd party library dependencies for optional imports -try: - from pika.adapters.tornado_connection import TornadoConnection -except ImportError: - TornadoConnection = None - -try: - from pika.adapters.twisted_connection import TwistedConnection - from pika.adapters.twisted_connection import TwistedProtocolConnection -except ImportError: - TwistedConnection = None - TwistedProtocolConnection = None - -try: - from pika.adapters.libev_connection import LibevConnection -except ImportError: - LibevConnection = None diff --git a/packages_o/pika-0.10.0/pika/adapters/base_connection.py b/packages_o/pika-0.10.0/pika/adapters/base_connection.py deleted file mode 100644 index 8f2629d2..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/base_connection.py +++ /dev/null @@ -1,491 +0,0 @@ -"""Base class extended by connection adapters. This extends the -connection.Connection class to encapsulate connection behavior but still -isolate socket and low level communication. - -""" -import errno -import logging -import socket -import ssl - -import pika.compat -from pika import connection -from pika import exceptions - -try: - SOL_TCP = socket.SOL_TCP -except AttributeError: - SOL_TCP = 6 - - -if pika.compat.PY2: - _SOCKET_ERROR = socket.error -else: - # socket.error was deprecated and replaced by OSError in python 3.3 - _SOCKET_ERROR = OSError - - -LOGGER = logging.getLogger(__name__) - - -class BaseConnection(connection.Connection): - """BaseConnection class that should be extended by connection adapters""" - - # Use epoll's constants to keep life easy - READ = 0x0001 - WRITE = 0x0004 - ERROR = 0x0008 - - ERRORS_TO_ABORT = [errno.EBADF, errno.ECONNABORTED, errno.EPIPE] - ERRORS_TO_IGNORE = [errno.EWOULDBLOCK, errno.EAGAIN, errno.EINTR] - DO_HANDSHAKE = True - WARN_ABOUT_IOLOOP = False - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - ioloop=None, - stop_ioloop_on_close=True): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Method to call on connection open - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param method on_close_callback: Method to call on connection close - :param object ioloop: IOLoop object to use - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :raises: RuntimeError - :raises: ValueError - - """ - if parameters and not isinstance(parameters, connection.Parameters): - raise ValueError('Expected instance of Parameters, not %r' % - parameters) - - # Let the developer know we could not import SSL - if parameters and parameters.ssl and not ssl: - raise RuntimeError("SSL specified but it is not available") - self.base_events = self.READ | self.ERROR - self.event_state = self.base_events - self.ioloop = ioloop - self.socket = None - self.stop_ioloop_on_close = stop_ioloop_on_close - self.write_buffer = None - super(BaseConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback) - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - return self.ioloop.add_timeout(deadline, callback_method) - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - super(BaseConnection, self).close(reply_code, reply_text) - self._handle_ioloop_stop() - - def remove_timeout(self, timeout_id): - """Remove the timeout from the IOLoop by the ID returned from - add_timeout. - - :rtype: str - - """ - self.ioloop.remove_timeout(timeout_id) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker, returning True if connected. - - :returns: error string or exception instance on error; None on success - - """ - # Get the addresses for the socket, supporting IPv4 & IPv6 - while True: - try: - addresses = socket.getaddrinfo(self.params.host, self.params.port, - 0, socket.SOCK_STREAM, - socket.IPPROTO_TCP) - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - - LOGGER.critical('Could not get addresses to use: %s (%s)', error, - self.params.host) - return error - - # If the socket is created and connected, continue on - error = "No socket addresses available" - for sock_addr in addresses: - error = self._create_and_connect_to_socket(sock_addr) - if not error: - # Make the socket non-blocking after the connect - self.socket.setblocking(0) - return None - self._cleanup_socket() - - # Failed to connect - return error - - def _adapter_disconnect(self): - """Invoked if the connection is being told to disconnect""" - try: - self._remove_heartbeat() - self._cleanup_socket() - self._check_state_on_disconnect() - finally: - # Ensure proper cleanup since _check_state_on_disconnect may raise - # an exception - self._handle_ioloop_stop() - self._init_connection_state() - - def _check_state_on_disconnect(self): - """Checks to see if we were in opening a connection with RabbitMQ when - we were disconnected and raises exceptions for the anticipated - exception types. - - """ - if self.connection_state == self.CONNECTION_PROTOCOL: - LOGGER.error('Incompatible Protocol Versions') - raise exceptions.IncompatibleProtocolError - elif self.connection_state == self.CONNECTION_START: - LOGGER.error("Socket closed while authenticating indicating a " - "probable authentication error") - raise exceptions.ProbableAuthenticationError - elif self.connection_state == self.CONNECTION_TUNE: - LOGGER.error("Socket closed while tuning the connection indicating " - "a probable permission error when accessing a virtual " - "host") - raise exceptions.ProbableAccessDeniedError - elif self.is_open: - LOGGER.warning("Socket closed when connection was open") - elif not self.is_closed and not self.is_closing: - LOGGER.warning('Unknown state on disconnect: %i', - self.connection_state) - - def _cleanup_socket(self): - """Close the socket cleanly""" - if self.socket: - try: - self.socket.shutdown(socket.SHUT_RDWR) - except _SOCKET_ERROR: - pass - self.socket.close() - self.socket = None - - def _create_and_connect_to_socket(self, sock_addr_tuple): - """Create socket and connect to it, using SSL if enabled. - - :returns: error string on failure; None on success - """ - self.socket = socket.socket(sock_addr_tuple[0], socket.SOCK_STREAM, 0) - self.socket.setsockopt(SOL_TCP, socket.TCP_NODELAY, 1) - self.socket.settimeout(self.params.socket_timeout) - - # Wrap socket if using SSL - if self.params.ssl: - self.socket = self._wrap_socket(self.socket) - ssl_text = " with SSL" - else: - ssl_text = "" - - LOGGER.info('Connecting to %s:%s%s', sock_addr_tuple[4][0], - sock_addr_tuple[4][1], ssl_text) - - # Connect to the socket - try: - self.socket.connect(sock_addr_tuple[4]) - except socket.timeout: - error = 'Connection to %s:%s failed: timeout' % ( - sock_addr_tuple[4][0], sock_addr_tuple[4][1] - ) - LOGGER.error(error) - return error - except _SOCKET_ERROR as error: - error = 'Connection to %s:%s failed: %s' % (sock_addr_tuple[4][0], - sock_addr_tuple[4][1], - error) - LOGGER.warning(error) - return error - - # Handle SSL Connection Negotiation - if self.params.ssl and self.DO_HANDSHAKE: - try: - self._do_ssl_handshake() - except ssl.SSLError as error: - error = 'SSL connection to %s:%s failed: %s' % ( - sock_addr_tuple[4][0], sock_addr_tuple[4][1], error - ) - LOGGER.error(error) - return error - # Made it this far - return None - - def _do_ssl_handshake(self): - """Perform SSL handshaking, copied from python stdlib test_ssl.py. - - """ - if not self.DO_HANDSHAKE: - return - while True: - try: - self.socket.do_handshake() - break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - self.event_state = self.READ - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - self.event_state = self.WRITE - else: - raise - self._manage_event_state() - - @staticmethod - def _get_error_code(error_value): - """Get the error code from the error_value accounting for Python - version differences. - - :rtype: int - - """ - if not error_value: - return None - if hasattr(error_value, 'errno'): # Python >= 2.6 - return error_value.errno - elif error_value is not None: - return error_value[0] # Python <= 2.5 - return None - - def _flush_outbound(self): - """write early, if the socket will take the data why not get it out - there asap. - """ - self._handle_write() - self._manage_event_state() - - def _handle_disconnect(self): - """Called internally when the socket is disconnected already - """ - self._adapter_disconnect() - self._on_connection_closed(None, True) - - def _handle_ioloop_stop(self): - """Invoked when the connection is closed to determine if the IOLoop - should be stopped or not. - - """ - if self.stop_ioloop_on_close and self.ioloop: - self.ioloop.stop() - elif self.WARN_ABOUT_IOLOOP: - LOGGER.warning('Connection is closed but not stopping IOLoop') - - def _handle_error(self, error_value): - """Internal error handling method. Here we expect a socket.error - coming in and will handle different socket errors differently. - - :param int|object error_value: The inbound error - - """ - if 'timed out' in str(error_value): - raise socket.timeout - error_code = self._get_error_code(error_value) - if not error_code: - LOGGER.critical("Tried to handle an error where no error existed") - return - - # Ok errors, just continue what we were doing before - if error_code in self.ERRORS_TO_IGNORE: - LOGGER.debug("Ignoring %s", error_code) - return - - # Socket is no longer connected, abort - elif error_code in self.ERRORS_TO_ABORT: - LOGGER.error("Fatal Socket Error: %r", error_value) - - elif self.params.ssl and isinstance(error_value, ssl.SSLError): - - if error_value.args[0] == ssl.SSL_ERROR_WANT_READ: - self.event_state = self.READ - elif error_value.args[0] == ssl.SSL_ERROR_WANT_WRITE: - self.event_state = self.WRITE - else: - LOGGER.error("SSL Socket error: %r", error_value) - - else: - # Haven't run into this one yet, log it. - LOGGER.error("Socket Error: %s", error_code) - - # Disconnect from our IOLoop and let Connection know what's up - self._handle_disconnect() - - def _handle_timeout(self): - """Handle a socket timeout in read or write. - We don't do anything in the non-blocking handlers because we - only have the socket in a blocking state during connect.""" - pass - - def _handle_events(self, fd, events, error=None, write_only=False): - """Handle IO/Event loop events, processing them. - - :param int fd: The file descriptor for the events - :param int events: Events from the IO/Event loop - :param int error: Was an error specified - :param bool write_only: Only handle write events - - """ - if not self.socket: - LOGGER.error('Received events on closed socket: %r', fd) - return - - if self.socket and (events & self.WRITE): - self._handle_write() - self._manage_event_state() - - if self.socket and not write_only and (events & self.READ): - self._handle_read() - - if (self.socket and write_only and (events & self.READ) and - (events & self.ERROR)): - LOGGER.error('BAD libc: Write-Only but Read+Error. ' - 'Assume socket disconnected.') - self._handle_disconnect() - - if self.socket and (events & self.ERROR): - LOGGER.error('Error event %r, %r', events, error) - self._handle_error(error) - - def _handle_read(self): - """Read from the socket and call our on_data_available with the data.""" - try: - while True: - try: - if self.params.ssl: - data = self.socket.read(self._buffer_size) - else: - data = self.socket.recv(self._buffer_size) - - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - else: - raise - - except socket.timeout: - self._handle_timeout() - return 0 - - except ssl.SSLError as error: - if error.args[0] == ssl.SSL_ERROR_WANT_READ: - # ssl wants more data but there is nothing currently - # available in the socket, wait for it to become readable. - return 0 - return self._handle_error(error) - - except _SOCKET_ERROR as error: - if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): - return 0 - return self._handle_error(error) - - # Empty data, should disconnect - if not data or data == 0: - LOGGER.error('Read empty data, calling disconnect') - return self._handle_disconnect() - - # Pass the data into our top level frame dispatching method - self._on_data_available(data) - return len(data) - - def _handle_write(self): - """Try and write as much as we can, if we get blocked requeue - what's left""" - bytes_written = 0 - try: - while self.outbound_buffer: - frame = self.outbound_buffer.popleft() - while True: - try: - bw = self.socket.send(frame) - break - except _SOCKET_ERROR as error: - if error.errno == errno.EINTR: - continue - else: - raise - - bytes_written += bw - if bw < len(frame): - LOGGER.debug("Partial write, requeing remaining data") - self.outbound_buffer.appendleft(frame[bw:]) - break - - except socket.timeout: - # Will only come here if the socket is blocking - LOGGER.debug("socket timeout, requeuing frame") - self.outbound_buffer.appendleft(frame) - self._handle_timeout() - - except _SOCKET_ERROR as error: - if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK): - LOGGER.debug("Would block, requeuing frame") - self.outbound_buffer.appendleft(frame) - else: - return self._handle_error(error) - - return bytes_written - - - def _init_connection_state(self): - """Initialize or reset all of our internal state variables for a given - connection. If we disconnect and reconnect, all of our state needs to - be wiped. - - """ - super(BaseConnection, self)._init_connection_state() - self.base_events = self.READ | self.ERROR - self.event_state = self.base_events - self.socket = None - - def _manage_event_state(self): - """Manage the bitmask for reading/writing/error which is used by the - io/event handler to specify when there is an event such as a read or - write. - - """ - if self.outbound_buffer: - if not self.event_state & self.WRITE: - self.event_state |= self.WRITE - self.ioloop.update_handler(self.socket.fileno(), - self.event_state) - elif self.event_state & self.WRITE: - self.event_state = self.base_events - self.ioloop.update_handler(self.socket.fileno(), self.event_state) - - def _wrap_socket(self, sock): - """Wrap the socket for connecting over SSL. - - :rtype: ssl.SSLSocket - - """ - return ssl.wrap_socket(sock, - do_handshake_on_connect=self.DO_HANDSHAKE, - **self.params.ssl_options) diff --git a/packages_o/pika-0.10.0/pika/adapters/blocking_connection.py b/packages_o/pika-0.10.0/pika/adapters/blocking_connection.py deleted file mode 100644 index 09051dae..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/blocking_connection.py +++ /dev/null @@ -1,2477 +0,0 @@ -"""The blocking connection adapter module implements blocking semantics on top -of Pika's core AMQP driver. While most of the asynchronous expectations are -removed when using the blocking connection adapter, it attempts to remain true -to the asynchronous RPC nature of the AMQP protocol, supporting server sent -RPC commands. - -The user facing classes in the module consist of the -:py:class:`~pika.adapters.blocking_connection.BlockingConnection` -and the :class:`~pika.adapters.blocking_connection.BlockingChannel` -classes. - -""" -# Disable "access to protected member warnings: this wrapper implementation is -# a friend of those instances -# pylint: disable=W0212 - -from collections import namedtuple, deque -import contextlib -import functools -import logging -import time - -import pika.channel -from pika import compat -from pika import exceptions -import pika.spec -# NOTE: import SelectConnection after others to avoid circular depenency -from pika.adapters.select_connection import SelectConnection - -LOGGER = logging.getLogger(__name__) - - -class _CallbackResult(object): - """ CallbackResult is a non-thread-safe implementation for receiving - callback results; INTERNAL USE ONLY! - """ - __slots__ = ('_value_class', '_ready', '_values') - def __init__(self, value_class=None): - """ - :param callable value_class: only needed if the CallbackResult - instance will be used with - `set_value_once` and `append_element`. - *args and **kwargs of the value setter - methods will be passed to this class. - - """ - self._value_class = value_class - self._ready = None - self._values = None - self.reset() - - def reset(self): - """Reset value, but not _value_class""" - self._ready = False - self._values = None - - def __bool__(self): - """ Called by python runtime to implement truth value testing and the - built-in operation bool(); NOTE: python 3.x - """ - return self.is_ready() - - # python 2.x version of __bool__ - __nonzero__ = __bool__ - - def __enter__(self): - """ Entry into context manager that automatically resets the object - on exit; this usage pattern helps garbage-collection by eliminating - potential circular references. - """ - return self - - def __exit__(self, *args, **kwargs): - """Reset value""" - self.reset() - - def is_ready(self): - """ - :returns: True if the object is in a signaled state - """ - return self._ready - - @property - def ready(self): - """True if the object is in a signaled state""" - return self._ready - - def signal_once(self, *_args, **_kwargs): # pylint: disable=W0613 - """ Set as ready - - :raises AssertionError: if result was already signalled - """ - assert not self._ready, '_CallbackResult was already set' - self._ready = True - - def set_value_once(self, *args, **kwargs): - """ Set as ready with value; the value may be retrived via the `value` - property getter - - :raises AssertionError: if result was already set - """ - self.signal_once() - try: - self._values = (self._value_class(*args, **kwargs),) - except Exception: - LOGGER.error( - "set_value_once failed: value_class=%r; args=%r; kwargs=%r", - self._value_class, args, kwargs) - raise - - def append_element(self, *args, **kwargs): - """Append an element to values""" - assert not self._ready or isinstance(self._values, list), ( - '_CallbackResult state is incompatible with append_element: ' - 'ready=%r; values=%r' % (self._ready, self._values)) - - try: - value = self._value_class(*args, **kwargs) - except Exception: - LOGGER.error( - "append_element failed: value_class=%r; args=%r; kwargs=%r", - self._value_class, args, kwargs) - raise - - if self._values is None: - self._values = [value] - else: - self._values.append(value) - - self._ready = True - - - @property - def value(self): - """ - :returns: a reference to the value that was set via `set_value_once` - :raises AssertionError: if result was not set or value is incompatible - with `set_value_once` - """ - assert self._ready, '_CallbackResult was not set' - assert isinstance(self._values, tuple) and len(self._values) == 1, ( - '_CallbackResult value is incompatible with set_value_once: %r' - % (self._values,)) - - return self._values[0] - - - @property - def elements(self): - """ - :returns: a reference to the list containing one or more elements that - were added via `append_element` - :raises AssertionError: if result was not set or value is incompatible - with `append_element` - """ - assert self._ready, '_CallbackResult was not set' - assert isinstance(self._values, list) and len(self._values) > 0, ( - '_CallbackResult value is incompatible with append_element: %r' - % (self._values,)) - - return self._values - - -class _IoloopTimerContext(object): # pylint: disable=R0903 - """Context manager for registering and safely unregistering a - SelectConnection ioloop-based timer - """ - - def __init__(self, duration, connection): - """ - :param float duration: non-negative timer duration in seconds - :param SelectConnection connection: - """ - assert hasattr(connection, 'add_timeout'), connection - self._duration = duration - self._connection = connection - self._callback_result = _CallbackResult() - self._timer_id = None - - def __enter__(self): - """Register a timer""" - self._timer_id = self._connection.add_timeout( - self._duration, - self._callback_result.signal_once) - return self - - def __exit__(self, *_args, **_kwargs): - """Unregister timer if it hasn't fired yet""" - if not self._callback_result: - self._connection.remove_timeout(self._timer_id) - - def is_ready(self): - """ - :returns: True if timer has fired, False otherwise - """ - return self._callback_result.is_ready() - - -class _TimerEvt(object): # pylint: disable=R0903 - """Represents a timer created via `BlockingConnection.add_timeout`""" - __slots__ = ('timer_id', '_callback') - - def __init__(self, callback): - """ - :param callback: see callback_method in `BlockingConnection.add_timeout` - """ - self._callback = callback - - # Will be set to timer id returned from the underlying implementation's - # `add_timeout` method - self.timer_id = None - - def __repr__(self): - return '%s(timer_id=%s, callback=%s)' % (self.__class__.__name__, - self.timer_id, self._callback) - - def dispatch(self): - """Dispatch the user's callback method""" - self._callback() - - -class _ConnectionBlockedUnblockedEvtBase(object): # pylint: disable=R0903 - """Base class for `_ConnectionBlockedEvt` and `_ConnectionUnblockedEvt`""" - __slots__ = ('_callback', '_method_frame') - - def __init__(self, callback, method_frame): - """ - :param callback: see callback_method parameter in - `BlockingConnection.add_on_connection_blocked_callback` and - `BlockingConnection.add_on_connection_unblocked_callback` - :param pika.frame.Method method_frame: with method_frame.method of type - `pika.spec.Connection.Blocked` or `pika.spec.Connection.Unblocked` - """ - self._callback = callback - self._method_frame = method_frame - - def __repr__(self): - return '%s(callback=%s, frame=%s)' % (self.__class__.__name__, - self._callback, - self._method_frame) - - def dispatch(self): - """Dispatch the user's callback method""" - self._callback(self._method_frame) - - -class _ConnectionBlockedEvt( # pylint: disable=R0903 - _ConnectionBlockedUnblockedEvtBase): - """Represents a Connection.Blocked notification from RabbitMQ broker`""" - pass - - -class _ConnectionUnblockedEvt( # pylint: disable=R0903 - _ConnectionBlockedUnblockedEvtBase): - """Represents a Connection.Unblocked notification from RabbitMQ broker`""" - pass - - -class BlockingConnection(object): # pylint: disable=R0902 - """The BlockingConnection creates a layer on top of Pika's asynchronous core - providing methods that will block until their expected response has - returned. Due to the asynchronous nature of the `Basic.Deliver` and - `Basic.Return` calls from RabbitMQ to your application, you can still - implement continuation-passing style asynchronous methods if you'd like to - receive messages from RabbitMQ using - :meth:`basic_consume ` or if you want to be - notified of a delivery failure when using - :meth:`basic_publish ` . - - For more information about communicating with the blocking_connection - adapter, be sure to check out the - :class:`BlockingChannel ` class which implements the - :class:`Channel ` based communication for the - blocking_connection adapter. - - """ - # Connection-opened callback args - _OnOpenedArgs = namedtuple('BlockingConnection__OnOpenedArgs', - 'connection') - - # Connection-establishment error callback args - _OnOpenErrorArgs = namedtuple('BlockingConnection__OnOpenErrorArgs', - 'connection error_text') - - # Connection-closing callback args - _OnClosedArgs = namedtuple('BlockingConnection__OnClosedArgs', - 'connection reason_code reason_text') - - # Channel-opened callback args - _OnChannelOpenedArgs = namedtuple( - 'BlockingConnection__OnChannelOpenedArgs', - 'channel') - - def __init__(self, parameters=None, _impl_class=None): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param _impl_class: for tests/debugging only; implementation class; - None=default - - :raises RuntimeError: - - """ - # Used by the _acquire_event_dispatch decorator; when already greater - # than 0, event dispatch is already acquired higher up the call stack - self._event_dispatch_suspend_depth = 0 - - # Connection-specific events that are ready for dispatch: _TimerEvt, - # _ConnectionBlockedEvt, _ConnectionUnblockedEvt - self._ready_events = deque() - - # Channel numbers of channels that are requesting a call to their - # BlockingChannel._dispatch_events method; See - # `_request_channel_dispatch` - self._channels_pending_dispatch = set() - - # Receives on_open_callback args from Connection - self._opened_result = _CallbackResult(self._OnOpenedArgs) - - # Receives on_open_error_callback args from Connection - self._open_error_result = _CallbackResult(self._OnOpenErrorArgs) - - # Receives on_close_callback args from Connection - self._closed_result = _CallbackResult(self._OnClosedArgs) - - # Set to True when when user calls close() on the connection - # NOTE: this is a workaround to detect socket error because - # on_close_callback passes reason_code=0 when called due to socket error - self._user_initiated_close = False - - impl_class = _impl_class or SelectConnection - self._impl = impl_class( - parameters=parameters, - on_open_callback=self._opened_result.set_value_once, - on_open_error_callback=self._open_error_result.set_value_once, - on_close_callback=self._closed_result.set_value_once, - stop_ioloop_on_close=False) - - self._process_io_for_connection_setup() - - def _cleanup(self): - """Clean up members that might inhibit garbage collection""" - self._ready_events.clear() - self._opened_result.reset() - self._open_error_result.reset() - self._closed_result.reset() - - @contextlib.contextmanager - def _acquire_event_dispatch(self): - """ Context manager that controls access to event dispatcher for - preventing reentrancy. - - The "as" value is True if the managed code block owns the event - dispatcher and False if caller higher up in the call stack already owns - it. Only managed code that gets ownership (got True) is permitted to - dispatch - """ - try: - # __enter__ part - self._event_dispatch_suspend_depth += 1 - yield self._event_dispatch_suspend_depth == 1 - finally: - # __exit__ part - self._event_dispatch_suspend_depth -= 1 - - def _process_io_for_connection_setup(self): # pylint: disable=C0103 - """ Perform follow-up processing for connection setup request: flush - connection output and process input while waiting for connection-open - or connection-error. - - :raises AMQPConnectionError: on connection open error - """ - self._flush_output(self._opened_result.is_ready, - self._open_error_result.is_ready) - - if self._open_error_result.ready: - raise exceptions.AMQPConnectionError( - self._open_error_result.value.error_text) - - assert self._opened_result.ready - assert self._opened_result.value.connection is self._impl - - def _flush_output(self, *waiters): - """ Flush output and process input while waiting for any of the given - callbacks to return true. The wait is aborted upon connection-close. - Otherwise, processing continues until the output is flushed AND at least - one of the callbacks returns true. If there are no callbacks, then - processing ends when all output is flushed. - - :param waiters: sequence of zero or more callables taking no args and - returning true when it's time to stop processing. - Their results are OR'ed together. - """ - if self._impl.is_closed: - raise exceptions.ConnectionClosed() - - # Conditions for terminating the processing loop: - # connection closed - # OR - # empty outbound buffer and no waiters - # OR - # empty outbound buffer and any waiter is ready - is_done = (lambda: - self._closed_result.ready or - (not self._impl.outbound_buffer and - (not waiters or any(ready() for ready in waiters)))) - - # Process I/O until our completion condition is satisified - while not is_done(): - self._impl.ioloop.poll() - self._impl.ioloop.process_timeouts() - - if self._closed_result.ready: - try: - result = self._closed_result.value - if result.reason_code not in [0, 200]: - LOGGER.critical('Connection close detected; result=%r', - result) - raise exceptions.ConnectionClosed(result.reason_code, - result.reason_text) - elif not self._user_initiated_close: - # NOTE: unfortunately, upon socket error, on_close_callback - # presently passes reason_code=0, so we don't detect that as - # an error - LOGGER.critical('Connection close detected') - raise exceptions.ConnectionClosed() - else: - LOGGER.info('Connection closed; result=%r', result) - finally: - self._cleanup() - - def _request_channel_dispatch(self, channel_number): - """Called by BlockingChannel instances to request a call to their - _dispatch_events method or to terminate `process_data_events`; - BlockingConnection will honor these requests from a safe context. - - :param int channel_number: positive channel number to request a call - to the channel's `_dispatch_events`; a negative channel number to - request termination of `process_data_events` - """ - self._channels_pending_dispatch.add(channel_number) - - def _dispatch_channel_events(self): - """Invoke the `_dispatch_events` method on open channels that requested - it - """ - if not self._channels_pending_dispatch: - return - - with self._acquire_event_dispatch() as dispatch_acquired: - if not dispatch_acquired: - # Nested dispatch or dispatch blocked higher in call stack - return - - candidates = list(self._channels_pending_dispatch) - self._channels_pending_dispatch.clear() - - for channel_number in candidates: - if channel_number < 0: - # This was meant to terminate process_data_events - continue - - try: - impl_channel = self._impl._channels[channel_number] - except KeyError: - continue - - if impl_channel.is_open: - impl_channel._get_cookie()._dispatch_events() - - def _on_timer_ready(self, evt): - """Handle expiry of a timer that was registered via `add_timeout` - - :param _TimerEvt evt: - - """ - self._ready_events.append(evt) - - def _on_connection_blocked(self, user_callback, method_frame): - """Handle Connection.Blocked notification from RabbitMQ broker - - :param callable user_callback: callback_method passed to - `add_on_connection_blocked_callback` - :param pika.frame.Method method_frame: method frame having `method` - member of type `pika.spec.Connection.Blocked` - """ - self._ready_events.append( - _ConnectionBlockedEvt(user_callback, method_frame)) - - def _on_connection_unblocked(self, user_callback, method_frame): - """Handle Connection.Unblocked notification from RabbitMQ broker - - :param callable user_callback: callback_method passed to - `add_on_connection_unblocked_callback` - :param pika.frame.Method method_frame: method frame having `method` - member of type `pika.spec.Connection.Blocked` - """ - self._ready_events.append( - _ConnectionUnblockedEvt(user_callback, method_frame)) - - def _dispatch_connection_events(self): - """Dispatch ready connection events""" - if not self._ready_events: - return - - with self._acquire_event_dispatch() as dispatch_acquired: - if not dispatch_acquired: - # Nested dispatch or dispatch blocked higher in call stack - return - - # Limit dispatch to the number of currently ready events to avoid - # getting stuck in this loop - for _ in compat.xrange(len(self._ready_events)): - try: - evt = self._ready_events.popleft() - except IndexError: - # Some events (e.g., timers) must have been cancelled - break - - evt.dispatch() - - def add_on_connection_blocked_callback(self, # pylint: disable=C0103 - callback_method): - """Add a callback to be notified when RabbitMQ has sent a - `Connection.Blocked` frame indicating that RabbitMQ is low on - resources. Publishers can use this to voluntarily suspend publishing, - instead of relying on back pressure throttling. The callback - will be passed the `Connection.Blocked` method frame. - - :param method callback_method: Callback to call on `Connection.Blocked`, - having the signature callback_method(pika.frame.Method), where the - method frame's `method` member is of type - `pika.spec.Connection.Blocked` - - """ - self._impl.add_on_connection_blocked_callback( - functools.partial(self._on_connection_blocked, callback_method)) - - def add_on_connection_unblocked_callback(self, # pylint: disable=C0103 - callback_method): - """Add a callback to be notified when RabbitMQ has sent a - `Connection.Unblocked` frame letting publishers know it's ok - to start publishing again. The callback will be passed the - `Connection.Unblocked` method frame. - - :param method callback_method: Callback to call on - `Connection.Unblocked`, having the signature - callback_method(pika.frame.Method), where the method frame's - `method` member is of type `pika.spec.Connection.Unblocked` - - """ - self._impl.add_on_connection_unblocked_callback( - functools.partial(self._on_connection_unblocked, callback_method)) - - def add_timeout(self, deadline, callback_method): - """Create a single-shot timer to fire after deadline seconds. Do not - confuse with Tornado's timeout where you pass in the time you want to - have your callback called. Only pass in the seconds until it's to be - called. - - NOTE: the timer callbacks are dispatched only in the scope of - specially-designated methods: see - `BlockingConnection.process_data_events` and - `BlockingChannel.start_consuming`. - - :param float deadline: The number of seconds to wait to call callback - :param callable callback_method: The callback method with the signature - callback_method() - - :returns: opaque timer id - - """ - if not callable(callback_method): - raise ValueError( - 'callback_method parameter must be callable, but got %r' - % (callback_method,)) - - evt = _TimerEvt(callback=callback_method) - timer_id = self._impl.add_timeout( - deadline, - functools.partial(self._on_timer_ready, evt)) - evt.timer_id = timer_id - - return timer_id - - def remove_timeout(self, timeout_id): - """Remove a timer if it's still in the timeout stack - - :param timeout_id: The opaque timer id to remove - - """ - # Remove from the impl's timeout stack - self._impl.remove_timeout(timeout_id) - - # Remove from ready events, if the timer fired already - for i, evt in enumerate(self._ready_events): - if isinstance(evt, _TimerEvt) and evt.timer_id == timeout_id: - index_to_remove = i - break - else: - # Not found - return - - del self._ready_events[index_to_remove] - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - LOGGER.info('Closing connection (%s): %s', reply_code, reply_text) - - self._user_initiated_close = True - - # Close channels that remain opened - for impl_channel in pika.compat.dictvalues(self._impl._channels): - channel = impl_channel._get_cookie() - if channel.is_open: - channel.close(reply_code, reply_text) - - # Close the connection - self._impl.close(reply_code, reply_text) - - self._flush_output(self._closed_result.is_ready) - - def process_data_events(self, time_limit=0): - """Will make sure that data events are processed. Dispatches timer and - channel callbacks if not called from the scope of BlockingConnection or - BlockingChannel callback. Your app can block on this method. - - :param float time_limit: suggested upper bound on processing time in - seconds. The actual blocking time depends on the granularity of the - underlying ioloop. Zero means return as soon as possible. None means - there is no limit on processing time and the function will block - until I/O produces actionalable events. Defaults to 0 for backward - compatibility. This parameter is NEW in pika 0.10.0. - """ - common_terminator = lambda: bool( - self._channels_pending_dispatch or self._ready_events) - - if time_limit is None: - self._flush_output(common_terminator) - else: - with _IoloopTimerContext(time_limit, self._impl) as timer: - self._flush_output(timer.is_ready, common_terminator) - - if self._ready_events: - self._dispatch_connection_events() - - if self._channels_pending_dispatch: - self._dispatch_channel_events() - - def sleep(self, duration): - """A safer way to sleep than calling time.sleep() directly that would - keep the adapter from ignoring frames sent from the broker. The - connection will "sleep" or block the number of seconds specified in - duration in small intervals. - - :param float duration: The time to sleep in seconds - - """ - assert duration >= 0, duration - - deadline = time.time() + duration - time_limit = duration - # Process events at least once - while True: - self.process_data_events(time_limit) - time_limit = deadline - time.time() - if time_limit <= 0: - break - - def channel(self, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - :rtype: pika.synchronous_connection.BlockingChannel - """ - with _CallbackResult(self._OnChannelOpenedArgs) as opened_args: - impl_channel = self._impl.channel( - on_open_callback=opened_args.set_value_once, - channel_number=channel_number) - - # Create our proxy channel - channel = BlockingChannel(impl_channel, self) - - # Link implementation channel with our proxy channel - impl_channel._set_cookie(channel) - - # Drive I/O until Channel.Open-ok - channel._flush_output(opened_args.is_ready) - - - return channel - - def __enter__(self): - # Prepare `with` context - return self - - def __exit__(self, tp, value, traceback): - # Close connection after `with` context - self.close() - - # - # Connections state properties - # - - @property - def is_closed(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_closed - - @property - def is_closing(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_closing - - @property - def is_open(self): - """ - Returns a boolean reporting the current connection state. - """ - return self._impl.is_open - - # - # Properties that reflect server capabilities for the current connection - # - - @property - def basic_nack_supported(self): - """Specifies if the server supports basic.nack on the active connection. - - :rtype: bool - - """ - return self._impl.basic_nack - - @property - def consumer_cancel_notify_supported(self): # pylint: disable=C0103 - """Specifies if the server supports consumer cancel notification on the - active connection. - - :rtype: bool - - """ - return self._impl.consumer_cancel_notify - - @property - def exchange_exchange_bindings_supported(self): # pylint: disable=C0103 - """Specifies if the active connection supports exchange to exchange - bindings. - - :rtype: bool - - """ - return self._impl.exchange_exchange_bindings - - @property - def publisher_confirms_supported(self): - """Specifies if the active connection can use publisher confirmations. - - :rtype: bool - - """ - return self._impl.publisher_confirms - - # Legacy property names for backward compatibility - basic_nack = basic_nack_supported - consumer_cancel_notify = consumer_cancel_notify_supported - exchange_exchange_bindings = exchange_exchange_bindings_supported - publisher_confirms = publisher_confirms_supported - - -class _ChannelPendingEvt(object): # pylint: disable=R0903 - """Base class for BlockingChannel pending events""" - pass - - -class _ConsumerDeliveryEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents consumer message delivery `Basic.Deliver`; it - contains method, properties, and body of the delivered message. - """ - - __slots__ = ('method', 'properties', 'body') - - def __init__(self, method, properties, body): - """ - :param spec.Basic.Deliver method: NOTE: consumer_tag and delivery_tag - are valid only within source channel - :param spec.BasicProperties properties: message properties - :param body: message body; empty string if no body - :type body: str or unicode - """ - self.method = method - self.properties = properties - self.body = body - - -class _ConsumerCancellationEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents server-initiated consumer cancellation delivered to - client via Basic.Cancel. After receiving Basic.Cancel, there will be no - further deliveries for the consumer identified by `consumer_tag` in - `Basic.Cancel` - """ - - __slots__ = ('method_frame') - - def __init__(self, method_frame): - """ - :param pika.frame.Method method_frame: method frame with method of type - `spec.Basic.Cancel` - """ - self.method_frame = method_frame - - def __repr__(self): - return '%s(method_frame=%r)' % (self.__class__.__name__, - self.method_frame) - - @property - def method(self): - """method of type spec.Basic.Cancel""" - return self.method_frame.method - - -class _ReturnedMessageEvt(_ChannelPendingEvt): # pylint: disable=R0903 - """This event represents a message returned by broker via `Basic.Return`""" - - __slots__ = ('callback', 'channel', 'method', 'properties', 'body') - - def __init__(self, callback, channel, method, properties, body): # pylint: disable=R0913 - """ - :param callable callback: user's callback, having the signature - callback(channel, method, properties, body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - :param pika.Channel channel: - :param pika.spec.Basic.Return method: - :param pika.spec.BasicProperties properties: - :param body: str, unicode, or bytes (python 3.x) - """ - self.callback = callback - self.channel = channel - self.method = method - self.properties = properties - self.body = body - - def __repr__(self): - return ('%s(callback=%r, channel=%r, method=%r, properties=%r, ' - 'body=%.300r') % (self.__class__.__name__, self.callback, - self.channel, self.method, self.properties, - self.body) - - def dispatch(self): - """Dispatch user's callback""" - self.callback(self.channel, self.method, self.properties, self.body) - - -class ReturnedMessage(object): # pylint: disable=R0903 - """Represents a message returned via Basic.Return in publish-acknowledgments - mode - """ - - __slots__ = ('method', 'properties', 'body') - - def __init__(self, method, properties, body): - """ - :param spec.Basic.Return method: - :param spec.BasicProperties properties: message properties - :param body: message body; empty string if no body - :type body: str or unicode - """ - self.method = method - self.properties = properties - self.body = body - - -class _ConsumerInfo(object): - """Information about an active consumer""" - - __slots__ = ('consumer_tag', 'no_ack', 'consumer_cb', - 'alternate_event_sink', 'state') - - # Consumer states - SETTING_UP = 1 - ACTIVE = 2 - TEARING_DOWN = 3 - CANCELLED_BY_BROKER = 4 - - def __init__(self, consumer_tag, no_ack, consumer_cb=None, - alternate_event_sink=None): - """ - NOTE: exactly one of consumer_cb/alternate_event_sink musts be non-None. - - :param str consumer_tag: - :param bool no_ack: the no-ack value for the consumer - :param callable consumer_cb: The function for dispatching messages to - user, having the signature: - consumer_callback(channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - :param callable alternate_event_sink: if specified, _ConsumerDeliveryEvt - and _ConsumerCancellationEvt objects will be diverted to this - callback instead of being deposited in the channel's - `_pending_events` container. Signature: - alternate_event_sink(evt) - """ - assert (consumer_cb is None) != (alternate_event_sink is None), ( - 'exactly one of consumer_cb/alternate_event_sink must be non-None', - consumer_cb, alternate_event_sink) - self.consumer_tag = consumer_tag - self.no_ack = no_ack - self.consumer_cb = consumer_cb - self.alternate_event_sink = alternate_event_sink - self.state = self.SETTING_UP - - @property - def setting_up(self): - """True if in SETTING_UP state""" - return self.state == self.SETTING_UP - - @property - def active(self): - """True if in ACTIVE state""" - return self.state == self.ACTIVE - - @property - def tearing_down(self): - """True if in TEARING_DOWN state""" - return self.state == self.TEARING_DOWN - - @property - def cancelled_by_broker(self): - """True if in CANCELLED_BY_BROKER state""" - return self.state == self.CANCELLED_BY_BROKER - - -class _QueueConsumerGeneratorInfo(object): # pylint: disable=R0903 - """Container for information about the active queue consumer generator """ - __slots__ = ('params', 'consumer_tag', 'pending_events') - - def __init__(self, params, consumer_tag): - """ - :params tuple params: a three-tuple (queue, no_ack, exclusive) that were - used to create the queue consumer - :param str consumer_tag: consumer tag - """ - self.params = params - self.consumer_tag = consumer_tag - #self.messages = deque() - - # Holds pending events of types _ConsumerDeliveryEvt and - # _ConsumerCancellationEvt - self.pending_events = deque() - - def __repr__(self): - return '%s(params=%r, consumer_tag=%r)' % ( - self.__class__.__name__, self.params, self.consumer_tag) - - -class BlockingChannel(object): # pylint: disable=R0904,R0902 - """The BlockingChannel implements blocking semantics for most things that - one would use callback-passing-style for with the - :py:class:`~pika.channel.Channel` class. In addition, - the `BlockingChannel` class implements a :term:`generator` that allows - you to :doc:`consume messages ` - without using callbacks. - - Example of creating a BlockingChannel:: - - import pika - - # Create our connection object - connection = pika.BlockingConnection() - - # The returned object will be a synchronous channel - channel = connection.channel() - - """ - - # Used as value_class with _CallbackResult for receiving Basic.GetOk args - _RxMessageArgs = namedtuple( - 'BlockingChannel__RxMessageArgs', - [ - 'channel', # implementation pika.Channel instance - 'method', # Basic.GetOk - 'properties', # pika.spec.BasicProperties - 'body' # str, unicode, or bytes (python 3.x) - ]) - - - # For use as value_class with any _CallbackResult that expects method_frame - # as the only arg - _MethodFrameCallbackResultArgs = namedtuple( - 'BlockingChannel__MethodFrameCallbackResultArgs', - 'method_frame') - - # Broker's basic-ack/basic-nack args when delivery confirmation is enabled; - # may concern a single or multiple messages - _OnMessageConfirmationReportArgs = namedtuple( # pylint: disable=C0103 - 'BlockingChannel__OnMessageConfirmationReportArgs', - 'method_frame') - - # Parameters for broker-inititated Channel.Close request: reply_code - # holds the broker's non-zero error code and reply_text holds the - # corresponding error message text. - _OnChannelClosedByBrokerArgs = namedtuple( - 'BlockingChannel__OnChannelClosedByBrokerArgs', - 'method_frame') - - # For use as value_class with _CallbackResult expecting Channel.Flow - # confirmation. - _FlowOkCallbackResultArgs = namedtuple( - 'BlockingChannel__FlowOkCallbackResultArgs', - 'active' # True if broker will start or continue sending; False if not - ) - - _CONSUMER_CANCELLED_CB_KEY = 'blocking_channel_consumer_cancelled' - - def __init__(self, channel_impl, connection): - """Create a new instance of the Channel - - :param channel_impl: Channel implementation object as returned from - SelectConnection.channel() - :param BlockingConnection connection: The connection object - - """ - self._impl = channel_impl - self._connection = connection - - # A mapping of consumer tags to _ConsumerInfo for active consumers - self._consumer_infos = dict() - - # Queue consumer generator generator info of type - # _QueueConsumerGeneratorInfo created by BlockingChannel.consume - self._queue_consumer_generator = None - - # Whether RabbitMQ delivery confirmation has been enabled - self._delivery_confirmation = False - - # Receives message delivery confirmation report (Basic.ack or - # Basic.nack) from broker when delivery confirmations are enabled - self._message_confirmation_result = _CallbackResult( - self._OnMessageConfirmationReportArgs) - - # deque of pending events: _ConsumerDeliveryEvt and - # _ConsumerCancellationEvt objects that will be returned by - # `BlockingChannel.get_event()` - self._pending_events = deque() - - # Holds a ReturnedMessage object representing a message received via - # Basic.Return in publisher-acknowledgments mode. - self._puback_return = None - - # Receives Basic.ConsumeOk reply from server - self._basic_consume_ok_result = _CallbackResult() - - # Receives the broker-inititated Channel.Close parameters - self._channel_closed_by_broker_result = _CallbackResult( # pylint: disable=C0103 - self._OnChannelClosedByBrokerArgs) - - # Receives args from Basic.GetEmpty response - # http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.get - self._basic_getempty_result = _CallbackResult( - self._MethodFrameCallbackResultArgs) - - self._impl.add_on_cancel_callback(self._on_consumer_cancelled_by_broker) - - self._impl.add_callback( - self._basic_consume_ok_result.signal_once, - replies=[pika.spec.Basic.ConsumeOk], - one_shot=False) - - self._impl.add_callback( - self._channel_closed_by_broker_result.set_value_once, - replies=[pika.spec.Channel.Close], - one_shot=True) - - self._impl.add_callback( - self._basic_getempty_result.set_value_once, - replies=[pika.spec.Basic.GetEmpty], - one_shot=False) - - LOGGER.info("Created channel=%s", self.channel_number) - - def _cleanup(self): - """Clean up members that might inhibit garbage collection""" - self._message_confirmation_result.reset() - self._pending_events = deque() - self._consumer_infos = dict() - - def __int__(self): - """Return the channel object as its channel number - - :rtype: int - - """ - return self.channel_number - - @property - def channel_number(self): - """Channel number""" - return self._impl.channel_number - - @property - def connection(self): - """The channel's BlockingConnection instance""" - return self._connection - - @property - def is_closed(self): - """Returns True if the channel is closed. - - :rtype: bool - - """ - return self._impl.is_closed - - @property - def is_closing(self): - """Returns True if the channel is closing. - - :rtype: bool - - """ - return self._impl.is_closing - - @property - def is_open(self): - """Returns True if the channel is open. - - :rtype: bool - - """ - return self._impl.is_open - - _ALWAYS_READY_WAITERS = ((lambda: True), ) - - def _flush_output(self, *waiters): - """ Flush output and process input while waiting for any of the given - callbacks to return true. The wait is aborted upon channel-close or - connection-close. - Otherwise, processing continues until the output is flushed AND at least - one of the callbacks returns true. If there are no callbacks, then - processing ends when all output is flushed. - - :param waiters: sequence of zero or more callables taking no args and - returning true when it's time to stop processing. - Their results are OR'ed together. - """ - if self._impl.is_closed: - raise exceptions.ChannelClosed() - - if not waiters: - waiters = self._ALWAYS_READY_WAITERS - - self._connection._flush_output( - self._channel_closed_by_broker_result.is_ready, - *waiters) - - if self._channel_closed_by_broker_result: - # Channel was force-closed by broker - self._cleanup() - method = ( - self._channel_closed_by_broker_result.value.method_frame.method) - raise exceptions.ChannelClosed(method.reply_code, method.reply_text) - - def _on_puback_message_returned(self, channel, method, properties, body): - """Called as the result of Basic.Return from broker in - publisher-acknowledgements mode. Saves the info as a ReturnedMessage - instance in self._puback_return. - - :param pika.Channel channel: our self._impl channel - :param pika.spec.Basic.Return method: - :param pika.spec.BasicProperties properties: message properties - :param body: returned message body; empty string if no body - :type body: str, unicode - - """ - assert channel is self._impl, ( - channel.channel_number, self.channel_number) - - assert isinstance(method, pika.spec.Basic.Return), method - assert isinstance(properties, pika.spec.BasicProperties), ( - properties) - - LOGGER.warn( - "Published message was returned: _delivery_confirmation=%s; " - "channel=%s; method=%r; properties=%r; body_size=%d; " - "body_prefix=%.255r", self._delivery_confirmation, - channel.channel_number, method, properties, - len(body) if body is not None else None, body) - - self._puback_return = ReturnedMessage(method, properties, body) - - - def _add_pending_event(self, evt): - """Append an event to the channel's list of events that are ready for - dispatch to user and signal our connection that this channel is ready - for event dispatch - - :param _ChannelPendingEvt evt: an event derived from _ChannelPendingEvt - """ - self._pending_events.append(evt) - self.connection._request_channel_dispatch(self.channel_number) - - - def _on_consumer_cancelled_by_broker(self, # pylint: disable=C0103 - method_frame): - """Called by impl when broker cancels consumer via Basic.Cancel. - - This is a RabbitMQ-specific feature. The circumstances include deletion - of queue being consumed as well as failure of a HA node responsible for - the queue being consumed. - - :param pika.frame.Method method_frame: method frame with the - `spec.Basic.Cancel` method - - """ - evt = _ConsumerCancellationEvt(method_frame) - - consumer = self._consumer_infos[method_frame.method.consumer_tag] - - # Don't interfere with client-initiated cancellation flow - if not consumer.tearing_down: - consumer.state = _ConsumerInfo.CANCELLED_BY_BROKER - - if consumer.alternate_event_sink is not None: - consumer.alternate_event_sink(evt) - else: - self._add_pending_event(evt) - - def _on_consumer_message_delivery(self, channel, # pylint: disable=W0613 - method, properties, body): - """Called by impl when a message is delivered for a consumer - - :param Channel channel: The implementation channel object - :param spec.Basic.Deliver method: - :param pika.spec.BasicProperties properties: message properties - :param body: delivered message body; empty string if no body - :type body: str, unicode, or bytes (python 3.x) - - """ - evt = _ConsumerDeliveryEvt(method, properties, body) - - consumer = self._consumer_infos[method.consumer_tag] - - if consumer.alternate_event_sink is not None: - consumer.alternate_event_sink(evt) - else: - self._add_pending_event(evt) - - def _on_consumer_generator_event(self, evt): - """Sink for the queue consumer generator's consumer events; append the - event to queue consumer generator's pending events buffer. - - :param evt: an object of type _ConsumerDeliveryEvt or - _ConsumerCancellationEvt - """ - self._queue_consumer_generator.pending_events.append(evt) - # Schedule termination of connection.process_data_events using a - # negative channel number - self.connection._request_channel_dispatch(-self.channel_number) - - def _cancel_all_consumers(self): - """Cancel all consumers. - - NOTE: pending non-ackable messages will be lost; pending ackable - messages will be rejected. - - """ - if self._consumer_infos: - LOGGER.debug('Cancelling %i consumers', len(self._consumer_infos)) - - if self._queue_consumer_generator is not None: - # Cancel queue consumer generator - self.cancel() - - # Cancel consumers created via basic_consume - for consumer_tag in pika.compat.dictkeys(self._consumer_infos): - self.basic_cancel(consumer_tag) - - def _dispatch_events(self): - """Called by BlockingConnection to dispatch pending events. - - `BlockingChannel` schedules this callback via - `BlockingConnection._request_channel_dispatch` - """ - while self._pending_events: - evt = self._pending_events.popleft() - - if type(evt) is _ConsumerDeliveryEvt: - consumer_info = self._consumer_infos[evt.method.consumer_tag] - consumer_info.consumer_cb(self, evt.method, evt.properties, - evt.body) - - elif type(evt) is _ConsumerCancellationEvt: - del self._consumer_infos[evt.method_frame.method.consumer_tag] - - self._impl.callbacks.process(self.channel_number, - self._CONSUMER_CANCELLED_CB_KEY, - self, - evt.method_frame) - else: - evt.dispatch() - - - def close(self, reply_code=0, reply_text="Normal Shutdown"): - """Will invoke a clean shutdown of the channel with the AMQP Broker. - - :param int reply_code: The reply code to close the channel with - :param str reply_text: The reply text to close the channel with - - """ - LOGGER.info('Channel.close(%s, %s)', reply_code, reply_text) - - # Cancel remaining consumers - self._cancel_all_consumers() - - # Close the channel - try: - with _CallbackResult() as close_ok_result: - self._impl.add_callback(callback=close_ok_result.signal_once, - replies=[pika.spec.Channel.CloseOk], - one_shot=True) - - self._impl.close(reply_code=reply_code, reply_text=reply_text) - self._flush_output(close_ok_result.is_ready) - finally: - self._cleanup() - - def flow(self, active): - """Turn Channel flow control off and on. - - NOTE: RabbitMQ doesn't support active=False; per - https://www.rabbitmq.com/specification.html: "active=false is not - supported by the server. Limiting prefetch with basic.qos provides much - better control" - - For more information, please reference: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#channel.flow - - :param bool active: Turn flow on (True) or off (False) - - :returns: True if broker will start or continue sending; False if not - :rtype: bool - - """ - with _CallbackResult(self._FlowOkCallbackResultArgs) as flow_ok_result: - self._impl.flow(callback=flow_ok_result.set_value_once, - active=active) - self._flush_output(flow_ok_result.is_ready) - return flow_ok_result.value.active - - def add_on_cancel_callback(self, callback): - """Pass a callback function that will be called when Basic.Cancel - is sent by the broker. The callback function should receive a method - frame parameter. - - :param callable callback: a callable for handling broker's Basic.Cancel - notification with the call signature: callback(method_frame) - where method_frame is of type `pika.frame.Method` with method of - type `spec.Basic.Cancel` - - - """ - self._impl.callbacks.add(self.channel_number, - self._CONSUMER_CANCELLED_CB_KEY, - callback, - one_shot=False) - - def add_on_return_callback(self, callback): - """Pass a callback function that will be called when a published - message is rejected and returned by the server via `Basic.Return`. - - :param callable callback: The method to call on callback with the - signature callback(channel, method, properties, body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - """ - self._impl.add_on_return_callback( - lambda _channel, method, properties, body: ( - self._add_pending_event( - _ReturnedMessageEvt( - callback, self, method, properties, body)))) - - def basic_consume(self, # pylint: disable=R0913 - consumer_callback, - queue, - no_ack=False, - exclusive=False, - consumer_tag=None, - arguments=None): - """Sends the AMQP command Basic.Consume to the broker and binds messages - for the consumer_tag to the consumer callback. If you do not pass in - a consumer_tag, one will be automatically generated for you. Returns - the consumer tag. - - NOTE: the consumer callbacks are dispatched only in the scope of - specially-designated methods: see - `BlockingConnection.process_data_events` and - `BlockingChannel.start_consuming`. - - For more information about Basic.Consume, see: - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.consume - - :param callable consumer_callback: The function for dispatching messages - to user, having the signature: - consumer_callback(channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - :param queue: The queue to consume from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a response (i.e., - no ack/nack) - :param bool exclusive: Don't allow other consumers on the queue - :param consumer_tag: You may specify your own consumer tag; if left - empty, a consumer tag will be generated automatically - :type consumer_tag: str or unicode - :param dict arguments: Custom key/value pair arguments for the consumer - :returns: consumer tag - :rtype: str - - :raises pika.exceptions.DuplicateConsumerTag: if consumer with given - consumer_tag is already present. - - """ - if not callable(consumer_callback): - raise ValueError('consumer callback must be callable; got %r' - % consumer_callback) - - return self._basic_consume_impl( - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments, - consumer_callback=consumer_callback) - - def _basic_consume_impl(self, # pylint: disable=R0913 - queue, - no_ack, - exclusive, - consumer_tag, - arguments=None, - consumer_callback=None, - alternate_event_sink=None): - """The low-level implementation used by `basic_consume` and `consume`. - See `basic_consume` docstring for more info. - - NOTE: exactly one of consumer_callback/alternate_event_sink musts be - non-None. - - This method has one additional parameter alternate_event_sink over the - args described in `basic_consume`. - - :param callable alternate_event_sink: if specified, _ConsumerDeliveryEvt - and _ConsumerCancellationEvt objects will be diverted to this - callback instead of being deposited in the channel's - `_pending_events` container. Signature: - alternate_event_sink(evt) - - :raises pika.exceptions.DuplicateConsumerTag: if consumer with given - consumer_tag is already present. - - """ - if (consumer_callback is None) == (alternate_event_sink is None): - raise ValueError( - ('exactly one of consumer_callback/alternate_event_sink must ' - 'be non-None', consumer_callback, alternate_event_sink)) - - if not consumer_tag: - # Need a consumer tag to register consumer info before sending - # request to broker, because I/O might dispatch incoming messages - # immediately following Basic.Consume-ok before _flush_output - # returns - consumer_tag = self._impl._generate_consumer_tag() - - if consumer_tag in self._consumer_infos: - raise exceptions.DuplicateConsumerTag(consumer_tag) - - # Create new consumer - self._consumer_infos[consumer_tag] = _ConsumerInfo( - consumer_tag, - no_ack=no_ack, - consumer_cb=consumer_callback, - alternate_event_sink=alternate_event_sink) - - try: - with self._basic_consume_ok_result as ok_result: - tag = self._impl.basic_consume( - consumer_callback=self._on_consumer_message_delivery, - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments) - - assert tag == consumer_tag, (tag, consumer_tag) - - self._flush_output(ok_result.is_ready) - except Exception: - # If channel was closed, self._consumer_infos will be empty - if consumer_tag in self._consumer_infos: - del self._consumer_infos[consumer_tag] - raise - - # NOTE: Consumer could get cancelled by broker immediately after opening - # (e.g., queue getting deleted externally) - if self._consumer_infos[consumer_tag].setting_up: - self._consumer_infos[consumer_tag].state = _ConsumerInfo.ACTIVE - - return consumer_tag - - def basic_cancel(self, consumer_tag): - """This method cancels a consumer. This does not affect already - delivered messages, but it does mean the server will not send any more - messages for that consumer. The client may receive an arbitrary number - of messages in between sending the cancel method and receiving the - cancel-ok reply. - - NOTE: When cancelling a no_ack=False consumer, this implementation - automatically Nacks and suppresses any incoming messages that have not - yet been dispatched to the consumer's callback. However, when cancelling - a no_ack=True consumer, this method will return any pending messages - that arrived before broker confirmed the cancellation. - - :param str consumer_tag: Identifier for the consumer; the result of - passing a consumer_tag that was created on another channel is - undefined (bad things will happen) - - :returns: (NEW IN pika 0.10.0) empty sequence for a no_ack=False - consumer; for a no_ack=True consumer, returns a (possibly empty) - sequence of pending messages that arrived before broker confirmed - the cancellation (this is done instead of via consumer's callback in - order to prevent reentrancy/recursion. Each message is four-tuple: - (channel, method, properties, body) - channel: BlockingChannel - method: spec.Basic.Deliver - properties: spec.BasicProperties - body: str or unicode - """ - try: - consumer_info = self._consumer_infos[consumer_tag] - except KeyError: - LOGGER.warn("User is attempting to cancel an unknown consumer=%s; " - "already cancelled by user or broker?", consumer_tag) - return [] - - try: - # Assertion failure here is most likely due to reentrance - assert consumer_info.active or consumer_info.cancelled_by_broker, ( - consumer_info.state) - - # Assertion failure here signals disconnect between consumer state - # in BlockingConnection and Connection - assert (consumer_info.cancelled_by_broker or - consumer_tag in self._impl._consumers), consumer_tag - - no_ack = consumer_info.no_ack - - consumer_info.state = _ConsumerInfo.TEARING_DOWN - - with _CallbackResult() as cancel_ok_result: - # Nack pending messages for no_ack=False consumer - if not no_ack: - pending_messages = self._remove_pending_deliveries( - consumer_tag) - if pending_messages: - # NOTE: we use impl's basic_reject to avoid the - # possibility of redelivery before basic_cancel takes - # control of nacking. - # NOTE: we can't use basic_nack with the multiple option - # to avoid nacking messages already held by our client. - for message in pending_messages: - self._impl.basic_reject(message.method.delivery_tag, - requeue=True) - - # Cancel the consumer; impl takes care of rejecting any - # additional deliveries that arrive for a no_ack=False - # consumer - self._impl.basic_cancel( - callback=cancel_ok_result.signal_once, - consumer_tag=consumer_tag, - nowait=False) - - # Flush output and wait for Basic.Cancel-ok or - # broker-initiated Basic.Cancel - self._flush_output( - cancel_ok_result.is_ready, - lambda: consumer_tag not in self._impl._consumers) - - if no_ack: - # Return pending messages for no_ack=True consumer - return [ - (evt.method, evt.properties, evt.body) - for evt in self._remove_pending_deliveries(consumer_tag)] - else: - # impl takes care of rejecting any incoming deliveries during - # cancellation - messages = self._remove_pending_deliveries(consumer_tag) - assert not messages, messages - - return [] - finally: - # NOTE: The entry could be purged if channel or connection closes - if consumer_tag in self._consumer_infos: - del self._consumer_infos[consumer_tag] - - def _remove_pending_deliveries(self, consumer_tag): - """Extract _ConsumerDeliveryEvt objects destined for the given consumer - from pending events, discarding the _ConsumerCancellationEvt, if any - - :param str consumer_tag: - - :returns: a (possibly empty) sequence of _ConsumerDeliveryEvt destined - for the given consumer tag - """ - remaining_events = deque() - unprocessed_messages = [] - while self._pending_events: - evt = self._pending_events.popleft() - if type(evt) is _ConsumerDeliveryEvt: - if evt.method.consumer_tag == consumer_tag: - unprocessed_messages.append(evt) - continue - if type(evt) is _ConsumerCancellationEvt: - if evt.method_frame.method.consumer_tag == consumer_tag: - # A broker-initiated Basic.Cancel must have arrived - # before our cancel request completed - continue - - remaining_events.append(evt) - - self._pending_events = remaining_events - - return unprocessed_messages - - def start_consuming(self): - """Processes I/O events and dispatches timers and `basic_consume` - callbacks until all consumers are cancelled. - - NOTE: this blocking function may not be called from the scope of a - pika callback, because dispatching `basic_consume` callbacks from this - context would constitute recursion. - - :raises pika.exceptions.RecursionError: if called from the scope of a - `BlockingConnection` or `BlockingChannel` callback - - """ - # Check if called from the scope of an event dispatch callback - with self.connection._acquire_event_dispatch() as dispatch_allowed: - if not dispatch_allowed: - raise exceptions.RecursionError( - 'start_consuming may not be called from the scope of ' - 'another BlockingConnection or BlockingChannel callback') - - # Process events as long as consumers exist on this channel - while self._consumer_infos: - self.connection.process_data_events(time_limit=None) - - def stop_consuming(self, consumer_tag=None): - """ Cancels all consumers, signalling the `start_consuming` loop to - exit. - - NOTE: pending non-ackable messages will be lost; pending ackable - messages will be rejected. - - """ - if consumer_tag: - self.basic_cancel(consumer_tag) - else: - self._cancel_all_consumers() - - def consume(self, queue, no_ack=False, # pylint: disable=R0913 - exclusive=False, arguments=None, - inactivity_timeout=None): - """Blocking consumption of a queue instead of via a callback. This - method is a generator that yields each message as a tuple of method, - properties, and body. The active generator iterator terminates when the - consumer is cancelled by client or broker. - - Example: - - for method, properties, body in channel.consume('queue'): - print body - channel.basic_ack(method.delivery_tag) - - You should call `BlockingChannel.cancel()` when you escape out of the - generator loop. - - If you don't cancel this consumer, then next call on the same channel - to `consume()` with the exact same (queue, no_ack, exclusive) parameters - will resume the existing consumer generator; however, calling with - different parameters will result in an exception. - - :param queue: The queue name to consume - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a ack/nack response - :param bool exclusive: Don't allow other consumers on the queue - :param dict arguments: Custom key/value pair arguments for the consumer - :param float inactivity_timeout: if a number is given (in - seconds), will cause the method to yield None after the given period - of inactivity; this permits for pseudo-regular maintenance - activities to be carried out by the user while waiting for messages - to arrive. If None is given (default), then the method blocks until - the next event arrives. NOTE that timing granularity is limited by - the timer resolution of the underlying implementation. - NEW in pika 0.10.0. - - :yields: tuple(spec.Basic.Deliver, spec.BasicProperties, str or unicode) - - :raises ValueError: if consumer-creation parameters don't match those - of the existing queue consumer generator, if any. - NEW in pika 0.10.0 - """ - params = (queue, no_ack, exclusive) - - if self._queue_consumer_generator is not None: - if params != self._queue_consumer_generator.params: - raise ValueError( - 'Consume with different params not allowed on existing ' - 'queue consumer generator; previous params: %r; ' - 'new params: %r' - % (self._queue_consumer_generator.params, - (queue, no_ack, exclusive))) - else: - LOGGER.debug('Creating new queue consumer generator; params: %r', - params) - # Need a consumer tag to register consumer info before sending - # request to broker, because I/O might pick up incoming messages - # in addition to Basic.Consume-ok - consumer_tag = self._impl._generate_consumer_tag() - - self._queue_consumer_generator = _QueueConsumerGeneratorInfo( - params, - consumer_tag) - - try: - self._basic_consume_impl( - queue=queue, - no_ack=no_ack, - exclusive=exclusive, - consumer_tag=consumer_tag, - arguments=arguments, - alternate_event_sink=self._on_consumer_generator_event) - except Exception: - self._queue_consumer_generator = None - raise - - LOGGER.info('Created new queue consumer generator %r', - self._queue_consumer_generator) - - while self._queue_consumer_generator is not None: - if self._queue_consumer_generator.pending_events: - evt = self._queue_consumer_generator.pending_events.popleft() - if type(evt) is _ConsumerCancellationEvt: - # Consumer was cancelled by broker - self._queue_consumer_generator = None - break - else: - yield (evt.method, evt.properties, evt.body) - continue - - # Wait for a message to arrive - if inactivity_timeout is None: - self.connection.process_data_events(time_limit=None) - continue - - # Wait with inactivity timeout - wait_start_time = time.time() - wait_deadline = wait_start_time + inactivity_timeout - delta = inactivity_timeout - - while (self._queue_consumer_generator is not None and - not self._queue_consumer_generator.pending_events): - self.connection.process_data_events(time_limit=delta) - - if not self._queue_consumer_generator: - # Consumer was cancelled by client - break - - if self._queue_consumer_generator.pending_events: - # Got message(s) - break - - delta = wait_deadline - time.time() - if delta <= 0.0: - # Signal inactivity timeout - yield None - break - - def get_waiting_message_count(self): - """Returns the number of messages that may be retrieved from the current - queue consumer generator via `BasicChannel.consume` without blocking. - NEW in pika 0.10.0 - - :rtype: int - """ - if self._queue_consumer_generator is not None: - pending_events = self._queue_consumer_generator.pending_events - count = len(pending_events) - if count and type(pending_events[-1]) is _ConsumerCancellationEvt: - count -= 1 - else: - count = 0 - - return count - - def cancel(self): - """Cancel the queue consumer created by `BlockingChannel.consume`, - rejecting all pending ackable messages. - - NOTE: If you're looking to cancel a consumer issued with - BlockingChannel.basic_consume then you should call - BlockingChannel.basic_cancel. - - :return int: The number of messages requeued by Basic.Nack. - NEW in 0.10.0: returns 0 - - """ - if self._queue_consumer_generator is None: - LOGGER.warning('cancel: queue consumer generator is inactive ' - '(already cancelled by client or broker?)') - return 0 - - try: - _, no_ack, _ = self._queue_consumer_generator.params - if not no_ack: - # Reject messages held by queue consumer generator; NOTE: we - # can't use basic_nack with the multiple option to avoid nacking - # messages already held by our client. - pending_events = self._queue_consumer_generator.pending_events - for _ in compat.xrange(self.get_waiting_message_count()): - evt = pending_events.popleft() - self._impl.basic_reject(evt.method.delivery_tag, - requeue=True) - - self.basic_cancel(self._queue_consumer_generator.consumer_tag) - finally: - self._queue_consumer_generator = None - - # Return 0 for compatibility with legacy implementation; the number of - # nacked messages is not meaningful since only messages consumed with - # no_ack=False may be nacked, and those arriving after calling - # basic_cancel will be rejected automatically by impl channel, so we'll - # never know how many of those were nacked. - return 0 - - def basic_ack(self, delivery_tag=0, multiple=False): - """Acknowledge one or more messages. When sent by the client, this - method acknowledges one or more messages delivered via the Deliver or - Get-Ok methods. When sent by server, this method acknowledges one or - more messages published with the Publish method on a channel in - confirm mode. The acknowledgement can be for a single message or a - set of messages up to and including a specific message. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - """ - self._impl.basic_ack(delivery_tag=delivery_tag, multiple=multiple) - self._flush_output() - - def basic_nack(self, delivery_tag=None, multiple=False, requeue=True): - """This method allows a client to reject one or more incoming messages. - It can be used to interrupt and cancel large incoming messages, or - return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - self._impl.basic_nack(delivery_tag=delivery_tag, multiple=multiple, - requeue=requeue) - self._flush_output() - - def basic_get(self, queue=None, no_ack=False): - """Get a single message from the AMQP broker. Returns a sequence with - the method frame, message properties, and body. - - :param queue: Name of queue to get a message from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a reply - :returns: a three-tuple; (None, None, None) if the queue was empty; - otherwise (method, properties, body); NOTE: body may be None - :rtype: (None, None, None)|(spec.Basic.GetOk, - spec.BasicProperties, - str or unicode or None) - """ - assert not self._basic_getempty_result - # NOTE: nested with for python 2.6 compatibility - with _CallbackResult(self._RxMessageArgs) as get_ok_result: - with self._basic_getempty_result: - self._impl.basic_get(callback=get_ok_result.set_value_once, - queue=queue, - no_ack=no_ack) - self._flush_output(get_ok_result.is_ready, - self._basic_getempty_result.is_ready) - if get_ok_result: - evt = get_ok_result.value - return (evt.method, evt.properties, evt.body) - else: - assert self._basic_getempty_result, ( - "wait completed without GetOk and GetEmpty") - return None, None, None - - def basic_publish(self, exchange, routing_key, body, # pylint: disable=R0913 - properties=None, mandatory=False, immediate=False): - """Publish to the channel with the given exchange, routing key and body. - Returns a boolean value indicating the success of the operation. - - This is the legacy BlockingChannel method for publishing. See also - `BasicChannel.publish` that provides more information about failures. - - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - NOTE: mandatory and immediate may be enabled even without delivery - confirmation, but in the absence of delivery confirmation the - synchronous implementation has no way to know how long to wait for - the Basic.Return or lack thereof. - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body; empty string if no body - :type body: str or unicode - :param pika.spec.BasicProperties properties: message properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - :returns: True if delivery confirmation is not enabled (NEW in pika - 0.10.0); otherwise returns False if the message could not be - deliveved (Basic.nack and/or Basic.Return) and True if the message - was delivered (Basic.ack and no Basic.Return) - """ - try: - self.publish(exchange, routing_key, body, properties, - mandatory, immediate) - except (exceptions.NackError, exceptions.UnroutableError): - return False - else: - return True - - def publish(self, exchange, routing_key, body, # pylint: disable=R0913 - properties=None, mandatory=False, immediate=False): - """Publish to the channel with the given exchange, routing key, and - body. Unlike the legacy `BlockingChannel.basic_publish`, this method - provides more information about failures via exceptions. - - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - NOTE: mandatory and immediate may be enabled even without delivery - confirmation, but in the absence of delivery confirmation the - synchronous implementation has no way to know how long to wait for - the Basic.Return. - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body; empty string if no body - :type body: str or unicode - :param pika.spec.BasicProperties properties: message properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - :raises UnroutableError: raised when a message published in - publisher-acknowledgments mode (see - `BlockingChannel.confirm_delivery`) is returned via `Basic.Return` - followed by `Basic.Ack`. - :raises NackError: raised when a message published in - publisher-acknowledgements mode is Nack'ed by the broker. See - `BlockingChannel.confirm_delivery`. - - """ - if self._delivery_confirmation: - # In publisher-acknowledgments mode - with self._message_confirmation_result: - self._impl.basic_publish(exchange=exchange, - routing_key=routing_key, - body=body, - properties=properties, - mandatory=mandatory, - immediate=immediate) - - self._flush_output(self._message_confirmation_result.is_ready) - conf_method = (self._message_confirmation_result.value - .method_frame - .method) - - if isinstance(conf_method, pika.spec.Basic.Nack): - # Broker was unable to process the message due to internal - # error - LOGGER.warn( - "Message was Nack'ed by broker: nack=%r; channel=%s; " - "exchange=%s; routing_key=%s; mandatory=%r; " - "immediate=%r", conf_method, self.channel_number, - exchange, routing_key, mandatory, immediate) - if self._puback_return is not None: - returned_messages = [self._puback_return] - self._puback_return = None - else: - returned_messages = [] - raise exceptions.NackError(returned_messages) - - else: - assert isinstance(conf_method, pika.spec.Basic.Ack), ( - conf_method) - - if self._puback_return is not None: - # Unroutable message was returned - messages = [self._puback_return] - self._puback_return = None - raise exceptions.UnroutableError(messages) - else: - # In non-publisher-acknowledgments mode - self._impl.basic_publish(exchange=exchange, - routing_key=routing_key, - body=body, - properties=properties, - mandatory=mandatory, - immediate=immediate) - self._flush_output() - - def basic_qos(self, prefetch_size=0, prefetch_count=0, all_channels=False): - """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 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. - - :param int prefetch_size: This field specifies the prefetch window - size. 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 in the consumer. - :param int prefetch_count: 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 in the consumer. - :param bool all_channels: Should the QoS apply to all channels - - """ - with _CallbackResult() as qos_ok_result: - self._impl.basic_qos(callback=qos_ok_result.signal_once, - prefetch_size=prefetch_size, - prefetch_count=prefetch_count, - all_channels=all_channels) - self._flush_output(qos_ok_result.is_ready) - - def basic_recover(self, requeue=False): - """This method asks the server to redeliver all unacknowledged messages - on a specified channel. Zero or more messages may be redelivered. This - method replaces the asynchronous Recover. - - :param bool requeue: If False, the message will be redelivered to the - original recipient. If True, the server will - attempt to requeue the message, potentially then - delivering it to an alternative subscriber. - - """ - with _CallbackResult() as recover_ok_result: - self._impl.basic_recover(callback=recover_ok_result.signal_once, - requeue=requeue) - self._flush_output(recover_ok_result.is_ready) - - def basic_reject(self, delivery_tag=None, requeue=True): - """Reject an incoming message. This method allows a client to reject a - message. It can be used to interrupt and cancel large incoming messages, - or return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - self._impl.basic_reject(delivery_tag=delivery_tag, requeue=requeue) - self._flush_output() - - def confirm_delivery(self): - """Turn on RabbitMQ-proprietary Confirm mode in the channel. - - For more information see: - http://www.rabbitmq.com/extensions.html#confirms - """ - if self._delivery_confirmation: - LOGGER.error('confirm_delivery: confirmation was already enabled ' - 'on channel=%s', self.channel_number) - return - - with _CallbackResult() as select_ok_result: - self._impl.add_callback(callback=select_ok_result.signal_once, - replies=[pika.spec.Confirm.SelectOk], - one_shot=True) - - self._impl.confirm_delivery( - callback=self._message_confirmation_result.set_value_once, - nowait=False) - - self._flush_output(select_ok_result.is_ready) - - self._delivery_confirmation = True - - # Unroutable messages returned after this point will be in the context - # of publisher acknowledgments - self._impl.add_on_return_callback(self._on_puback_message_returned) - - def exchange_declare(self, exchange=None, # pylint: disable=R0913 - exchange_type='direct', passive=False, durable=False, - auto_delete=False, internal=False, - arguments=None, **kwargs): - """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. - - If passive set, the server will reply with Declare-Ok if the exchange - already exists with the same name, and raise an error if not and if the - exchange does not already exist, the server MUST raise a channel - exception with reply code 404 (not found). - - :param exchange: The exchange name consists of a non-empty sequence of - these characters: letters, digits, hyphen, underscore, - period, or colon. - :type exchange: str or unicode - :param str exchange_type: The exchange type to use - :param bool passive: Perform a declare or just check to see if it exists - :param bool durable: Survive a reboot of RabbitMQ - :param bool auto_delete: Remove when no more queues are bound to it - :param bool internal: Can only be published to by other exchanges - :param dict arguments: Custom key/value pair arguments for the exchange - :param str type: via kwargs: the deprecated exchange type parameter - - :returns: Method frame from the Exchange.Declare-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.DeclareOk` - - """ - assert len(kwargs) <= 1, kwargs - - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as declare_ok_result: - self._impl.exchange_declare( - callback=declare_ok_result.set_value_once, - exchange=exchange, - exchange_type=exchange_type, - passive=passive, - durable=durable, - auto_delete=auto_delete, - internal=internal, - nowait=False, - arguments=arguments, - type=kwargs["type"] if kwargs else None) - - self._flush_output(declare_ok_result.is_ready) - return declare_ok_result.value.method_frame - - def exchange_delete(self, exchange=None, if_unused=False): - """Delete the exchange. - - :param exchange: The exchange name - :type exchange: str or unicode - :param bool if_unused: only delete if the exchange is unused - - :returns: Method frame from the Exchange.Delete-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.DeleteOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as delete_ok_result: - self._impl.exchange_delete( - callback=delete_ok_result.set_value_once, - exchange=exchange, - if_unused=if_unused, - nowait=False) - - self._flush_output(delete_ok_result.is_ready) - return delete_ok_result.value.method_frame - - def exchange_bind(self, destination=None, source=None, routing_key='', - arguments=None): - """Bind an exchange to another exchange. - - :param destination: The destination exchange to bind - :type destination: str or unicode - :param source: The source exchange to bind to - :type source: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Exchange.Bind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.BindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as bind_ok_result: - self._impl.exchange_bind( - callback=bind_ok_result.set_value_once, - destination=destination, - source=source, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(bind_ok_result.is_ready) - return bind_ok_result.value.method_frame - - def exchange_unbind(self, destination=None, source=None, routing_key='', - arguments=None): - """Unbind an exchange from another exchange. - - :param destination: The destination exchange to unbind - :type destination: str or unicode - :param source: The source exchange to unbind from - :type source: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Exchange.Unbind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Exchange.UnbindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as unbind_ok_result: - self._impl.exchange_unbind( - callback=unbind_ok_result.set_value_once, - destination=destination, - source=source, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(unbind_ok_result.is_ready) - return unbind_ok_result.value.method_frame - - def queue_declare(self, queue='', passive=False, durable=False, # pylint: disable=R0913 - exclusive=False, auto_delete=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. - - Leave the queue name empty for a auto-named queue in RabbitMQ - - :param queue: The queue name - :type queue: str or unicode; if empty string, the broker will create a - unique queue name; - :param bool passive: Only check to see if the queue exists - :param bool durable: Survive reboots of the broker - :param bool exclusive: Only allow access by the current connection - :param bool auto_delete: Delete after consumer cancels or disconnects - :param dict arguments: Custom key/value arguments for the queue - - :returns: Method frame from the Queue.Declare-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.DeclareOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as declare_ok_result: - self._impl.queue_declare( - callback=declare_ok_result.set_value_once, - queue=queue, - passive=passive, - durable=durable, - exclusive=exclusive, - auto_delete=auto_delete, - nowait=False, - arguments=arguments) - - self._flush_output(declare_ok_result.is_ready) - return declare_ok_result.value.method_frame - - def queue_delete(self, queue='', if_unused=False, if_empty=False): - """Delete a queue from the broker. - - :param queue: The queue to delete - :type queue: str or unicode - :param bool if_unused: only delete if it's unused - :param bool if_empty: only delete if the queue is empty - - :returns: Method frame from the Queue.Delete-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.DeleteOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as delete_ok_result: - self._impl.queue_delete(callback=delete_ok_result.set_value_once, - queue=queue, - if_unused=if_unused, - if_empty=if_empty, - nowait=False) - - self._flush_output(delete_ok_result.is_ready) - return delete_ok_result.value.method_frame - - def queue_purge(self, queue=''): - """Purge all of the messages from the specified queue - - :param queue: The queue to purge - :type queue: str or unicode - - :returns: Method frame from the Queue.Purge-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.PurgeOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as purge_ok_result: - self._impl.queue_purge(callback=purge_ok_result.set_value_once, - queue=queue, - nowait=False) - - self._flush_output(purge_ok_result.is_ready) - return purge_ok_result.value.method_frame - - def queue_bind(self, queue, exchange, routing_key=None, - arguments=None): - """Bind the queue to the specified exchange - - :param queue: The queue to bind to the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Queue.Bind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.BindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as bind_ok_result: - self._impl.queue_bind(callback=bind_ok_result.set_value_once, - queue=queue, - exchange=exchange, - routing_key=routing_key, - nowait=False, - arguments=arguments) - - self._flush_output(bind_ok_result.is_ready) - return bind_ok_result.value.method_frame - - def queue_unbind(self, queue='', exchange=None, routing_key=None, - arguments=None): - """Unbind a queue from an exchange. - - :param queue: The queue to unbind from the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind from - :type exchange: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - :returns: Method frame from the Queue.Unbind-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Queue.UnbindOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as unbind_ok_result: - self._impl.queue_unbind(callback=unbind_ok_result.set_value_once, - queue=queue, - exchange=exchange, - routing_key=routing_key, - arguments=arguments) - self._flush_output(unbind_ok_result.is_ready) - return unbind_ok_result.value.method_frame - - 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. - - :returns: Method frame from the Tx.Select-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.SelectOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as select_ok_result: - self._impl.tx_select(select_ok_result.set_value_once) - - self._flush_output(select_ok_result.is_ready) - return select_ok_result.value.method_frame - - def tx_commit(self): - """Commit a transaction. - - :returns: Method frame from the Tx.Commit-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.CommitOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as commit_ok_result: - self._impl.tx_commit(commit_ok_result.set_value_once) - - self._flush_output(commit_ok_result.is_ready) - return commit_ok_result.value.method_frame - - def tx_rollback(self): - """Rollback a transaction. - - :returns: Method frame from the Tx.Commit-ok response - :rtype: `pika.frame.Method` having `method` attribute of type - `spec.Tx.CommitOk` - - """ - with _CallbackResult( - self._MethodFrameCallbackResultArgs) as rollback_ok_result: - self._impl.tx_rollback(rollback_ok_result.set_value_once) - - self._flush_output(rollback_ok_result.is_ready) - return rollback_ok_result.value.method_frame diff --git a/packages_o/pika-0.10.0/pika/adapters/libev_connection.py b/packages_o/pika-0.10.0/pika/adapters/libev_connection.py deleted file mode 100644 index 232fd572..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/libev_connection.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Use pika with the libev IOLoop via pyev""" -import pyev -import signal -import array -import logging -import warnings -from collections import deque - -from pika.adapters.base_connection import BaseConnection - -LOGGER = logging.getLogger(__name__) - -global_sigint_watcher, global_sigterm_watcher = None, None - - -class LibevConnection(BaseConnection): - """The LibevConnection runs on the libev IOLoop. If you're running the - connection in a web app, make sure you set stop_ioloop_on_close to False, - which is the default behavior for this adapter, otherwise the web app - will stop taking requests. - - You should be familiar with pyev and libev to use this adapter, esp. - with regard to the use of libev ioloops. - - If an on_signal_callback method is provided, the adapter creates signal - watchers the first time; subsequent instantiations with a provided method - reuse the same watchers but will call the new method upon receiving a - signal. See pyev/libev signal handling to understand why this is done. - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection can't - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the default_loop in libev - :param on_signal_callback: Method to call if SIGINT or SIGTERM occur - :type on_signal_callback: method - - """ - WARN_ABOUT_IOLOOP = True - - # use static arrays to translate masks between pika and libev - _PIKA_TO_LIBEV_ARRAY = array.array('i', [0] * ( - (BaseConnection.READ | BaseConnection.WRITE | BaseConnection.ERROR) + 1 - )) - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ] = pyev.EV_READ - _PIKA_TO_LIBEV_ARRAY[BaseConnection.WRITE] = pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | - BaseConnection.WRITE] = pyev.EV_READ | pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | - BaseConnection.ERROR] = pyev.EV_READ - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.WRITE | - BaseConnection.ERROR] = pyev.EV_WRITE - - _PIKA_TO_LIBEV_ARRAY[BaseConnection.READ | BaseConnection.WRITE | - BaseConnection.ERROR] = pyev.EV_READ | pyev.EV_WRITE - - _LIBEV_TO_PIKA_ARRAY = array.array('i', [0] * - ((pyev.EV_READ | pyev.EV_WRITE) + 1)) - - _LIBEV_TO_PIKA_ARRAY[pyev.EV_READ] = BaseConnection.READ - _LIBEV_TO_PIKA_ARRAY[pyev.EV_WRITE] = BaseConnection.WRITE - - _LIBEV_TO_PIKA_ARRAY[pyev.EV_READ | pyev.EV_WRITE] = \ - BaseConnection.READ | BaseConnection.WRITE - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False, - custom_ioloop=None, - on_signal_callback=None): - """Create a new instance of the LibevConnection class, connecting - to RabbitMQ automatically - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cannot - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the default IOLoop in libev - :param on_signal_callback: Method to call if SIGINT or SIGTERM occur - :type on_signal_callback: method - - """ - if custom_ioloop: - self.ioloop = custom_ioloop - else: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", RuntimeWarning) - self.ioloop = pyev.default_loop() - - self.async = None - self._on_signal_callback = on_signal_callback - self._io_watcher = None - self._active_timers = {} - self._stopped_timers = deque() - - super(LibevConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, self.ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the remote socket, adding the socket to the IOLoop if - connected - - :rtype: bool - - """ - LOGGER.debug('init io and signal watchers if any') - # reuse existing signal watchers, can only be declared for 1 ioloop - global global_sigint_watcher, global_sigterm_watcher - error = super(LibevConnection, self)._adapter_connect() - - if not error: - if self._on_signal_callback and not global_sigterm_watcher: - global_sigterm_watcher = \ - self.ioloop.signal(signal.SIGTERM, - self._handle_sigterm) - - if self._on_signal_callback and not global_sigint_watcher: - global_sigint_watcher = self.ioloop.signal(signal.SIGINT, - self._handle_sigint) - - if not self._io_watcher: - self._io_watcher = \ - self.ioloop.io(self.socket.fileno(), - self._PIKA_TO_LIBEV_ARRAY[self.event_state], - self._handle_events) - - self.async = pyev.Async(self.ioloop, self._noop_callable) - self.async.start() - if self._on_signal_callback: - global_sigterm_watcher.start() - if self._on_signal_callback: - global_sigint_watcher.start() - self._io_watcher.start() - - return error - - def _noop_callable(self, *args, **kwargs): - pass - - def _init_connection_state(self): - """Initialize or reset all of our internal state variables for a given - connection. If we disconnect and reconnect, all of our state needs to - be wiped. - - """ - for timer in self._active_timers.keys(): - self.remove_timeout(timer) - if global_sigint_watcher: - global_sigint_watcher.stop() - if global_sigterm_watcher: - global_sigterm_watcher.stop() - if self._io_watcher: - self._io_watcher.stop() - super(LibevConnection, self)._init_connection_state() - - def _handle_sigint(self, signal_watcher, libev_events): - """If an on_signal_callback has been defined, call it returning the - string 'SIGINT'. - - """ - LOGGER.debug('SIGINT') - self._on_signal_callback('SIGINT') - - def _handle_sigterm(self, signal_watcher, libev_events): - """If an on_signal_callback has been defined, call it returning the - string 'SIGTERM'. - - """ - LOGGER.debug('SIGTERM') - self._on_signal_callback('SIGTERM') - - def _handle_events(self, io_watcher, libev_events, **kwargs): - """Handle IO events by efficiently translating to BaseConnection - events and calling super. - - """ - super(LibevConnection, - self)._handle_events(io_watcher.fd, - self._LIBEV_TO_PIKA_ARRAY[libev_events], - **kwargs) - - def _reset_io_watcher(self): - """Reset the IO watcher; retry as necessary - - """ - self._io_watcher.stop() - - retries = 0 - while True: - try: - self._io_watcher.set( - self._io_watcher.fd, - self._PIKA_TO_LIBEV_ARRAY[self.event_state]) - - break - except: # sometimes the stop() doesn't complete in time - if retries > 5: raise - self._io_watcher.stop() # so try it again - retries += 1 - - self._io_watcher.start() - - def _manage_event_state(self): - """Manage the bitmask for reading/writing/error which is used by the - io/event handler to specify when there is an event such as a read or - write. - - """ - if self.outbound_buffer: - if not self.event_state & self.WRITE: - self.event_state |= self.WRITE - self._reset_io_watcher() - elif self.event_state & self.WRITE: - self.event_state = self.base_events - self._reset_io_watcher() - - def _timer_callback(self, timer, libev_events): - """Manage timer callbacks indirectly.""" - if timer in self._active_timers: - (callback_method, callback_timeout, - kwargs) = self._active_timers[timer] - - if callback_timeout: - callback_method(timeout=timer, **kwargs) - else: - callback_method(**kwargs) - - self.remove_timeout(timer) - else: - LOGGER.warning('Timer callback_method not found') - - def _get_timer(self, deadline): - """Get a timer from the pool or allocate a new one.""" - if self._stopped_timers: - timer = self._stopped_timers.pop() - timer.set(deadline, 0.0) - else: - timer = self.ioloop.timer(deadline, 0.0, self._timer_callback) - - return timer - - def add_timeout(self, deadline, callback_method, - callback_timeout=False, **callback_kwargs): - """Add the callback_method indirectly to the IOLoop timer to fire - after deadline seconds. Returns the timer handle. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :param callback_timeout: Whether timeout kwarg is passed on callback - :type callback_timeout: boolean - :param kwargs callback_kwargs: additional kwargs to pass on callback - :rtype: timer instance handle. - - """ - LOGGER.debug('deadline: {0}'.format(deadline)) - timer = self._get_timer(deadline) - self._active_timers[timer] = (callback_method, callback_timeout, - callback_kwargs) - timer.start() - return timer - - def remove_timeout(self, timer): - """Remove the timer from the IOLoop using the handle returned from - add_timeout. - - param: timer instance handle - - """ - LOGGER.debug('stop') - self._active_timers.pop(timer, None) - timer.stop() - self._stopped_timers.append(timer) - - def _create_and_connect_to_socket(self, sock_addr_tuple): - """Call super and then set the socket to nonblocking.""" - result = super(LibevConnection, - self)._create_and_connect_to_socket(sock_addr_tuple) - if result: - self.socket.setblocking(0) - return result diff --git a/packages_o/pika-0.10.0/pika/adapters/select_connection.py b/packages_o/pika-0.10.0/pika/adapters/select_connection.py deleted file mode 100644 index f217b234..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/select_connection.py +++ /dev/null @@ -1,613 +0,0 @@ -"""A connection adapter that tries to use the best polling method for the -platform pika is running on. - -""" -import os -import logging -import socket -import select -import errno -import time -from collections import defaultdict -import threading - -import pika.compat -from pika.compat import dictkeys - -from pika.adapters.base_connection import BaseConnection - -LOGGER = logging.getLogger(__name__) - -# One of select, epoll, kqueue or poll -SELECT_TYPE = None - -# Use epoll's constants to keep life easy -READ = 0x0001 -WRITE = 0x0004 -ERROR = 0x0008 - - -if pika.compat.PY2: - _SELECT_ERROR = select.error -else: - # select.error was deprecated and replaced by OSError in python 3.3 - _SELECT_ERROR = OSError - - -def _get_select_errno(error): - if pika.compat.PY2: - assert isinstance(error, select.error), repr(error) - return error.args[0] - else: - assert isinstance(error, OSError), repr(error) - return error.errno - - -class SelectConnection(BaseConnection): - """An asynchronous connection adapter that attempts to use the fastest - event loop adapter for the given platform. - - """ - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=True, - custom_ioloop=None): - """Create a new instance of the Connection object. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Method to call on connection open - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param method on_close_callback: Method to call on connection close - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - :raises: RuntimeError - - """ - ioloop = custom_ioloop or IOLoop() - super(SelectConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker, returning True on success, False - on failure. - - :rtype: bool - - """ - error = super(SelectConnection, self)._adapter_connect() - if not error: - self.ioloop.add_handler(self.socket.fileno(), self._handle_events, - self.event_state) - return error - - def _adapter_disconnect(self): - """Disconnect from the RabbitMQ broker""" - if self.socket: - self.ioloop.remove_handler(self.socket.fileno()) - super(SelectConnection, self)._adapter_disconnect() - - -class IOLoop(object): - """Singlton wrapper that decides which type of poller to use, creates an - instance of it in start_poller and keeps the invoking application in a - blocking state by calling the pollers start method. Poller should keep - looping until IOLoop.instance().stop() is called or there is a socket - error. - - Passes through all operations to the loaded poller object. - - """ - - def __init__(self): - self._poller = self._get_poller() - - def __getattr__(self, attr): - return getattr(self._poller, attr) - - def _get_poller(self): - """Determine the best poller to use for this enviroment.""" - - poller = None - - if hasattr(select, 'epoll'): - if not SELECT_TYPE or SELECT_TYPE == 'epoll': - LOGGER.debug('Using EPollPoller') - poller = EPollPoller() - - if not poller and hasattr(select, 'kqueue'): - if not SELECT_TYPE or SELECT_TYPE == 'kqueue': - LOGGER.debug('Using KQueuePoller') - poller = KQueuePoller() - - if (not poller and hasattr(select, 'poll') and - hasattr(select.poll(), 'modify')): # pylint: disable=E1101 - if not SELECT_TYPE or SELECT_TYPE == 'poll': - LOGGER.debug('Using PollPoller') - poller = PollPoller() - - if not poller: - LOGGER.debug('Using SelectPoller') - poller = SelectPoller() - - return poller - - -class SelectPoller(object): - """Default behavior is to use Select since it's the widest supported and has - all of the methods we need for child classes as well. One should only need - to override the update_handler and start methods for additional types. - - """ - # Drop out of the poll loop every POLL_TIMEOUT secs as a worst case, this - # is only a backstop value. We will run timeouts when they are scheduled. - POLL_TIMEOUT = 5 - # if the poller uses MS specify 1000 - POLL_TIMEOUT_MULT = 1 - - def __init__(self): - """Create an instance of the SelectPoller - - """ - # fd-to-handler function mappings - self._fd_handlers = dict() - - # event-to-fdset mappings - self._fd_events = {READ: set(), WRITE: set(), ERROR: set()} - - self._stopping = False - self._timeouts = {} - self._next_timeout = None - self._processing_fd_event_map = {} - - # Mutex for controlling critical sections where ioloop-interrupt sockets - # are created, used, and destroyed. Needed in case `stop()` is called - # from a thread. - self._mutex = threading.Lock() - - # ioloop-interrupt socket pair; initialized in start() - self._r_interrupt = None - self._w_interrupt = None - - def get_interrupt_pair(self): - """ Use a socketpair to be able to interrupt the ioloop if called - from another thread. Socketpair() is not supported on some OS (Win) - so use a pair of simple UDP sockets instead. The sockets will be - closed and garbage collected by python when the ioloop itself is. - """ - try: - read_sock, write_sock = socket.socketpair() - - except AttributeError: - LOGGER.debug("Using custom socketpair for interrupt") - read_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - read_sock.bind(('localhost', 0)) - write_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - write_sock.connect(read_sock.getsockname()) - - read_sock.setblocking(0) - write_sock.setblocking(0) - return read_sock, write_sock - - def read_interrupt(self, interrupt_sock, - events, write_only): # pylint: disable=W0613 - """ Read the interrupt byte(s). We ignore the event mask and write_only - flag as we can ony get here if there's data to be read on our fd. - - :param int interrupt_sock: The file descriptor to read from - :param int events: (unused) The events generated for this fd - :param bool write_only: (unused) True if poll was called to trigger a - write - """ - try: - os.read(interrupt_sock, 512) - except OSError as err: - if err.errno != errno.EAGAIN: - raise - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - timeout_at = time.time() + deadline - value = {'deadline': timeout_at, 'callback': callback_method} - timeout_id = hash(frozenset(value.items())) - self._timeouts[timeout_id] = value - - if not self._next_timeout or timeout_at < self._next_timeout: - self._next_timeout = timeout_at - - return timeout_id - - def remove_timeout(self, timeout_id): - """Remove a timeout if it's still in the timeout stack - - :param str timeout_id: The timeout id to remove - - """ - try: - timeout = self._timeouts.pop(timeout_id) - if timeout['deadline'] == self._next_timeout: - self._next_timeout = None - except KeyError: - pass - - def get_next_deadline(self): - """Get the interval to the next timeout event, or a default interval - """ - if self._next_timeout: - timeout = max((self._next_timeout - time.time(), 0)) - - elif self._timeouts: - deadlines = [t['deadline'] for t in self._timeouts.values()] - self._next_timeout = min(deadlines) - timeout = max((self._next_timeout - time.time(), 0)) - - else: - timeout = SelectPoller.POLL_TIMEOUT - - timeout = min((timeout, SelectPoller.POLL_TIMEOUT)) - return timeout * SelectPoller.POLL_TIMEOUT_MULT - - - def process_timeouts(self): - """Process the self._timeouts event stack""" - - now = time.time() - # Run the timeouts in order of deadlines. Although this shouldn't - # be strictly necessary it preserves old behaviour when timeouts - # were only run periodically. - to_run = sorted([(k, timer) for (k, timer) in self._timeouts.items() - if timer['deadline'] <= now], - key=lambda item: item[1]['deadline']) - - for k, timer in to_run: - if k not in self._timeouts: - # Previous invocation(s) should have deleted the timer. - continue - try: - timer['callback']() - finally: - # Don't do 'del self._timeout[k]' as the key might - # have been deleted just now. - if self._timeouts.pop(k, None) is not None: - self._next_timeout = None - - - def add_handler(self, fileno, handler, events): - """Add a new fileno to the set to be monitored - - :param int fileno: The file descriptor - :param method handler: What is called when an event happens - :param int events: The event mask - - """ - self._fd_handlers[fileno] = handler - self.update_handler(fileno, events) - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - - for ev in (READ, WRITE, ERROR): - if events & ev: - self._fd_events[ev].add(fileno) - else: - self._fd_events[ev].discard(fileno) - - - def remove_handler(self, fileno): - """Remove a file descriptor from the set - - :param int fileno: The file descriptor - - """ - try: - del self._processing_fd_event_map[fileno] - except KeyError: - pass - - self.update_handler(fileno, 0) - del self._fd_handlers[fileno] - - def start(self): - """Start the main poller loop. It will loop here until self._stopping""" - - LOGGER.debug('Starting IOLoop') - self._stopping = False - - with self._mutex: - # Watch out for reentry - if self._r_interrupt is None: - # Create ioloop-interrupt socket pair and register read handler. - # NOTE: we defer their creation because some users (e.g., - # BlockingConnection adapter) don't use the event loop and these - # sockets would get reported as leaks - self._r_interrupt, self._w_interrupt = self.get_interrupt_pair() - self.add_handler(self._r_interrupt.fileno(), - self.read_interrupt, - READ) - interrupt_sockets_created = True - else: - interrupt_sockets_created = False - try: - # Run event loop - while not self._stopping: - self.poll() - self.process_timeouts() - finally: - # Unregister and close ioloop-interrupt socket pair - if interrupt_sockets_created: - with self._mutex: - self.remove_handler(self._r_interrupt.fileno()) - self._r_interrupt.close() - self._r_interrupt = None - self._w_interrupt.close() - self._w_interrupt = None - - def stop(self): - """Request exit from the ioloop.""" - - LOGGER.debug('Stopping IOLoop') - self._stopping = True - - with self._mutex: - if self._w_interrupt is None: - return - - try: - # Send byte to interrupt the poll loop, use write() for - # consitency. - os.write(self._w_interrupt.fileno(), b'X') - except OSError as err: - if err.errno != errno.EWOULDBLOCK: - raise - except Exception as err: - # There's nothing sensible to do here, we'll exit the interrupt - # loop after POLL_TIMEOUT secs in worst case anyway. - LOGGER.warning("Failed to send ioloop interrupt: %s", err) - raise - - def poll(self, write_only=False): - """Wait for events on interested filedescriptors. - - :param bool write_only: Passed through to the hadnlers to indicate - that they should only process write events. - """ - while True: - try: - read, write, error = select.select(self._fd_events[READ], - self._fd_events[WRITE], - self._fd_events[ERROR], - self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - # Build an event bit mask for each fileno we've recieved an event for - - fd_event_map = defaultdict(int) - for fd_set, ev in zip((read, write, error), (READ, WRITE, ERROR)): - for fileno in fd_set: - fd_event_map[fileno] |= ev - - self._process_fd_events(fd_event_map, write_only) - - def _process_fd_events(self, fd_event_map, write_only): - """ Processes the callbacks for each fileno we've recieved events. - Before doing so we re-calculate the event mask based on what is - currently set in case it has been changed under our feet by a - previous callback. We also take a store a refernce to the - fd_event_map in the class so that we can detect removal of an - fileno during processing of another callback and not generate - spurious callbacks on it. - - :param dict fd_event_map: Map of fds to events recieved on them. - """ - - self._processing_fd_event_map = fd_event_map - - for fileno in dictkeys(fd_event_map): - if fileno not in fd_event_map: - # the fileno has been removed from the map under our feet. - continue - - events = fd_event_map[fileno] - for ev in [READ, WRITE, ERROR]: - if fileno not in self._fd_events[ev]: - events &= ~ev - - if events: - handler = self._fd_handlers[fileno] - handler(fileno, events, write_only=write_only) - -class KQueuePoller(SelectPoller): - """KQueuePoller works on BSD based systems and is faster than select""" - - def __init__(self): - """Create an instance of the KQueuePoller - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._kqueue = select.kqueue() - super(KQueuePoller, self).__init__() - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - - kevents = list() - if not events & READ: - if fileno in self._fd_events[READ]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_READ, - flags=select.KQ_EV_DELETE)) - else: - if fileno not in self._fd_events[READ]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_READ, - flags=select.KQ_EV_ADD)) - if not events & WRITE: - if fileno in self._fd_events[WRITE]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_WRITE, - flags=select.KQ_EV_DELETE)) - else: - if fileno not in self._fd_events[WRITE]: - kevents.append(select.kevent(fileno, - filter=select.KQ_FILTER_WRITE, - flags=select.KQ_EV_ADD)) - for event in kevents: - self._kqueue.control([event], 0) - super(KQueuePoller, self).update_handler(fileno, events) - - def _map_event(self, kevent): - """return the event type associated with a kevent object - - :param kevent kevent: a kevent object as returned by kqueue.control() - - """ - if kevent.filter == select.KQ_FILTER_READ: - return READ - elif kevent.filter == select.KQ_FILTER_WRITE: - return WRITE - elif kevent.flags & select.KQ_EV_ERROR: - return ERROR - - def poll(self, write_only=False): - """Check to see if the events that are cared about have fired. - - :param bool write_only: Don't look at self.events, just look to see if - the adapter can write. - - """ - while True: - try: - kevents = self._kqueue.control(None, 1000, - self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - fd_event_map = defaultdict(int) - for event in kevents: - fileno = event.ident - fd_event_map[fileno] |= self._map_event(event) - - self._process_fd_events(fd_event_map, write_only) - - -class PollPoller(SelectPoller): - """Poll works on Linux and can have better performance than EPoll in - certain scenarios. Both are faster than select. - - """ - POLL_TIMEOUT_MULT = 1000 - - def __init__(self): - """Create an instance of the KQueuePoller - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._poll = self.create_poller() - super(PollPoller, self).__init__() - - def create_poller(self): - return select.poll() # pylint: disable=E1101 - - def add_handler(self, fileno, handler, events): - """Add a file descriptor to the poll set - - :param int fileno: The file descriptor to check events for - :param method handler: What is called when an event happens - :param int events: The events to look for - - """ - self._poll.register(fileno, events) - super(PollPoller, self).add_handler(fileno, handler, events) - - def update_handler(self, fileno, events): - """Set the events to the current events - - :param int fileno: The file descriptor - :param int events: The event mask - - """ - super(PollPoller, self).update_handler(fileno, events) - self._poll.modify(fileno, events) - - def remove_handler(self, fileno): - """Remove a fileno to the set - - :param int fileno: The file descriptor - - """ - super(PollPoller, self).remove_handler(fileno) - self._poll.unregister(fileno) - - def poll(self, write_only=False): - """Poll until the next timeout waiting for an event - - :param bool write_only: Only process write events - - """ - while True: - try: - events = self._poll.poll(self.get_next_deadline()) - break - except _SELECT_ERROR as error: - if _get_select_errno(error) == errno.EINTR: - continue - else: - raise - - fd_event_map = defaultdict(int) - for fileno, event in events: - fd_event_map[fileno] |= event - - self._process_fd_events(fd_event_map, write_only) - - -class EPollPoller(PollPoller): - """EPoll works on Linux and can have better performance than Poll in - certain scenarios. Both are faster than select. - - """ - POLL_TIMEOUT_MULT = 1 - - def create_poller(self): - return select.epoll() # pylint: disable=E1101 diff --git a/packages_o/pika-0.10.0/pika/adapters/tornado_connection.py b/packages_o/pika-0.10.0/pika/adapters/tornado_connection.py deleted file mode 100644 index 1c5c6078..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/tornado_connection.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Use pika with the Tornado IOLoop""" -from tornado import ioloop -import logging -import time - -from pika.adapters import base_connection - -LOGGER = logging.getLogger(__name__) - - -class TornadoConnection(base_connection.BaseConnection): - """The TornadoConnection runs on the Tornado IOLoop. If you're running the - connection in a web app, make sure you set stop_ioloop_on_close to False, - which is the default behavior for this adapter, otherwise the web app - will stop taking requests. - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - - """ - WARN_ABOUT_IOLOOP = True - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False, - custom_ioloop=None): - """Create a new instance of the TornadoConnection class, connecting - to RabbitMQ automatically - - :param pika.connection.Parameters parameters: Connection parameters - :param on_open_callback: The method to call when the connection is open - :type on_open_callback: method - :param on_open_error_callback: Method to call if the connection cant - be opened - :type on_open_error_callback: method - :param bool stop_ioloop_on_close: Call ioloop.stop() if disconnected - :param custom_ioloop: Override using the global IOLoop in Tornado - - """ - self.sleep_counter = 0 - self.ioloop = custom_ioloop or ioloop.IOLoop.instance() - super(TornadoConnection, self).__init__(parameters, on_open_callback, - on_open_error_callback, - on_close_callback, self.ioloop, - stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the remote socket, adding the socket to the IOLoop if - connected. - - :rtype: bool - - """ - error = super(TornadoConnection, self)._adapter_connect() - if not error: - self.ioloop.add_handler(self.socket.fileno(), self._handle_events, - self.event_state) - return error - - def _adapter_disconnect(self): - """Disconnect from the RabbitMQ broker""" - if self.socket: - self.ioloop.remove_handler(self.socket.fileno()) - super(TornadoConnection, self)._adapter_disconnect() - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: str - - """ - return self.ioloop.add_timeout(time.time() + deadline, callback_method) - - def remove_timeout(self, timeout_id): - """Remove the timeout from the IOLoop by the ID returned from - add_timeout. - - :rtype: str - - """ - return self.ioloop.remove_timeout(timeout_id) diff --git a/packages_o/pika-0.10.0/pika/adapters/twisted_connection.py b/packages_o/pika-0.10.0/pika/adapters/twisted_connection.py deleted file mode 100644 index 2ee65b26..00000000 --- a/packages_o/pika-0.10.0/pika/adapters/twisted_connection.py +++ /dev/null @@ -1,451 +0,0 @@ -"""Using Pika with a Twisted reactor. - -Supports two methods of establishing the connection, using TwistedConnection -or TwistedProtocolConnection. For details about each method, see the docstrings -of the corresponding classes. - -The interfaces in this module are Deferred-based when possible. This means that -the connection.channel() method and most of the channel methods return -Deferreds instead of taking a callback argument and that basic_consume() -returns a Twisted DeferredQueue where messages from the server will be -stored. Refer to the docstrings for TwistedConnection.channel() and the -TwistedChannel class for details. - -""" -import functools -from twisted.internet import defer, error, reactor -from twisted.python import log - -from pika import exceptions -from pika.adapters import base_connection - - -class ClosableDeferredQueue(defer.DeferredQueue): - """ - Like the normal Twisted DeferredQueue, but after close() is called with an - Exception instance all pending Deferreds are errbacked and further attempts - to call get() or put() return a Failure wrapping that exception. - """ - - def __init__(self, size=None, backlog=None): - self.closed = None - super(ClosableDeferredQueue, self).__init__(size, backlog) - - def put(self, obj): - if self.closed: - return defer.fail(self.closed) - return defer.DeferredQueue.put(self, obj) - - def get(self): - if self.closed: - return defer.fail(self.closed) - return defer.DeferredQueue.get(self) - - def close(self, reason): - self.closed = reason - while self.waiting: - self.waiting.pop().errback(reason) - self.pending = [] - - -class TwistedChannel(object): - """A wrapper wround Pika's Channel. - - Channel methods that normally take a callback argument are wrapped to - return a Deferred that fires with whatever would be passed to the callback. - If the channel gets closed, all pending Deferreds are errbacked with a - ChannelClosed exception. The returned Deferreds fire with whatever - arguments the callback to the original method would receive. - - The basic_consume method is wrapped in a special way, see its docstring for - details. - """ - - WRAPPED_METHODS = ('exchange_declare', 'exchange_delete', 'queue_declare', - 'queue_bind', 'queue_purge', 'queue_unbind', 'basic_qos', - 'basic_get', 'basic_recover', 'tx_select', 'tx_commit', - 'tx_rollback', 'flow', 'basic_cancel') - - def __init__(self, channel): - self.__channel = channel - self.__closed = None - self.__calls = set() - self.__consumers = {} - - channel.add_on_close_callback(self.channel_closed) - - def channel_closed(self, channel, reply_code, reply_text): - # enter the closed state - self.__closed = exceptions.ChannelClosed(reply_code, reply_text) - # errback all pending calls - for d in self.__calls: - d.errback(self.__closed) - # close all open queues - for consumers in self.__consumers.values(): - for c in consumers: - c.close(self.__closed) - # release references to stored objects - self.__calls = set() - self.__consumers = {} - - def basic_consume(self, *args, **kwargs): - """Consume from a server queue. Returns a Deferred that fires with a - tuple: (queue_object, consumer_tag). The queue object is an instance of - ClosableDeferredQueue, where data received from the queue will be - stored. Clients should use its get() method to fetch individual - message. - """ - if self.__closed: - return defer.fail(self.__closed) - - queue = ClosableDeferredQueue() - queue_name = kwargs['queue'] - kwargs['consumer_callback'] = lambda *args: queue.put(args) - self.__consumers.setdefault(queue_name, set()).add(queue) - - try: - consumer_tag = self.__channel.basic_consume(*args, **kwargs) - except: - return defer.fail() - - return defer.succeed((queue, consumer_tag)) - - def queue_delete(self, *args, **kwargs): - """Wraps the method the same way all the others are wrapped, but removes - the reference to the queue object after it gets deleted on the server. - - """ - wrapped = self.__wrap_channel_method('queue_delete') - queue_name = kwargs['queue'] - - d = wrapped(*args, **kwargs) - return d.addCallback(self.__clear_consumer, queue_name) - - def basic_publish(self, *args, **kwargs): - """Make sure the channel is not closed and then publish. Return a - Deferred that fires with the result of the channel's basic_publish. - - """ - if self.__closed: - return defer.fail(self.__closed) - return defer.succeed(self.__channel.basic_publish(*args, **kwargs)) - - def __wrap_channel_method(self, name): - """Wrap Pika's Channel method to make it return a Deferred that fires - when the method completes and errbacks if the channel gets closed. If - the original method's callback would receive more than one argument, the - Deferred fires with a tuple of argument values. - - """ - method = getattr(self.__channel, name) - - @functools.wraps(method) - def wrapped(*args, **kwargs): - if self.__closed: - return defer.fail(self.__closed) - - d = defer.Deferred() - self.__calls.add(d) - d.addCallback(self.__clear_call, d) - - def single_argument(*args): - """ - Make sure that the deferred is called with a single argument. - In case the original callback fires with more than one, convert - to a tuple. - """ - if len(args) > 1: - d.callback(tuple(args)) - else: - d.callback(*args) - - kwargs['callback'] = single_argument - - try: - method(*args, **kwargs) - except: - return defer.fail() - return d - - return wrapped - - def __clear_consumer(self, ret, queue_name): - self.__consumers.pop(queue_name, None) - return ret - - def __clear_call(self, ret, d): - self.__calls.discard(d) - return ret - - def __getattr__(self, name): - # Wrap methods defined in WRAPPED_METHODS, forward the rest of accesses - # to the channel. - if name in self.WRAPPED_METHODS: - return self.__wrap_channel_method(name) - return getattr(self.__channel, name) - - -class IOLoopReactorAdapter(object): - """An adapter providing Pika's IOLoop interface using a Twisted reactor. - - Accepts a TwistedConnection object and a Twisted reactor object. - - """ - - def __init__(self, connection, reactor): - self.connection = connection - self.reactor = reactor - self.started = False - - def add_timeout(self, deadline, callback_method): - """Add the callback_method to the IOLoop timer to fire after deadline - seconds. Returns a handle to the timeout. Do not confuse with - Tornado's timeout where you pass in the time you want to have your - callback called. Only pass in the seconds until it's to be called. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - :rtype: twisted.internet.interfaces.IDelayedCall - - """ - return self.reactor.callLater(deadline, callback_method) - - def remove_timeout(self, call): - """Remove a call - - :param twisted.internet.interfaces.IDelayedCall call: The call to cancel - - """ - call.cancel() - - def stop(self): - # Guard against stopping the reactor multiple times - if not self.started: - return - self.started = False - self.reactor.stop() - - def start(self): - # Guard against starting the reactor multiple times - if self.started: - return - self.started = True - self.reactor.run() - - def remove_handler(self, _): - # The fileno is irrelevant, as it's the connection's job to provide it - # to the reactor when asked to do so. Removing the handler from the - # ioloop is removing it from the reactor in Twisted's parlance. - self.reactor.removeReader(self.connection) - self.reactor.removeWriter(self.connection) - - def update_handler(self, _, event_state): - # Same as in remove_handler, the fileno is irrelevant. First remove the - # connection entirely from the reactor, then add it back depending on - # the event state. - self.reactor.removeReader(self.connection) - self.reactor.removeWriter(self.connection) - - if event_state & self.connection.READ: - self.reactor.addReader(self.connection) - - if event_state & self.connection.WRITE: - self.reactor.addWriter(self.connection) - - -class TwistedConnection(base_connection.BaseConnection): - """A standard Pika connection adapter. You instantiate the class passing the - connection parameters and the connected callback and when it gets called - you can start using it. - - The problem is that connection establishing is done using the blocking - socket module. For instance, if the host you are connecting to is behind a - misconfigured firewall that just drops packets, the whole process will - freeze until the connection timeout passes. To work around that problem, - use TwistedProtocolConnection, but read its docstring first. - - Objects of this class get put in the Twisted reactor which will notify them - when the socket connection becomes readable or writable, so apart from - implementing the BaseConnection interface, they also provide Twisted's - IReadWriteDescriptor interface. - - """ - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None, - stop_ioloop_on_close=False): - super(TwistedConnection, self).__init__( - parameters=parameters, - on_open_callback=on_open_callback, - on_open_error_callback=on_open_error_callback, - on_close_callback=on_close_callback, - ioloop=IOLoopReactorAdapter(self, reactor), - stop_ioloop_on_close=stop_ioloop_on_close) - - def _adapter_connect(self): - """Connect to the RabbitMQ broker""" - # Connect (blockignly!) to the server - error = super(TwistedConnection, self)._adapter_connect() - if not error: - # Set the I/O events we're waiting for (see IOLoopReactorAdapter - # docstrings for why it's OK to pass None as the file descriptor) - self.ioloop.update_handler(None, self.event_state) - return error - - def _adapter_disconnect(self): - """Called when the adapter should disconnect""" - self.ioloop.remove_handler(None) - self._cleanup_socket() - - def _handle_disconnect(self): - """Do not stop the reactor, this would cause the entire process to exit, - just fire the disconnect callbacks - - """ - self._on_connection_closed(None, True) - - def _on_connected(self): - """Call superclass and then update the event state to flush the outgoing - frame out. Commit 50d842526d9f12d32ad9f3c4910ef60b8c301f59 removed a - self._flush_outbound call that was in _send_frame which previously - made this step unnecessary. - - """ - super(TwistedConnection, self)._on_connected() - self._manage_event_state() - - def channel(self, channel_number=None): - """Return a Deferred that fires with an instance of a wrapper around the - Pika Channel class. - - """ - d = defer.Deferred() - base_connection.BaseConnection.channel(self, d.callback, channel_number) - return d.addCallback(TwistedChannel) - - # IReadWriteDescriptor methods - - def fileno(self): - return self.socket.fileno() - - def logPrefix(self): - return "twisted-pika" - - def connectionLost(self, reason): - # If the connection was not closed cleanly, log the error - if not reason.check(error.ConnectionDone): - log.err(reason) - - self._handle_disconnect() - - def doRead(self): - self._handle_read() - - def doWrite(self): - self._handle_write() - self._manage_event_state() - - -class TwistedProtocolConnection(base_connection.BaseConnection): - """A hybrid between a Pika Connection and a Twisted Protocol. Allows using - Twisted's non-blocking connectTCP/connectSSL methods for connecting to the - server. - - It has one caveat: TwistedProtocolConnection objects have a ready - instance variable that's a Deferred which fires when the connection is - ready to be used (the initial AMQP handshaking has been done). You *have* - to wait for this Deferred to fire before requesting a channel. - - Since it's Twisted handling connection establishing it does not accept - connect callbacks, you have to implement that within Twisted. Also remember - that the host, port and ssl values of the connection parameters are ignored - because, yet again, it's Twisted who manages the connection. - - """ - - def __init__(self, parameters): - self.ready = defer.Deferred() - super(TwistedProtocolConnection, self).__init__( - parameters=parameters, - on_open_callback=self.connectionReady, - on_open_error_callback=self.connectionFailed, - on_close_callback=None, - ioloop=IOLoopReactorAdapter(self, reactor), - stop_ioloop_on_close=False) - - def connect(self): - # The connection is open asynchronously by Twisted, so skip the whole - # connect() part, except for setting the connection state - self._set_connection_state(self.CONNECTION_INIT) - - def _adapter_connect(self): - # Should never be called, as we override connect() and leave the - # building of a TCP connection to Twisted, but implement anyway to keep - # the interface - return False - - def _adapter_disconnect(self): - # Disconnect from the server - self.transport.loseConnection() - - def _flush_outbound(self): - """Override BaseConnection._flush_outbound to send all bufferred data - the Twisted way, by writing to the transport. No need for buffering, - Twisted handles that for us. - """ - while self.outbound_buffer: - self.transport.write(self.outbound_buffer.popleft()) - - def channel(self, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - Return a Deferred that fires with an instance of a wrapper around the - Pika Channel class. - - :param int channel_number: The channel number to use, defaults to the - next available. - - """ - d = defer.Deferred() - base_connection.BaseConnection.channel(self, d.callback, channel_number) - return d.addCallback(TwistedChannel) - - # IProtocol methods - - def dataReceived(self, data): - # Pass the bytes to Pika for parsing - self._on_data_available(data) - - def connectionLost(self, reason): - # Let the caller know there's been an error - d, self.ready = self.ready, None - if d: - d.errback(reason) - - def makeConnection(self, transport): - self.transport = transport - self.connectionMade() - - def connectionMade(self): - # Tell everyone we're connected - self._on_connected() - - # Our own methods - - def connectionReady(self, res): - d, self.ready = self.ready, None - if d: - d.callback(res) - - def connectionFailed(self, connection_unused, error_message=None): - d, self.ready = self.ready, None - if d: - attempts = self.params.connection_attempts - exc = exceptions.AMQPConnectionError(attempts) - d.errback(exc) diff --git a/packages_o/pika-0.10.0/pika/amqp_object.py b/packages_o/pika-0.10.0/pika/amqp_object.py deleted file mode 100644 index 576a2c41..00000000 --- a/packages_o/pika-0.10.0/pika/amqp_object.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Base classes that are extended by low level AMQP frames and higher level -AMQP classes and methods. - -""" - - -class AMQPObject(object): - """Base object that is extended by AMQP low level frames and AMQP classes - and methods. - - """ - NAME = 'AMQPObject' - INDEX = None - - def __repr__(self): - items = list() - for key, value in self.__dict__.items(): - if getattr(self.__class__, key, None) != value: - items.append('%s=%s' % (key, value)) - if not items: - return "<%s>" % self.NAME - return "<%s(%s)>" % (self.NAME, sorted(items)) - - -class Class(AMQPObject): - """Is extended by AMQP classes""" - NAME = 'Unextended Class' - - -class Method(AMQPObject): - """Is extended by AMQP methods""" - NAME = 'Unextended Method' - synchronous = False - - def _set_content(self, properties, body): - """If the method is a content frame, set the properties and body to - be carried as attributes of the class. - - :param pika.frame.Properties properties: AMQP Basic Properties - :param body: The message body - :type body: str or unicode - - """ - self._properties = properties - self._body = body - - def get_properties(self): - """Return the properties if they are set. - - :rtype: pika.frame.Properties - - """ - return self._properties - - def get_body(self): - """Return the message body if it is set. - - :rtype: str|unicode - - """ - return self._body - - -class Properties(AMQPObject): - """Class to encompass message properties (AMQP Basic.Properties)""" - NAME = 'Unextended Properties' diff --git a/packages_o/pika-0.10.0/pika/callback.py b/packages_o/pika-0.10.0/pika/callback.py deleted file mode 100644 index 6ac58bd9..00000000 --- a/packages_o/pika-0.10.0/pika/callback.py +++ /dev/null @@ -1,410 +0,0 @@ -"""Callback management class, common area for keeping track of all callbacks in -the Pika stack. - -""" -import functools -import logging - -from pika import frame -from pika import amqp_object -from pika.compat import xrange, canonical_str - -LOGGER = logging.getLogger(__name__) - - -def name_or_value(value): - """Will take Frame objects, classes, etc and attempt to return a valid - string identifier for them. - - :param value: The value to sanitize - :type value: pika.amqp_object.AMQPObject|pika.frame.Frame|int|unicode|str - :rtype: str - - """ - # Is it subclass of AMQPObject - try: - if issubclass(value, amqp_object.AMQPObject): - return value.NAME - except TypeError: - pass - - # Is it a Pika frame object? - if isinstance(value, frame.Method): - return value.method.NAME - - # Is it a Pika frame object (go after Method since Method extends this) - if isinstance(value, amqp_object.AMQPObject): - return value.NAME - - # Cast the value to a str (python 2 and python 3); encoding as UTF-8 on Python 2 - return canonical_str(value) - - -def sanitize_prefix(function): - """Automatically call name_or_value on the prefix passed in.""" - - @functools.wraps(function) - def wrapper(*args, **kwargs): - args = list(args) - offset = 1 - if 'prefix' in kwargs: - kwargs['prefix'] = name_or_value(kwargs['prefix']) - elif len(args) - 1 >= offset: - args[offset] = name_or_value(args[offset]) - offset += 1 - if 'key' in kwargs: - kwargs['key'] = name_or_value(kwargs['key']) - elif len(args) - 1 >= offset: - args[offset] = name_or_value(args[offset]) - - return function(*tuple(args), **kwargs) - - return wrapper - - -def check_for_prefix_and_key(function): - """Automatically return false if the key or prefix is not in the callbacks - for the instance. - - """ - - @functools.wraps(function) - def wrapper(*args, **kwargs): - offset = 1 - # Sanitize the prefix - if 'prefix' in kwargs: - prefix = name_or_value(kwargs['prefix']) - else: - prefix = name_or_value(args[offset]) - offset += 1 - - # Make sure to sanitize the key as well - if 'key' in kwargs: - key = name_or_value(kwargs['key']) - else: - key = name_or_value(args[offset]) - - # Make sure prefix and key are in the stack - if prefix not in args[0]._stack or key not in args[0]._stack[prefix]: - return False - - # Execute the method - return function(*args, **kwargs) - - return wrapper - - -class CallbackManager(object): - """CallbackManager is a global callback system designed to be a single place - where Pika can manage callbacks and process them. It should be referenced - by the CallbackManager.instance() method instead of constructing new - instances of it. - - """ - CALLS = 'calls' - ARGUMENTS = 'arguments' - DUPLICATE_WARNING = 'Duplicate callback found for "%s:%s"' - CALLBACK = 'callback' - ONE_SHOT = 'one_shot' - ONLY_CALLER = 'only' - - def __init__(self): - """Create an instance of the CallbackManager""" - self._stack = dict() - - @sanitize_prefix - def add(self, prefix, key, callback, - one_shot=True, - only_caller=None, - arguments=None): - """Add a callback to the stack for the specified key. If the call is - specified as one_shot, it will be removed after being fired - - The prefix is usually the channel number but the class is generic - and prefix and key may be any value. If you pass in only_caller - CallbackManager will restrict processing of the callback to only - the calling function/object that you specify. - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :param method callback: The callback to call - :param bool one_shot: Remove this callback after it is called - :param object only_caller: Only allow one_caller value to call the - event that fires the callback. - :param dict arguments: Arguments to validate when processing - :rtype: tuple(prefix, key) - - """ - # Prep the stack - if prefix not in self._stack: - self._stack[prefix] = dict() - - if key not in self._stack[prefix]: - self._stack[prefix][key] = list() - - # Check for a duplicate - for callback_dict in self._stack[prefix][key]: - if (callback_dict[self.CALLBACK] == callback and - callback_dict[self.ARGUMENTS] == arguments and - callback_dict[self.ONLY_CALLER] == only_caller): - if callback_dict[self.ONE_SHOT] is True: - callback_dict[self.CALLS] += 1 - LOGGER.debug('Incremented callback reference counter: %r', - callback_dict) - else: - LOGGER.warning(self.DUPLICATE_WARNING, prefix, key) - return prefix, key - - # Create the callback dictionary - callback_dict = self._callback_dict(callback, one_shot, only_caller, - arguments) - self._stack[prefix][key].append(callback_dict) - LOGGER.debug('Added: %r', callback_dict) - return prefix, key - - def clear(self): - """Clear all the callbacks if there are any defined.""" - self._stack = dict() - LOGGER.debug('Callbacks cleared') - - @sanitize_prefix - def cleanup(self, prefix): - """Remove all callbacks from the stack by a prefix. Returns True - if keys were there to be removed - - :param str or int prefix: The prefix for keeping track of callbacks with - :rtype: bool - - """ - LOGGER.debug('Clearing out %r from the stack', prefix) - if prefix not in self._stack or not self._stack[prefix]: - return False - del self._stack[prefix] - return True - - @sanitize_prefix - def pending(self, prefix, key): - """Return count of callbacks for a given prefix or key or None - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :rtype: None or int - - """ - if not prefix in self._stack or not key in self._stack[prefix]: - return None - return len(self._stack[prefix][key]) - - @sanitize_prefix - @check_for_prefix_and_key - def process(self, prefix, key, caller, *args, **keywords): - """Run through and process all the callbacks for the specified keys. - Caller should be specified at all times so that callbacks which - require a specific function to call CallbackManager.process will - not be processed. - - :param prefix: Categorize the callback - :type prefix: str or int - :param key: The key for the callback - :type key: object or str or dict - :param object caller: Who is firing the event - :param list args: Any optional arguments - :param dict keywords: Optional keyword arguments - :rtype: bool - - """ - LOGGER.debug('Processing %s:%s', prefix, key) - if prefix not in self._stack or key not in self._stack[prefix]: - return False - - callbacks = list() - # Check each callback, append it to the list if it should be called - for callback_dict in list(self._stack[prefix][key]): - if self._should_process_callback(callback_dict, caller, list(args)): - callbacks.append(callback_dict[self.CALLBACK]) - if callback_dict[self.ONE_SHOT]: - self._use_one_shot_callback(prefix, key, callback_dict) - - # Call each callback - for callback in callbacks: - LOGGER.debug('Calling %s for "%s:%s"', callback, prefix, key) - try: - callback(*args, **keywords) - except: - LOGGER.exception('Calling %s for "%s:%s" failed', callback, - prefix, key) - raise - return True - - @sanitize_prefix - @check_for_prefix_and_key - def remove(self, prefix, key, callback_value=None, arguments=None): - """Remove a callback from the stack by prefix, key and optionally - the callback itself. If you only pass in prefix and key, all - callbacks for that prefix and key will be removed. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - :param method callback_value: The method defined to call on callback - :param dict arguments: Optional arguments to check - :rtype: bool - - """ - if callback_value: - offsets_to_remove = list() - for offset in xrange(len(self._stack[prefix][key]), 0, -1): - callback_dict = self._stack[prefix][key][offset - 1] - - if (callback_dict[self.CALLBACK] == callback_value and - self._arguments_match(callback_dict, [arguments])): - offsets_to_remove.append(offset - 1) - - for offset in offsets_to_remove: - try: - LOGGER.debug('Removing callback #%i: %r', offset, - self._stack[prefix][key][offset]) - del self._stack[prefix][key][offset] - except KeyError: - pass - - self._cleanup_callback_dict(prefix, key) - return True - - @sanitize_prefix - @check_for_prefix_and_key - def remove_all(self, prefix, key): - """Remove all callbacks for the specified prefix and key. - - :param str prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - - """ - del self._stack[prefix][key] - self._cleanup_callback_dict(prefix, key) - - def _arguments_match(self, callback_dict, args): - """Validate if the arguments passed in match the expected arguments in - the callback_dict. We expect this to be a frame passed in to *args for - process or passed in as a list from remove. - - :param dict callback_dict: The callback dictionary to evaluate against - :param list args: The arguments passed in as a list - - """ - if callback_dict[self.ARGUMENTS] is None: - return True - if not args: - return False - if isinstance(args[0], dict): - return self._dict_arguments_match(args[0], - callback_dict[self.ARGUMENTS]) - return self._obj_arguments_match(args[0].method - if hasattr(args[0], 'method') else - args[0], callback_dict[self.ARGUMENTS]) - - def _callback_dict(self, callback, one_shot, only_caller, arguments): - """Return the callback dictionary. - - :param method callback: The callback to call - :param bool one_shot: Remove this callback after it is called - :param object only_caller: Only allow one_caller value to call the - event that fires the callback. - :rtype: dict - - """ - value = { - self.CALLBACK: callback, - self.ONE_SHOT: one_shot, - self.ONLY_CALLER: only_caller, - self.ARGUMENTS: arguments - } - if one_shot: - value[self.CALLS] = 1 - return value - - def _cleanup_callback_dict(self, prefix, key=None): - """Remove empty dict nodes in the callback stack. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - - """ - if key and key in self._stack[prefix] and not self._stack[prefix][key]: - del self._stack[prefix][key] - if prefix in self._stack and not self._stack[prefix]: - del self._stack[prefix] - - @staticmethod - def _dict_arguments_match(value, expectation): - """Checks an dict to see if it has attributes that meet the expectation. - - :param dict value: The dict to evaluate - :param dict expectation: The values to check against - :rtype: bool - - """ - LOGGER.debug('Comparing %r to %r', value, expectation) - for key in expectation: - if value.get(key) != expectation[key]: - LOGGER.debug('Values in dict do not match for %s', key) - return False - return True - - @staticmethod - def _obj_arguments_match(value, expectation): - """Checks an object to see if it has attributes that meet the - expectation. - - :param object value: The object to evaluate - :param dict expectation: The values to check against - :rtype: bool - - """ - for key in expectation: - if not hasattr(value, key): - LOGGER.debug('%r does not have required attribute: %s', - type(value), key) - return False - if getattr(value, key) != expectation[key]: - LOGGER.debug('Values in %s do not match for %s', type(value), - key) - return False - return True - - def _should_process_callback(self, callback_dict, caller, args): - """Returns True if the callback should be processed. - - :param dict callback_dict: The callback configuration - :param object caller: Who is firing the event - :param list args: Any optional arguments - :rtype: bool - - """ - if not self._arguments_match(callback_dict, args): - LOGGER.debug('Arguments do not match for %r, %r', callback_dict, - args) - return False - return (callback_dict[self.ONLY_CALLER] is None or - (callback_dict[self.ONLY_CALLER] and - callback_dict[self.ONLY_CALLER] == caller)) - - def _use_one_shot_callback(self, prefix, key, callback_dict): - """Process the one-shot callback, decrementing the use counter and - removing it from the stack if it's now been fully used. - - :param str or int prefix: The prefix for keeping track of callbacks with - :param str key: The callback key - :param dict callback_dict: The callback dict to process - - """ - LOGGER.debug('Processing use of oneshot callback') - callback_dict[self.CALLS] -= 1 - LOGGER.debug('%i registered uses left', callback_dict[self.CALLS]) - - if callback_dict[self.CALLS] <= 0: - self.remove(prefix, key, callback_dict[self.CALLBACK], - callback_dict[self.ARGUMENTS]) diff --git a/packages_o/pika-0.10.0/pika/channel.py b/packages_o/pika-0.10.0/pika/channel.py deleted file mode 100644 index 2bf3b0f0..00000000 --- a/packages_o/pika-0.10.0/pika/channel.py +++ /dev/null @@ -1,1254 +0,0 @@ -"""The Channel class provides a wrapper for interacting with RabbitMQ -implementing the methods and behaviors for an AMQP Channel. - -""" -import collections -import logging -import warnings -import uuid - -import pika.frame as frame -import pika.exceptions as exceptions -import pika.spec as spec -from pika.utils import is_callable -from pika.compat import unicode_type, dictkeys, as_bytes - - -LOGGER = logging.getLogger(__name__) -MAX_CHANNELS = 32768 - - -class Channel(object): - """A Channel is the primary communication method for interacting with - RabbitMQ. It is recommended that you do not directly invoke - the creation of a channel object in your application code but rather - construct the a channel by calling the active connection's channel() - method. - - """ - CLOSED = 0 - OPENING = 1 - OPEN = 2 - CLOSING = 3 - - _ON_CHANNEL_CLEANUP_CB_KEY = '_on_channel_cleanup' - - def __init__(self, connection, channel_number, on_open_callback=None): - """Create a new instance of the Channel - - :param pika.connection.Connection connection: The connection - :param int channel_number: The channel number for this instance - :param method on_open_callback: The method to call on channel open - - """ - if not isinstance(channel_number, int): - raise exceptions.InvalidChannelNumber - self.channel_number = channel_number - self.callbacks = connection.callbacks - self.connection = connection - - # The frame-handler changes depending on the type of frame processed - self.frame_dispatcher = ContentFrameDispatcher() - - self._blocked = collections.deque(list()) - self._blocking = None - self._has_on_flow_callback = False - self._cancelled = set() - self._consumers = dict() - self._consumers_with_noack = set() - self._on_flowok_callback = None - self._on_getok_callback = None - self._on_openok_callback = on_open_callback - self._pending = dict() - self._state = self.CLOSED - - # opaque cookie value set by wrapper layer (e.g., BlockingConnection) - # via _set_cookie - self._cookie = None - - def __int__(self): - """Return the channel object as its channel number - - :rtype: int - - """ - return self.channel_number - - def add_callback(self, callback, replies, one_shot=True): - """Pass in a callback handler and a list replies from the - RabbitMQ broker which you'd like the callback notified of. Callbacks - should allow for the frame parameter to be passed in. - - :param method callback: The method to call - :param list replies: The replies to get a callback for - :param bool one_shot: Only handle the first type callback - - """ - for reply in replies: - self.callbacks.add(self.channel_number, reply, callback, one_shot) - - def add_on_cancel_callback(self, callback): - """Pass a callback function that will be called when the basic_cancel - is sent by the server. The callback function should receive a frame - parameter. - - :param method callback: The method to call on callback - - """ - self.callbacks.add(self.channel_number, spec.Basic.Cancel, callback, - False) - - def add_on_close_callback(self, callback): - """Pass a callback function that will be called when the channel is - closed. The callback function will receive the channel, the - reply_code (int) and the reply_text (int) sent by the server describing - why the channel was closed. - - :param method callback: The method to call on callback - - """ - self.callbacks.add(self.channel_number, '_on_channel_close', callback, - False, self) - - def add_on_flow_callback(self, callback): - """Pass a callback function that will be called when Channel.Flow is - called by the remote server. Note that newer versions of RabbitMQ - will not issue this but instead use TCP backpressure - - :param method callback: The method to call on callback - - """ - self._has_on_flow_callback = True - self.callbacks.add(self.channel_number, spec.Channel.Flow, callback, - False) - - def add_on_return_callback(self, callback): - """Pass a callback function that will be called when basic_publish as - sent a message that has been rejected and returned by the server. - - :param method callback: The method to call on callback with the - signature callback(channel, method, properties, - body), where - channel: pika.Channel - method: pika.spec.Basic.Return - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - """ - self.callbacks.add(self.channel_number, '_on_return', callback, False) - - def basic_ack(self, delivery_tag=0, multiple=False): - """Acknowledge one or more messages. When sent by the client, this - method acknowledges one or more messages delivered via the Deliver or - Get-Ok methods. When sent by server, this method acknowledges one or - more messages published with the Publish method on a channel in - confirm mode. The acknowledgement can be for a single message or a - set of messages up to and including a specific message. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - """ - if not self.is_open: - raise exceptions.ChannelClosed() - return self._send_method(spec.Basic.Ack(delivery_tag, multiple)) - - def basic_cancel(self, callback=None, consumer_tag='', nowait=False): - """This method cancels a consumer. This does not affect already - delivered messages, but it does mean the server will not send any more - messages for that consumer. The client may receive an arbitrary number - of messages in between sending the cancel method and receiving the - cancel-ok reply. It may also be sent from the server to the client in - the event of the consumer being unexpectedly cancelled (i.e. cancelled - for any reason other than the server receiving the corresponding - basic.cancel from the client). This allows clients to be notified of - the loss of consumers due to events such as queue deletion. - - :param method callback: Method to call for a Basic.CancelOk response - :param str consumer_tag: Identifier for the consumer - :param bool nowait: Do not expect a Basic.CancelOk response - :raises: ValueError - - """ - self._validate_channel_and_callback(callback) - if consumer_tag not in self.consumer_tags: - return - if callback: - if nowait is True: - raise ValueError('Can not pass a callback if nowait is True') - self.callbacks.add(self.channel_number, spec.Basic.CancelOk, - callback) - self._cancelled.add(consumer_tag) - self._rpc(spec.Basic.Cancel(consumer_tag=consumer_tag, - nowait=nowait), self._on_cancelok, - [(spec.Basic.CancelOk, {'consumer_tag': consumer_tag})] if - nowait is False else []) - - def basic_consume(self, consumer_callback, - queue='', - no_ack=False, - exclusive=False, - consumer_tag=None, - arguments=None): - """Sends the AMQP command Basic.Consume to the broker and binds messages - for the consumer_tag to the consumer callback. If you do not pass in - a consumer_tag, one will be automatically generated for you. Returns - the consumer tag. - - For more information on basic_consume, see: - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.consume - - :param method consumer_callback: The method to callback when consuming - with the signature consumer_callback(channel, method, properties, - body), where - channel: pika.Channel - method: pika.spec.Basic.Deliver - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - - :param queue: The queue to consume from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a response - :param bool exclusive: Don't allow other consumers on the queue - :param consumer_tag: Specify your own consumer tag - :type consumer_tag: str or unicode - :param dict arguments: Custom key/value pair arguments for the consume - :rtype: str - - """ - self._validate_channel_and_callback(consumer_callback) - - # If a consumer tag was not passed, create one - if not consumer_tag: - consumer_tag = self._generate_consumer_tag() - - if consumer_tag in self._consumers or consumer_tag in self._cancelled: - raise exceptions.DuplicateConsumerTag(consumer_tag) - - if no_ack: - self._consumers_with_noack.add(consumer_tag) - - self._consumers[consumer_tag] = consumer_callback - self._pending[consumer_tag] = list() - self._rpc(spec.Basic.Consume(queue=queue, - consumer_tag=consumer_tag, - no_ack=no_ack, - exclusive=exclusive, - arguments=arguments or dict()), - self._on_eventok, [(spec.Basic.ConsumeOk, - {'consumer_tag': consumer_tag})]) - - return consumer_tag - - def _generate_consumer_tag(self): - """Generate a consumer tag - - NOTE: this protected method may be called by derived classes - - :returns: consumer tag - :rtype: str - """ - return 'ctag%i.%s' % (self.channel_number, - uuid.uuid4().hex) - - def basic_get(self, callback=None, queue='', no_ack=False): - """Get a single message from the AMQP broker. If you want to - be notified of Basic.GetEmpty, use the Channel.add_callback method - adding your Basic.GetEmpty callback which should expect only one - parameter, frame. For more information on basic_get and its - parameters, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.get - - :param method callback: The method to callback with a message that has - the signature callback(channel, method, properties, body), where: - channel: pika.Channel - method: pika.spec.Basic.GetOk - properties: pika.spec.BasicProperties - body: str, unicode, or bytes (python 3.x) - :param queue: The queue to get a message from - :type queue: str or unicode - :param bool no_ack: Tell the broker to not expect a reply - - """ - self._validate_channel_and_callback(callback) - self._on_getok_callback = callback - self._send_method(spec.Basic.Get(queue=queue, no_ack=no_ack)) - - def basic_nack(self, delivery_tag=None, multiple=False, requeue=True): - """This method allows a client to reject one or more incoming messages. - It can be used to interrupt and cancel large incoming messages, or - return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool multiple: If set to True, the delivery tag is treated as - "up to and including", so that multiple messages - can be acknowledged with a single method. If set - to False, the delivery tag refers to a single - message. If the multiple field is 1, and the - delivery tag is zero, this indicates - acknowledgement of all outstanding messages. - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - return self._send_method(spec.Basic.Nack(delivery_tag, multiple, - requeue)) - - def basic_publish(self, exchange, routing_key, body, - properties=None, - mandatory=False, - immediate=False): - """Publish to the channel with the given exchange, routing key and body. - For more information on basic_publish and what the parameters do, see: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.publish - - :param exchange: The exchange to publish to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param body: The message body - :type body: str or unicode - :param pika.spec.BasicProperties properties: Basic.properties - :param bool mandatory: The mandatory flag - :param bool immediate: The immediate flag - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - if immediate: - LOGGER.warning('The immediate flag is deprecated in RabbitMQ') - if isinstance(body, unicode_type): - body = body.encode('utf-8') - properties = properties or spec.BasicProperties() - self._send_method(spec.Basic.Publish(exchange=exchange, - routing_key=routing_key, - mandatory=mandatory, - immediate=immediate), - (properties, body)) - - def basic_qos(self, - callback=None, - prefetch_size=0, - prefetch_count=0, - all_channels=False): - """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 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. - - :param method callback: The method to callback for Basic.QosOk response - :param int prefetch_size: This field specifies the prefetch window - size. 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. - :param int prefetch_count: 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. - :param bool all_channels: Should the QoS apply to all channels - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Basic.Qos(prefetch_size, prefetch_count, - all_channels), callback, - [spec.Basic.QosOk]) - - def basic_reject(self, delivery_tag, requeue=True): - """Reject an incoming message. This method allows a client to reject a - message. It can be used to interrupt and cancel large incoming messages, - or return untreatable messages to their original queue. - - :param int delivery-tag: The server-assigned delivery tag - :param bool requeue: If requeue is true, the server will attempt to - requeue the message. If requeue is false or the - requeue attempt fails the messages are discarded or - dead-lettered. - :raises: TypeError - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - if not isinstance(delivery_tag, int): - raise TypeError('delivery_tag must be an integer') - return self._send_method(spec.Basic.Reject(delivery_tag, requeue)) - - def basic_recover(self, callback=None, requeue=False): - """This method asks the server to redeliver all unacknowledged messages - on a specified channel. Zero or more messages may be redelivered. This - method replaces the asynchronous Recover. - - :param method callback: Method to call when receiving Basic.RecoverOk - :param bool requeue: If False, the message will be redelivered to the - original recipient. If True, the server will - attempt to requeue the message, potentially then - delivering it to an alternative subscriber. - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Basic.Recover(requeue), callback, - [spec.Basic.RecoverOk]) - - def close(self, reply_code=0, reply_text="Normal Shutdown"): - """Will invoke a clean shutdown of the channel with the AMQP Broker. - - :param int reply_code: The reply code to close the channel with - :param str reply_text: The reply text to close the channel with - - """ - if not self.is_open: - raise exceptions.ChannelClosed() - LOGGER.info('Channel.close(%s, %s)', reply_code, reply_text) - if self._consumers: - LOGGER.debug('Cancelling %i consumers', len(self._consumers)) - for consumer_tag in dictkeys(self._consumers): - self.basic_cancel(consumer_tag=consumer_tag) - self._set_state(self.CLOSING) - self._rpc(spec.Channel.Close(reply_code, reply_text, 0, 0), - self._on_closeok, [spec.Channel.CloseOk]) - - def confirm_delivery(self, callback=None, nowait=False): - """Turn on Confirm mode in the channel. Pass in a callback to be - notified by the Broker when a message has been confirmed as received or - rejected (Basic.Ack, Basic.Nack) from the broker to the publisher. - - For more information see: - http://www.rabbitmq.com/extensions.html#confirms - - :param method callback: The callback for delivery confirmations - :param bool nowait: Do not send a reply frame (Confirm.SelectOk) - - """ - self._validate_channel_and_callback(callback) - if (self.connection.publisher_confirms is False or - self.connection.basic_nack is False): - raise exceptions.MethodNotImplemented('Not Supported on Server') - - # Add the ack and nack callbacks - if callback is not None: - self.callbacks.add(self.channel_number, spec.Basic.Ack, callback, - False) - self.callbacks.add(self.channel_number, spec.Basic.Nack, callback, - False) - - # Send the RPC command - self._rpc(spec.Confirm.Select(nowait), self._on_selectok, - [spec.Confirm.SelectOk] if nowait is False else []) - - @property - def consumer_tags(self): - """Property method that returns a list of currently active consumers - - :rtype: list - - """ - return dictkeys(self._consumers) - - def exchange_bind(self, - callback=None, - destination=None, - source=None, - routing_key='', - nowait=False, - arguments=None): - """Bind an exchange to another exchange. - - :param method callback: The method to call on Exchange.BindOk - :param destination: The destination exchange to bind - :type destination: str or unicode - :param source: The source exchange to bind to - :type source: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param bool nowait: Do not wait for an Exchange.BindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Bind(0, destination, source, routing_key, - nowait, arguments or dict()), - callback, [spec.Exchange.BindOk] if nowait is False - else []) - - def exchange_declare(self, - callback=None, - exchange=None, - exchange_type='direct', - passive=False, - durable=False, - auto_delete=False, - internal=False, - nowait=False, - arguments=None, - type=None): - """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. - - If passive set, the server will reply with Declare-Ok if the exchange - already exists with the same name, and raise an error if not and if the - exchange does not already exist, the server MUST raise a channel - exception with reply code 404 (not found). - - :param method callback: Call this method on Exchange.DeclareOk - :param exchange: The exchange name consists of a non-empty - :type exchange: str or unicode - sequence of these characters: letters, - digits, hyphen, underscore, period, or - colon. - :param str exchange_type: The exchange type to use - :param bool passive: Perform a declare or just check to see if it exists - :param bool durable: Survive a reboot of RabbitMQ - :param bool auto_delete: Remove when no more queues are bound to it - :param bool internal: Can only be published to by other exchanges - :param bool nowait: Do not expect an Exchange.DeclareOk response - :param dict arguments: Custom key/value pair arguments for the exchange - :param str type: The deprecated exchange type parameter - - """ - self._validate_channel_and_callback(callback) - if type is not None: - warnings.warn('type is deprecated, use exchange_type instead', - DeprecationWarning) - if exchange_type == 'direct' and type != exchange_type: - exchange_type = type - return self._rpc(spec.Exchange.Declare(0, exchange, exchange_type, - passive, durable, auto_delete, - internal, nowait, - arguments or dict()), callback, - [spec.Exchange.DeclareOk] if nowait is False else []) - - def exchange_delete(self, - callback=None, - exchange=None, - if_unused=False, - nowait=False): - """Delete the exchange. - - :param method callback: The method to call on Exchange.DeleteOk - :param exchange: The exchange name - :type exchange: str or unicode - :param bool if_unused: only delete if the exchange is unused - :param bool nowait: Do not wait for an Exchange.DeleteOk - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Delete(0, exchange, if_unused, nowait), - callback, [spec.Exchange.DeleteOk] if nowait is False - else []) - - def exchange_unbind(self, - callback=None, - destination=None, - source=None, - routing_key='', - nowait=False, - arguments=None): - """Unbind an exchange from another exchange. - - :param method callback: The method to call on Exchange.UnbindOk - :param destination: The destination exchange to unbind - :type destination: str or unicode - :param source: The source exchange to unbind from - :type source: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param bool nowait: Do not wait for an Exchange.UnbindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Exchange.Unbind(0, destination, source, - routing_key, nowait, arguments), - callback, [spec.Exchange.UnbindOk] if nowait is False - else []) - - def flow(self, callback, active): - """Turn Channel flow control off and on. Pass a callback to be notified - of the response from the server. active is a bool. Callback should - expect a bool in response indicating channel flow state. For more - information, please reference: - - http://www.rabbitmq.com/amqp-0-9-1-reference.html#channel.flow - - :param method callback: The callback method - :param bool active: Turn flow on or off - - """ - self._validate_channel_and_callback(callback) - self._on_flowok_callback = callback - self._rpc(spec.Channel.Flow(active), self._on_flowok, - [spec.Channel.FlowOk]) - - @property - def is_closed(self): - """Returns True if the channel is closed. - - :rtype: bool - - """ - return self._state == self.CLOSED - - @property - def is_closing(self): - """Returns True if the channel is closing. - - :rtype: bool - - """ - return self._state == self.CLOSING - - @property - def is_open(self): - """Returns True if the channel is open. - - :rtype: bool - - """ - return self._state == self.OPEN - - def open(self): - """Open the channel""" - self._set_state(self.OPENING) - self._add_callbacks() - self._rpc(spec.Channel.Open(), self._on_openok, [spec.Channel.OpenOk]) - - def queue_bind(self, callback, queue, exchange, - routing_key=None, - nowait=False, - arguments=None): - """Bind the queue to the specified exchange - - :param method callback: The method to call on Queue.BindOk - :param queue: The queue to bind to the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind to - :type exchange: str or unicode - :param routing_key: The routing key to bind on - :type routing_key: str or unicode - :param bool nowait: Do not wait for a Queue.BindOk - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - replies = [spec.Queue.BindOk] if nowait is False else [] - if routing_key is None: - routing_key = queue - return self._rpc(spec.Queue.Bind(0, queue, exchange, routing_key, - nowait, arguments or dict()), callback, - replies) - - def queue_declare(self, callback, - queue='', - passive=False, - durable=False, - exclusive=False, - auto_delete=False, - 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. - - Leave the queue name empty for a auto-named queue in RabbitMQ - - :param method callback: The method to call on Queue.DeclareOk - :param queue: The queue name - :type queue: str or unicode - :param bool passive: Only check to see if the queue exists - :param bool durable: Survive reboots of the broker - :param bool exclusive: Only allow access by the current connection - :param bool auto_delete: Delete after consumer cancels or disconnects - :param bool nowait: Do not wait for a Queue.DeclareOk - :param dict arguments: Custom key/value arguments for the queue - - """ - if queue: - condition = (spec.Queue.DeclareOk, - {'queue': queue}) - else: - condition = spec.Queue.DeclareOk - replies = [condition] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Declare(0, queue, passive, durable, - exclusive, auto_delete, nowait, - arguments or dict()), callback, - replies) - - def queue_delete(self, - callback=None, - queue='', - if_unused=False, - if_empty=False, - nowait=False): - """Delete a queue from the broker. - - :param method callback: The method to call on Queue.DeleteOk - :param queue: The queue to delete - :type queue: str or unicode - :param bool if_unused: only delete if it's unused - :param bool if_empty: only delete if the queue is empty - :param bool nowait: Do not wait for a Queue.DeleteOk - - """ - replies = [spec.Queue.DeleteOk] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Delete(0, queue, if_unused, if_empty, - nowait), callback, replies) - - def queue_purge(self, callback=None, queue='', nowait=False): - """Purge all of the messages from the specified queue - - :param method callback: The method to call on Queue.PurgeOk - :param queue: The queue to purge - :type queue: str or unicode - :param bool nowait: Do not expect a Queue.PurgeOk response - - """ - replies = [spec.Queue.PurgeOk] if nowait is False else [] - self._validate_channel_and_callback(callback) - return self._rpc(spec.Queue.Purge(0, queue, nowait), callback, replies) - - def queue_unbind(self, - callback=None, - queue='', - exchange=None, - routing_key=None, - arguments=None): - """Unbind a queue from an exchange. - - :param method callback: The method to call on Queue.UnbindOk - :param queue: The queue to unbind from the exchange - :type queue: str or unicode - :param exchange: The source exchange to bind from - :type exchange: str or unicode - :param routing_key: The routing key to unbind - :type routing_key: str or unicode - :param dict arguments: Custom key/value pair arguments for the binding - - """ - self._validate_channel_and_callback(callback) - if routing_key is None: - routing_key = queue - return self._rpc(spec.Queue.Unbind(0, queue, exchange, routing_key, - arguments or dict()), callback, - [spec.Queue.UnbindOk]) - - def tx_commit(self, callback=None): - """Commit a transaction - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Commit(), callback, [spec.Tx.CommitOk]) - - def tx_rollback(self, callback=None): - """Rollback a transaction. - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Rollback(), callback, [spec.Tx.RollbackOk]) - - def tx_select(self, callback=None): - """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. - - :param method callback: The callback for delivery confirmations - - """ - self._validate_channel_and_callback(callback) - return self._rpc(spec.Tx.Select(), callback, [spec.Tx.SelectOk]) - - # Internal methods - - def _add_callbacks(self): - """Callbacks that add the required behavior for a channel when - connecting and connected to a server. - - """ - # Add a callback for Basic.GetEmpty - self.callbacks.add(self.channel_number, spec.Basic.GetEmpty, - self._on_getempty, False) - - # Add a callback for Basic.Cancel - self.callbacks.add(self.channel_number, spec.Basic.Cancel, - self._on_cancel, False) - - # Deprecated in newer versions of RabbitMQ but still register for it - self.callbacks.add(self.channel_number, spec.Channel.Flow, - self._on_flow, False) - - # Add a callback for when the server closes our channel - self.callbacks.add(self.channel_number, spec.Channel.Close, - self._on_close, True) - - def _add_on_cleanup_callback(self, callback): - """For internal use only (e.g., Connection needs to remove closed - channels from its channel container). Pass a callback function that will - be called when the channel is being cleaned up after all channel-close - callbacks callbacks. - - :param method callback: The method to call on callback with the - signature: callback(channel) - - """ - self.callbacks.add(self.channel_number, self._ON_CHANNEL_CLEANUP_CB_KEY, - callback, one_shot=True, only_caller=self) - - def _add_pending_msg(self, consumer_tag, method_frame, header_frame, body): - """Add the received message to the pending message stack. - - :param str consumer_tag: The consumer tag for the message - :param pika.frame.Method method_frame: The received method frame - :param pika.frame.Header header_frame: The received header frame - :param body: The message body - :type body: str or unicode - - """ - self._pending[consumer_tag].append((self, method_frame.method, - header_frame.properties, body)) - - def _cleanup(self): - """Remove all consumers and any callbacks for the channel.""" - self.callbacks.process(self.channel_number, - self._ON_CHANNEL_CLEANUP_CB_KEY, self, - self) - self._consumers = dict() - self.callbacks.cleanup(str(self.channel_number)) - self._cookie = None - - def _cleanup_consumer_ref(self, consumer_tag): - """Remove any references to the consumer tag in internal structures - for consumer state. - - :param str consumer_tag: The consumer tag to cleanup - - """ - if consumer_tag in self._consumers_with_noack: - self._consumers_with_noack.remove(consumer_tag) - if consumer_tag in self._consumers: - del self._consumers[consumer_tag] - if consumer_tag in self._pending: - del self._pending[consumer_tag] - self._cancelled.discard(consumer_tag) - - def _get_cookie(self): - """Used by the wrapper implementation (e.g., `BlockingChannel`) to - retrieve the cookie that it set via `_set_cookie` - - :returns: opaque cookie value that was set via `_set_cookie` - """ - return self._cookie - - def _get_pending_msg(self, consumer_tag): - """Get a pending message for the consumer tag from the stack. - - :param str consumer_tag: The consumer tag to get a message from - :rtype: tuple(pika.frame.Header, pika.frame.Method, str|unicode) - - """ - return self._pending[consumer_tag].pop(0) - - def _handle_content_frame(self, frame_value): - """This is invoked by the connection when frames that are not registered - with the CallbackManager have been found. This should only be the case - when the frames are related to content delivery. - - The frame_dispatcher will be invoked which will return the fully formed - message in three parts when all of the body frames have been received. - - :param pika.amqp_object.Frame frame_value: The frame to deliver - - """ - try: - response = self.frame_dispatcher.process(frame_value) - except exceptions.UnexpectedFrameError: - return self._unexpected_frame(frame_value) - - if response: - if isinstance(response[0].method, spec.Basic.Deliver): - self._on_deliver(*response) - elif isinstance(response[0].method, spec.Basic.GetOk): - self._on_getok(*response) - elif isinstance(response[0].method, spec.Basic.Return): - self._on_return(*response) - - def _has_content(self, method_frame): - """Return a bool if it's a content method as defined by the spec - - :param pika.amqp_object.Method method_frame: The method frame received - - """ - return spec.has_content(method_frame.INDEX) - - def _on_cancel(self, method_frame): - """When the broker cancels a consumer, delete it from our internal - dictionary. - - :param pika.frame.Method method_frame: The method frame received - - """ - if method_frame.method.consumer_tag in self._cancelled: - # User-initiated cancel is waiting for Cancel-ok - return - - self._cleanup_consumer_ref(method_frame.method.consumer_tag) - - def _on_cancelok(self, method_frame): - """Called in response to a frame from the Broker when the - client sends Basic.Cancel - - :param pika.frame.Method method_frame: The method frame received - - """ - self._cleanup_consumer_ref(method_frame.method.consumer_tag) - - def _on_close(self, method_frame): - """Handle the case where our channel has been closed for us - - :param pika.frame.Method method_frame: The close frame - - """ - LOGGER.info('%s', method_frame) - LOGGER.warning('Received remote Channel.Close (%s): %s', - method_frame.method.reply_code, - method_frame.method.reply_text) - if self.connection.is_open: - self._send_method(spec.Channel.CloseOk()) - self._set_state(self.CLOSED) - self.callbacks.process(self.channel_number, '_on_channel_close', self, - self, method_frame.method.reply_code, - method_frame.method.reply_text) - self._cleanup() - - def _on_closeok(self, method_frame): - """Invoked when RabbitMQ replies to a Channel.Close method - - :param pika.frame.Method method_frame: The CloseOk frame - - """ - self._set_state(self.CLOSED) - self.callbacks.process(self.channel_number, '_on_channel_close', self, - self, 0, '') - self._cleanup() - - def _on_deliver(self, method_frame, header_frame, body): - """Cope with reentrancy. If a particular consumer is still active when - another delivery appears for it, queue the deliveries up until it - finally exits. - - :param pika.frame.Method method_frame: The method frame received - :param pika.frame.Header header_frame: The header frame received - :param body: The body received - :type body: str or unicode - - """ - consumer_tag = method_frame.method.consumer_tag - if consumer_tag in self._cancelled: - if self.is_open and consumer_tag not in self._consumers_with_noack: - self.basic_reject(method_frame.method.delivery_tag) - return - if consumer_tag not in self._consumers: - return self._add_pending_msg(consumer_tag, method_frame, - header_frame, body) - while self._pending[consumer_tag]: - self._consumers[consumer_tag](*self._get_pending_msg(consumer_tag)) - self._consumers[consumer_tag](self, method_frame.method, - header_frame.properties, body) - - def _on_eventok(self, method_frame): - """Generic events that returned ok that may have internal callbacks. - We keep a list of what we've yet to implement so that we don't silently - drain events that we don't support. - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug('Discarding frame %r', method_frame) - - def _on_flow(self, method_frame_unused): - """Called if the server sends a Channel.Flow frame. - - :param pika.frame.Method method_frame_unused: The Channel.Flow frame - - """ - if self._has_on_flow_callback is False: - LOGGER.warning('Channel.Flow received from server') - - def _on_flowok(self, method_frame): - """Called in response to us asking the server to toggle on Channel.Flow - - :param pika.frame.Method method_frame: The method frame received - - """ - self.flow_active = method_frame.method.active - if self._on_flowok_callback: - self._on_flowok_callback(method_frame.method.active) - self._on_flowok_callback = None - else: - LOGGER.warning('Channel.FlowOk received with no active callbacks') - - def _on_getempty(self, method_frame): - """When we receive an empty reply do nothing but log it - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug('Received Basic.GetEmpty: %r', method_frame) - - def _on_getok(self, method_frame, header_frame, body): - """Called in reply to a Basic.Get when there is a message. - - :param pika.frame.Method method_frame: The method frame received - :param pika.frame.Header header_frame: The header frame received - :param body: The body received - :type body: str or unicode - - """ - if self._on_getok_callback is not None: - callback = self._on_getok_callback - self._on_getok_callback = None - callback(self, method_frame.method, header_frame.properties, body) - else: - LOGGER.error('Basic.GetOk received with no active callback') - - def _on_openok(self, frame_unused): - """Called by our callback handler when we receive a Channel.OpenOk and - subsequently calls our _on_openok_callback which was passed into the - Channel constructor. The reason we do this is because we want to make - sure that the on_open_callback parameter passed into the Channel - constructor is not the first callback we make. - - :param pika.frame.Method frame_unused: Unused Channel.OpenOk frame - - """ - self._set_state(self.OPEN) - if self._on_openok_callback is not None: - self._on_openok_callback(self) - - def _on_return(self, method_frame, header_frame, body): - """Called if the server sends a Basic.Return frame. - - :param pika.frame.Method method_frame: The Basic.Return frame - :param pika.frame.Header header_frame: The content header frame - :param body: The message body - :type body: str or unicode - - """ - if not self.callbacks.process(self.channel_number, '_on_return', self, - self, - method_frame.method, - header_frame.properties, - body): - LOGGER.warning('Basic.Return received from server (%r, %r)', - method_frame.method, header_frame.properties) - - def _on_selectok(self, method_frame): - """Called when the broker sends a Confirm.SelectOk frame - - :param pika.frame.Method method_frame: The method frame received - - """ - LOGGER.debug("Confirm.SelectOk Received: %r", method_frame) - - def _on_synchronous_complete(self, method_frame_unused): - """This is called when a synchronous command is completed. It will undo - the blocking state and send all the frames that stacked up while we - were in the blocking state. - - :param pika.frame.Method method_frame_unused: The method frame received - - """ - LOGGER.debug('%i blocked frames', len(self._blocked)) - self._blocking = None - while len(self._blocked) > 0 and self._blocking is None: - self._rpc(*self._blocked.popleft()) - - def _rpc(self, method_frame, callback=None, acceptable_replies=None): - """Shortcut wrapper to the Connection's rpc command using its callback - stack, passing in our channel number. - - :param pika.amqp_object.Method method_frame: The method frame to call - :param method callback: The callback for the RPC response - :param list acceptable_replies: The replies this RPC call expects - - """ - # Make sure the channel is open - if self.is_closed: - raise exceptions.ChannelClosed - - # If the channel is blocking, add subsequent commands to our stack - if self._blocking: - return self._blocked.append([method_frame, callback, - acceptable_replies]) - - # Validate we got None or a list of acceptable_replies - if acceptable_replies and not isinstance(acceptable_replies, list): - raise TypeError("acceptable_replies should be list or None") - - # Validate the callback is callable - if callback and not is_callable(callback): - raise TypeError("callback should be None, a function or method.") - - # Block until a response frame is received for synchronous frames - if method_frame.synchronous: - self._blocking = method_frame.NAME - - # If acceptable replies are set, add callbacks - if acceptable_replies: - for reply in acceptable_replies or list(): - if isinstance(reply, tuple): - reply, arguments = reply - else: - arguments = None - LOGGER.debug('Adding in on_synchronous_complete callback') - self.callbacks.add(self.channel_number, reply, - self._on_synchronous_complete, - arguments=arguments) - if callback: - LOGGER.debug('Adding passed in callback') - self.callbacks.add(self.channel_number, reply, callback, - arguments=arguments) - - self._send_method(method_frame) - - def _send_method(self, method_frame, content=None): - """Shortcut wrapper to send a method through our connection, passing in - the channel number - - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - self.connection._send_method(self.channel_number, method_frame, content) - - def _set_cookie(self, cookie): - """Used by wrapper layer (e.g., `BlockingConnection`) to link the - channel implementation back to the proxy. See `_get_cookie`. - - :param cookie: an opaque value; typically a proxy channel implementation - instance (e.g., `BlockingChannel` instance) - """ - self._cookie = cookie - - def _set_state(self, connection_state): - """Set the channel connection state to the specified state value. - - :param int connection_state: The connection_state value - - """ - self._state = connection_state - - def _unexpected_frame(self, frame_value): - """Invoked when a frame is received that is not setup to be processed. - - :param pika.frame.Frame frame_value: The frame received - - """ - LOGGER.warning('Unexpected frame: %r', frame_value) - - def _validate_channel_and_callback(self, callback): - if not self.is_open: - raise exceptions.ChannelClosed() - if callback is not None and not is_callable(callback): - raise ValueError('callback must be a function or method') - - -class ContentFrameDispatcher(object): - """Handle content related frames, building a message and return the message - back in three parts upon receipt. - - """ - - def __init__(self): - """Create a new instance of the Dispatcher passing in the callback - manager. - - """ - self._method_frame = None - self._header_frame = None - self._seen_so_far = 0 - self._body_fragments = list() - - def process(self, frame_value): - """Invoked by the Channel object when passed frames that are not - setup in the rpc process and that don't have explicit reply types - defined. This includes Basic.Publish, Basic.GetOk and Basic.Return - - :param Method|Header|Body frame_value: The frame to process - - """ - if (isinstance(frame_value, frame.Method) and - spec.has_content(frame_value.method.INDEX)): - self._method_frame = frame_value - elif isinstance(frame_value, frame.Header): - self._header_frame = frame_value - if frame_value.body_size == 0: - return self._finish() - elif isinstance(frame_value, frame.Body): - return self._handle_body_frame(frame_value) - else: - raise exceptions.UnexpectedFrameError(frame_value) - - def _finish(self): - """Invoked when all of the message has been received - - :rtype: tuple(pika.frame.Method, pika.frame.Header, str) - - """ - content = (self._method_frame, self._header_frame, - b''.join(self._body_fragments)) - self._reset() - return content - - def _handle_body_frame(self, body_frame): - """Receive body frames and append them to the stack. When the body size - matches, call the finish method. - - :param Body body_frame: The body frame - :raises: pika.exceptions.BodyTooLongError - :rtype: tuple(pika.frame.Method, pika.frame.Header, str)|None - - """ - self._seen_so_far += len(body_frame.fragment) - self._body_fragments.append(body_frame.fragment) - if self._seen_so_far == self._header_frame.body_size: - return self._finish() - elif self._seen_so_far > self._header_frame.body_size: - raise exceptions.BodyTooLongError(self._seen_so_far, - self._header_frame.body_size) - return None - - def _reset(self): - """Reset the values for processing frames""" - self._method_frame = None - self._header_frame = None - self._seen_so_far = 0 - self._body_fragments = list() diff --git a/packages_o/pika-0.10.0/pika/compat.py b/packages_o/pika-0.10.0/pika/compat.py deleted file mode 100644 index 73f3879f..00000000 --- a/packages_o/pika-0.10.0/pika/compat.py +++ /dev/null @@ -1,105 +0,0 @@ -import sys as _sys - - -PY2 = _sys.version_info < (3,) -PY3 = not PY2 - - -if not PY2: - # these were moved around for Python 3 - from urllib.parse import unquote as url_unquote, urlencode - - # Python 3 does not have basestring anymore; we include - # *only* the str here as this is used for textual data. - basestring = (str,) - - # for assertions that the data is either encoded or non-encoded text - str_or_bytes = (str, bytes) - - # xrange is gone, replace it with range - xrange = range - - # the unicode type is str - unicode_type = str - - - def dictkeys(dct): - """ - Returns a list of keys of dictionary - - dict.keys returns a view that works like .keys in Python 2 - *except* any modifications in the dictionary will be visible - (and will cause errors if the view is being iterated over while - it is modified). - """ - - return list(dct.keys()) - - def dictvalues(dct): - """ - Returns a list of values of a dictionary - - dict.values returns a view that works like .values in Python 2 - *except* any modifications in the dictionary will be visible - (and will cause errors if the view is being iterated over while - it is modified). - """ - return list(dct.values()) - - def byte(*args): - """ - This is the same as Python 2 `chr(n)` for bytes in Python 3 - - Returns a single byte `bytes` for the given int argument (we - optimize it a bit here by passing the positional argument tuple - directly to the bytes constructor. - """ - return bytes(args) - - class long(int): - """ - A marker class that signifies that the integer value should be - serialized as `l` instead of `I` - """ - - def __repr__(self): - return str(self) + 'L' - - def canonical_str(value): - """ - Return the canonical str value for the string. - In both Python 3 and Python 2 this is str. - """ - - return str(value) - -else: - from urllib import unquote as url_unquote, urlencode - - basestring = basestring - str_or_bytes = basestring - xrange = xrange - unicode_type = unicode - dictkeys = dict.keys - dictvalues = dict.values - byte = chr - long = long - - def canonical_str(value): - """ - Returns the canonical string value of the given string. - In Python 2 this is the value unchanged if it is an str, otherwise - it is the unicode value encoded as UTF-8. - """ - - try: - return str(value) - except UnicodeEncodeError: - return str(value.encode('utf-8')) - - -def as_bytes(value): - if not isinstance(value, bytes): - return value.encode('UTF-8') - return value - diff --git a/packages_o/pika-0.10.0/pika/connection.py b/packages_o/pika-0.10.0/pika/connection.py deleted file mode 100644 index d15581f8..00000000 --- a/packages_o/pika-0.10.0/pika/connection.py +++ /dev/null @@ -1,1634 +0,0 @@ -"""Core connection objects""" -import ast -import sys -import collections -import logging -import math -import platform -import threading -import urllib -import warnings - -if sys.version_info > (3,): - import urllib.parse as urlparse -else: - import urlparse - -from pika import __version__ -from pika import callback -from pika import channel -from pika import credentials as pika_credentials -from pika import exceptions -from pika import frame -from pika import heartbeat -from pika import utils - -from pika import spec - -from pika.compat import basestring, url_unquote, dictkeys - - -BACKPRESSURE_WARNING = ("Pika: Write buffer exceeded warning threshold at " - "%i bytes and an estimated %i frames behind") -PRODUCT = "Pika Python Client Library" - -LOGGER = logging.getLogger(__name__) - - -class Parameters(object): - """Base connection parameters class definition - - :param str DEFAULT_HOST: 'localhost' - :param int DEFAULT_PORT: 5672 - :param str DEFAULT_VIRTUAL_HOST: '/' - :param str DEFAULT_USERNAME: 'guest' - :param str DEFAULT_PASSWORD: 'guest' - :param int DEFAULT_HEARTBEAT_INTERVAL: None - :param int DEFAULT_CHANNEL_MAX: 0 - :param int DEFAULT_FRAME_MAX: pika.spec.FRAME_MAX_SIZE - :param str DEFAULT_LOCALE: 'en_US' - :param int DEFAULT_CONNECTION_ATTEMPTS: 1 - :param int|float DEFAULT_RETRY_DELAY: 2.0 - :param int|float DEFAULT_SOCKET_TIMEOUT: 0.25 - :param bool DEFAULT_SSL: False - :param dict DEFAULT_SSL_OPTIONS: {} - :param int DEFAULT_SSL_PORT: 5671 - :param bool DEFAULT_BACKPRESSURE_DETECTION: False - - """ - DEFAULT_BACKPRESSURE_DETECTION = False - DEFAULT_CONNECTION_ATTEMPTS = 1 - DEFAULT_CHANNEL_MAX = 0 - DEFAULT_FRAME_MAX = spec.FRAME_MAX_SIZE - DEFAULT_HEARTBEAT_INTERVAL = None # accept server's proposal - DEFAULT_HOST = 'localhost' - DEFAULT_LOCALE = 'en_US' - DEFAULT_PASSWORD = 'guest' - DEFAULT_PORT = 5672 - DEFAULT_RETRY_DELAY = 2.0 - DEFAULT_SOCKET_TIMEOUT = 0.25 - DEFAULT_SSL = False - DEFAULT_SSL_OPTIONS = {} - DEFAULT_SSL_PORT = 5671 - DEFAULT_USERNAME = 'guest' - DEFAULT_VIRTUAL_HOST = '/' - - def __init__(self): - self.virtual_host = self.DEFAULT_VIRTUAL_HOST - self.backpressure_detection = self.DEFAULT_BACKPRESSURE_DETECTION - self.channel_max = self.DEFAULT_CHANNEL_MAX - self.connection_attempts = self.DEFAULT_CONNECTION_ATTEMPTS - self.credentials = self._credentials(self.DEFAULT_USERNAME, - self.DEFAULT_PASSWORD) - self.frame_max = self.DEFAULT_FRAME_MAX - self.heartbeat = self.DEFAULT_HEARTBEAT_INTERVAL - self.host = self.DEFAULT_HOST - self.locale = self.DEFAULT_LOCALE - self.port = self.DEFAULT_PORT - self.retry_delay = self.DEFAULT_RETRY_DELAY - self.ssl = self.DEFAULT_SSL - self.ssl_options = self.DEFAULT_SSL_OPTIONS - self.socket_timeout = self.DEFAULT_SOCKET_TIMEOUT - - def __repr__(self): - """Represent the info about the instance. - - :rtype: str - - """ - return ('<%s host=%s port=%s virtual_host=%s ssl=%s>' % - (self.__class__.__name__, self.host, self.port, - self.virtual_host, self.ssl)) - - def _credentials(self, username, password): - """Return a plain credentials object for the specified username and - password. - - :param str username: The username to use - :param str password: The password to use - :rtype: pika_credentials.PlainCredentials - - """ - return pika_credentials.PlainCredentials(username, password) - - def _validate_backpressure(self, backpressure_detection): - """Validate that the backpressure detection option is a bool. - - :param bool backpressure_detection: The backpressure detection value - :rtype: bool - :raises: TypeError - - """ - if not isinstance(backpressure_detection, bool): - raise TypeError('backpressure detection must be a bool') - return True - - def _validate_channel_max(self, channel_max): - """Validate that the channel_max value is an int - - :param int channel_max: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(channel_max, int): - raise TypeError('channel_max must be an int') - if channel_max < 1 or channel_max > 65535: - raise ValueError('channel_max must be <= 65535 and > 0') - return True - - def _validate_connection_attempts(self, connection_attempts): - """Validate that the connection_attempts value is an int - - :param int connection_attempts: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(connection_attempts, int): - raise TypeError('connection_attempts must be an int') - if connection_attempts < 1: - raise ValueError('connection_attempts must be None or > 0') - return True - - def _validate_credentials(self, credentials): - """Validate the credentials passed in are using a valid object type. - - :param pika.credentials.Credentials credentials: Credentials to validate - :rtype: bool - :raises: TypeError - - """ - for credential_type in pika_credentials.VALID_TYPES: - if isinstance(credentials, credential_type): - return True - raise TypeError('Credentials must be an object of type: %r' % - pika_credentials.VALID_TYPES) - - def _validate_frame_max(self, frame_max): - """Validate that the frame_max value is an int and does not exceed - the maximum frame size and is not less than the frame min size. - - :param int frame_max: The value to validate - :rtype: bool - :raises: TypeError - :raises: InvalidMinimumFrameSize - - """ - if not isinstance(frame_max, int): - raise TypeError('frame_max must be an int') - if frame_max < spec.FRAME_MIN_SIZE: - raise exceptions.InvalidMinimumFrameSize - elif frame_max > spec.FRAME_MAX_SIZE: - raise exceptions.InvalidMaximumFrameSize - return True - - def _validate_heartbeat_interval(self, heartbeat_interval): - """Validate that the heartbeat_interval value is an int - - :param int heartbeat_interval: The value to validate - :rtype: bool - :raises: TypeError - :raises: ValueError - - """ - if not isinstance(heartbeat_interval, int): - raise TypeError('heartbeat must be an int') - if heartbeat_interval < 0: - raise ValueError('heartbeat_interval must >= 0') - return True - - def _validate_host(self, host): - """Validate that the host value is an str - - :param str|unicode host: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(host, basestring): - raise TypeError('host must be a str or unicode str') - return True - - def _validate_locale(self, locale): - """Validate that the locale value is an str - - :param str locale: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(locale, basestring): - raise TypeError('locale must be a str') - return True - - def _validate_port(self, port): - """Validate that the port value is an int - - :param int port: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(port, int): - raise TypeError('port must be an int') - return True - - def _validate_retry_delay(self, retry_delay): - """Validate that the retry_delay value is an int or float - - :param int|float retry_delay: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not any([isinstance(retry_delay, int), - isinstance(retry_delay, float)]): - raise TypeError('retry_delay must be a float or int') - return True - - def _validate_socket_timeout(self, socket_timeout): - """Validate that the socket_timeout value is an int or float - - :param int|float socket_timeout: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not any([isinstance(socket_timeout, int), - isinstance(socket_timeout, float)]): - raise TypeError('socket_timeout must be a float or int') - if not socket_timeout > 0: - raise ValueError('socket_timeout must be > 0') - return True - - def _validate_ssl(self, ssl): - """Validate the SSL toggle is a bool - - :param bool ssl: The SSL enabled/disabled value - :rtype: bool - :raises: TypeError - - """ - if not isinstance(ssl, bool): - raise TypeError('ssl must be a bool') - return True - - def _validate_ssl_options(self, ssl_options): - """Validate the SSL options value is a dictionary. - - :param dict|None ssl_options: SSL Options to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(ssl_options, dict) and ssl_options is not None: - raise TypeError('ssl_options must be either None or dict') - return True - - def _validate_virtual_host(self, virtual_host): - """Validate that the virtual_host value is an str - - :param str virtual_host: The value to validate - :rtype: bool - :raises: TypeError - - """ - if not isinstance(virtual_host, basestring): - raise TypeError('virtual_host must be a str') - return True - - -class ConnectionParameters(Parameters): - """Connection parameters object that is passed into the connection adapter - upon construction. - - :param str host: Hostname or IP Address to connect to - :param int port: TCP port to connect to - :param str virtual_host: RabbitMQ virtual host to use - :param pika.credentials.Credentials credentials: auth credentials - :param int channel_max: Maximum number of channels to allow - :param int frame_max: The maximum byte size for an AMQP frame - :param int heartbeat_interval: How often to send heartbeats - :param bool ssl: Enable SSL - :param dict ssl_options: Arguments passed to ssl.wrap_socket as - :param int connection_attempts: Maximum number of retry attempts - :param int|float retry_delay: Time to wait in seconds, before the next - :param int|float socket_timeout: Use for high latency networks - :param str locale: Set the locale value - :param bool backpressure_detection: Toggle backpressure detection - - """ - - def __init__(self, - host=None, - port=None, - virtual_host=None, - credentials=None, - channel_max=None, - frame_max=None, - heartbeat_interval=None, - ssl=None, - ssl_options=None, - connection_attempts=None, - retry_delay=None, - socket_timeout=None, - locale=None, - backpressure_detection=None): - """Create a new ConnectionParameters instance. - - :param str host: Hostname or IP Address to connect to - :param int port: TCP port to connect to - :param str virtual_host: RabbitMQ virtual host to use - :param pika.credentials.Credentials credentials: auth credentials - :param int channel_max: Maximum number of channels to allow - :param int frame_max: The maximum byte size for an AMQP frame - :param int heartbeat_interval: How often to send heartbeats. - Min between this value and server's proposal - will be used. Use 0 to deactivate heartbeats - and None to accept server's proposal. - :param bool ssl: Enable SSL - :param dict ssl_options: Arguments passed to ssl.wrap_socket - :param int connection_attempts: Maximum number of retry attempts - :param int|float retry_delay: Time to wait in seconds, before the next - :param int|float socket_timeout: Use for high latency networks - :param str locale: Set the locale value - :param bool backpressure_detection: Toggle backpressure detection - - """ - super(ConnectionParameters, self).__init__() - - # Create the default credentials object - if not credentials: - credentials = self._credentials(self.DEFAULT_USERNAME, - self.DEFAULT_PASSWORD) - - # Assign the values - if host and self._validate_host(host): - self.host = host - if port is not None and self._validate_port(port): - self.port = port - if virtual_host and self._validate_virtual_host(virtual_host): - self.virtual_host = virtual_host - if credentials and self._validate_credentials(credentials): - self.credentials = credentials - if channel_max is not None and self._validate_channel_max(channel_max): - self.channel_max = channel_max - if frame_max is not None and self._validate_frame_max(frame_max): - self.frame_max = frame_max - if locale and self._validate_locale(locale): - self.locale = locale - if (heartbeat_interval is not None and - self._validate_heartbeat_interval(heartbeat_interval)): - self.heartbeat = heartbeat_interval - if ssl is not None and self._validate_ssl(ssl): - self.ssl = ssl - if ssl_options and self._validate_ssl_options(ssl_options): - self.ssl_options = ssl_options or dict() - if (connection_attempts is not None and - self._validate_connection_attempts(connection_attempts)): - self.connection_attempts = connection_attempts - if retry_delay is not None and self._validate_retry_delay(retry_delay): - self.retry_delay = retry_delay - if (socket_timeout is not None and - self._validate_socket_timeout(socket_timeout)): - self.socket_timeout = socket_timeout - if (backpressure_detection is not None and - self._validate_backpressure(backpressure_detection)): - self.backpressure_detection = backpressure_detection - - -class URLParameters(Parameters): - """Connect to RabbitMQ via an AMQP URL in the format:: - - amqp://username:password@host:port/[?query-string] - - Ensure that the virtual host is URI encoded when specified. For example if - you are using the default "/" virtual host, the value should be `%2f`. - - Valid query string values are: - - - backpressure_detection: - Toggle backpressure detection, possible values are `t` or `f` - - channel_max: - Override the default maximum channel count value - - connection_attempts: - Specify how many times pika should try and reconnect before it gives up - - frame_max: - Override the default maximum frame size for communication - - heartbeat_interval: - Specify the number of seconds between heartbeat frames to ensure that - the link between RabbitMQ and your application is up - - locale: - Override the default `en_US` locale value - - ssl: - Toggle SSL, possible values are `t`, `f` - - ssl_options: - Arguments passed to :meth:`ssl.wrap_socket` - - retry_delay: - The number of seconds to sleep before attempting to connect on - connection failure. - - socket_timeout: - Override low level socket timeout value - - :param str url: The AMQP URL to connect to - - """ - - def __init__(self, url): - """Create a new URLParameters instance. - - :param str url: The URL value - - """ - super(URLParameters, self).__init__() - self._process_url(url) - - def _process_url(self, url): - """Take an AMQP URL and break it up into the various parameters. - - :param str url: The URL to parse - - """ - if url[0:4] == 'amqp': - url = 'http' + url[4:] - - parts = urlparse.urlparse(url) - - # Handle the Protocol scheme, changing to HTTPS so urlparse doesnt barf - if parts.scheme == 'https': - self.ssl = True - - if self._validate_host(parts.hostname): - self.host = parts.hostname - if not parts.port: - if self.ssl: - self.port = self.DEFAULT_SSL_PORT if \ - self.ssl else self.DEFAULT_PORT - elif self._validate_port(parts.port): - self.port = parts.port - - if parts.username is not None: - self.credentials = pika_credentials.PlainCredentials(parts.username, - parts.password) - - # Get the Virtual Host - if len(parts.path) <= 1: - self.virtual_host = self.DEFAULT_VIRTUAL_HOST - else: - path_parts = parts.path.split('/') - virtual_host = url_unquote(path_parts[1]) - if self._validate_virtual_host(virtual_host): - self.virtual_host = virtual_host - - # Handle query string values, validating and assigning them - values = urlparse.parse_qs(parts.query) - - # Cast the various numeric values to the appropriate values - for key in dictkeys(values): - # Always reassign the first list item in query values - values[key] = values[key].pop(0) - if values[key].isdigit(): - values[key] = int(values[key]) - else: - try: - values[key] = float(values[key]) - except ValueError: - pass - - if 'backpressure_detection' in values: - if values['backpressure_detection'] == 't': - self.backpressure_detection = True - elif values['backpressure_detection'] == 'f': - self.backpressure_detection = False - else: - raise ValueError('Invalid backpressure_detection value: %s' % - values['backpressure_detection']) - - if ('channel_max' in values and - self._validate_channel_max(values['channel_max'])): - self.channel_max = values['channel_max'] - - if ('connection_attempts' in values and - self._validate_connection_attempts(values['connection_attempts'])): - self.connection_attempts = values['connection_attempts'] - - if ('frame_max' in values and - self._validate_frame_max(values['frame_max'])): - self.frame_max = values['frame_max'] - - if ('heartbeat_interval' in values and - self._validate_heartbeat_interval(values['heartbeat_interval'])): - self.heartbeat = values['heartbeat_interval'] - - if ('locale' in values and self._validate_locale(values['locale'])): - self.locale = values['locale'] - - if ('retry_delay' in values and - self._validate_retry_delay(values['retry_delay'])): - self.retry_delay = values['retry_delay'] - - if ('socket_timeout' in values and - self._validate_socket_timeout(values['socket_timeout'])): - self.socket_timeout = values['socket_timeout'] - - if 'ssl_options' in values: - options = ast.literal_eval(values['ssl_options']) - if self._validate_ssl_options(options): - self.ssl_options = options - - -class Connection(object): - """This is the core class that implements communication with RabbitMQ. This - class should not be invoked directly but rather through the use of an - adapter such as SelectConnection or BlockingConnection. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Called when the connection is opened - :param method on_open_error_callback: Called if the connection cant - be opened - :param method on_close_callback: Called when the connection is closed - - """ - ON_CONNECTION_BACKPRESSURE = '_on_connection_backpressure' - ON_CONNECTION_BLOCKED = '_on_connection_blocked' - ON_CONNECTION_CLOSED = '_on_connection_closed' - ON_CONNECTION_ERROR = '_on_connection_error' - ON_CONNECTION_OPEN = '_on_connection_open' - ON_CONNECTION_UNBLOCKED = '_on_connection_unblocked' - CONNECTION_CLOSED = 0 - CONNECTION_INIT = 1 - CONNECTION_PROTOCOL = 2 - CONNECTION_START = 3 - CONNECTION_TUNE = 4 - CONNECTION_OPEN = 5 - CONNECTION_CLOSING = 6 - - def __init__(self, - parameters=None, - on_open_callback=None, - on_open_error_callback=None, - on_close_callback=None): - """Connection initialization expects an object that has implemented the - Parameters class and a callback function to notify when we have - successfully connected to the AMQP Broker. - - Available Parameters classes are the ConnectionParameters class and - URLParameters class. - - :param pika.connection.Parameters parameters: Connection parameters - :param method on_open_callback: Called when the connection is opened - :param method on_open_error_callback: Called if the connection cant - be opened - :param method on_close_callback: Called when the connection is closed - - """ - self._write_lock = threading.Lock() - - # Define our callback dictionary - self.callbacks = callback.CallbackManager() - - # Add the on connection error callback - self.callbacks.add(0, self.ON_CONNECTION_ERROR, - on_open_error_callback or self._on_connection_error, - False) - - self.heartbeat = None - - # On connection callback - if on_open_callback: - self.add_on_open_callback(on_open_callback) - - # On connection callback - if on_close_callback: - self.add_on_close_callback(on_close_callback) - - # Set our configuration options - self.params = parameters or ConnectionParameters() - - # Initialize the connection state and connect - self._init_connection_state() - self.connect() - - def add_backpressure_callback(self, callback_method): - """Call method "callback" when pika believes backpressure is being - applied. - - :param method callback_method: The method to call - - """ - self.callbacks.add(0, self.ON_CONNECTION_BACKPRESSURE, callback_method, - False) - - def add_on_close_callback(self, callback_method): - """Add a callback notification when the connection has closed. The - callback will be passed the connection, the reply_code (int) and the - reply_text (str), if sent by the remote server. - - :param method callback_method: Callback to call on close - - """ - self.callbacks.add(0, self.ON_CONNECTION_CLOSED, callback_method, False) - - def add_on_connection_blocked_callback(self, callback_method): - """Add a callback to be notified when RabbitMQ has sent a - ``Connection.Blocked`` frame indicating that RabbitMQ is low on - resources. Publishers can use this to voluntarily suspend publishing, - instead of relying on back pressure throttling. The callback - will be passed the ``Connection.Blocked`` method frame. - - :param method callback_method: Callback to call on `Connection.Blocked` - - """ - self.callbacks.add(0, spec.Connection.Blocked, callback_method, False) - - def add_on_connection_unblocked_callback(self, callback_method): - """Add a callback to be notified when RabbitMQ has sent a - ``Connection.Unblocked`` frame letting publishers know it's ok - to start publishing again. The callback will be passed the - ``Connection.Unblocked`` method frame. - - :param method callback_method: Callback to call on - `Connection.Unblocked` - - """ - self.callbacks.add(0, spec.Connection.Unblocked, callback_method, False) - - def add_on_open_callback(self, callback_method): - """Add a callback notification when the connection has opened. - - :param method callback_method: Callback to call when open - - """ - self.callbacks.add(0, self.ON_CONNECTION_OPEN, callback_method, False) - - def add_on_open_error_callback(self, callback_method, remove_default=True): - """Add a callback notification when the connection can not be opened. - - The callback method should accept the connection object that could not - connect, and an optional error message. - - :param method callback_method: Callback to call when can't connect - :param bool remove_default: Remove default exception raising callback - - """ - if remove_default: - self.callbacks.remove(0, self.ON_CONNECTION_ERROR, - self._on_connection_error) - self.callbacks.add(0, self.ON_CONNECTION_ERROR, callback_method, False) - - def add_timeout(self, deadline, callback_method): - """Adapters should override to call the callback after the - specified number of seconds have elapsed, using a timer, or a - thread, or similar. - - :param int deadline: The number of seconds to wait to call callback - :param method callback_method: The callback method - - """ - raise NotImplementedError - - def channel(self, on_open_callback, channel_number=None): - """Create a new channel with the next available channel number or pass - in a channel number to use. Must be non-zero if you would like to - specify but it is recommended that you let Pika manage the channel - numbers. - - :param method on_open_callback: The callback when the channel is opened - :param int channel_number: The channel number to use, defaults to the - next available. - :rtype: pika.channel.Channel - - """ - if not channel_number: - channel_number = self._next_channel_number() - self._channels[channel_number] = self._create_channel(channel_number, - on_open_callback) - self._add_channel_callbacks(channel_number) - self._channels[channel_number].open() - return self._channels[channel_number] - - def close(self, reply_code=200, reply_text='Normal shutdown'): - """Disconnect from RabbitMQ. If there are any open channels, it will - attempt to close them prior to fully disconnecting. Channels which - have active consumers will attempt to send a Basic.Cancel to RabbitMQ - to cleanly stop the delivery of messages prior to closing the channel. - - :param int reply_code: The code number for the close - :param str reply_text: The text reason for the close - - """ - if self.is_closing or self.is_closed: - return - - if self._has_open_channels: - self._close_channels(reply_code, reply_text) - - # Set our connection state - self._set_connection_state(self.CONNECTION_CLOSING) - LOGGER.info("Closing connection (%s): %s", reply_code, reply_text) - self.closing = reply_code, reply_text - - if not self._has_open_channels: - # if there are open channels then _on_close_ready will finally be - # called in _on_channel_cleanup once all channels have been closed - self._on_close_ready() - - def connect(self): - """Invoke if trying to reconnect to a RabbitMQ server. Constructing the - Connection object should connect on its own. - - """ - self._set_connection_state(self.CONNECTION_INIT) - error = self._adapter_connect() - if not error: - return self._on_connected() - self.remaining_connection_attempts -= 1 - LOGGER.warning('Could not connect, %i attempts left', - self.remaining_connection_attempts) - if self.remaining_connection_attempts: - LOGGER.info('Retrying in %i seconds', self.params.retry_delay) - self.add_timeout(self.params.retry_delay, self.connect) - else: - self.callbacks.process(0, self.ON_CONNECTION_ERROR, self, self, - error) - self.remaining_connection_attempts = self.params.connection_attempts - self._set_connection_state(self.CONNECTION_CLOSED) - - def remove_timeout(self, callback_method): - """Adapters should override to call the callback after the - specified number of seconds have elapsed, using a timer, or a - thread, or similar. - - :param method callback_method: The callback to remove a timeout for - - """ - raise NotImplementedError - - def set_backpressure_multiplier(self, value=10): - """Alter the backpressure multiplier value. We set this to 10 by default. - This value is used to raise warnings and trigger the backpressure - callback. - - :param int value: The multiplier value to set - - """ - self._backpressure = value - - # - # Connections state properties - # - - @property - def is_closed(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_CLOSED - - @property - def is_closing(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_CLOSING - - @property - def is_open(self): - """ - Returns a boolean reporting the current connection state. - """ - return self.connection_state == self.CONNECTION_OPEN - - # - # Properties that reflect server capabilities for the current connection - # - - @property - def basic_nack(self): - """Specifies if the server supports basic.nack on the active connection. - - :rtype: bool - - """ - return self.server_capabilities.get('basic.nack', False) - - @property - def consumer_cancel_notify(self): - """Specifies if the server supports consumer cancel notification on the - active connection. - - :rtype: bool - - """ - return self.server_capabilities.get('consumer_cancel_notify', False) - - @property - def exchange_exchange_bindings(self): - """Specifies if the active connection supports exchange to exchange - bindings. - - :rtype: bool - - """ - return self.server_capabilities.get('exchange_exchange_bindings', False) - - @property - def publisher_confirms(self): - """Specifies if the active connection can use publisher confirmations. - - :rtype: bool - - """ - return self.server_capabilities.get('publisher_confirms', False) - - # - # Internal methods for managing the communication process - # - - def _adapter_connect(self): - """Subclasses should override to set up the outbound socket connection. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _adapter_disconnect(self): - """Subclasses should override this to cause the underlying transport - (socket) to close. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _add_channel_callbacks(self, channel_number): - """Add the appropriate callbacks for the specified channel number. - - :param int channel_number: The channel number for the callbacks - - """ - # This permits us to garbage-collect our reference to the channel - # regardless of whether it was closed by client or broker, and do so - # after all channel-close callbacks. - self._channels[channel_number]._add_on_cleanup_callback( - self._on_channel_cleanup) - - def _add_connection_start_callback(self): - """Add a callback for when a Connection.Start frame is received from - the broker. - - """ - self.callbacks.add(0, spec.Connection.Start, self._on_connection_start) - - def _add_connection_tune_callback(self): - """Add a callback for when a Connection.Tune frame is received.""" - self.callbacks.add(0, spec.Connection.Tune, self._on_connection_tune) - - def _append_frame_buffer(self, value): - """Append the bytes to the frame buffer. - - :param str value: The bytes to append to the frame buffer - - """ - self._frame_buffer += value - - @property - def _buffer_size(self): - """Return the suggested buffer size from the connection state/tune or - the default if that is None. - - :rtype: int - - """ - return self.params.frame_max or spec.FRAME_MAX_SIZE - - def _check_for_protocol_mismatch(self, value): - """Invoked when starting a connection to make sure it's a supported - protocol. - - :param pika.frame.Method value: The frame to check - :raises: ProtocolVersionMismatch - - """ - if (value.method.version_major, - value.method.version_minor) != spec.PROTOCOL_VERSION[0:2]: - raise exceptions.ProtocolVersionMismatch(frame.ProtocolHeader(), - value) - - @property - def _client_properties(self): - """Return the client properties dictionary. - - :rtype: dict - - """ - return { - 'product': PRODUCT, - 'platform': 'Python %s' % platform.python_version(), - 'capabilities': { - 'authentication_failure_close': True, - 'basic.nack': True, - 'connection.blocked': True, - 'consumer_cancel_notify': True, - 'publisher_confirms': True - }, - 'information': 'See http://pika.rtfd.org', - 'version': __version__ - } - - def _close_channels(self, reply_code, reply_text): - """Close the open channels with the specified reply_code and reply_text. - - :param int reply_code: The code for why the channels are being closed - :param str reply_text: The text reason for why the channels are closing - - """ - if self.is_open: - for channel_number in dictkeys(self._channels): - if self._channels[channel_number].is_open: - self._channels[channel_number].close(reply_code, reply_text) - else: - del self._channels[channel_number] - # Force any lingering callbacks to be removed - # moved inside else block since channel's _cleanup removes - # callbacks - self.callbacks.cleanup(channel_number) - else: - self._channels = dict() - - def _combine(self, a, b): - """Pass in two values, if a is 0, return b otherwise if b is 0, - return a. If neither case matches return the smallest value. - - :param int a: The first value - :param int b: The second value - :rtype: int - - """ - return min(a, b) or (a or b) - - def _connect(self): - """Attempt to connect to RabbitMQ - - :rtype: bool - - """ - warnings.warn('This method is deprecated, use Connection.connect', - DeprecationWarning) - - def _create_channel(self, channel_number, on_open_callback): - """Create a new channel using the specified channel number and calling - back the method specified by on_open_callback - - :param int channel_number: The channel number to use - :param method on_open_callback: The callback when the channel is opened - - """ - LOGGER.debug('Creating channel %s', channel_number) - return channel.Channel(self, channel_number, on_open_callback) - - def _create_heartbeat_checker(self): - """Create a heartbeat checker instance if there is a heartbeat interval - set. - - :rtype: pika.heartbeat.Heartbeat - - """ - if self.params.heartbeat is not None and self.params.heartbeat > 0: - LOGGER.debug('Creating a HeartbeatChecker: %r', - self.params.heartbeat) - return heartbeat.HeartbeatChecker(self, self.params.heartbeat) - - def _remove_heartbeat(self): - """Stop the heartbeat checker if it exists - - """ - if self.heartbeat: - self.heartbeat.stop() - self.heartbeat = None - - def _deliver_frame_to_channel(self, value): - """Deliver the frame to the channel specified in the frame. - - :param pika.frame.Method value: The frame to deliver - - """ - if not value.channel_number in self._channels: - if self._is_basic_deliver_frame(value): - self._reject_out_of_band_delivery(value.channel_number, - value.method.delivery_tag) - else: - LOGGER.warning("Received %r for non-existing channel %i", value, - value.channel_number) - return - return self._channels[value.channel_number]._handle_content_frame(value) - - def _detect_backpressure(self): - """Attempt to calculate if TCP backpressure is being applied due to - our outbound buffer being larger than the average frame size over - a window of frames. - - """ - avg_frame_size = self.bytes_sent / self.frames_sent - buffer_size = sum([len(frame) for frame in self.outbound_buffer]) - if buffer_size > (avg_frame_size * self._backpressure): - LOGGER.warning(BACKPRESSURE_WARNING, buffer_size, - int(buffer_size / avg_frame_size)) - self.callbacks.process(0, self.ON_CONNECTION_BACKPRESSURE, self) - - def _ensure_closed(self): - """If the connection is not closed, close it.""" - if self.is_open: - self.close() - - def _flush_outbound(self): - """Adapters should override to flush the contents of outbound_buffer - out along the socket. - - :raises: NotImplementedError - - """ - raise NotImplementedError - - def _get_body_frame_max_length(self): - """Calculate the maximum amount of bytes that can be in a body frame. - - :rtype: int - - """ - return ( - self.params.frame_max - spec.FRAME_HEADER_SIZE - spec.FRAME_END_SIZE - ) - - def _get_credentials(self, method_frame): - """Get credentials for authentication. - - :param pika.frame.MethodFrame method_frame: The Connection.Start frame - :rtype: tuple(str, str) - - """ - (auth_type, - response) = self.params.credentials.response_for(method_frame.method) - if not auth_type: - raise exceptions.AuthenticationError(self.params.credentials.TYPE) - self.params.credentials.erase_credentials() - return auth_type, response - - @property - def _has_open_channels(self): - """Returns true if channels are open. - - :rtype: bool - - """ - return any([self._channels[num].is_open - for num in dictkeys(self._channels)]) - - def _has_pending_callbacks(self, value): - """Return true if there are any callbacks pending for the specified - frame. - - :param pika.frame.Method value: The frame to check - :rtype: bool - - """ - return self.callbacks.pending(value.channel_number, value.method) - - def _init_connection_state(self): - """Initialize or reset all of the internal state variables for a given - connection. On disconnect or reconnect all of the state needs to - be wiped. - - """ - # Connection state - self._set_connection_state(self.CONNECTION_CLOSED) - - # Negotiated server properties - self.server_properties = None - - # Outbound buffer for buffering writes until we're able to send them - self.outbound_buffer = collections.deque([]) - - # Inbound buffer for decoding frames - self._frame_buffer = bytes() - - # Dict of open channels - self._channels = dict() - - # Remaining connection attempts - self.remaining_connection_attempts = self.params.connection_attempts - - # Data used for Heartbeat checking and back-pressure detection - self.bytes_sent = 0 - self.bytes_received = 0 - self.frames_sent = 0 - self.frames_received = 0 - self.heartbeat = None - - # Default back-pressure multiplier value - self._backpressure = 10 - - # When closing, hold reason why - self.closing = 0, 'Not specified' - - # Our starting point once connected, first frame received - self._add_connection_start_callback() - - def _is_basic_deliver_frame(self, frame_value): - """Returns true if the frame is a Basic.Deliver - - :param pika.frame.Method frame_value: The frame to check - :rtype: bool - - """ - return isinstance(frame_value, spec.Basic.Deliver) - - def _is_connection_close_frame(self, value): - """Returns true if the frame is a Connection.Close frame. - - :param pika.frame.Method value: The frame to check - :rtype: bool - - """ - if not value: - return False - return isinstance(value.method, spec.Connection.Close) - - def _is_method_frame(self, value): - """Returns true if the frame is a method frame. - - :param pika.frame.Frame value: The frame to evaluate - :rtype: bool - - """ - return isinstance(value, frame.Method) - - def _is_protocol_header_frame(self, value): - """Returns True if it's a protocol header frame. - - :rtype: bool - - """ - return isinstance(value, frame.ProtocolHeader) - - def _next_channel_number(self): - """Return the next available channel number or raise an exception. - - :rtype: int - - """ - limit = self.params.channel_max or channel.MAX_CHANNELS - if len(self._channels) == limit: - raise exceptions.NoFreeChannels() - - ckeys = set(self._channels.keys()) - if not ckeys: - return 1 - return [x + 1 for x in sorted(ckeys) if x + 1 not in ckeys][0] - - def _on_channel_cleanup(self, channel): - """Remove the channel from the dict of channels when Channel.CloseOk is - sent. If connection is closing and no more channels remain, proceed to - `_on_close_ready`. - - :param pika.channel.Channel channel: channel instance - - """ - try: - del self._channels[channel.channel_number] - LOGGER.debug('Removed channel %s', channel.channel_number) - except KeyError: - LOGGER.error('Channel %r not in channels', - channel.channel_number) - if self.is_closing and not self._has_open_channels: - self._on_close_ready() - - def _on_close_ready(self): - """Called when the Connection is in a state that it can close after - a close has been requested. This happens, for example, when all of the - channels are closed that were open when the close request was made. - - """ - if self.is_closed: - LOGGER.warning('Invoked while already closed') - return - self._send_connection_close(self.closing[0], self.closing[1]) - - def _on_connected(self): - """Invoked when the socket is connected and it's time to start speaking - AMQP with the broker. - - """ - self._set_connection_state(self.CONNECTION_PROTOCOL) - - # Start the communication with the RabbitMQ Broker - self._send_frame(frame.ProtocolHeader()) - - def _on_connection_closed(self, method_frame, from_adapter=False): - """Called when the connection is closed remotely. The from_adapter value - will be true if the connection adapter has been disconnected from - the broker and the method was invoked directly instead of by receiving - a Connection.Close frame. - - :param pika.frame.Method: The Connection.Close frame - :param bool from_adapter: Called by the connection adapter - - """ - if method_frame and self._is_connection_close_frame(method_frame): - self.closing = (method_frame.method.reply_code, - method_frame.method.reply_text) - - # Save the codes because self.closing gets reset by _adapter_disconnect - reply_code, reply_text = self.closing - - # Stop the heartbeat checker if it exists - self._remove_heartbeat() - - # If this did not come from the connection adapter, close the socket - if not from_adapter: - self._adapter_disconnect() - - # Invoke a method frame neutral close - self._on_disconnect(reply_code, reply_text) - - def _on_connection_error(self, connection_unused, error_message=None): - """Default behavior when the connecting connection can not connect. - - :raises: exceptions.AMQPConnectionError - - """ - raise exceptions.AMQPConnectionError(error_message or - self.params.connection_attempts) - - def _on_connection_open(self, method_frame): - """ - This is called once we have tuned the connection with the server and - called the Connection.Open on the server and it has replied with - Connection.Ok. - """ - self.known_hosts = method_frame.method.known_hosts - - # Add a callback handler for the Broker telling us to disconnect - self.callbacks.add(0, spec.Connection.Close, self._on_connection_closed) - - # We're now connected at the AMQP level - self._set_connection_state(self.CONNECTION_OPEN) - - # Call our initial callback that we're open - self.callbacks.process(0, self.ON_CONNECTION_OPEN, self, self) - - def _on_connection_start(self, method_frame): - """This is called as a callback once we have received a Connection.Start - from the server. - - :param pika.frame.Method method_frame: The frame received - :raises: UnexpectedFrameError - - """ - self._set_connection_state(self.CONNECTION_START) - if self._is_protocol_header_frame(method_frame): - raise exceptions.UnexpectedFrameError - self._check_for_protocol_mismatch(method_frame) - self._set_server_information(method_frame) - self._add_connection_tune_callback() - self._send_connection_start_ok(*self._get_credentials(method_frame)) - - def _on_connection_tune(self, method_frame): - """Once the Broker sends back a Connection.Tune, we will set our tuning - variables that have been returned to us and kick off the Heartbeat - monitor if required, send our TuneOk and then the Connection. Open rpc - call on channel 0. - - :param pika.frame.Method method_frame: The frame received - - """ - self._set_connection_state(self.CONNECTION_TUNE) - - # Get our max channels, frames and heartbeat interval - self.params.channel_max = self._combine(self.params.channel_max, - method_frame.method.channel_max) - self.params.frame_max = self._combine(self.params.frame_max, - method_frame.method.frame_max) - if self.params.heartbeat is None: - self.params.heartbeat = method_frame.method.heartbeat - elif self.params.heartbeat != 0: - self.params.heartbeat = self._combine(self.params.heartbeat, - method_frame.method.heartbeat) - - # Calculate the maximum pieces for body frames - self._body_max_length = self._get_body_frame_max_length() - - # Create a new heartbeat checker if needed - self.heartbeat = self._create_heartbeat_checker() - - # Send the TuneOk response with what we've agreed upon - self._send_connection_tune_ok() - - # Send the Connection.Open RPC call for the vhost - self._send_connection_open() - - def _on_data_available(self, data_in): - """This is called by our Adapter, passing in the data from the socket. - As long as we have buffer try and map out frame data. - - :param str data_in: The data that is available to read - - """ - self._append_frame_buffer(data_in) - while self._frame_buffer: - consumed_count, frame_value = self._read_frame() - if not frame_value: - return - self._trim_frame_buffer(consumed_count) - self._process_frame(frame_value) - - def _on_disconnect(self, reply_code, reply_text): - """Invoke passing in the reply_code and reply_text from internal - methods to the adapter. Called from on_connection_closed and Heartbeat - timeouts. - - :param str reply_code: The numeric close code - :param str reply_text: The text close reason - - """ - LOGGER.warning('Disconnected from RabbitMQ at %s:%i (%s): %s', - self.params.host, self.params.port, reply_code, - reply_text) - self._set_connection_state(self.CONNECTION_CLOSED) - for channel in dictkeys(self._channels): - if channel not in self._channels: - continue - method_frame = frame.Method(channel, spec.Channel.Close(reply_code, - reply_text)) - self._channels[channel]._on_close(method_frame) - self._process_connection_closed_callbacks(reply_code, reply_text) - self._remove_connection_callbacks() - - def _process_callbacks(self, frame_value): - """Process the callbacks for the frame if the frame is a method frame - and if it has any callbacks pending. - - :param pika.frame.Method frame_value: The frame to process - :rtype: bool - - """ - if (self._is_method_frame(frame_value) and - self._has_pending_callbacks(frame_value)): - self.callbacks.process(frame_value.channel_number, # Prefix - frame_value.method, # Key - self, # Caller - frame_value) # Args - return True - return False - - def _process_connection_closed_callbacks(self, reason_code, reason_text): - """Process any callbacks that should be called when the connection is - closed. - - :param str reason_code: The numeric code from RabbitMQ for the close - :param str reason_text: The text reason fro closing - - """ - self.callbacks.process(0, self.ON_CONNECTION_CLOSED, self, self, - reason_code, reason_text) - - def _process_frame(self, frame_value): - """Process an inbound frame from the socket. - - :param frame_value: The frame to process - :type frame_value: pika.frame.Frame | pika.frame.Method - - """ - # Will receive a frame type of -1 if protocol version mismatch - if frame_value.frame_type < 0: - return - - # Keep track of how many frames have been read - self.frames_received += 1 - - # Process any callbacks, if True, exit method - if self._process_callbacks(frame_value): - return - - # If a heartbeat is received, update the checker - if isinstance(frame_value, frame.Heartbeat): - if self.heartbeat: - self.heartbeat.received() - else: - LOGGER.warning('Received heartbeat frame without a heartbeat ' - 'checker') - - # If the frame has a channel number beyond the base channel, deliver it - elif frame_value.channel_number > 0: - self._deliver_frame_to_channel(frame_value) - - def _read_frame(self): - """Try and read from the frame buffer and decode a frame. - - :rtype tuple: (int, pika.frame.Frame) - - """ - return frame.decode_frame(self._frame_buffer) - - def _reject_out_of_band_delivery(self, channel_number, delivery_tag): - """Reject a delivery on the specified channel number and delivery tag - because said channel no longer exists. - - :param int channel_number: The channel number - :param int delivery_tag: The delivery tag - - """ - LOGGER.warning('Rejected out-of-band delivery on channel %i (%s)', - channel_number, delivery_tag) - self._send_method(channel_number, spec.Basic.Reject(delivery_tag)) - - def _remove_callback(self, channel_number, method_frame): - """Remove the specified method_frame callback if it is set for the - specified channel number. - - :param int channel_number: The channel number to remove the callback on - :param pika.object.Method: The method frame for the callback - - """ - self.callbacks.remove(str(channel_number), method_frame) - - def _remove_callbacks(self, channel_number, method_frames): - """Remove the callbacks for the specified channel number and list of - method frames. - - :param int channel_number: The channel number to remove the callback on - :param list method_frames: The method frames for the callback - - """ - for method_frame in method_frames: - self._remove_callback(channel_number, method_frame) - - def _remove_connection_callbacks(self): - """Remove all callbacks for the connection""" - self._remove_callbacks(0, [spec.Connection.Close, spec.Connection.Start, - spec.Connection.Open]) - - def _rpc(self, channel_number, method_frame, - callback_method=None, - acceptable_replies=None): - """Make an RPC call for the given callback, channel number and method. - acceptable_replies lists out what responses we'll process from the - server with the specified callback. - - :param int channel_number: The channel number for the RPC call - :param pika.object.Method method_frame: The method frame to call - :param method callback_method: The callback for the RPC response - :param list acceptable_replies: The replies this RPC call expects - - """ - # Validate that acceptable_replies is a list or None - if acceptable_replies and not isinstance(acceptable_replies, list): - raise TypeError('acceptable_replies should be list or None') - - # Validate the callback is callable - if callback_method: - if not utils.is_callable(callback_method): - raise TypeError('callback should be None, function or method.') - - for reply in acceptable_replies: - self.callbacks.add(channel_number, reply, callback_method) - - # Send the rpc call to RabbitMQ - self._send_method(channel_number, method_frame) - - def _send_connection_close(self, reply_code, reply_text): - """Send a Connection.Close method frame. - - :param int reply_code: The reason for the close - :param str reply_text: The text reason for the close - - """ - self._rpc(0, spec.Connection.Close(reply_code, reply_text, 0, 0), - self._on_connection_closed, [spec.Connection.CloseOk]) - - def _send_connection_open(self): - """Send a Connection.Open frame""" - self._rpc(0, spec.Connection.Open(self.params.virtual_host, - insist=True), - self._on_connection_open, [spec.Connection.OpenOk]) - - def _send_connection_start_ok(self, authentication_type, response): - """Send a Connection.StartOk frame - - :param str authentication_type: The auth type value - :param str response: The encoded value to send - - """ - self._send_method(0, - spec.Connection.StartOk(self._client_properties, - authentication_type, response, - self.params.locale)) - - def _send_connection_tune_ok(self): - """Send a Connection.TuneOk frame""" - self._send_method(0, spec.Connection.TuneOk(self.params.channel_max, - self.params.frame_max, - self.params.heartbeat)) - - def _send_frame(self, frame_value): - """This appends the fully generated frame to send to the broker to the - output buffer which will be then sent via the connection adapter. - - :param frame_value: The frame to write - :type frame_value: pika.frame.Frame|pika.frame.ProtocolHeader - :raises: exceptions.ConnectionClosed - - """ - if self.is_closed: - LOGGER.critical('Attempted to send frame when closed') - raise exceptions.ConnectionClosed - - marshaled_frame = frame_value.marshal() - self.bytes_sent += len(marshaled_frame) - self.frames_sent += 1 - self.outbound_buffer.append(marshaled_frame) - self._flush_outbound() - if self.params.backpressure_detection: - self._detect_backpressure() - - def _send_method(self, channel_number, method_frame, content=None): - """Constructs a RPC method frame and then sends it to the broker. - - :param int channel_number: The channel number for the frame - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - if not content: - with self._write_lock: - self._send_frame(frame.Method(channel_number, method_frame)) - return - self._send_message(channel_number, method_frame, content) - - def _send_message(self, channel_number, method_frame, content=None): - """Send the message directly, bypassing the single _send_frame - invocation by directly appending to the output buffer and flushing - within a lock. - - :param int channel_number: The channel number for the frame - :param pika.object.Method method_frame: The method frame to send - :param tuple content: If set, is a content frame, is tuple of - properties and body. - - """ - length = len(content[1]) - write_buffer = [frame.Method(channel_number, method_frame).marshal(), - frame.Header(channel_number, length, - content[0]).marshal()] - if content[1]: - chunks = int(math.ceil(float(length) / self._body_max_length)) - for chunk in range(0, chunks): - s = chunk * self._body_max_length - e = s + self._body_max_length - if e > length: - e = length - write_buffer.append(frame.Body(channel_number, - content[1][s:e]).marshal()) - - with self._write_lock: - self.outbound_buffer += write_buffer - self.frames_sent += len(write_buffer) - self._flush_outbound() - if self.params.backpressure_detection: - self._detect_backpressure() - - def _set_connection_state(self, connection_state): - """Set the connection state. - - :param int connection_state: The connection state to set - - """ - self.connection_state = connection_state - - def _set_server_information(self, method_frame): - """Set the server properties and capabilities - - :param spec.connection.Start method_frame: The Connection.Start frame - - """ - self.server_properties = method_frame.method.server_properties - self.server_capabilities = self.server_properties.get('capabilities', - dict()) - if hasattr(self.server_properties, 'capabilities'): - del self.server_properties['capabilities'] - - def _trim_frame_buffer(self, byte_count): - """Trim the leading N bytes off the frame buffer and increment the - counter that keeps track of how many bytes have been read/used from the - socket. - - :param int byte_count: The number of bytes consumed - - """ - self._frame_buffer = self._frame_buffer[byte_count:] - self.bytes_received += byte_count diff --git a/packages_o/pika-0.10.0/pika/credentials.py b/packages_o/pika-0.10.0/pika/credentials.py deleted file mode 100644 index 4bb3801b..00000000 --- a/packages_o/pika-0.10.0/pika/credentials.py +++ /dev/null @@ -1,104 +0,0 @@ -"""The credentials classes are used to encapsulate all authentication -information for the :class:`~pika.connection.ConnectionParameters` class. - -The :class:`~pika.credentials.PlainCredentials` class returns the properly -formatted username and password to the :class:`~pika.connection.Connection`. - -To authenticate with Pika, create a :class:`~pika.credentials.PlainCredentials` -object passing in the username and password and pass it as the credentials -argument value to the :class:`~pika.connection.ConnectionParameters` object. - -If you are using :class:`~pika.connection.URLParameters` you do not need a -credentials object, one will automatically be created for you. - -If you are looking to implement SSL certificate style authentication, you would -extend the :class:`~pika.credentials.ExternalCredentials` class implementing -the required behavior. - -""" -from .compat import as_bytes -import logging - -LOGGER = logging.getLogger(__name__) - - -class PlainCredentials(object): - """A credentials object for the default authentication methodology with - RabbitMQ. - - If you do not pass in credentials to the ConnectionParameters object, it - will create credentials for 'guest' with the password of 'guest'. - - If you pass True to erase_on_connect the credentials will not be stored - in memory after the Connection attempt has been made. - - :param str username: The username to authenticate with - :param str password: The password to authenticate with - :param bool erase_on_connect: erase credentials on connect. - - """ - TYPE = 'PLAIN' - - def __init__(self, username, password, erase_on_connect=False): - """Create a new instance of PlainCredentials - - :param str username: The username to authenticate with - :param str password: The password to authenticate with - :param bool erase_on_connect: erase credentials on connect. - - """ - self.username = username - self.password = password - self.erase_on_connect = erase_on_connect - - def response_for(self, start): - """Validate that this type of authentication is supported - - :param spec.Connection.Start start: Connection.Start method - :rtype: tuple(str|None, str|None) - - """ - if as_bytes(PlainCredentials.TYPE) not in\ - as_bytes(start.mechanisms).split(): - return None, None - return (PlainCredentials.TYPE, - b'\0' + as_bytes(self.username) + - b'\0' + as_bytes(self.password)) - - def erase_credentials(self): - """Called by Connection when it no longer needs the credentials""" - if self.erase_on_connect: - LOGGER.info("Erasing stored credential values") - self.username = None - self.password = None - - -class ExternalCredentials(object): - """The ExternalCredentials class allows the connection to use EXTERNAL - authentication, generally with a client SSL certificate. - - """ - TYPE = 'EXTERNAL' - - def __init__(self): - """Create a new instance of ExternalCredentials""" - self.erase_on_connect = False - - def response_for(self, start): - """Validate that this type of authentication is supported - - :param spec.Connection.Start start: Connection.Start method - :rtype: tuple(str or None, str or None) - - """ - if as_bytes(ExternalCredentials.TYPE) not in\ - as_bytes(start.mechanisms).split(): - return None, None - return ExternalCredentials.TYPE, b'' - - def erase_credentials(self): - """Called by Connection when it no longer needs the credentials""" - LOGGER.debug('Not supported by this Credentials type') - -# Append custom credential types to this list for validation support -VALID_TYPES = [PlainCredentials, ExternalCredentials] diff --git a/packages_o/pika-0.10.0/pika/data.py b/packages_o/pika-0.10.0/pika/data.py deleted file mode 100644 index fe35e371..00000000 --- a/packages_o/pika-0.10.0/pika/data.py +++ /dev/null @@ -1,291 +0,0 @@ -"""AMQP Table Encoding/Decoding""" -import struct -import decimal -import calendar -from datetime import datetime - -from pika import exceptions -from pika.compat import unicode_type, PY2, long, as_bytes - - -def encode_short_string(pieces, value): - """Encode a string value as short string and append it to pieces list - returning the size of the encoded value. - - :param list pieces: Already encoded values - :param value: String value to encode - :type value: str or unicode - :rtype: int - - """ - encoded_value = as_bytes(value) - length = len(encoded_value) - - # 4.2.5.3 - # Short strings, stored as an 8-bit unsigned integer length followed by zero - # or more octets of data. Short strings can carry up to 255 octets of UTF-8 - # data, but may not contain binary zero octets. - # ... - # 4.2.5.5 - # The server SHOULD validate field names and upon receiving an invalid field - # name, it SHOULD signal a connection exception with reply code 503 (syntax - # error). - # -> validate length (avoid truncated utf-8 / corrupted data), but skip null - # byte check. - if length > 255: - raise exceptions.ShortStringTooLong(encoded_value) - - pieces.append(struct.pack('B', length)) - pieces.append(encoded_value) - return 1 + length - - -if PY2: - def decode_short_string(encoded, offset): - """Decode a short string value from ``encoded`` data at ``offset``. - """ - length = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - # Purely for compatibility with original python2 code. No idea what - # and why this does. - value = encoded[offset:offset + length] - try: - value = bytes(value) - except UnicodeEncodeError: - pass - offset += length - return value, offset - -else: - def decode_short_string(encoded, offset): - """Decode a short string value from ``encoded`` data at ``offset``. - """ - length = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - value = encoded[offset:offset + length].decode('utf8') - offset += length - return value, offset - - -def encode_table(pieces, table): - """Encode a dict as an AMQP table appending the encded table to the - pieces list passed in. - - :param list pieces: Already encoded frame pieces - :param dict table: The dict to encode - :rtype: int - - """ - table = table or {} - length_index = len(pieces) - pieces.append(None) # placeholder - tablesize = 0 - for (key, value) in table.items(): - tablesize += encode_short_string(pieces, key) - tablesize += encode_value(pieces, value) - - pieces[length_index] = struct.pack('>I', tablesize) - return tablesize + 4 - - -def encode_value(pieces, value): - """Encode the value passed in and append it to the pieces list returning - the the size of the encoded value. - - :param list pieces: Already encoded values - :param any value: The value to encode - :rtype: int - - """ - - if PY2: - if isinstance(value, basestring): - if isinstance(value, unicode_type): - value = value.encode('utf-8') - pieces.append(struct.pack('>cI', b'S', len(value))) - pieces.append(value) - return 5 + len(value) - else: - # support only str on Python 3 - if isinstance(value, str): - value = value.encode('utf-8') - pieces.append(struct.pack('>cI', b'S', len(value))) - pieces.append(value) - return 5 + len(value) - if isinstance(value, bool): - pieces.append(struct.pack('>cB', b't', int(value))) - return 2 - if isinstance(value, long): - pieces.append(struct.pack('>cq', b'l', value)) - return 9 - elif isinstance(value, int): - pieces.append(struct.pack('>ci', b'I', value)) - return 5 - elif isinstance(value, decimal.Decimal): - value = value.normalize() - if value.as_tuple().exponent < 0: - decimals = -value.as_tuple().exponent - raw = int(value * (decimal.Decimal(10) ** decimals)) - pieces.append(struct.pack('>cBi', b'D', decimals, raw)) - else: - # per spec, the "decimals" octet is unsigned (!) - pieces.append(struct.pack('>cBi', b'D', 0, int(value))) - return 6 - elif isinstance(value, datetime): - pieces.append(struct.pack('>cQ', b'T', - calendar.timegm(value.utctimetuple()))) - return 9 - elif isinstance(value, dict): - pieces.append(struct.pack('>c', b'F')) - return 1 + encode_table(pieces, value) - elif isinstance(value, list): - p = [] - for v in value: - encode_value(p, v) - piece = b''.join(p) - pieces.append(struct.pack('>cI', b'A', len(piece))) - pieces.append(piece) - return 5 + len(piece) - elif value is None: - pieces.append(struct.pack('>c', b'V')) - return 1 - else: - raise exceptions.UnsupportedAMQPFieldException(pieces, value) - - -def decode_table(encoded, offset): - """Decode the AMQP table passed in from the encoded value returning the - decoded result and the number of bytes read plus the offset. - - :param str encoded: The binary encoded data to decode - :param int offset: The starting byte offset - :rtype: tuple - - """ - result = {} - tablesize = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - limit = offset + tablesize - while offset < limit: - key, offset = decode_short_string(encoded, offset) - value, offset = decode_value(encoded, offset) - result[key] = value - return result, offset - - -def decode_value(encoded, offset): - """Decode the value passed in returning the decoded value and the number - of bytes read in addition to the starting offset. - - :param str encoded: The binary encoded data to decode - :param int offset: The starting byte offset - :rtype: tuple - :raises: pika.exceptions.InvalidFieldTypeException - - """ - # slice to get bytes in Python 3 and str in Python 2 - kind = encoded[offset:offset + 1] - offset += 1 - - # Bool - if kind == b't': - value = struct.unpack_from('>B', encoded, offset)[0] - value = bool(value) - offset += 1 - - # Short-Short Int - elif kind == b'b': - value = struct.unpack_from('>B', encoded, offset)[0] - offset += 1 - - # Short-Short Unsigned Int - elif kind == b'B': - value = struct.unpack_from('>b', encoded, offset)[0] - offset += 1 - - # Short Int - elif kind == b'U': - value = struct.unpack_from('>h', encoded, offset)[0] - offset += 2 - - # Short Unsigned Int - elif kind == b'u': - value = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - - # Long Int - elif kind == b'I': - value = struct.unpack_from('>i', encoded, offset)[0] - offset += 4 - - # Long Unsigned Int - elif kind == b'i': - value = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - - # Long-Long Int - elif kind == b'L': - value = long(struct.unpack_from('>q', encoded, offset)[0]) - offset += 8 - - # Long-Long Unsigned Int - elif kind == b'l': - value = long(struct.unpack_from('>Q', encoded, offset)[0]) - offset += 8 - - # Float - elif kind == b'f': - value = long(struct.unpack_from('>f', encoded, offset)[0]) - offset += 4 - - # Double - elif kind == b'd': - value = long(struct.unpack_from('>d', encoded, offset)[0]) - offset += 8 - - # Decimal - elif kind == b'D': - decimals = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - raw = struct.unpack_from('>i', encoded, offset)[0] - offset += 4 - value = decimal.Decimal(raw) * (decimal.Decimal(10) ** -decimals) - - # Short String - elif kind == b's': - value, offset = decode_short_string(encoded, offset) - - # Long String - elif kind == b'S': - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - value = encoded[offset:offset + length].decode('utf8') - offset += length - - # Field Array - elif kind == b'A': - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - offset_end = offset + length - value = [] - while offset < offset_end: - v, offset = decode_value(encoded, offset) - value.append(v) - - # Timestamp - elif kind == b'T': - value = datetime.utcfromtimestamp(struct.unpack_from('>Q', encoded, - offset)[0]) - offset += 8 - - # Field Table - elif kind == b'F': - (value, offset) = decode_table(encoded, offset) - - # Null / Void - elif kind == b'V': - value = None - else: - raise exceptions.InvalidFieldTypeException(kind) - - return value, offset diff --git a/packages_o/pika-0.10.0/pika/exceptions.py b/packages_o/pika-0.10.0/pika/exceptions.py deleted file mode 100644 index c56f6a09..00000000 --- a/packages_o/pika-0.10.0/pika/exceptions.py +++ /dev/null @@ -1,237 +0,0 @@ -"""Pika specific exceptions""" - - -class AMQPError(Exception): - - def __repr__(self): - return 'An unspecified AMQP error has occurred' - - -class AMQPConnectionError(AMQPError): - - def __repr__(self): - if len(self.args) == 1: - if self.args[0] == 1: - return ('No connection could be opened after 1 ' - 'connection attempt') - elif isinstance(self.args[0], int): - return ('No connection could be opened after %s ' - 'connection attempts' % self.args[0]) - else: - return ('No connection could be opened: %s' % self.args[0]) - elif len(self.args) == 2: - return '%s: %s' % (self.args[0], self.args[1]) - - -class IncompatibleProtocolError(AMQPConnectionError): - - def __repr__(self): - return 'The protocol returned by the server is not supported' - - -class AuthenticationError(AMQPConnectionError): - - def __repr__(self): - return ('Server and client could not negotiate use of the %s ' - 'authentication mechanism' % self.args[0]) - - -class ProbableAuthenticationError(AMQPConnectionError): - - def __repr__(self): - return ('Client was disconnected at a connection stage indicating a ' - 'probable authentication error') - - -class ProbableAccessDeniedError(AMQPConnectionError): - - def __repr__(self): - return ('Client was disconnected at a connection stage indicating a ' - 'probable denial of access to the specified virtual host') - - -class NoFreeChannels(AMQPConnectionError): - - def __repr__(self): - return 'The connection has run out of free channels' - - -class ConnectionClosed(AMQPConnectionError): - - def __repr__(self): - if len(self.args) == 2: - return 'The AMQP connection was closed (%s) %s' % (self.args[0], - self.args[1]) - else: - return 'The AMQP connection was closed: %s' % (self.args,) - - -class AMQPChannelError(AMQPError): - - def __repr__(self): - return 'An unspecified AMQP channel error has occurred' - - -class ChannelClosed(AMQPChannelError): - - def __repr__(self): - if len(self.args) == 2: - return 'The channel was closed (%s) %s' % (self.args[0], - self.args[1]) - else: - return 'The channel was closed: %s' % (self.args,) - - -class DuplicateConsumerTag(AMQPChannelError): - - def __repr__(self): - return ('The consumer tag specified already exists for this ' - 'channel: %s' % self.args[0]) - - -class ConsumerCancelled(AMQPChannelError): - - def __repr__(self): - return 'Server cancelled consumer' - - -class UnroutableError(AMQPChannelError): - """Exception containing one or more unroutable messages returned by broker - via Basic.Return. - - Used by BlockingChannel. - - In publisher-acknowledgements mode, this is raised upon receipt of Basic.Ack - from broker; in the event of Basic.Nack from broker, `NackError` is raised - instead - """ - - def __init__(self, messages): - """ - :param messages: sequence of returned unroutable messages - :type messages: sequence of `blocking_connection.ReturnedMessage` - objects - """ - super(UnroutableError, self).__init__( - "%s unroutable message(s) returned" % (len(messages))) - - self.messages = messages - - def __repr__(self): - return '%s: %i unroutable messages returned by broker' % ( - self.__class__.__name__, len(self.messages)) - - -class NackError(AMQPChannelError): - """This exception is raised when a message published in - publisher-acknowledgements mode is Nack'ed by the broker. - - Used by BlockingChannel. - """ - - def __init__(self, messages): - """ - :param messages: sequence of returned unroutable messages - :type messages: sequence of `blocking_connection.ReturnedMessage` - objects - """ - super(NackError, self).__init__( - "%s message(s) NACKed" % (len(messages))) - - self.messages = messages - - def __repr__(self): - return '%s: %i unroutable messages returned by broker' % ( - self.__class__.__name__, len(self.messages)) - - -class InvalidChannelNumber(AMQPError): - - def __repr__(self): - return 'An invalid channel number has been specified: %s' % self.args[0] - - -class ProtocolSyntaxError(AMQPError): - - def __repr__(self): - return 'An unspecified protocol syntax error occurred' - - -class UnexpectedFrameError(ProtocolSyntaxError): - - def __repr__(self): - return 'Received a frame out of sequence: %r' % self.args[0] - - -class ProtocolVersionMismatch(ProtocolSyntaxError): - - def __repr__(self): - return 'Protocol versions did not match: %r vs %r' % (self.args[0], - self.args[1]) - - -class BodyTooLongError(ProtocolSyntaxError): - - def __repr__(self): - return ('Received too many bytes for a message delivery: ' - 'Received %i, expected %i' % (self.args[0], self.args[1])) - - -class InvalidFrameError(ProtocolSyntaxError): - - def __repr__(self): - return 'Invalid frame received: %r' % self.args[0] - - -class InvalidFieldTypeException(ProtocolSyntaxError): - - def __repr__(self): - return 'Unsupported field kind %s' % self.args[0] - - -class UnsupportedAMQPFieldException(ProtocolSyntaxError): - - def __repr__(self): - return 'Unsupported field kind %s' % type(self.args[1]) - - -class UnspportedAMQPFieldException(UnsupportedAMQPFieldException): - """Deprecated version of UnsupportedAMQPFieldException""" - - -class MethodNotImplemented(AMQPError): - pass - - -class ChannelError(Exception): - - def __repr__(self): - return 'An unspecified error occurred with the Channel' - - -class InvalidMinimumFrameSize(ProtocolSyntaxError): - - def __repr__(self): - return 'AMQP Minimum Frame Size is 4096 Bytes' - - -class InvalidMaximumFrameSize(ProtocolSyntaxError): - - def __repr__(self): - return 'AMQP Maximum Frame Size is 131072 Bytes' - - -class RecursionError(Exception): - """The requested operation would result in unsupported recursion or - reentrancy. - - Used by BlockingConnection/BlockingChannel - - """ - - -class ShortStringTooLong(AMQPError): - - def __repr__(self): - return ('AMQP Short String can contain up to 255 bytes: ' - '%.300s' % self.args[0]) diff --git a/packages_o/pika-0.10.0/pika/frame.py b/packages_o/pika-0.10.0/pika/frame.py deleted file mode 100644 index 9a07ec36..00000000 --- a/packages_o/pika-0.10.0/pika/frame.py +++ /dev/null @@ -1,265 +0,0 @@ -"""Frame objects that do the frame demarshaling and marshaling.""" -import logging -import struct - -from pika import amqp_object -from pika import exceptions -from pika import spec -from pika.compat import byte - - -LOGGER = logging.getLogger(__name__) - - -class Frame(amqp_object.AMQPObject): - """Base Frame object mapping. Defines a behavior for all child classes for - assignment of core attributes and implementation of the a core _marshal - method which child classes use to create the binary AMQP frame. - - """ - NAME = 'Frame' - - def __init__(self, frame_type, channel_number): - """Create a new instance of a frame - - :param int frame_type: The frame type - :param int channel_number: The channel number for the frame - - """ - self.frame_type = frame_type - self.channel_number = channel_number - - def _marshal(self, pieces): - """Create the full AMQP wire protocol frame data representation - - :rtype: bytes - - """ - payload = b''.join(pieces) - return struct.pack('>BHI', self.frame_type, self.channel_number, - len(payload)) + payload + byte(spec.FRAME_END) - - def marshal(self): - """To be ended by child classes - - :raises NotImplementedError - - """ - raise NotImplementedError - - -class Method(Frame): - """Base Method frame object mapping. AMQP method frames are mapped on top - of this class for creating or accessing their data and attributes. - - """ - NAME = 'METHOD' - - def __init__(self, channel_number, method): - """Create a new instance of a frame - - :param int channel_number: The frame type - :param pika.Spec.Class.Method method: The AMQP Class.Method - - """ - Frame.__init__(self, spec.FRAME_METHOD, channel_number) - self.method = method - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - pieces = self.method.encode() - pieces.insert(0, struct.pack('>I', self.method.INDEX)) - return self._marshal(pieces) - - -class Header(Frame): - """Header frame object mapping. AMQP content header frames are mapped - on top of this class for creating or accessing their data and attributes. - - """ - NAME = 'Header' - - def __init__(self, channel_number, body_size, props): - """Create a new instance of a AMQP ContentHeader object - - :param int channel_number: The channel number for the frame - :param int body_size: The number of bytes for the body - :param pika.spec.BasicProperties props: Basic.Properties object - - """ - Frame.__init__(self, spec.FRAME_HEADER, channel_number) - self.body_size = body_size - self.properties = props - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - pieces = self.properties.encode() - pieces.insert(0, struct.pack('>HxxQ', self.properties.INDEX, - self.body_size)) - return self._marshal(pieces) - - -class Body(Frame): - """Body frame object mapping class. AMQP content body frames are mapped on - to this base class for getting/setting of attributes/data. - - """ - NAME = 'Body' - - def __init__(self, channel_number, fragment): - """ - Parameters: - - - channel_number: int - - fragment: unicode or str - """ - Frame.__init__(self, spec.FRAME_BODY, channel_number) - self.fragment = fragment - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - return self._marshal([self.fragment]) - - -class Heartbeat(Frame): - """Heartbeat frame object mapping class. AMQP Heartbeat frames are mapped - on to this class for a common access structure to the attributes/data - values. - - """ - NAME = 'Heartbeat' - - def __init__(self): - """Create a new instance of the Heartbeat frame""" - Frame.__init__(self, spec.FRAME_HEARTBEAT, 0) - - def marshal(self): - """Return the AMQP binary encoded value of the frame - - :rtype: str - - """ - return self._marshal(list()) - - -class ProtocolHeader(amqp_object.AMQPObject): - """AMQP Protocol header frame class which provides a pythonic interface - for creating AMQP Protocol headers - - """ - NAME = 'ProtocolHeader' - - def __init__(self, major=None, minor=None, revision=None): - """Construct a Protocol Header frame object for the specified AMQP - version - - :param int major: Major version number - :param int minor: Minor version number - :param int revision: Revision - - """ - self.frame_type = -1 - self.major = major or spec.PROTOCOL_VERSION[0] - self.minor = minor or spec.PROTOCOL_VERSION[1] - self.revision = revision or spec.PROTOCOL_VERSION[2] - - def marshal(self): - """Return the full AMQP wire protocol frame data representation of the - ProtocolHeader frame - - :rtype: str - - """ - return b'AMQP' + struct.pack('BBBB', 0, self.major, self.minor, - self.revision) - - -def decode_frame(data_in): - """Receives raw socket data and attempts to turn it into a frame. - Returns bytes used to make the frame and the frame - - :param str data_in: The raw data stream - :rtype: tuple(bytes consumed, frame) - :raises: pika.exceptions.InvalidFrameError - - """ - # Look to see if it's a protocol header frame - try: - if data_in[0:4] == b'AMQP': - major, minor, revision = struct.unpack_from('BBB', data_in, 5) - return 8, ProtocolHeader(major, minor, revision) - except (IndexError, struct.error): - return 0, None - - # Get the Frame Type, Channel Number and Frame Size - try: - (frame_type, channel_number, - frame_size) = struct.unpack('>BHL', data_in[0:7]) - except struct.error: - return 0, None - - # Get the frame data - frame_end = spec.FRAME_HEADER_SIZE + frame_size + spec.FRAME_END_SIZE - - # We don't have all of the frame yet - if frame_end > len(data_in): - return 0, None - - # The Frame termination chr is wrong - if data_in[frame_end - 1:frame_end] != byte(spec.FRAME_END): - raise exceptions.InvalidFrameError("Invalid FRAME_END marker") - - # Get the raw frame data - frame_data = data_in[spec.FRAME_HEADER_SIZE:frame_end - 1] - - if frame_type == spec.FRAME_METHOD: - - # Get the Method ID from the frame data - method_id = struct.unpack_from('>I', frame_data)[0] - - # Get a Method object for this method_id - method = spec.methods[method_id]() - - # Decode the content - method.decode(frame_data, 4) - - # Return the amount of data consumed and the Method object - return frame_end, Method(channel_number, method) - - elif frame_type == spec.FRAME_HEADER: - - # Return the header class and body size - class_id, weight, body_size = struct.unpack_from('>HHQ', frame_data) - - # Get the Properties type - properties = spec.props[class_id]() - - # Decode the properties - out = properties.decode(frame_data[12:]) - - # Return a Header frame - return frame_end, Header(channel_number, body_size, properties) - - elif frame_type == spec.FRAME_BODY: - - # Return the amount of data consumed and the Body frame w/ data - return frame_end, Body(channel_number, frame_data) - - elif frame_type == spec.FRAME_HEARTBEAT: - - # Return the amount of data and a Heartbeat frame - return frame_end, Heartbeat() - - raise exceptions.InvalidFrameError("Unknown frame type: %i" % frame_type) diff --git a/packages_o/pika-0.10.0/pika/heartbeat.py b/packages_o/pika-0.10.0/pika/heartbeat.py deleted file mode 100644 index 2d5d28e2..00000000 --- a/packages_o/pika-0.10.0/pika/heartbeat.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Handle AMQP Heartbeats""" -import logging - -from pika import frame - -LOGGER = logging.getLogger(__name__) - - -class HeartbeatChecker(object): - """Checks to make sure that our heartbeat is received at the expected - intervals. - - """ - MAX_IDLE_COUNT = 2 - _CONNECTION_FORCED = 320 - _STALE_CONNECTION = "Too Many Missed Heartbeats, No reply in %i seconds" - - def __init__(self, connection, interval, idle_count=MAX_IDLE_COUNT): - """Create a heartbeat on connection sending a heartbeat frame every - interval seconds. - - :param pika.connection.Connection: Connection object - :param int interval: Heartbeat check interval - :param int idle_count: Number of heartbeat intervals missed until the - connection is considered idle and disconnects - - """ - self._connection = connection - self._interval = interval - self._max_idle_count = idle_count - - # Initialize counters - self._bytes_received = 0 - self._bytes_sent = 0 - self._heartbeat_frames_received = 0 - self._heartbeat_frames_sent = 0 - self._idle_byte_intervals = 0 - - # The handle for the last timer - self._timer = None - - # Setup the timer to fire in _interval seconds - self._setup_timer() - - @property - def active(self): - """Return True if the connection's heartbeat attribute is set to this - instance. - - :rtype True - - """ - return self._connection.heartbeat is self - - @property - def bytes_received_on_connection(self): - """Return the number of bytes received by the connection bytes object. - - :rtype int - - """ - return self._connection.bytes_received - - @property - def connection_is_idle(self): - """Returns true if the byte count hasn't changed in enough intervals - to trip the max idle threshold. - - """ - return self._idle_byte_intervals >= self._max_idle_count - - def received(self): - """Called when a heartbeat is received""" - LOGGER.debug('Received heartbeat frame') - self._heartbeat_frames_received += 1 - - def send_and_check(self): - """Invoked by a timer to send a heartbeat when we need to, check to see - if we've missed any heartbeats and disconnect our connection if it's - been idle too long. - - """ - LOGGER.debug('Received %i heartbeat frames, sent %i', - self._heartbeat_frames_received, - self._heartbeat_frames_sent) - - if self.connection_is_idle: - return self._close_connection() - - # Connection has not received any data, increment the counter - if not self._has_received_data: - self._idle_byte_intervals += 1 - else: - self._idle_byte_intervals = 0 - - # Update the counters of bytes sent/received and the frames received - self._update_counters() - - # Send a heartbeat frame - self._send_heartbeat_frame() - - # Update the timer to fire again - self._start_timer() - - def stop(self): - """Stop the heartbeat checker""" - if self._timer: - LOGGER.debug('Removing timeout for next heartbeat interval') - self._connection.remove_timeout(self._timer) - self._timer = None - - def _close_connection(self): - """Close the connection with the AMQP Connection-Forced value.""" - LOGGER.info('Connection is idle, %i stale byte intervals', - self._idle_byte_intervals) - duration = self._max_idle_count * self._interval - text = HeartbeatChecker._STALE_CONNECTION % duration - self._connection.close(HeartbeatChecker._CONNECTION_FORCED, text) - self._connection._adapter_disconnect() - self._connection._on_disconnect(HeartbeatChecker._CONNECTION_FORCED, - text) - - @property - def _has_received_data(self): - """Returns True if the connection has received data on the connection. - - :rtype: bool - - """ - return not self._bytes_received == self.bytes_received_on_connection - - def _new_heartbeat_frame(self): - """Return a new heartbeat frame. - - :rtype pika.frame.Heartbeat - - """ - return frame.Heartbeat() - - def _send_heartbeat_frame(self): - """Send a heartbeat frame on the connection. - - """ - LOGGER.debug('Sending heartbeat frame') - self._connection._send_frame(self._new_heartbeat_frame()) - self._heartbeat_frames_sent += 1 - - def _setup_timer(self): - """Use the connection objects delayed_call function which is - implemented by the Adapter for calling the check_heartbeats function - every interval seconds. - - """ - self._timer = self._connection.add_timeout(self._interval, - self.send_and_check) - - def _start_timer(self): - """If the connection still has this object set for heartbeats, add a - new timer. - - """ - if self.active: - self._setup_timer() - - def _update_counters(self): - """Update the internal counters for bytes sent and received and the - number of frames received - - """ - self._bytes_sent = self._connection.bytes_sent - self._bytes_received = self._connection.bytes_received diff --git a/packages_o/pika-0.10.0/pika/spec.py b/packages_o/pika-0.10.0/pika/spec.py deleted file mode 100644 index b3d7cc94..00000000 --- a/packages_o/pika-0.10.0/pika/spec.py +++ /dev/null @@ -1,2312 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# -# For copyright and licensing please refer to COPYING. -# -# ***** END LICENSE BLOCK ***** - -# NOTE: Autogenerated code by codegen.py, do not edit - -import struct -from pika import amqp_object -from pika import data -from pika.compat import str_or_bytes, unicode_type - -str = bytes - - -PROTOCOL_VERSION = (0, 9, 1) -PORT = 5672 - -ACCESS_REFUSED = 403 -CHANNEL_ERROR = 504 -COMMAND_INVALID = 503 -CONNECTION_FORCED = 320 -CONTENT_TOO_LARGE = 311 -FRAME_BODY = 3 -FRAME_END = 206 -FRAME_END_SIZE = 1 -FRAME_ERROR = 501 -FRAME_HEADER = 2 -FRAME_HEADER_SIZE = 7 -FRAME_HEARTBEAT = 8 -FRAME_MAX_SIZE = 131072 -FRAME_METHOD = 1 -FRAME_MIN_SIZE = 4096 -INTERNAL_ERROR = 541 -INVALID_PATH = 402 -NOT_ALLOWED = 530 -NOT_FOUND = 404 -NOT_IMPLEMENTED = 540 -NO_CONSUMERS = 313 -NO_ROUTE = 312 -PRECONDITION_FAILED = 406 -REPLY_SUCCESS = 200 -RESOURCE_ERROR = 506 -RESOURCE_LOCKED = 405 -SYNTAX_ERROR = 502 -UNEXPECTED_FRAME = 505 - - -class Connection(amqp_object.Class): - - INDEX = 0x000A # 10 - NAME = 'Connection' - - class Start(amqp_object.Method): - - INDEX = 0x000A000A # 10, 10; 655370 - NAME = 'Connection.Start' - - def __init__(self, version_major=0, version_minor=9, server_properties=None, mechanisms='PLAIN', locales='en_US'): - self.version_major = version_major - self.version_minor = version_minor - self.server_properties = server_properties - self.mechanisms = mechanisms - self.locales = locales - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.version_major = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.version_minor = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - (self.server_properties, offset) = data.decode_table(encoded, offset) - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.mechanisms = encoded[offset:offset + length] - try: - self.mechanisms = str(self.mechanisms) - except UnicodeEncodeError: - pass - offset += length - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.locales = encoded[offset:offset + length] - try: - self.locales = str(self.locales) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('B', self.version_major)) - pieces.append(struct.pack('B', self.version_minor)) - data.encode_table(pieces, self.server_properties) - assert isinstance(self.mechanisms, str_or_bytes),\ - 'A non-string value was supplied for self.mechanisms' - value = self.mechanisms.encode('utf-8') if isinstance(self.mechanisms, unicode_type) else self.mechanisms - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - assert isinstance(self.locales, str_or_bytes),\ - 'A non-string value was supplied for self.locales' - value = self.locales.encode('utf-8') if isinstance(self.locales, unicode_type) else self.locales - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class StartOk(amqp_object.Method): - - INDEX = 0x000A000B # 10, 11; 655371 - NAME = 'Connection.StartOk' - - def __init__(self, client_properties=None, mechanism='PLAIN', response=None, locale='en_US'): - self.client_properties = client_properties - self.mechanism = mechanism - self.response = response - self.locale = locale - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - (self.client_properties, offset) = data.decode_table(encoded, offset) - self.mechanism, offset = data.decode_short_string(encoded, offset) - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.response = encoded[offset:offset + length] - try: - self.response = str(self.response) - except UnicodeEncodeError: - pass - offset += length - self.locale, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - data.encode_table(pieces, self.client_properties) - assert isinstance(self.mechanism, str_or_bytes),\ - 'A non-string value was supplied for self.mechanism' - data.encode_short_string(pieces, self.mechanism) - assert isinstance(self.response, str_or_bytes),\ - 'A non-string value was supplied for self.response' - value = self.response.encode('utf-8') if isinstance(self.response, unicode_type) else self.response - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - assert isinstance(self.locale, str_or_bytes),\ - 'A non-string value was supplied for self.locale' - data.encode_short_string(pieces, self.locale) - return pieces - - class Secure(amqp_object.Method): - - INDEX = 0x000A0014 # 10, 20; 655380 - NAME = 'Connection.Secure' - - def __init__(self, challenge=None): - self.challenge = challenge - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.challenge = encoded[offset:offset + length] - try: - self.challenge = str(self.challenge) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.challenge, str_or_bytes),\ - 'A non-string value was supplied for self.challenge' - value = self.challenge.encode('utf-8') if isinstance(self.challenge, unicode_type) else self.challenge - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class SecureOk(amqp_object.Method): - - INDEX = 0x000A0015 # 10, 21; 655381 - NAME = 'Connection.SecureOk' - - def __init__(self, response=None): - self.response = response - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.response = encoded[offset:offset + length] - try: - self.response = str(self.response) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.response, str_or_bytes),\ - 'A non-string value was supplied for self.response' - value = self.response.encode('utf-8') if isinstance(self.response, unicode_type) else self.response - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class Tune(amqp_object.Method): - - INDEX = 0x000A001E # 10, 30; 655390 - NAME = 'Connection.Tune' - - def __init__(self, channel_max=0, frame_max=0, heartbeat=0): - self.channel_max = channel_max - self.frame_max = frame_max - self.heartbeat = heartbeat - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.channel_max = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.frame_max = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.heartbeat = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.channel_max)) - pieces.append(struct.pack('>I', self.frame_max)) - pieces.append(struct.pack('>H', self.heartbeat)) - return pieces - - class TuneOk(amqp_object.Method): - - INDEX = 0x000A001F # 10, 31; 655391 - NAME = 'Connection.TuneOk' - - def __init__(self, channel_max=0, frame_max=0, heartbeat=0): - self.channel_max = channel_max - self.frame_max = frame_max - self.heartbeat = heartbeat - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.channel_max = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.frame_max = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.heartbeat = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.channel_max)) - pieces.append(struct.pack('>I', self.frame_max)) - pieces.append(struct.pack('>H', self.heartbeat)) - return pieces - - class Open(amqp_object.Method): - - INDEX = 0x000A0028 # 10, 40; 655400 - NAME = 'Connection.Open' - - def __init__(self, virtual_host='/', capabilities='', insist=False): - self.virtual_host = virtual_host - self.capabilities = capabilities - self.insist = insist - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.virtual_host, offset = data.decode_short_string(encoded, offset) - self.capabilities, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.insist = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.virtual_host, str_or_bytes),\ - 'A non-string value was supplied for self.virtual_host' - data.encode_short_string(pieces, self.virtual_host) - assert isinstance(self.capabilities, str_or_bytes),\ - 'A non-string value was supplied for self.capabilities' - data.encode_short_string(pieces, self.capabilities) - bit_buffer = 0 - if self.insist: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class OpenOk(amqp_object.Method): - - INDEX = 0x000A0029 # 10, 41; 655401 - NAME = 'Connection.OpenOk' - - def __init__(self, known_hosts=''): - self.known_hosts = known_hosts - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.known_hosts, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.known_hosts, str_or_bytes),\ - 'A non-string value was supplied for self.known_hosts' - data.encode_short_string(pieces, self.known_hosts) - return pieces - - class Close(amqp_object.Method): - - INDEX = 0x000A0032 # 10, 50; 655410 - NAME = 'Connection.Close' - - def __init__(self, reply_code=None, reply_text='', class_id=None, method_id=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.class_id = class_id - self.method_id = method_id - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.class_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.method_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - pieces.append(struct.pack('>H', self.class_id)) - pieces.append(struct.pack('>H', self.method_id)) - return pieces - - class CloseOk(amqp_object.Method): - - INDEX = 0x000A0033 # 10, 51; 655411 - NAME = 'Connection.CloseOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Blocked(amqp_object.Method): - - INDEX = 0x000A003C # 10, 60; 655420 - NAME = 'Connection.Blocked' - - def __init__(self, reason=''): - self.reason = reason - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.reason, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.reason, str_or_bytes),\ - 'A non-string value was supplied for self.reason' - data.encode_short_string(pieces, self.reason) - return pieces - - class Unblocked(amqp_object.Method): - - INDEX = 0x000A003D # 10, 61; 655421 - NAME = 'Connection.Unblocked' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Channel(amqp_object.Class): - - INDEX = 0x0014 # 20 - NAME = 'Channel' - - class Open(amqp_object.Method): - - INDEX = 0x0014000A # 20, 10; 1310730 - NAME = 'Channel.Open' - - def __init__(self, out_of_band=''): - self.out_of_band = out_of_band - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.out_of_band, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.out_of_band, str_or_bytes),\ - 'A non-string value was supplied for self.out_of_band' - data.encode_short_string(pieces, self.out_of_band) - return pieces - - class OpenOk(amqp_object.Method): - - INDEX = 0x0014000B # 20, 11; 1310731 - NAME = 'Channel.OpenOk' - - def __init__(self, channel_id=''): - self.channel_id = channel_id - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - length = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.channel_id = encoded[offset:offset + length] - try: - self.channel_id = str(self.channel_id) - except UnicodeEncodeError: - pass - offset += length - return self - - def encode(self): - pieces = list() - assert isinstance(self.channel_id, str_or_bytes),\ - 'A non-string value was supplied for self.channel_id' - value = self.channel_id.encode('utf-8') if isinstance(self.channel_id, unicode_type) else self.channel_id - pieces.append(struct.pack('>I', len(value))) - pieces.append(value) - return pieces - - class Flow(amqp_object.Method): - - INDEX = 0x00140014 # 20, 20; 1310740 - NAME = 'Channel.Flow' - - def __init__(self, active=None): - self.active = active - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.active = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.active: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class FlowOk(amqp_object.Method): - - INDEX = 0x00140015 # 20, 21; 1310741 - NAME = 'Channel.FlowOk' - - def __init__(self, active=None): - self.active = active - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.active = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.active: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Close(amqp_object.Method): - - INDEX = 0x00140028 # 20, 40; 1310760 - NAME = 'Channel.Close' - - def __init__(self, reply_code=None, reply_text='', class_id=None, method_id=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.class_id = class_id - self.method_id = method_id - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.class_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.method_id = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - pieces.append(struct.pack('>H', self.class_id)) - pieces.append(struct.pack('>H', self.method_id)) - return pieces - - class CloseOk(amqp_object.Method): - - INDEX = 0x00140029 # 20, 41; 1310761 - NAME = 'Channel.CloseOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Access(amqp_object.Class): - - INDEX = 0x001E # 30 - NAME = 'Access' - - class Request(amqp_object.Method): - - INDEX = 0x001E000A # 30, 10; 1966090 - NAME = 'Access.Request' - - def __init__(self, realm='/data', exclusive=False, passive=True, active=True, write=True, read=True): - self.realm = realm - self.exclusive = exclusive - self.passive = passive - self.active = active - self.write = write - self.read = read - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.realm, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.exclusive = (bit_buffer & (1 << 0)) != 0 - self.passive = (bit_buffer & (1 << 1)) != 0 - self.active = (bit_buffer & (1 << 2)) != 0 - self.write = (bit_buffer & (1 << 3)) != 0 - self.read = (bit_buffer & (1 << 4)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.realm, str_or_bytes),\ - 'A non-string value was supplied for self.realm' - data.encode_short_string(pieces, self.realm) - bit_buffer = 0 - if self.exclusive: - bit_buffer = bit_buffer | (1 << 0) - if self.passive: - bit_buffer = bit_buffer | (1 << 1) - if self.active: - bit_buffer = bit_buffer | (1 << 2) - if self.write: - bit_buffer = bit_buffer | (1 << 3) - if self.read: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RequestOk(amqp_object.Method): - - INDEX = 0x001E000B # 30, 11; 1966091 - NAME = 'Access.RequestOk' - - def __init__(self, ticket=1): - self.ticket = ticket - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - return pieces - - -class Exchange(amqp_object.Class): - - INDEX = 0x0028 # 40 - NAME = 'Exchange' - - class Declare(amqp_object.Method): - - INDEX = 0x0028000A # 40, 10; 2621450 - NAME = 'Exchange.Declare' - - def __init__(self, ticket=0, exchange=None, type='direct', passive=False, durable=False, auto_delete=False, internal=False, nowait=False, arguments={}): - self.ticket = ticket - self.exchange = exchange - self.type = type - self.passive = passive - self.durable = durable - self.auto_delete = auto_delete - self.internal = internal - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.type, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.passive = (bit_buffer & (1 << 0)) != 0 - self.durable = (bit_buffer & (1 << 1)) != 0 - self.auto_delete = (bit_buffer & (1 << 2)) != 0 - self.internal = (bit_buffer & (1 << 3)) != 0 - self.nowait = (bit_buffer & (1 << 4)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.type, str_or_bytes),\ - 'A non-string value was supplied for self.type' - data.encode_short_string(pieces, self.type) - bit_buffer = 0 - if self.passive: - bit_buffer = bit_buffer | (1 << 0) - if self.durable: - bit_buffer = bit_buffer | (1 << 1) - if self.auto_delete: - bit_buffer = bit_buffer | (1 << 2) - if self.internal: - bit_buffer = bit_buffer | (1 << 3) - if self.nowait: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class DeclareOk(amqp_object.Method): - - INDEX = 0x0028000B # 40, 11; 2621451 - NAME = 'Exchange.DeclareOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Delete(amqp_object.Method): - - INDEX = 0x00280014 # 40, 20; 2621460 - NAME = 'Exchange.Delete' - - def __init__(self, ticket=0, exchange=None, if_unused=False, nowait=False): - self.ticket = ticket - self.exchange = exchange - self.if_unused = if_unused - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.if_unused = (bit_buffer & (1 << 0)) != 0 - self.nowait = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - bit_buffer = 0 - if self.if_unused: - bit_buffer = bit_buffer | (1 << 0) - if self.nowait: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class DeleteOk(amqp_object.Method): - - INDEX = 0x00280015 # 40, 21; 2621461 - NAME = 'Exchange.DeleteOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Bind(amqp_object.Method): - - INDEX = 0x0028001E # 40, 30; 2621470 - NAME = 'Exchange.Bind' - - def __init__(self, ticket=0, destination=None, source=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.destination = destination - self.source = source - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.destination, offset = data.decode_short_string(encoded, offset) - self.source, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.destination, str_or_bytes),\ - 'A non-string value was supplied for self.destination' - data.encode_short_string(pieces, self.destination) - assert isinstance(self.source, str_or_bytes),\ - 'A non-string value was supplied for self.source' - data.encode_short_string(pieces, self.source) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class BindOk(amqp_object.Method): - - INDEX = 0x0028001F # 40, 31; 2621471 - NAME = 'Exchange.BindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Unbind(amqp_object.Method): - - INDEX = 0x00280028 # 40, 40; 2621480 - NAME = 'Exchange.Unbind' - - def __init__(self, ticket=0, destination=None, source=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.destination = destination - self.source = source - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.destination, offset = data.decode_short_string(encoded, offset) - self.source, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.destination, str_or_bytes),\ - 'A non-string value was supplied for self.destination' - data.encode_short_string(pieces, self.destination) - assert isinstance(self.source, str_or_bytes),\ - 'A non-string value was supplied for self.source' - data.encode_short_string(pieces, self.source) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class UnbindOk(amqp_object.Method): - - INDEX = 0x00280033 # 40, 51; 2621491 - NAME = 'Exchange.UnbindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Queue(amqp_object.Class): - - INDEX = 0x0032 # 50 - NAME = 'Queue' - - class Declare(amqp_object.Method): - - INDEX = 0x0032000A # 50, 10; 3276810 - NAME = 'Queue.Declare' - - def __init__(self, ticket=0, queue='', passive=False, durable=False, exclusive=False, auto_delete=False, nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.passive = passive - self.durable = durable - self.exclusive = exclusive - self.auto_delete = auto_delete - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.passive = (bit_buffer & (1 << 0)) != 0 - self.durable = (bit_buffer & (1 << 1)) != 0 - self.exclusive = (bit_buffer & (1 << 2)) != 0 - self.auto_delete = (bit_buffer & (1 << 3)) != 0 - self.nowait = (bit_buffer & (1 << 4)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.passive: - bit_buffer = bit_buffer | (1 << 0) - if self.durable: - bit_buffer = bit_buffer | (1 << 1) - if self.exclusive: - bit_buffer = bit_buffer | (1 << 2) - if self.auto_delete: - bit_buffer = bit_buffer | (1 << 3) - if self.nowait: - bit_buffer = bit_buffer | (1 << 4) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class DeclareOk(amqp_object.Method): - - INDEX = 0x0032000B # 50, 11; 3276811 - NAME = 'Queue.DeclareOk' - - def __init__(self, queue=None, message_count=None, consumer_count=None): - self.queue = queue - self.message_count = message_count - self.consumer_count = consumer_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.queue, offset = data.decode_short_string(encoded, offset) - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.consumer_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - pieces.append(struct.pack('>I', self.message_count)) - pieces.append(struct.pack('>I', self.consumer_count)) - return pieces - - class Bind(amqp_object.Method): - - INDEX = 0x00320014 # 50, 20; 3276820 - NAME = 'Queue.Bind' - - def __init__(self, ticket=0, queue='', exchange=None, routing_key='', nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.exchange = exchange - self.routing_key = routing_key - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class BindOk(amqp_object.Method): - - INDEX = 0x00320015 # 50, 21; 3276821 - NAME = 'Queue.BindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Purge(amqp_object.Method): - - INDEX = 0x0032001E # 50, 30; 3276830 - NAME = 'Queue.Purge' - - def __init__(self, ticket=0, queue='', nowait=False): - self.ticket = ticket - self.queue = queue - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class PurgeOk(amqp_object.Method): - - INDEX = 0x0032001F # 50, 31; 3276831 - NAME = 'Queue.PurgeOk' - - def __init__(self, message_count=None): - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class Delete(amqp_object.Method): - - INDEX = 0x00320028 # 50, 40; 3276840 - NAME = 'Queue.Delete' - - def __init__(self, ticket=0, queue='', if_unused=False, if_empty=False, nowait=False): - self.ticket = ticket - self.queue = queue - self.if_unused = if_unused - self.if_empty = if_empty - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.if_unused = (bit_buffer & (1 << 0)) != 0 - self.if_empty = (bit_buffer & (1 << 1)) != 0 - self.nowait = (bit_buffer & (1 << 2)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.if_unused: - bit_buffer = bit_buffer | (1 << 0) - if self.if_empty: - bit_buffer = bit_buffer | (1 << 1) - if self.nowait: - bit_buffer = bit_buffer | (1 << 2) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class DeleteOk(amqp_object.Method): - - INDEX = 0x00320029 # 50, 41; 3276841 - NAME = 'Queue.DeleteOk' - - def __init__(self, message_count=None): - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class Unbind(amqp_object.Method): - - INDEX = 0x00320032 # 50, 50; 3276850 - NAME = 'Queue.Unbind' - - def __init__(self, ticket=0, queue='', exchange=None, routing_key='', arguments={}): - self.ticket = ticket - self.queue = queue - self.exchange = exchange - self.routing_key = routing_key - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - data.encode_table(pieces, self.arguments) - return pieces - - class UnbindOk(amqp_object.Method): - - INDEX = 0x00320033 # 50, 51; 3276851 - NAME = 'Queue.UnbindOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Basic(amqp_object.Class): - - INDEX = 0x003C # 60 - NAME = 'Basic' - - class Qos(amqp_object.Method): - - INDEX = 0x003C000A # 60, 10; 3932170 - NAME = 'Basic.Qos' - - def __init__(self, prefetch_size=0, prefetch_count=0, global_=False): - self.prefetch_size = prefetch_size - self.prefetch_count = prefetch_count - self.global_ = global_ - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.prefetch_size = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - self.prefetch_count = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.global_ = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>I', self.prefetch_size)) - pieces.append(struct.pack('>H', self.prefetch_count)) - bit_buffer = 0 - if self.global_: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class QosOk(amqp_object.Method): - - INDEX = 0x003C000B # 60, 11; 3932171 - NAME = 'Basic.QosOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Consume(amqp_object.Method): - - INDEX = 0x003C0014 # 60, 20; 3932180 - NAME = 'Basic.Consume' - - def __init__(self, ticket=0, queue='', consumer_tag='', no_local=False, no_ack=False, exclusive=False, nowait=False, arguments={}): - self.ticket = ticket - self.queue = queue - self.consumer_tag = consumer_tag - self.no_local = no_local - self.no_ack = no_ack - self.exclusive = exclusive - self.nowait = nowait - self.arguments = arguments - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.no_local = (bit_buffer & (1 << 0)) != 0 - self.no_ack = (bit_buffer & (1 << 1)) != 0 - self.exclusive = (bit_buffer & (1 << 2)) != 0 - self.nowait = (bit_buffer & (1 << 3)) != 0 - (self.arguments, offset) = data.decode_table(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - bit_buffer = 0 - if self.no_local: - bit_buffer = bit_buffer | (1 << 0) - if self.no_ack: - bit_buffer = bit_buffer | (1 << 1) - if self.exclusive: - bit_buffer = bit_buffer | (1 << 2) - if self.nowait: - bit_buffer = bit_buffer | (1 << 3) - pieces.append(struct.pack('B', bit_buffer)) - data.encode_table(pieces, self.arguments) - return pieces - - class ConsumeOk(amqp_object.Method): - - INDEX = 0x003C0015 # 60, 21; 3932181 - NAME = 'Basic.ConsumeOk' - - def __init__(self, consumer_tag=None): - self.consumer_tag = consumer_tag - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - return pieces - - class Cancel(amqp_object.Method): - - INDEX = 0x003C001E # 60, 30; 3932190 - NAME = 'Basic.Cancel' - - def __init__(self, consumer_tag=None, nowait=False): - self.consumer_tag = consumer_tag - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class CancelOk(amqp_object.Method): - - INDEX = 0x003C001F # 60, 31; 3932191 - NAME = 'Basic.CancelOk' - - def __init__(self, consumer_tag=None): - self.consumer_tag = consumer_tag - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - return pieces - - class Publish(amqp_object.Method): - - INDEX = 0x003C0028 # 60, 40; 3932200 - NAME = 'Basic.Publish' - - def __init__(self, ticket=0, exchange='', routing_key='', mandatory=False, immediate=False): - self.ticket = ticket - self.exchange = exchange - self.routing_key = routing_key - self.mandatory = mandatory - self.immediate = immediate - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.mandatory = (bit_buffer & (1 << 0)) != 0 - self.immediate = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - bit_buffer = 0 - if self.mandatory: - bit_buffer = bit_buffer | (1 << 0) - if self.immediate: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Return(amqp_object.Method): - - INDEX = 0x003C0032 # 60, 50; 3932210 - NAME = 'Basic.Return' - - def __init__(self, reply_code=None, reply_text='', exchange=None, routing_key=None): - self.reply_code = reply_code - self.reply_text = reply_text - self.exchange = exchange - self.routing_key = routing_key - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.reply_code = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.reply_text, offset = data.decode_short_string(encoded, offset) - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.reply_code)) - assert isinstance(self.reply_text, str_or_bytes),\ - 'A non-string value was supplied for self.reply_text' - data.encode_short_string(pieces, self.reply_text) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - return pieces - - class Deliver(amqp_object.Method): - - INDEX = 0x003C003C # 60, 60; 3932220 - NAME = 'Basic.Deliver' - - def __init__(self, consumer_tag=None, delivery_tag=None, redelivered=False, exchange=None, routing_key=None): - self.consumer_tag = consumer_tag - self.delivery_tag = delivery_tag - self.redelivered = redelivered - self.exchange = exchange - self.routing_key = routing_key - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.consumer_tag, offset = data.decode_short_string(encoded, offset) - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.redelivered = (bit_buffer & (1 << 0)) != 0 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.consumer_tag, str_or_bytes),\ - 'A non-string value was supplied for self.consumer_tag' - data.encode_short_string(pieces, self.consumer_tag) - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.redelivered: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - return pieces - - class Get(amqp_object.Method): - - INDEX = 0x003C0046 # 60, 70; 3932230 - NAME = 'Basic.Get' - - def __init__(self, ticket=0, queue='', no_ack=False): - self.ticket = ticket - self.queue = queue - self.no_ack = no_ack - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - self.ticket = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - self.queue, offset = data.decode_short_string(encoded, offset) - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.no_ack = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>H', self.ticket)) - assert isinstance(self.queue, str_or_bytes),\ - 'A non-string value was supplied for self.queue' - data.encode_short_string(pieces, self.queue) - bit_buffer = 0 - if self.no_ack: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class GetOk(amqp_object.Method): - - INDEX = 0x003C0047 # 60, 71; 3932231 - NAME = 'Basic.GetOk' - - def __init__(self, delivery_tag=None, redelivered=False, exchange=None, routing_key=None, message_count=None): - self.delivery_tag = delivery_tag - self.redelivered = redelivered - self.exchange = exchange - self.routing_key = routing_key - self.message_count = message_count - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.redelivered = (bit_buffer & (1 << 0)) != 0 - self.exchange, offset = data.decode_short_string(encoded, offset) - self.routing_key, offset = data.decode_short_string(encoded, offset) - self.message_count = struct.unpack_from('>I', encoded, offset)[0] - offset += 4 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.redelivered: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - assert isinstance(self.exchange, str_or_bytes),\ - 'A non-string value was supplied for self.exchange' - data.encode_short_string(pieces, self.exchange) - assert isinstance(self.routing_key, str_or_bytes),\ - 'A non-string value was supplied for self.routing_key' - data.encode_short_string(pieces, self.routing_key) - pieces.append(struct.pack('>I', self.message_count)) - return pieces - - class GetEmpty(amqp_object.Method): - - INDEX = 0x003C0048 # 60, 72; 3932232 - NAME = 'Basic.GetEmpty' - - def __init__(self, cluster_id=''): - self.cluster_id = cluster_id - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.cluster_id, offset = data.decode_short_string(encoded, offset) - return self - - def encode(self): - pieces = list() - assert isinstance(self.cluster_id, str_or_bytes),\ - 'A non-string value was supplied for self.cluster_id' - data.encode_short_string(pieces, self.cluster_id) - return pieces - - class Ack(amqp_object.Method): - - INDEX = 0x003C0050 # 60, 80; 3932240 - NAME = 'Basic.Ack' - - def __init__(self, delivery_tag=0, multiple=False): - self.delivery_tag = delivery_tag - self.multiple = multiple - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.multiple = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.multiple: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Reject(amqp_object.Method): - - INDEX = 0x003C005A # 60, 90; 3932250 - NAME = 'Basic.Reject' - - def __init__(self, delivery_tag=None, requeue=True): - self.delivery_tag = delivery_tag - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RecoverAsync(amqp_object.Method): - - INDEX = 0x003C0064 # 60, 100; 3932260 - NAME = 'Basic.RecoverAsync' - - def __init__(self, requeue=False): - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class Recover(amqp_object.Method): - - INDEX = 0x003C006E # 60, 110; 3932270 - NAME = 'Basic.Recover' - - def __init__(self, requeue=False): - self.requeue = requeue - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.requeue = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.requeue: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class RecoverOk(amqp_object.Method): - - INDEX = 0x003C006F # 60, 111; 3932271 - NAME = 'Basic.RecoverOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Nack(amqp_object.Method): - - INDEX = 0x003C0078 # 60, 120; 3932280 - NAME = 'Basic.Nack' - - def __init__(self, delivery_tag=0, multiple=False, requeue=True): - self.delivery_tag = delivery_tag - self.multiple = multiple - self.requeue = requeue - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - self.delivery_tag = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.multiple = (bit_buffer & (1 << 0)) != 0 - self.requeue = (bit_buffer & (1 << 1)) != 0 - return self - - def encode(self): - pieces = list() - pieces.append(struct.pack('>Q', self.delivery_tag)) - bit_buffer = 0 - if self.multiple: - bit_buffer = bit_buffer | (1 << 0) - if self.requeue: - bit_buffer = bit_buffer | (1 << 1) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - -class Tx(amqp_object.Class): - - INDEX = 0x005A # 90 - NAME = 'Tx' - - class Select(amqp_object.Method): - - INDEX = 0x005A000A # 90, 10; 5898250 - NAME = 'Tx.Select' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class SelectOk(amqp_object.Method): - - INDEX = 0x005A000B # 90, 11; 5898251 - NAME = 'Tx.SelectOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Commit(amqp_object.Method): - - INDEX = 0x005A0014 # 90, 20; 5898260 - NAME = 'Tx.Commit' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class CommitOk(amqp_object.Method): - - INDEX = 0x005A0015 # 90, 21; 5898261 - NAME = 'Tx.CommitOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class Rollback(amqp_object.Method): - - INDEX = 0x005A001E # 90, 30; 5898270 - NAME = 'Tx.Rollback' - - def __init__(self): - pass - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - class RollbackOk(amqp_object.Method): - - INDEX = 0x005A001F # 90, 31; 5898271 - NAME = 'Tx.RollbackOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class Confirm(amqp_object.Class): - - INDEX = 0x0055 # 85 - NAME = 'Confirm' - - class Select(amqp_object.Method): - - INDEX = 0x0055000A # 85, 10; 5570570 - NAME = 'Confirm.Select' - - def __init__(self, nowait=False): - self.nowait = nowait - - @property - def synchronous(self): - return True - - def decode(self, encoded, offset=0): - bit_buffer = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - self.nowait = (bit_buffer & (1 << 0)) != 0 - return self - - def encode(self): - pieces = list() - bit_buffer = 0 - if self.nowait: - bit_buffer = bit_buffer | (1 << 0) - pieces.append(struct.pack('B', bit_buffer)) - return pieces - - class SelectOk(amqp_object.Method): - - INDEX = 0x0055000B # 85, 11; 5570571 - NAME = 'Confirm.SelectOk' - - def __init__(self): - pass - - @property - def synchronous(self): - return False - - def decode(self, encoded, offset=0): - return self - - def encode(self): - pieces = list() - return pieces - - -class BasicProperties(amqp_object.Properties): - - CLASS = Basic - INDEX = 0x003C # 60 - NAME = 'BasicProperties' - - FLAG_CONTENT_TYPE = (1 << 15) - FLAG_CONTENT_ENCODING = (1 << 14) - FLAG_HEADERS = (1 << 13) - FLAG_DELIVERY_MODE = (1 << 12) - FLAG_PRIORITY = (1 << 11) - FLAG_CORRELATION_ID = (1 << 10) - FLAG_REPLY_TO = (1 << 9) - FLAG_EXPIRATION = (1 << 8) - FLAG_MESSAGE_ID = (1 << 7) - FLAG_TIMESTAMP = (1 << 6) - FLAG_TYPE = (1 << 5) - FLAG_USER_ID = (1 << 4) - FLAG_APP_ID = (1 << 3) - FLAG_CLUSTER_ID = (1 << 2) - - def __init__(self, content_type=None, content_encoding=None, headers=None, delivery_mode=None, priority=None, correlation_id=None, reply_to=None, expiration=None, message_id=None, timestamp=None, type=None, user_id=None, app_id=None, cluster_id=None): - self.content_type = content_type - self.content_encoding = content_encoding - self.headers = headers - self.delivery_mode = delivery_mode - self.priority = priority - self.correlation_id = correlation_id - self.reply_to = reply_to - self.expiration = expiration - self.message_id = message_id - self.timestamp = timestamp - self.type = type - self.user_id = user_id - self.app_id = app_id - self.cluster_id = cluster_id - - def decode(self, encoded, offset=0): - flags = 0 - flagword_index = 0 - while True: - partial_flags = struct.unpack_from('>H', encoded, offset)[0] - offset += 2 - flags = flags | (partial_flags << (flagword_index * 16)) - if not (partial_flags & 1): - break - flagword_index += 1 - if flags & BasicProperties.FLAG_CONTENT_TYPE: - self.content_type, offset = data.decode_short_string(encoded, offset) - else: - self.content_type = None - if flags & BasicProperties.FLAG_CONTENT_ENCODING: - self.content_encoding, offset = data.decode_short_string(encoded, offset) - else: - self.content_encoding = None - if flags & BasicProperties.FLAG_HEADERS: - (self.headers, offset) = data.decode_table(encoded, offset) - else: - self.headers = None - if flags & BasicProperties.FLAG_DELIVERY_MODE: - self.delivery_mode = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - else: - self.delivery_mode = None - if flags & BasicProperties.FLAG_PRIORITY: - self.priority = struct.unpack_from('B', encoded, offset)[0] - offset += 1 - else: - self.priority = None - if flags & BasicProperties.FLAG_CORRELATION_ID: - self.correlation_id, offset = data.decode_short_string(encoded, offset) - else: - self.correlation_id = None - if flags & BasicProperties.FLAG_REPLY_TO: - self.reply_to, offset = data.decode_short_string(encoded, offset) - else: - self.reply_to = None - if flags & BasicProperties.FLAG_EXPIRATION: - self.expiration, offset = data.decode_short_string(encoded, offset) - else: - self.expiration = None - if flags & BasicProperties.FLAG_MESSAGE_ID: - self.message_id, offset = data.decode_short_string(encoded, offset) - else: - self.message_id = None - if flags & BasicProperties.FLAG_TIMESTAMP: - self.timestamp = struct.unpack_from('>Q', encoded, offset)[0] - offset += 8 - else: - self.timestamp = None - if flags & BasicProperties.FLAG_TYPE: - self.type, offset = data.decode_short_string(encoded, offset) - else: - self.type = None - if flags & BasicProperties.FLAG_USER_ID: - self.user_id, offset = data.decode_short_string(encoded, offset) - else: - self.user_id = None - if flags & BasicProperties.FLAG_APP_ID: - self.app_id, offset = data.decode_short_string(encoded, offset) - else: - self.app_id = None - if flags & BasicProperties.FLAG_CLUSTER_ID: - self.cluster_id, offset = data.decode_short_string(encoded, offset) - else: - self.cluster_id = None - return self - - def encode(self): - pieces = list() - flags = 0 - if self.content_type is not None: - flags = flags | BasicProperties.FLAG_CONTENT_TYPE - assert isinstance(self.content_type, str_or_bytes),\ - 'A non-string value was supplied for self.content_type' - data.encode_short_string(pieces, self.content_type) - if self.content_encoding is not None: - flags = flags | BasicProperties.FLAG_CONTENT_ENCODING - assert isinstance(self.content_encoding, str_or_bytes),\ - 'A non-string value was supplied for self.content_encoding' - data.encode_short_string(pieces, self.content_encoding) - if self.headers is not None: - flags = flags | BasicProperties.FLAG_HEADERS - data.encode_table(pieces, self.headers) - if self.delivery_mode is not None: - flags = flags | BasicProperties.FLAG_DELIVERY_MODE - pieces.append(struct.pack('B', self.delivery_mode)) - if self.priority is not None: - flags = flags | BasicProperties.FLAG_PRIORITY - pieces.append(struct.pack('B', self.priority)) - if self.correlation_id is not None: - flags = flags | BasicProperties.FLAG_CORRELATION_ID - assert isinstance(self.correlation_id, str_or_bytes),\ - 'A non-string value was supplied for self.correlation_id' - data.encode_short_string(pieces, self.correlation_id) - if self.reply_to is not None: - flags = flags | BasicProperties.FLAG_REPLY_TO - assert isinstance(self.reply_to, str_or_bytes),\ - 'A non-string value was supplied for self.reply_to' - data.encode_short_string(pieces, self.reply_to) - if self.expiration is not None: - flags = flags | BasicProperties.FLAG_EXPIRATION - assert isinstance(self.expiration, str_or_bytes),\ - 'A non-string value was supplied for self.expiration' - data.encode_short_string(pieces, self.expiration) - if self.message_id is not None: - flags = flags | BasicProperties.FLAG_MESSAGE_ID - assert isinstance(self.message_id, str_or_bytes),\ - 'A non-string value was supplied for self.message_id' - data.encode_short_string(pieces, self.message_id) - if self.timestamp is not None: - flags = flags | BasicProperties.FLAG_TIMESTAMP - pieces.append(struct.pack('>Q', self.timestamp)) - if self.type is not None: - flags = flags | BasicProperties.FLAG_TYPE - assert isinstance(self.type, str_or_bytes),\ - 'A non-string value was supplied for self.type' - data.encode_short_string(pieces, self.type) - if self.user_id is not None: - flags = flags | BasicProperties.FLAG_USER_ID - assert isinstance(self.user_id, str_or_bytes),\ - 'A non-string value was supplied for self.user_id' - data.encode_short_string(pieces, self.user_id) - if self.app_id is not None: - flags = flags | BasicProperties.FLAG_APP_ID - assert isinstance(self.app_id, str_or_bytes),\ - 'A non-string value was supplied for self.app_id' - data.encode_short_string(pieces, self.app_id) - if self.cluster_id is not None: - flags = flags | BasicProperties.FLAG_CLUSTER_ID - assert isinstance(self.cluster_id, str_or_bytes),\ - 'A non-string value was supplied for self.cluster_id' - data.encode_short_string(pieces, self.cluster_id) - flag_pieces = list() - while True: - remainder = flags >> 16 - partial_flags = flags & 0xFFFE - if remainder != 0: - partial_flags |= 1 - flag_pieces.append(struct.pack('>H', partial_flags)) - flags = remainder - if not flags: - break - return flag_pieces + pieces - -methods = { - 0x000A000A: Connection.Start, - 0x000A000B: Connection.StartOk, - 0x000A0014: Connection.Secure, - 0x000A0015: Connection.SecureOk, - 0x000A001E: Connection.Tune, - 0x000A001F: Connection.TuneOk, - 0x000A0028: Connection.Open, - 0x000A0029: Connection.OpenOk, - 0x000A0032: Connection.Close, - 0x000A0033: Connection.CloseOk, - 0x000A003C: Connection.Blocked, - 0x000A003D: Connection.Unblocked, - 0x0014000A: Channel.Open, - 0x0014000B: Channel.OpenOk, - 0x00140014: Channel.Flow, - 0x00140015: Channel.FlowOk, - 0x00140028: Channel.Close, - 0x00140029: Channel.CloseOk, - 0x001E000A: Access.Request, - 0x001E000B: Access.RequestOk, - 0x0028000A: Exchange.Declare, - 0x0028000B: Exchange.DeclareOk, - 0x00280014: Exchange.Delete, - 0x00280015: Exchange.DeleteOk, - 0x0028001E: Exchange.Bind, - 0x0028001F: Exchange.BindOk, - 0x00280028: Exchange.Unbind, - 0x00280033: Exchange.UnbindOk, - 0x0032000A: Queue.Declare, - 0x0032000B: Queue.DeclareOk, - 0x00320014: Queue.Bind, - 0x00320015: Queue.BindOk, - 0x0032001E: Queue.Purge, - 0x0032001F: Queue.PurgeOk, - 0x00320028: Queue.Delete, - 0x00320029: Queue.DeleteOk, - 0x00320032: Queue.Unbind, - 0x00320033: Queue.UnbindOk, - 0x003C000A: Basic.Qos, - 0x003C000B: Basic.QosOk, - 0x003C0014: Basic.Consume, - 0x003C0015: Basic.ConsumeOk, - 0x003C001E: Basic.Cancel, - 0x003C001F: Basic.CancelOk, - 0x003C0028: Basic.Publish, - 0x003C0032: Basic.Return, - 0x003C003C: Basic.Deliver, - 0x003C0046: Basic.Get, - 0x003C0047: Basic.GetOk, - 0x003C0048: Basic.GetEmpty, - 0x003C0050: Basic.Ack, - 0x003C005A: Basic.Reject, - 0x003C0064: Basic.RecoverAsync, - 0x003C006E: Basic.Recover, - 0x003C006F: Basic.RecoverOk, - 0x003C0078: Basic.Nack, - 0x005A000A: Tx.Select, - 0x005A000B: Tx.SelectOk, - 0x005A0014: Tx.Commit, - 0x005A0015: Tx.CommitOk, - 0x005A001E: Tx.Rollback, - 0x005A001F: Tx.RollbackOk, - 0x0055000A: Confirm.Select, - 0x0055000B: Confirm.SelectOk -} - -props = { - 0x003C: BasicProperties -} - - -def has_content(methodNumber): - return methodNumber in ( - Basic.Publish.INDEX, - Basic.Return.INDEX, - Basic.Deliver.INDEX, - Basic.GetOk.INDEX, - ) diff --git a/packages_o/pika-0.10.0/pika/utils.py b/packages_o/pika-0.10.0/pika/utils.py deleted file mode 100644 index 57f93b0a..00000000 --- a/packages_o/pika-0.10.0/pika/utils.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Non-module specific functions shared by modules in the pika package - -""" -import collections - - -def is_callable(handle): - """Returns a bool value if the handle passed in is a callable - method/function - - :param any handle: The object to check - :rtype: bool - - """ - return isinstance(handle, collections.Callable) diff --git a/packages_o/pika-0.10.0/setup.cfg b/packages_o/pika-0.10.0/setup.cfg deleted file mode 100644 index 6f08d0e3..00000000 --- a/packages_o/pika-0.10.0/setup.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[bdist_wheel] -universal = 1 - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - diff --git a/packages_o/pika-0.10.0/setup.py b/packages_o/pika-0.10.0/setup.py deleted file mode 100644 index 2d28142b..00000000 --- a/packages_o/pika-0.10.0/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -from setuptools import setup -import os - -# Conditionally include additional modules for docs -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -requirements = list() -if on_rtd: - requirements.append('tornado') - requirements.append('twisted') - #requirements.append('pyev') - -long_description = ('Pika is a pure-Python implementation of the AMQP 0-9-1 ' - 'protocol that tries to stay fairly independent of the ' - 'underlying network support library. Pika was developed ' - 'primarily for use with RabbitMQ, but should also work ' - 'with other AMQP 0-9-1 brokers.') - -setup(name='pika', - version='0.10.0', - description='Pika Python AMQP Client Library', - long_description=open('README.rst').read(), - maintainer='Gavin M. Roy', - maintainer_email='gavinmroy@gmail.com', - url='https://pika.readthedocs.org ', - packages=['pika', 'pika.adapters'], - license='BSD', - install_requires=requirements, - package_data={'': ['LICENSE', 'README.rst']}, - extras_require={'tornado': ['tornado'], - 'twisted': ['twisted'], - 'libev': ['pyev']}, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: Jython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Communications', - 'Topic :: Internet', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: System :: Networking'], - zip_safe=True) diff --git a/registrationprocess.py b/registrationprocess.py index 0742fc88..96c3e418 100644 --- a/registrationprocess.py +++ b/registrationprocess.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # registrationprocess.py import sys import os @@ -45,7 +43,7 @@ def callback(self,ch,method,props,body): Each subtype is handled as a apart of an if-elif statement. """ - header, opt, msg = unpack(body) + header,msg = unpack(body) s_uniqid_str = nodeid_int2hexstr(header["s_uniqid"]) major = chr(header["msg_mj_type"]) @@ -111,7 +109,7 @@ def callback(self,ch,method,props,body): # Write the request to a file to be used by the CA for signing replyQueue = msg.split("\n")[0] msg = "\n".join(msg.split("\n")[1:]) - print(replyQueue) + print replyQueue certFile = "/tmp/" + self.name with open(certFile + "_req.pem","w+") as cert: cert.write(msg) @@ -122,7 +120,7 @@ def callback(self,ch,method,props,body): cert = "" with open(certFile + "_cert.pem","r") as cert: cert = cert.read() - print(cert) + print cert ch.basic_publish(exchange='', routing_key=replyQueue, body=cert) @@ -185,7 +183,7 @@ def callback(self,ch,method,props,body): "r_uniqid" : header["s_uniqid"], "resp_session": header["snd_session"] } - msg = "Congratulations node {}! You are registered under the queue {}!".format(s_uniqid_str, queue).encode('iso-8859-1') + msg = "Congratulations node {}! You are registered under the queue {}!".format(s_uniqid_str, queue) for packet in pack(resp_header,msg): response = packet self.channel.basic_publish(exchange='waggle_in',routing_key="in",body=response) diff --git a/systemd/beehive-server.service b/systemd/beehive-server.service index 137a6a8c..893b8da1 100644 --- a/systemd/beehive-server.service +++ b/systemd/beehive-server.service @@ -20,7 +20,7 @@ ExecStart=/bin/docker run \ --name ${CONTAINER} \ --net beehive \ -v ${DATA}/waggle/SSL/:/usr/lib/waggle/SSL/ \ - waggle/beehive-server:latest bash -c 'git pull ; git submodule update --init ; ./configure && python3 -u ./Server.py' + waggle/beehive-server:latest bash -c 'git pull ; git submodule update --init ; ./configure && python -u ./Server.py' ExecStop=/usr/bin/docker stop --time=10 ${CONTAINER} diff --git a/utilitiesprocess.py b/utilitiesprocess.py index e8e1eca6..9e7fb36b 100644 --- a/utilitiesprocess.py +++ b/utilitiesprocess.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # utilitiesprocess.py import sys sys.path.append("..") @@ -62,7 +60,7 @@ def _callback(self,ch,method,props,body): "r_uniqid" : header["s_uniqid"], "resp_session": header["snd_session"] } - packer = pack(resp_header,"Pong!".encode('iso-8859-1')) + packer = pack(resp_header,"Pong!") # Need to use a for each loop because packer yields packets, it doesnt return them for packet in packer: response = packet @@ -77,7 +75,7 @@ def _callback(self,ch,method,props,body): "r_uniqid" : header["s_uniqid"], "resp_session": header["snd_session"] } - packer = pack(resp_header,str(time.time()).encode('iso-8859-1')) # Stuff the time in a packet and send it to the router + packer = pack(resp_header,str(time.time())) # Stuff the time in a packet and send it to the router for packet in packer: response = packet self.channel.basic_publish(exchange='waggle_in',routing_key="in",body=response) diff --git a/waggle_protocol b/waggle_protocol index 08904b6e..13c40773 160000 --- a/waggle_protocol +++ b/waggle_protocol @@ -1 +1 @@ -Subproject commit 08904b6ee7af5d5319b13675b2f72bf1d51918cb +Subproject commit 13c4077314962919b02a3d976b836f1a28ad8ea9