Hello community, here is the log from the commit of package python-kombu for openSUSE:Factory checked in at 2015-04-23 08:04:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-kombu (Old) and /work/SRC/openSUSE:Factory/.python-kombu.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-kombu" Changes: -------- --- /work/SRC/openSUSE:Factory/python-kombu/python-kombu.changes 2014-11-24 11:09:01.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-kombu.new/python-kombu.changes 2015-04-23 08:05:00.000000000 +0200 @@ -1,0 +2,26 @@ +Wed Apr 22 13:39:19 UTC 2015 - tbecht...@suse.com + +- update to 3.0.25: + - pyamqp/librabbitmq now uses 5671 as default port when SSL is enabled + (Issue #459). + - Redis: Now supports passwords in ``redis+socket://:pass@host:port`` URLs + (Issue #460). + - ``Producer.publish`` now defines the ``expiration`` property in support + of the `RabbitMQ per-message TTL extension`_. + - Connection transport attribute now set correctly for all transports. + - qpid: Fixed bug where the connectionw as not being closed properly. + - :class:`~kombu.entity.bindings` is now JSON serializable (Issue #453). + - Fixed typo in error when yaml is not installed (said ``msgpack``). + - Redis: Now properly handles ``TimeoutError raised by py-redis. + - qpid: Adds additional string to check for when connecting to qpid. + When we connect to qpid, we need to ensure that we skip to the next SASL + mechanism if the current mechanism fails. Otherwise, we will keep retrying + the connection with a non-working mech. + - qpid: Handle ``NotFound`` exceptions. + - :class:`Queue.__repr__` now makes sure return value is not unicode + (Issue #440). + - qpid: ``Queue.purge`` incorrectly raised :exc:`AttributeErrror` if the + does not exist (Issue #439). + - Linux: Now ignores permission errors on epoll unregister. + +------------------------------------------------------------------- Old: ---- kombu-3.0.24.tar.gz New: ---- kombu-3.0.25.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-kombu.spec ++++++ --- /var/tmp/diff_new_pack.CZhid5/_old 2015-04-23 08:05:00.000000000 +0200 +++ /var/tmp/diff_new_pack.CZhid5/_new 2015-04-23 08:05:00.000000000 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-kombu # -# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-kombu -Version: 3.0.24 +Version: 3.0.25 Release: 0 Summary: AMQP Messaging Framework for Python License: BSD-3-Clause ++++++ kombu-3.0.24.tar.gz -> kombu-3.0.25.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/AUTHORS new/kombu-3.0.25/AUTHORS --- old/kombu-3.0.24/AUTHORS 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/AUTHORS 2015-04-17 15:49:28.000000000 +0200 @@ -8,6 +8,7 @@ Adam Wentz Alex Koshelev <daeva...@gmail.com> Alexandre Bourget <alexandre.bour...@savoirfairelinux.com> +Anastasis Andronidis <anastasi...@yahoo.gr> Andrew Watts Andrey Antukh <n...@niwi.be> Andrii Kostenko <and...@kostenko.name> @@ -100,6 +101,7 @@ Sean Bleier <seble...@gmail.com> Sean Creeley <sean.cree...@gmail.com> Seb Insua <sebastian.in...@saffrondigital.com> +Sergey Tikhonov <zimb...@gmail.com> Shane Caraveo <sh...@caraveo.com> Steeve Morin <steeve.mo...@gmail.com> Stefan Eletzhofer <s...@nexiles.de> @@ -120,3 +122,4 @@ Zhao Xiaohong <mrlua...@gmail.com> haridsv iSlava <sig.c...@gmail.com> +markow <mar...@red-sky.pl> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/Changelog new/kombu-3.0.25/Changelog --- old/kombu-3.0.24/Changelog 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/Changelog 2015-04-21 16:09:36.000000000 +0200 @@ -4,6 +4,68 @@ Change history ================ +.. _version-3.0.25: + +3.0.25 +====== +:release-date: 2015-04-21 02:00 P.M UTC +:releasy-by: Ask Solem + +- pyamqp/librabbitmq now uses 5671 as default port when SSL is enabled + (Issue #459). + +- Redis: Now supports passwords in ``redis+socket://:pass@host:port`` URLs + (Issue #460). + +- ``Producer.publish`` now defines the ``expiration`` property in support + of the `RabbitMQ per-message TTL extension`_. + + Contributed by Anastasis Andronidis. + +- Connection transport attribute now set correctly for all transports. + + Contributed by Alex Koshelev. + +- qpid: Fixed bug where the connectionw as not being closed properly. + + Contributed by Brian Bouterse. + +- :class:`~kombu.entity.bindings` is now JSON serializable (Issue #453). + + Contributed by Sergey Tikhonov. + +- Fixed typo in error when yaml is not installed (said ``msgpack``). + + Contributed by Joshua Harlow. + +- Redis: Now properly handles ``TimeoutError raised by py-redis. + + Contributed by markow. + +- qpid: Adds additional string to check for when connecting to qpid. + + When we connect to qpid, we need to ensure that we skip to the next SASL + mechanism if the current mechanism fails. Otherwise, we will keep retrying the + connection with a non-working mech. + + Contributed by Chris Duryee. + +- qpid: Handle ``NotFound`` exceptions. + + Contributed by Brian Bouterse. + +- :class:`Queue.__repr__` now makes sure return value is not unicode + (Issue #440). + +- qpid: ``Queue.purge`` incorrectly raised :exc:`AttributeErrror` if the + does not exist (Issue #439). + + Contributed by Brian Bouterse. + +- Linux: Now ignores permission errors on epoll unregister. + +.. _`RabbitMQ per-message TTL extension`: https://www.rabbitmq.com/ttl.html + .. _version-3.0.24: 3.0.24 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/LICENSE new/kombu-3.0.25/LICENSE --- old/kombu-3.0.24/LICENSE 2014-07-10 18:50:16.000000000 +0200 +++ new/kombu-3.0.25/LICENSE 2015-04-21 15:53:49.000000000 +0200 @@ -1,3 +1,4 @@ +Copyright (c) 2015 Ask Solem & contributors. All rights reserved. Copyright (c) 2012-2014 GoPivotal, Inc. All rights reserved. Copyright (c) 2009-2012, Ask Solem & contributors. All rights reserved. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/PKG-INFO new/kombu-3.0.25/PKG-INFO --- old/kombu-3.0.24/PKG-INFO 2014-11-18 01:11:14.000000000 +0100 +++ new/kombu-3.0.25/PKG-INFO 2015-04-21 16:26:29.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: kombu -Version: 3.0.24 +Version: 3.0.25 Summary: Messaging library for Python Home-page: http://kombu.readthedocs.org Author: Ask Solem diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/docs/changelog.rst new/kombu-3.0.25/docs/changelog.rst --- old/kombu-3.0.24/docs/changelog.rst 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/docs/changelog.rst 2015-04-21 16:09:36.000000000 +0200 @@ -4,6 +4,68 @@ Change history ================ +.. _version-3.0.25: + +3.0.25 +====== +:release-date: 2015-04-21 02:00 P.M UTC +:releasy-by: Ask Solem + +- pyamqp/librabbitmq now uses 5671 as default port when SSL is enabled + (Issue #459). + +- Redis: Now supports passwords in ``redis+socket://:pass@host:port`` URLs + (Issue #460). + +- ``Producer.publish`` now defines the ``expiration`` property in support + of the `RabbitMQ per-message TTL extension`_. + + Contributed by Anastasis Andronidis. + +- Connection transport attribute now set correctly for all transports. + + Contributed by Alex Koshelev. + +- qpid: Fixed bug where the connectionw as not being closed properly. + + Contributed by Brian Bouterse. + +- :class:`~kombu.entity.bindings` is now JSON serializable (Issue #453). + + Contributed by Sergey Tikhonov. + +- Fixed typo in error when yaml is not installed (said ``msgpack``). + + Contributed by Joshua Harlow. + +- Redis: Now properly handles ``TimeoutError raised by py-redis. + + Contributed by markow. + +- qpid: Adds additional string to check for when connecting to qpid. + + When we connect to qpid, we need to ensure that we skip to the next SASL + mechanism if the current mechanism fails. Otherwise, we will keep retrying the + connection with a non-working mech. + + Contributed by Chris Duryee. + +- qpid: Handle ``NotFound`` exceptions. + + Contributed by Brian Bouterse. + +- :class:`Queue.__repr__` now makes sure return value is not unicode + (Issue #440). + +- qpid: ``Queue.purge`` incorrectly raised :exc:`AttributeErrror` if the + does not exist (Issue #439). + + Contributed by Brian Bouterse. + +- Linux: Now ignores permission errors on epoll unregister. + +.. _`RabbitMQ per-message TTL extension`: https://www.rabbitmq.com/ttl.html + .. _version-3.0.24: 3.0.24 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/__init__.py new/kombu-3.0.25/kombu/__init__.py --- old/kombu-3.0.24/kombu/__init__.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/__init__.py 2015-04-21 15:53:16.000000000 +0200 @@ -7,7 +7,7 @@ 'version_info_t', ('major', 'minor', 'micro', 'releaselevel', 'serial'), ) -VERSION = version_info_t(3, 0, 24, '', '') +VERSION = version_info_t(3, 0, 25, '', '') __version__ = '{0.major}.{0.minor}.{0.micro}{0.releaselevel}'.format(VERSION) __author__ = 'Ask Solem' __contact__ = 'a...@celeryproject.org' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/connection.py new/kombu-3.0.25/kombu/connection.py --- old/kombu-3.0.24/kombu/connection.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/connection.py 2015-04-17 15:49:28.000000000 +0200 @@ -162,12 +162,12 @@ transport = self.uri_prefix = params['transport'] else: transport = transport or urlparse(hostname).scheme - if get_transport_cls(transport).can_parse_url: - # set the transport so that the default is not used. - params['transport'] = transport - else: + if not get_transport_cls(transport).can_parse_url: # we must parse the URL params.update(parse_url(hostname)) + + params['transport'] = transport + self._init_params(**params) # fallback hosts diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/entity.py new/kombu-3.0.25/kombu/entity.py --- old/kombu-3.0.24/kombu/entity.py 2014-09-30 17:28:50.000000000 +0200 +++ new/kombu-3.0.25/kombu/entity.py 2015-04-17 15:49:42.000000000 +0200 @@ -9,6 +9,7 @@ from .abstract import MaybeChannelBound from .exceptions import ContentDisallowed +from .five import string_t from .serialization import prepare_accept_content TRANSIENT_DELIVERY_MODE = 1 @@ -19,6 +20,13 @@ __all__ = ['Exchange', 'Queue', 'binding'] +def _reprstr(s): + s = repr(s) + if isinstance(s, string_t) and s.startswith("u'"): + return s[2:-1] + return s[1:-1] + + def pretty_bindings(bindings): return '[%s]' % (', '.join(map(str, bindings))) @@ -284,7 +292,7 @@ return super(Exchange, self).__repr__(str(self)) def __str__(self): - return 'Exchange %s(%s)' % (self.name or repr(''), self.type) + return 'Exchange %s(%s)' % (_reprstr(self.name) or repr(''), self.type) @property def can_cache_declaration(self): @@ -332,7 +340,9 @@ return '<binding: %s>' % (self, ) def __str__(self): - return '%s->%s' % (self.exchange.name, self.routing_key) + return '%s->%s' % ( + _reprstr(self.exchange.name), _reprstr(self.routing_key), + ) class Queue(MaybeChannelBound): @@ -663,12 +673,16 @@ def __repr__(self): s = super(Queue, self).__repr__ if self.bindings: - return s('Queue {0.name} -> {bindings}'.format( - self, bindings=pretty_bindings(self.bindings), + return s('Queue {name} -> {bindings}'.format( + name=_reprstr(self.name), + bindings=pretty_bindings(self.bindings), )) return s( - 'Queue {0.name} -> {0.exchange!r} -> {0.routing_key}'.format( - self)) + 'Queue {name} -> {0.exchange!r} -> {routing_key}'.format( + self, name=_reprstr(self.name), + routing_key=_reprstr(self.routing_key), + ), + ) @property def can_cache_declaration(self): @@ -716,3 +730,12 @@ queue_arguments=q_arguments, binding_arguments=b_arguments, bindings=bindings) + + def as_dict(self, recurse=False): + res = super(Queue, self).as_dict(recurse) + if not recurse: + return res + bindings = res.get('bindings') + if bindings: + res['bindings'] = [b.as_dict(recurse=True) for b in bindings] + return res diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/messaging.py new/kombu-3.0.25/kombu/messaging.py --- old/kombu-3.0.24/kombu/messaging.py 2014-09-30 17:28:50.000000000 +0200 +++ new/kombu-3.0.25/kombu/messaging.py 2015-04-17 15:16:01.000000000 +0200 @@ -114,7 +114,7 @@ mandatory=False, immediate=False, priority=0, content_type=None, content_encoding=None, serializer=None, headers=None, compression=None, exchange=None, retry=False, - retry_policy=None, declare=[], **properties): + retry_policy=None, declare=[], expiration=None, **properties): """Publish message to the specified exchange. :param body: Message body. @@ -138,6 +138,8 @@ connection is lost. :keyword retry_policy: Retry configuration, this is the keywords supported by :meth:`~kombu.Connection.ensure`. + :keyword expiration: A TTL in seconds can be specified per message. + Default is no expiration. :keyword \*\*properties: Additional message properties, see AMQP spec. """ @@ -155,6 +157,8 @@ if not isinstance(delivery_mode, numbers.Integral): delivery_mode = DELIVERY_MODES[delivery_mode] properties['delivery_mode'] = delivery_mode + if expiration is not None: + properties['expiration'] = str(int(expiration*1000)) body, content_type, content_encoding = self._prepare( body, serializer, content_type, content_encoding, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/serialization.py new/kombu-3.0.25/kombu/serialization.py --- old/kombu-3.0.24/kombu/serialization.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/serialization.py 2015-04-17 15:49:28.000000000 +0200 @@ -376,7 +376,7 @@ except (ImportError, ValueError): def not_available(*args, **kwargs): - """In case a client receives a msgpack message, but yaml + """In case a client receives a msgpack message, but msgpack isn't installed.""" raise SerializerNotInstalled( 'No decoder installed for msgpack. ' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/tests/transport/test_qpid.py new/kombu-3.0.25/kombu/tests/transport/test_qpid.py --- old/kombu-3.0.24/kombu/tests/transport/test_qpid.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/tests/transport/test_qpid.py 2015-04-17 15:50:21.000000000 +0200 @@ -14,15 +14,15 @@ from mock import call from kombu.five import Empty, keys, range, monotonic -from kombu.transport.qpid import AuthenticationFailure, QoS, Message -from kombu.transport.qpid import QpidMessagingExceptionHandler, Channel -from kombu.transport.qpid import Connection, ReceiversMonitor, Transport -from kombu.transport.qpid import ConnectionError +from kombu.transport.qpid import (AuthenticationFailure, Channel, Connection, + ConnectionError, Message, NotFound, QoS, + ReceiversMonitor, Transport) from kombu.transport.virtual import Base64 from kombu.tests.case import Case, Mock, case_no_pypy, case_no_python3 from kombu.tests.case import patch from kombu.utils.compat import OrderedDict + QPID_MODULE = 'kombu.transport.qpid' @@ -73,8 +73,15 @@ self.assertEqual(a[key], b[key]) -class MockException(Exception): - pass +class QpidException(Exception): + """ + An object used to mock Exceptions provided by qpid.messaging.exceptions + """ + + def __init__(self, code=None, text=None): + super(Exception, self).__init__(self) + self.code = code + self.text = text class BreakOutException(Exception): @@ -83,44 +90,6 @@ @case_no_python3 @case_no_pypy -class TestQpidMessagingExceptionHandler(Case): - - allowed_string = 'object in use' - not_allowed_string = 'a different string' - - def setUp(self): - """Create a mock ExceptionHandler for testing by this object.""" - self.handler = QpidMessagingExceptionHandler(self.allowed_string) - - def test_string_stored(self): - """Assert that the allowed_exception_string is stored correctly""" - handler_string = self.handler.allowed_exception_string - self.assertEqual(self.allowed_string, handler_string) - - def test_exception_positive(self): - """Assert that an exception is silenced if it contains the - allowed_string text.""" - exception_to_raise = Exception(self.allowed_string) - - def exception_raise_fun(): - raise exception_to_raise - decorated_fun = self.handler(exception_raise_fun) - decorated_fun() - - def test_exception_negative(self): - """Assert that an exception that does not contain the - allowed_string text is properly raised.""" - exception_to_raise = Exception(self.not_allowed_string) - - def exception_raise_fun(): - raise exception_to_raise - decorated_fun = self.handler(exception_raise_fun) - with self.assertRaises(Exception): - decorated_fun() - - -@case_no_python3 -@case_no_pypy class TestQoS__init__(Case): def setUp(self): @@ -328,7 +297,7 @@ @case_no_pypy class TestConnectionInit(ExtraAssertionsMixin, ConnectionTestBase): - def test_connection__init__stores_connection_options(self): + def test_stores_connection_options(self): # ensure that only one mech was passed into connection. The other # options should all be passed through as-is modified_conn_opts = self.connection_options @@ -337,67 +306,90 @@ modified_conn_opts, self.conn.connection_options, ) - def test_connection__init__variables(self): + def test_class_variables(self): self.assertIsInstance(self.conn.channels, list) self.assertIsInstance(self.conn._callbacks, dict) - def test_connection__init__establishes_connection(self): + def test_establishes_connection(self): modified_conn_opts = self.connection_options modified_conn_opts['sasl_mechanisms'] = 'PLAIN' self.mock_qpid_connection.establish.assert_called_with( **modified_conn_opts ) - def test_connection__init__saves_established_connection(self): + def test_saves_established_connection(self): created_conn = self.mock_qpid_connection.establish.return_value self.assertIs(self.conn._qpid_conn, created_conn) - @patch(QPID_MODULE + '.ConnectionError', new=(MockException, )) + @patch(QPID_MODULE + '.ConnectionError', new=(QpidException, )) @patch(QPID_MODULE + '.sys.exc_info') @patch(QPID_MODULE + '.qpid') - def test_init_mutates_ConnError_by_message(self, mock_qpid, mock_exc_info): - my_conn_error = MockException() - my_conn_error.text = 'connection-forced: Authentication failed(320)' + def test_mutates_ConnError_by_message(self, mock_qpid, mock_exc_info): + text = 'connection-forced: Authentication failed(320)' + my_conn_error = QpidException(text=text) mock_qpid.messaging.Connection.establish.side_effect = my_conn_error mock_exc_info.return_value = 'a', 'b', None try: self.conn = Connection(**self.connection_options) except AuthenticationFailure as error: exc_info = sys.exc_info() - self.assertNotIsInstance(error, MockException) + self.assertNotIsInstance(error, QpidException) self.assertIs(exc_info[1], 'b') self.assertIsNone(exc_info[2]) else: self.fail('ConnectionError type was not mutated correctly') - @patch(QPID_MODULE + '.ConnectionError', new=(MockException, )) + @patch(QPID_MODULE + '.ConnectionError', new=(QpidException, )) @patch(QPID_MODULE + '.sys.exc_info') @patch(QPID_MODULE + '.qpid') - def test__init_mutates_ConnError_by_code(self, mock_qpid, mock_exc_info): - my_conn_error = MockException() - my_conn_error.code = 320 - my_conn_error.text = 'someothertext' + def test_mutates_ConnError_by_code(self, mock_qpid, mock_exc_info): + my_conn_error = QpidException(code=320, text='someothertext') mock_qpid.messaging.Connection.establish.side_effect = my_conn_error mock_exc_info.return_value = 'a', 'b', None try: self.conn = Connection(**self.connection_options) except AuthenticationFailure as error: exc_info = sys.exc_info() - self.assertNotIsInstance(error, MockException) + self.assertNotIsInstance(error, QpidException) self.assertIs(exc_info[1], 'b') self.assertIsNone(exc_info[2]) else: self.fail('ConnectionError type was not mutated correctly') - @patch(QPID_MODULE + '.ConnectionError', new=(MockException, )) + @patch(QPID_MODULE + '.ConnectionError', new=(QpidException, )) @patch(QPID_MODULE + '.sys.exc_info') @patch(QPID_MODULE + '.qpid') - def test_init_unknown_connection_error(self, mock_qpid, mock_exc_info): + def test_connection__init__mutates_ConnError_by_message2(self, mock_qpid, + mock_exc_info): + """ + Test for PLAIN connection via python-saslwrapper, sans cyrus-sasl-plain + + This test is specific for what is returned when we attempt to connect + with PLAIN mech and python-saslwrapper is installed, but + cyrus-sasl-plain is not installed. + """ + my_conn_error = QpidException() + my_conn_error.text = 'Error in sasl_client_start (-4) SASL(-4): no '\ + 'mechanism available' + mock_qpid.messaging.Connection.establish.side_effect = my_conn_error + mock_exc_info.return_value = ('a', 'b', None) + try: + self.conn = Connection(**self.connection_options) + except AuthenticationFailure as error: + exc_info = sys.exc_info() + self.assertTrue(not isinstance(error, QpidException)) + self.assertTrue(exc_info[1] is 'b') + self.assertTrue(exc_info[2] is None) + else: + self.fail('ConnectionError type was not mutated correctly') + + @patch(QPID_MODULE + '.ConnectionError', new=(QpidException, )) + @patch(QPID_MODULE + '.sys.exc_info') + @patch(QPID_MODULE + '.qpid') + def test_unknown_connection_error(self, mock_qpid, mock_exc_info): # If we get a connection error that we don't understand, # bubble it up as-is - my_conn_error = MockException() - my_conn_error.code = 999 - my_conn_error.text = 'someothertext' + my_conn_error = QpidException(code=999, text='someothertext') mock_qpid.messaging.Connection.establish.side_effect = my_conn_error mock_exc_info.return_value = 'a', 'b', None try: @@ -407,10 +399,10 @@ else: self.fail('Connection should have thrown an exception') - @patch.object(Transport, 'channel_errors', new=(MockException, )) + @patch.object(Transport, 'channel_errors', new=(QpidException, )) @patch(QPID_MODULE + '.qpid') @patch(QPID_MODULE + '.ConnectionError', new=IOError) - def test_connection__init__non_qpid_error_raises(self, mock_qpid): + def test_non_qpid_error_raises(self, mock_qpid): mock_Qpid_Connection = mock_qpid.messaging.Connection my_conn_error = SyntaxError() my_conn_error.text = 'some non auth related error message' @@ -420,7 +412,7 @@ @patch(QPID_MODULE + '.qpid') @patch(QPID_MODULE + '.ConnectionError', new=IOError) - def test_connection__init__non_auth_conn_error_raises(self, mock_qpid): + def test_non_auth_conn_error_raises(self, mock_qpid): mock_Qpid_Connection = mock_qpid.messaging.Connection my_conn_error = IOError() my_conn_error.text = 'some non auth related error message' @@ -449,6 +441,16 @@ @case_no_python3 @case_no_pypy +class TestConnectionClose(ConnectionTestBase): + + def test_connection_close(self): + self.conn._qpid_conn = Mock() + self.conn.close() + self.conn._qpid_conn.close.assert_called_once_with() + + +@case_no_python3 +@case_no_pypy class TestConnectionCloseChannel(ConnectionTestBase): def setUp(self): @@ -496,32 +498,37 @@ super(TestChannelPurge, self).setUp() self.mock_queue = Mock() - def test_channel__purge_gets_queue(self): + def test_gets_queue(self): self.channel._purge(self.mock_queue) getQueue = self.mock_broker_agent.return_value.getQueue getQueue.assert_called_once_with(self.mock_queue) - def test_channel__purge_does_not_call_purge_if_message_count_is_zero(self): + def test_does_not_call_purge_if_message_count_is_zero(self): values = {'msgDepth': 0} queue_obj = self.mock_broker_agent.return_value.getQueue.return_value queue_obj.values = values self.channel._purge(self.mock_queue) self.assertFalse(queue_obj.purge.called) - def test_channel__purge_purges_all_messages_from_queue(self): + def test_purges_all_messages_from_queue(self): values = {'msgDepth': 5} queue_obj = self.mock_broker_agent.return_value.getQueue.return_value queue_obj.values = values self.channel._purge(self.mock_queue) queue_obj.purge.assert_called_with(5) - def test_channel__purge_returns_message_count(self): + def test_returns_message_count(self): values = {'msgDepth': 5} queue_obj = self.mock_broker_agent.return_value.getQueue.return_value queue_obj.values = values result = self.channel._purge(self.mock_queue) self.assertEqual(result, 5) + @patch(QPID_MODULE + '.NotFound', new=QpidException) + def test_raises_channel_error_if_queue_does_not_exist(self): + self.mock_broker_agent.return_value.getQueue.return_value = None + self.assertRaises(QpidException, self.channel._purge, self.mock_queue) + @case_no_python3 @case_no_pypy @@ -857,38 +864,38 @@ self.mock__delete.stop() super(TestChannelQueueDelete, self).tearDown() - def test_channel_queue_delete_checks_if_queue_exists(self): + def test_checks_if_queue_exists(self): self.channel.queue_delete(self.mock_queue) self.mock__has_queue.assert_called_once_with(self.mock_queue) - def test_channel_queue_delete_does_nothing_if_queue_does_not_exist(self): + def test_does_nothing_if_queue_does_not_exist(self): self.mock__has_queue.return_value = False self.channel.queue_delete(self.mock_queue) self.assertFalse(self.mock__delete.called) - def test_channel_queue_delete__not_empty_and_if_empty_True_no_delete(self): + def test_not_empty_and_if_empty_True_no_delete(self): self.mock__size.return_value = 1 self.channel.queue_delete(self.mock_queue, if_empty=True) mock_broker = self.mock_broker_agent.return_value self.assertFalse(mock_broker.getQueue.called) - def test_channel_queue_delete_calls_get_queue(self): + def test_calls_get_queue(self): self.channel.queue_delete(self.mock_queue) getQueue = self.mock_broker_agent.return_value.getQueue getQueue.assert_called_once_with(self.mock_queue) - def test_channel_queue_delete_gets_queue_attribute(self): + def test_gets_queue_attribute(self): self.channel.queue_delete(self.mock_queue) queue_obj = self.mock_broker_agent.return_value.getQueue.return_value queue_obj.getAttributes.assert_called_once_with() - def test_channel_queue_delete__queue_in_use_and_if_unused_no_delete(self): + def test_queue_in_use_and_if_unused_no_delete(self): queue_obj = self.mock_broker_agent.return_value.getQueue.return_value queue_obj.getAttributes.return_value = {'consumerCount': 1} self.channel.queue_delete(self.mock_queue, if_unused=True) self.assertFalse(self.mock__delete.called) - def test_channel_queue_delete_calls__delete_with_queue(self): + def test_calls__delete_with_queue(self): self.channel.queue_delete(self.mock_queue) self.mock__delete.assert_called_once_with(self.mock_queue) @@ -1462,7 +1469,8 @@ self.monitor.run() mock_monitor_receivers.has_calls([call(), call()]) - @patch.object(Transport, 'connection_errors', new=(MockException, )) + @patch.object(Transport, 'recoverable_connection_errors', + new=(QpidException, )) @patch.object(ReceiversMonitor, 'monitor_receivers') @patch(QPID_MODULE + '.time.sleep') @patch(QPID_MODULE + '.logger') @@ -1471,12 +1479,13 @@ def test_receivers_monitor_exits_when_recoverable_exception_raised( self, mock_sys_exc_info, mock_os_write, mock_logger, mock_sleep, mock_monitor_receivers): - mock_monitor_receivers.side_effect = MockException() + mock_monitor_receivers.side_effect = QpidException() mock_sleep.side_effect = BreakOutException() self.monitor.run() self.assertFalse(mock_logger.error.called) - @patch.object(Transport, 'connection_errors', new=(MockException, )) + @patch.object(Transport, 'recoverable_connection_errors', + new=(QpidException, )) @patch.object(ReceiversMonitor, 'monitor_receivers') @patch(QPID_MODULE + '.time.sleep') @patch(QPID_MODULE + '.logger') @@ -1484,7 +1493,7 @@ def test_receivers_monitor_saves_exception_when_recoverable_exc_raised( self, mock_os_write, mock_logger, mock_sleep, mock_monitor_receivers): - mock_monitor_receivers.side_effect = MockException() + mock_monitor_receivers.side_effect = QpidException() mock_sleep.side_effect = BreakOutException() self.monitor.run() self.assertIs( @@ -1492,7 +1501,8 @@ mock_monitor_receivers.side_effect, ) - @patch.object(Transport, 'connection_errors', new=(MockException, )) + @patch.object(Transport, 'recoverable_connection_errors', + new=(QpidException, )) @patch.object(ReceiversMonitor, 'monitor_receivers') @patch(QPID_MODULE + '.time.sleep') @patch(QPID_MODULE + '.logger') @@ -1501,7 +1511,7 @@ def test_receivers_monitor_writes_e_to_pipe_when_recoverable_exc_raised( self, mock_sys_exc_info, mock_os_write, mock_logger, mock_sleep, mock_monitor_receivers): - mock_monitor_receivers.side_effect = MockException() + mock_monitor_receivers.side_effect = QpidException() mock_sleep.side_effect = BreakOutException() self.monitor.run() mock_os_write.assert_called_once_with(self.mock_w, 'e') @@ -1598,8 +1608,8 @@ return mock_receiver def test_socket_timeout_raised_when_all_receivers_empty(self): - with patch(QPID_MODULE + '.QpidEmpty', new=MockException): - self.transport.session.next_receiver.side_effect = MockException() + with patch(QPID_MODULE + '.QpidEmpty', new=QpidException): + self.transport.session.next_receiver.side_effect = QpidException() with self.assertRaises(socket.timeout): self.transport.drain_events(Mock()) @@ -1870,11 +1880,20 @@ self.assertEqual('qpid', Transport.driver_type) self.assertEqual('qpid', Transport.driver_name) - def test_transport_channel_error_contains_qpid_ConnectionError(self): - self.assertIn(ConnectionError, Transport.connection_errors) - - def test_transport_channel_error_contains_socket_error(self): - self.assertIn(select.error, Transport.connection_errors) + def test_transport_verify_recoverable_connection_errors(self): + connection_errors = Transport.recoverable_connection_errors + self.assertIn(ConnectionError, connection_errors) + self.assertIn(select.error, connection_errors) + + def test_transport_verify_recoverable_channel_errors(self): + channel_errors = Transport.recoverable_channel_errors + self.assertIn(NotFound, channel_errors) + + def test_transport_verify_pre_kombu_3_0_exception_labels(self): + self.assertEqual(Transport.recoverable_channel_errors, + Transport.channel_errors) + self.assertEqual(Transport.recoverable_connection_errors, + Transport.connection_errors) @case_no_python3 @@ -1951,29 +1970,29 @@ self.patch_a.stop() @patch(QPID_MODULE + '.PY3', new=True) - def test_verify_runtime_env_raises_exception_for_Python3(self): + def test_raises_exception_for_Python3(self): with self.assertRaises(RuntimeError): self.verify_runtime_environment(self.transport) @patch('__builtin__.getattr') - def test_verify_runtime_env_raises_exc_for_PyPy(self, mock_getattr): + def test_raises_exc_for_PyPy(self, mock_getattr): mock_getattr.return_value = True with self.assertRaises(RuntimeError): self.verify_runtime_environment(self.transport) @patch(QPID_MODULE + '.dependency_is_none') - def test_verify_runtime_env_raises_exc_dep_missing(self, mock_dep_is_none): + def test_raises_exc_dep_missing(self, mock_dep_is_none): mock_dep_is_none.return_value = True with self.assertRaises(RuntimeError): self.verify_runtime_environment(self.transport) @patch(QPID_MODULE + '.dependency_is_none') - def test_runtime_env_calls_dependency_is_none(self, mock_dep_is_none): + def test_calls_dependency_is_none(self, mock_dep_is_none): mock_dep_is_none.return_value = False self.verify_runtime_environment(self.transport) self.assertTrue(mock_dep_is_none.called) - def test_verify_runtime_env_raises_no_exception(self): + def test_raises_no_exception(self): self.verify_runtime_environment(self.transport) @@ -1987,16 +2006,11 @@ self.mock_client = Mock() def test_close_connection(self): - """Test that close_connection calls close on each channel in the - list of channels on the connection object.""" + """Test that close_connection calls close on the connection.""" my_transport = Transport(self.mock_client) mock_connection = Mock() - mock_channel_1 = Mock() - mock_channel_2 = Mock() - mock_connection.channels = [mock_channel_1, mock_channel_2] my_transport.close_connection(mock_connection) - mock_channel_1.close.assert_called_with() - mock_channel_2.close.assert_called_with() + mock_connection.close.assert_called_once_with() def test_default_connection_params(self): """Test that the default_connection_params are correct""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/transport/librabbitmq.py new/kombu-3.0.25/kombu/transport/librabbitmq.py --- old/kombu-3.0.24/kombu/transport/librabbitmq.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/transport/librabbitmq.py 2015-04-17 15:49:28.000000000 +0200 @@ -27,6 +27,7 @@ so make sure you are using librabbitmq 1.5 when using rabbitmq > 3.3 """ DEFAULT_PORT = 5672 +DEFAULT_SSL_PORT = 5671 NO_SSL_ERROR = """\ ssl not supported by librabbitmq, please use pyamqp:// or stunnel\ @@ -71,6 +72,8 @@ Connection = Connection default_port = DEFAULT_PORT + default_ssl_port = DEFAULT_SSL_PORT + connection_errors = ( base.Transport.connection_errors + ( ConnectionError, socket.error, IOError, OSError) @@ -86,6 +89,8 @@ def __init__(self, client, **kwargs): self.client = client self.default_port = kwargs.get('default_port') or self.default_port + self.default_ssl_port = (kwargs.get('default_ssl_port') or + self.default_ssl_port) self.__reader = None def driver_version(self): @@ -161,6 +166,11 @@ @property def default_connection_params(self): - return {'userid': 'guest', 'password': 'guest', - 'port': self.default_port, - 'hostname': 'localhost', 'login_method': 'AMQPLAIN'} + return { + 'userid': 'guest', + 'password': 'guest', + 'port': (self.default_ssl_port if self.client.ssl + else self.default_port), + 'hostname': 'localhost', + 'login_method': 'AMQPLAIN', + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/transport/pyamqp.py new/kombu-3.0.25/kombu/transport/pyamqp.py --- old/kombu-3.0.24/kombu/transport/pyamqp.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/transport/pyamqp.py 2015-04-17 15:49:28.000000000 +0200 @@ -16,6 +16,7 @@ from . import base DEFAULT_PORT = 5672 +DEFAULT_SSL_PORT = 5671 class Message(base.Message): @@ -63,6 +64,7 @@ Connection = Connection default_port = DEFAULT_PORT + default_ssl_port = DEFAULT_SSL_PORT # it's very annoying that pyamqp sometimes raises AttributeError # if the connection is lost, but nothing we can do about that here. @@ -77,9 +79,11 @@ supports_heartbeats = True supports_ev = True - def __init__(self, client, default_port=None, **kwargs): + def __init__(self, client, + default_port=None, default_ssl_port=None, **kwargs): self.client = client self.default_port = default_port or self.default_port + self.default_ssl_port = default_ssl_port or self.default_ssl_port def driver_version(self): return amqp.__version__ @@ -138,9 +142,14 @@ @property def default_connection_params(self): - return {'userid': 'guest', 'password': 'guest', - 'port': self.default_port, - 'hostname': 'localhost', 'login_method': 'AMQPLAIN'} + return { + 'userid': 'guest', + 'password': 'guest', + 'port': (self.default_ssl_port if self.client.ssl + else self.default_port), + 'hostname': 'localhost', + 'login_method': 'AMQPLAIN', + } def get_manager(self, *args, **kwargs): return get_manager(self.client, *args, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/transport/qpid.py new/kombu-3.0.25/kombu/transport/qpid.py --- old/kombu-3.0.24/kombu/transport/qpid.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/transport/qpid.py 2015-04-17 15:49:28.000000000 +0200 @@ -51,10 +51,11 @@ qpidtoollibs = None # noqa try: - from qpid.messaging.exceptions import ConnectionError + from qpid.messaging.exceptions import ConnectionError, NotFound from qpid.messaging.exceptions import Empty as QpidEmpty except ImportError: # pragma: no cover ConnectionError = None + NotFound = None QpidEmpty = None try: @@ -98,59 +99,6 @@ pass -class QpidMessagingExceptionHandler(object): - """An exception handling decorator that silences some exceptions. - - An exception handling class designed to silence specific exceptions - that qpid.messaging raises as part of normal operation. qpid.messaging - exceptions require string parsing, and are not machine consumable. - This is designed to be used as a decorator, and accepts a whitelist - string as an argument. - - Usage: - @QpidMessagingExceptionHandler('whitelist string goes here') - - """ - - def __init__(self, allowed_exception_string): - """Instantiate a QpidMessagingExceptionHandler object. - - :param allowed_exception_string: a string that, if present in the - exception message, will be silenced. - :type allowed_exception_string: str - - """ - self.allowed_exception_string = allowed_exception_string - - def __call__(self, original_fun): - """The decorator method. - - Method that wraps the actual function with exception silencing - functionality. Any exception that contains the string - self.allowed_exception_string in the message will be silenced. - - :param original_fun: function that is automatically passed in - when this object is used as a decorator. - :type original_fun: function - - :return: A function that decorates (wraps) the original function. - :rtype: function - """ - - def decorator(*args, **kwargs): - """A runtime-built function that will be returned which contains - a reference to the original function, and wraps a call to it in - a try/except block that can silence errors. - """ - try: - return original_fun(*args, **kwargs) - except Exception as exc: - if self.allowed_exception_string not in str(exc): - raise - - return decorator - - class QoS(object): """A helper object for message prefetch and ACKing purposes. @@ -417,6 +365,8 @@ :return: The received message. :rtype: :class:`qpid.messaging.Message` + :raises: :class:`qpid.messaging.exceptions.Empty` if no + message is available. """ rx = self.transport.session.receiver(queue) try: @@ -479,11 +429,14 @@ """Purge all undelivered messages from a queue specified by name. An internal method to purge all undelivered messages from a queue - specified by name. The queue message depth is first checked, - and then the broker is asked to purge that number of messages. The - integer number of messages requested to be purged is returned. The - actual number of messages purged may be different than the - requested number of messages to purge (see below). + specified by name. If the queue does not exist a + :class:`qpid.messaging.exceptions.NotFound` exception is raised. + + The queue message depth is first checked, and then the broker is + asked to purge that number of messages. The integer number of + messages requested to be purged is returned. The actual number of + messages purged may be different than the requested number of + messages to purge (see below). Sometimes delivered messages are asked to be purged, but are not. This case fails silently, which is the correct behavior when a @@ -501,8 +454,14 @@ :return: The number of messages requested to be purged. :rtype: int + + :raises: :class:`qpid.messaging.exceptions.NotFound` if the queue + being purged cannot be found. """ queue_to_purge = self._broker.getQueue(queue) + if queue_to_purge is None: + error_text = "NOT_FOUND - no queue '{0}'".format(queue) + raise NotFound(code=404, text=error_text) message_count = queue_to_purge.values['msgDepth'] if message_count > 0: queue_to_purge.purge(message_count) @@ -677,7 +636,6 @@ return self._delete(queue) - @QpidMessagingExceptionHandler(OBJECT_ALREADY_EXISTS_STRING) def exchange_declare(self, exchange='', type='direct', durable=False, **kwargs): """Create a new exchange. @@ -704,7 +662,11 @@ :type durable: bool """ options = {'durable': durable} - self._broker.addExchange(type, exchange, options) + try: + self._broker.addExchange(type, exchange, options) + except Exception as exc: + if OBJECT_ALREADY_EXISTS_STRING not in str(exc): + raise exc def exchange_delete(self, exchange_name, **kwargs): """Delete an exchange specified by name @@ -757,12 +719,12 @@ def queue_purge(self, queue, **kwargs): """Remove all undelivered messages from queue. - Purge all undelivered messages from a queue specified by name. The - queue message depth is first checked, and then the broker is asked - to purge that number of messages. The integer number of messages - requested to be purged is returned. The actual number of messages - purged may be different than the requested number of messages to - purge. + Purge all undelivered messages from a queue specified by name. If the + queue does not exist an exception is raised. The queue message + depth is first checked, and then the broker is asked to purge that + number of messages. The integer number of messages requested to be + purged is returned. The actual number of messages purged may be + different than the requested number of messages to purge. Sometimes delivered messages are asked to be purged, but are not. This case fails silently, which is the correct behavior when a @@ -780,6 +742,9 @@ :return: The number of messages requested to be purged. :rtype: int + + :raises: :class:`qpid.messaging.exceptions.NotFound` if the queue + being purged cannot be found. """ return self._purge(queue) @@ -963,7 +928,7 @@ self.connection._callbacks.pop(queue, None) def close(self): - """Close Channel and all associated messages. + """Cancel all associated messages and close the Channel. This cancels all consumers by calling :meth:`basic_cancel` for each known consumer_tag. It also closes the self._broker sessions. Closing @@ -1271,14 +1236,20 @@ ) break except ConnectionError as conn_exc: + # if we get one of these errors, do not raise an exception. + # Raising will cause the connection to be retried. Instead, + # just continue on to the next mech. coded_as_auth_failure = getattr(conn_exc, 'code', None) == 320 contains_auth_fail_text = \ 'Authentication failed' in conn_exc.text contains_mech_fail_text = \ 'sasl negotiation failed: no mechanism agreed' \ in conn_exc.text + contains_mech_unavail_text = 'no mechanism available' \ + in conn_exc.text if coded_as_auth_failure or \ - contains_auth_fail_text or contains_mech_fail_text: + contains_auth_fail_text or contains_mech_fail_text or \ + contains_mech_unavail_text: logger.debug( 'Unable to connect to qpid with SASL mechanism %s', sasl_mech, @@ -1301,6 +1272,14 @@ """ return self._qpid_conn + def close(self): + """Close the connection + + Closing the connection will close all associated session, senders, or + receivers used by the Connection. + """ + self._qpid_conn.close() + def close_channel(self, channel): """Close a Channel. @@ -1332,9 +1311,7 @@ session.next_receiver() forever. The entry point of the thread is :meth:`run` which calls - :meth:`monitor_receivers` and catches and logs all exceptions raised. - After an exception is logged, the method sleeps for 10 seconds, and - re-enters :meth:`monitor_receivers` + :meth:`monitor_receivers`. The thread is designed to be daemonized, and will be forcefully killed when all non-daemon threads have already exited. @@ -1357,27 +1334,20 @@ def run(self): """Thread entry point for ReceiversMonitor - Calls :meth:`monitor_receivers` with a log-and-reenter behavior. This - guards against unexpected exceptions which could cause this thread to - exit unexpectedly. - - If a recoverable error occurs, then the exception needs to be - propagated to the Main Thread where an exception handler can properly - handle it. An Exception is checked if it is recoverable, and if so, - it is stored as saved_exception on the self._session object. The - character 'e' is then written to the self.w_fd file descriptor - causing Main Thread to raise the saved exception. Once the Exception - info is saved and the file descriptor is written, this Thread - gracefully exits. - - Typically recoverable errors are connection errors, and can be - recovered through a call to Transport.establish_connection which will - spawn a new ReceiversMonitor Thread. + Calls :meth:`monitor_receivers` with a log-and-reenter behavior for + non connection errors. This guards against unexpected exceptions + which could cause this thread to exit unexpectedly. + + If a connection error occurs, the exception needs to be propagated + to MainThread where the kombu exception handler can properly handle + it. The exception is stored as saved_exception on the self._session + object. The character 'e' is then written to the self.w_fd file + descriptor and then this thread exits. """ while True: try: self.monitor_receivers() - except Transport.connection_errors as exc: + except Transport.recoverable_connection_errors as exc: self._session.saved_exception = exc os.write(self._w_fd, 'e') break @@ -1418,12 +1388,17 @@ The Transport can create :class:`Channel` objects to communicate with the broker with using the :meth:`create_channel` method. - The Transport identifies recoverable errors, allowing for error recovery - when certain exceptions occur. These exception types are stored in the - Transport class attribute connection_errors. This adds support for Kombu - to retry an operation if a ConnectionError occurs. ConnectionErrors occur - when the Transport cannot communicate with the Qpid broker. - + The Transport identifies recoverable connection errors and recoverable + channel errors according to the Kombu 3.0 interface. These exception are + listed as tuples and store in the Transport class attribute + `recoverable_connection_errors` and `recoverable_channel_errors` + respectively. Any exception raised that is not a member of one of these + tuples is considered non-recoverable. This allows Kombu support for + automatic retry of certain operations to function correctly. + + For backwards compatibility to the pre Kombu 3.0 exception interface, the + recoverable errors are also listed as `connection_errors` and + `channel_errors`. """ # Reference to the class that should be used as the Connection object @@ -1442,11 +1417,25 @@ driver_type = 'qpid' driver_name = 'qpid' - connection_errors = ( + # Exceptions that can be recovered from, but where the connection must be + # closed and re-established first. + recoverable_connection_errors = ( ConnectionError, - select.error + select.error, + ) + + # Exceptions that can be automatically recovered from without + # re-establishing the connection. + recoverable_channel_errors = ( + NotFound, ) + # Support the pre 3.0 Kombu exception labeling interface which treats + # connection_errors and channel_errors both as recoverable via a + # reconnect. + connection_errors = recoverable_connection_errors + channel_errors = recoverable_channel_errors + def __init__(self, *args, **kwargs): """Instantiate a Transport object. @@ -1509,10 +1498,10 @@ ensuring that an accidental call to this method when no more messages will arrive will not cause indefinite blocking. - If the self.r file descriptor returns the character 'e', a - recoverable error occurred in the background thread, and this thread - should raise the saved exception. The exception is stored as - saved_exception on the session object. + If the self.r file descriptor receives the character 'e', an error + occurred in the background thread, and this thread should raise the + saved exception. The exception is stored as saved_exception on the + session object. Nothing is expected to be returned from :meth:`drain_events` because :meth:`drain_events` handles messages by calling callbacks that are @@ -1639,18 +1628,13 @@ return conn def close_connection(self, connection): - """Close the :class:`Connection` object, and all associated - :class:`Channel` objects. - - Iterates through all :class:`Channel` objects associated with the - :class:`Connection`, pops them from the list of channels, and calls - :meth:Channel.close` on each. + """ + Close the :class:`Connection` object. - :param connection: The Connection that should be closed - :type connection: Connection + :param connection: The Connection that should be closed. + :type connection: :class:`kombu.transport.qpid.Connection` """ - for channel in connection.channels: - channel.close() + connection.close() def drain_events(self, connection, timeout=0, **kwargs): """Handle and call callbacks for all ready Transport messages. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/transport/redis.py new/kombu-3.0.25/kombu/transport/redis.py --- old/kombu-3.0.24/kombu/transport/redis.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/transport/redis.py 2015-04-17 15:49:28.000000000 +0200 @@ -88,7 +88,8 @@ IOError, OSError, exceptions.ConnectionError, - exceptions.AuthenticationError)), + exceptions.AuthenticationError, + exceptions.TimeoutError)), (virtual.Transport.channel_errors + ( DataError, exceptions.InvalidResponse, @@ -781,11 +782,12 @@ 'socket_timeout': self.socket_timeout} host = connparams['host'] if '://' in host: - scheme, _, _, _, _, path, query = _parse_url(host) + scheme, _, _, _, password, path, query = _parse_url(host) if scheme == 'socket': connparams.update({ 'connection_class': redis.UnixDomainSocketConnection, - 'path': '/' + path}, **query) + 'path': '/' + path, + 'password': password}, **query) connparams.pop('host', None) connparams.pop('port', None) connparams['db'] = self._prepare_virtual_host( @@ -795,7 +797,7 @@ connection_cls = ( connparams.get('connection_class') or redis.Connection - ) + ) class Connection(connection_cls): def disconnect(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu/utils/eventio.py new/kombu-3.0.25/kombu/utils/eventio.py --- old/kombu-3.0.24/kombu/utils/eventio.py 2014-11-18 00:57:06.000000000 +0100 +++ new/kombu-3.0.25/kombu/utils/eventio.py 2015-04-17 15:49:28.000000000 +0200 @@ -86,7 +86,7 @@ except (socket.error, ValueError, KeyError, TypeError): pass except (IOError, OSError) as exc: - if get_errno(exc) != errno.ENOENT: + if get_errno(exc) not in (errno.ENOENT, errno.EPERM): raise def _poll(self, timeout): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kombu-3.0.24/kombu.egg-info/PKG-INFO new/kombu-3.0.25/kombu.egg-info/PKG-INFO --- old/kombu-3.0.24/kombu.egg-info/PKG-INFO 2014-11-18 01:11:10.000000000 +0100 +++ new/kombu-3.0.25/kombu.egg-info/PKG-INFO 2015-04-21 16:26:22.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: kombu -Version: 3.0.24 +Version: 3.0.25 Summary: Messaging library for Python Home-page: http://kombu.readthedocs.org Author: Ask Solem