Hello community, here is the log from the commit of package python-amqp for openSUSE:Factory checked in at 2020-06-11 14:44:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-amqp (Old) and /work/SRC/openSUSE:Factory/.python-amqp.new.3606 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-amqp" Thu Jun 11 14:44:37 2020 rev:32 rq:812617 version:2.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-amqp/python-amqp.changes 2019-11-04 17:04:37.920147389 +0100 +++ /work/SRC/openSUSE:Factory/.python-amqp.new.3606/python-amqp.changes 2020-06-11 14:44:59.245432392 +0200 @@ -1,0 +2,14 @@ +Mon Jun 8 13:29:28 UTC 2020 - Dirk Mueller <dmuel...@suse.com> + +- update to 2.6.0: + - Implement speedups in cython (#311) + - Updated some tests & code improvements + - Separate logger for Connection.heartbeat_tick method + - Cython generic content (#315) + - Improve documentation a_global parameter of basic_qos() method. + - Fix saving partial read buffer on windows during socket timeout. (#321) + - Fix deserialization of long string field values that are not utf-8. + - Added simple cythonization of abstract_channel.py + - Speedups of serialization.py are more restrictive + +------------------------------------------------------------------- Old: ---- amqp-2.5.2.tar.gz New: ---- amqp-2.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-amqp.spec ++++++ --- /var/tmp/diff_new_pack.ydMPfL/_old 2020-06-11 14:44:59.705433732 +0200 +++ /var/tmp/diff_new_pack.ydMPfL/_new 2020-06-11 14:44:59.709433744 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-amqp # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-amqp -Version: 2.5.2 +Version: 2.6.0 Release: 0 Summary: Low-level AMQP client for Python (fork of amqplib) License: LGPL-2.1-or-later ++++++ amqp-2.5.2.tar.gz -> amqp-2.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/Changelog new/amqp-2.6.0/Changelog --- old/amqp-2.5.2/Changelog 2019-09-30 14:52:24.000000000 +0200 +++ new/amqp-2.6.0/Changelog 2020-06-01 07:45:06.000000000 +0200 @@ -5,6 +5,23 @@ The previous amqplib changelog is here: http://code.google.com/p/py-amqplib/source/browse/CHANGES +.. _version-2.6.0: + +2.6.0 +===== +:release-date: 20-06-01 12.00 P.M UTC+6:00 +:release-by: Asif Saif Uddin + +- Implement speedups in cython (#311) +- Updated some tests & code improvements +- Separate logger for Connection.heartbeat_tick method +- Cython generic content (#315) +- Improve documentation a_global parameter of basic_qos() method. +- Fix saving partial read buffer on windows during socket timeout. (#321) +- Fix deserialization of long string field values that are not utf-8. +- Added simple cythonization of abstract_channel.py +- Speedups of serialization.py are more restrictive + .. _version-2.5.2: 2.5.2 @@ -12,10 +29,8 @@ :release-date: 2019-09-30 19.00 P.M UTC+6:00 :release-by: Asif Saif Uddin -- Ignore all methods except Close and Close-OK when channel/connection is closing -- Fix faulty ssl sni intiation parameters (#283) -- Undeprecate auto_delete flag for exchanges. (#287) -- Improved tests and testing environments +- Fixed a channel issue against a connection already closed +- Updated some tests & code improvements .. _version-2.5.1: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/PKG-INFO new/amqp-2.6.0/PKG-INFO --- old/amqp-2.5.2/PKG-INFO 2019-09-30 15:03:23.000000000 +0200 +++ new/amqp-2.6.0/PKG-INFO 2020-06-01 08:15:08.735072100 +0200 @@ -1,143 +1,13 @@ Metadata-Version: 1.2 Name: amqp -Version: 2.5.2 +Version: 2.6.0 Summary: Low-level AMQP client for Python (fork of amqplib). Home-page: http://github.com/celery/py-amqp Author: Barry Pederson Author-email: pya...@celeryproject.org -Maintainer: Asif Saif Uddin, Omer Katz +Maintainer: Asif Saif Uddin, Matus Valo License: BSD -Description: ===================================================================== - Python AMQP 0.9.1 client library - ===================================================================== - - |build-status| |coverage| |license| |wheel| |pyversion| |pyimp| - - :Version: 2.5.2 - :Web: https://amqp.readthedocs.io/ - :Download: https://pypi.org/project/amqp/ - :Source: http://github.com/celery/py-amqp/ - :Keywords: amqp, rabbitmq - - About - ===== - - This is a fork of amqplib_ which was originally written by Barry Pederson. - It is maintained by the Celery_ project, and used by `kombu`_ as a pure python - alternative when `librabbitmq`_ is not available. - - This library should be API compatible with `librabbitmq`_. - - .. _amqplib: https://pypi.org/project/amqplib/ - .. _Celery: http://celeryproject.org/ - .. _kombu: https://kombu.readthedocs.io/ - .. _librabbitmq: https://pypi.org/project/librabbitmq/ - - Differences from `amqplib`_ - =========================== - - - Supports draining events from multiple channels (``Connection.drain_events``) - - Support for timeouts - - Channels are restored after channel error, instead of having to close the - connection. - - Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. - - Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Authentication Failure Notifications - Instead of just closing the connection abruptly on invalid - credentials, py-amqp will raise an ``AccessRefused`` error - when connected to rabbitmq-server 3.2.0 or greater. - - Support for ``basic_return`` - - Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. - - Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". - - Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. - - Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. - - Exposes the underlying socket as ``Connection.sock``. - - Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. - - Slightly better at error recovery - - Further - ======= - - - Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - - - AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - - - RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - - - For more information about AMQP, visit - - http://www.amqp.org - - - For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev - - .. |build-status| image:: https://secure.travis-ci.org/celery/py-amqp.png?branch=master - :alt: Build status - :target: https://travis-ci.org/celery/py-amqp - - .. |coverage| image:: https://codecov.io/github/celery/py-amqp/coverage.svg?branch=master - :target: https://codecov.io/github/celery/py-amqp?branch=master - - .. |license| image:: https://img.shields.io/pypi/l/amqp.svg - :alt: BSD License - :target: https://opensource.org/licenses/BSD-3-Clause - - .. |wheel| image:: https://img.shields.io/pypi/wheel/amqp.svg - :alt: Python AMQP can be installed via wheel - :target: https://pypi.org/project/amqp/ - - .. |pyversion| image:: https://img.shields.io/pypi/pyversions/amqp.svg - :alt: Supported Python versions. - :target: https://pypi.org/project/amqp/ - - .. |pyimp| image:: https://img.shields.io/pypi/implementation/amqp.svg - :alt: Support Python implementations. - :target: https://pypi.org/project/amqp/ - - +Description: UNKNOWN Keywords: amqp rabbitmq cloudamqp messaging Platform: any Classifier: Development Status :: 5 - Production/Stable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/README.rst new/amqp-2.6.0/README.rst --- old/amqp-2.5.2/README.rst 2019-09-30 14:58:55.000000000 +0200 +++ new/amqp-2.6.0/README.rst 2020-06-01 07:47:59.000000000 +0200 @@ -4,7 +4,7 @@ |build-status| |coverage| |license| |wheel| |pyversion| |pyimp| -:Version: 2.5.2 +:Version: 2.6.0 :Web: https://amqp.readthedocs.io/ :Download: https://pypi.org/project/amqp/ :Source: http://github.com/celery/py-amqp/ @@ -82,6 +82,79 @@ that set the no_ack flag. - Slightly better at error recovery +Quick overview +============== + +Simple producer publishing messages to ``test`` queue using default exchange: + +.. code:: python + + import amqp + + with amqp.Connection('broker.example.com') as c: + ch = c.channel() + ch.basic_publish(amqp.Message('Hello World'), routing_key='test') + +Producer publishing to ``test_exchange`` exchange with publisher confirms enabled and using virtual_host ``test_vhost``: + +.. code:: python + + import amqp + + with amqp.Connection( + 'broker.example.com', exchange='test_exchange', + confirm_publish=True, virtual_host='test_vhost' + ) as c: + ch = c.channel() + ch.basic_publish(amqp.Message('Hello World'), routing_key='test') + +Consumer with acknowledgments enabled: + +.. code:: python + + import amqp + + with amqp.Connection('broker.example.com') as c: + ch = c.channel() + def on_message(message): + print('Received message (delivery tag: {}): {}'.format(message.delivery_tag, message.body)) + ch.basic_ack(message.delivery_tag) + ch.basic_consume(queue='test', callback=on_message) + while True: + c.drain_events() + + +Consumer with acknowledgments disabled: + +.. code:: python + + import amqp + + with amqp.Connection('broker.example.com') as c: + ch = c.channel() + def on_message(message): + print('Received message (delivery tag: {}): {}'.format(message.delivery_tag, message.body)) + ch.basic_consume(queue='test', callback=on_message, no_ack=True) + while True: + c.drain_events() + +Speedups +======== + +This library has **experimental** support of speedups. Speedups are implemented using Cython. To enable speedups, ``CELERY_ENABLE_SPEEDUPS`` environment variable must be set during building/installation. +Currently speedups can be installed: + +1. using source package (using ``--no-binary`` switch): + +.. code-block:: +CELERY_ENABLE_SPEEDUPS=true pip install --no-binary :all: amqp + + +2. building directly source code: + +.. code-block:: +CELERY_ENABLE_SPEEDUPS=true python setup.py install + Further ======= @@ -127,4 +200,9 @@ .. |pyimp| image:: https://img.shields.io/pypi/implementation/amqp.svg :alt: Support Python implementations. :target: https://pypi.org/project/amqp/ + +py-amqp as part of the Tidelift Subscription +======= + +The maintainers of py-amqp and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/pypi-amqp?utm_source=pypi-amqp&utm_medium=referral&utm_campaign=readme&utm_term=repo) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/__init__.py new/amqp-2.6.0/amqp/__init__.py --- old/amqp-2.5.2/amqp/__init__.py 2019-09-30 14:58:17.000000000 +0200 +++ new/amqp-2.6.0/amqp/__init__.py 2020-06-01 07:46:47.000000000 +0200 @@ -6,9 +6,9 @@ from collections import namedtuple -__version__ = '2.5.2' +__version__ = '2.6.0' __author__ = 'Barry Pederson' -__maintainer__ = 'Asif Saif Uddin, Omer Katz' +__maintainer__ = 'Asif Saif Uddin, Matus Valo' __contact__ = 'pya...@celeryproject.org' __homepage__ = 'http://github.com/celery/py-amqp' __docformat__ = 'restructuredtext' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/basic_message.py new/amqp-2.6.0/amqp/basic_message.py --- old/amqp-2.5.2/amqp/basic_message.py 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/amqp/basic_message.py 2020-06-01 07:25:14.000000000 +0200 @@ -55,7 +55,7 @@ message_id: shortstr The application message identifier - timestamp: datetime.datetime + timestamp: unsigned long The message timestamp type: shortstr diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/channel.py new/amqp-2.6.0/amqp/channel.py --- old/amqp-2.5.2/amqp/channel.py 2019-08-14 17:32:29.000000000 +0200 +++ new/amqp-2.6.0/amqp/channel.py 2020-06-01 07:25:14.000000000 +0200 @@ -1842,11 +1842,16 @@ a_global: boolean - apply to entire connection + Defines a scope of QoS. Semantics of this parameter differs + between AMQP 0-9-1 standard and RabbitMQ broker: - By default the QoS settings apply to the current - channel only. If this field is set, they are applied - to the entire connection. + MEANING IN AMQP 0-9-1: + False: shared across all consumers on the channel + True: shared across all consumers on the connection + MEANING IN RABBITMQ: + False: applied separately to each new consumer + on the channel + True: shared across all consumers on the channel """ return self.send_method( spec.Basic.Qos, argsig, (prefetch_size, prefetch_count, a_global), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/connection.py new/amqp-2.6.0/amqp/connection.py --- old/amqp-2.5.2/amqp/connection.py 2019-09-30 14:34:47.000000000 +0200 +++ new/amqp-2.6.0/amqp/connection.py 2020-06-01 07:25:14.000000000 +0200 @@ -42,6 +42,9 @@ __all__ = ['Connection'] AMQP_LOGGER = logging.getLogger('amqp') +AMQP_HEARTBEAT_LOGGER = logging.getLogger( + 'amqp.connection.Connection.heartbeat_tick' +) #: Default map for :attr:`Connection.library_properties` LIBRARY_PROPERTIES = { @@ -705,8 +708,8 @@ Keyword Arguments: rate (int): Previously used, but ignored now. """ - AMQP_LOGGER.debug('heartbeat_tick : for connection %s', - self._connection_id) + AMQP_HEARTBEAT_LOGGER.debug('heartbeat_tick : for connection %s', + self._connection_id) if not self.heartbeat: return @@ -719,7 +722,7 @@ self.last_heartbeat_received = monotonic() now = monotonic() - AMQP_LOGGER.debug( + AMQP_HEARTBEAT_LOGGER.debug( 'heartbeat_tick : Prev sent/recv: %s/%s, ' 'now - %s/%s, monotonic - %s, ' 'last_heartbeat_sent - %s, heartbeat int. - %s ' @@ -735,7 +738,7 @@ # send a heartbeat if it's time to do so if now > self.last_heartbeat_sent + self.heartbeat: - AMQP_LOGGER.debug( + AMQP_HEARTBEAT_LOGGER.debug( 'heartbeat_tick: sending heartbeat for connection %s', self._connection_id) self.send_heartbeat() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/serialization.py new/amqp-2.6.0/amqp/serialization.py --- old/amqp-2.5.2/amqp/serialization.py 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/amqp/serialization.py 2020-06-01 07:25:14.000000000 +0200 @@ -34,7 +34,7 @@ """ -def _read_item(buf, offset=0, unpack_from=unpack_from, ftype_t=ftype_t): +def _read_item(buf, offset): ftype = ftype_t(buf[offset]) if ftype_t else buf[offset] offset += 1 @@ -42,7 +42,11 @@ if ftype == 'S': slen, = unpack_from('>I', buf, offset) offset += 4 - val = pstr_t(buf[offset:offset + slen]) + try: + val = pstr_t(buf[offset:offset + slen]) + except UnicodeDecodeError: + val = buf[offset:offset + slen] + offset += slen # 's': short string elif ftype == 's': @@ -144,9 +148,7 @@ return val, offset -def loads(format, buf, offset=0, - ord=ord, unpack_from=unpack_from, - _read_item=_read_item, pstr_t=pstr_t): +def loads(format, buf, offset): """Deserialize amqp format. bit = b @@ -245,7 +247,7 @@ return values, offset -def _flushbits(bits, write, pack=pack): +def _flushbits(bits, write): if bits: write(pack('B' * len(bits), *bits)) bits[:] = [] @@ -325,7 +327,7 @@ return out.getvalue() -def _write_table(d, write, bits, pack=pack): +def _write_table(d, write, bits): out = BytesIO() twrite = out.write for k, v in items(d): @@ -343,7 +345,7 @@ write(table_data) -def _write_array(l, write, bits, pack=pack): +def _write_array(l, write, bits): out = BytesIO() awrite = out.write for v in l: @@ -357,11 +359,7 @@ write(array_data) -def _write_item(v, write, bits, pack=pack, - string_t=string_t, bytes=bytes, string=string, bool=bool, - float=float, int_types=int_types, Decimal=Decimal, - datetime=datetime, dict=dict, list=list, tuple=tuple, - None_t=None): +def _write_item(v, write, bits): if isinstance(v, (string_t, bytes)): if isinstance(v, string): v = v.encode('utf-8', 'surrogatepass') @@ -393,14 +391,13 @@ elif isinstance(v, (list, tuple)): write(b'A') _write_array(v, write, bits) - elif v is None_t: + elif v is None: write(b'V') else: raise ValueError() -def decode_properties_basic(buf, offset=0, - unpack_from=unpack_from, pstr_t=pstr_t): +def decode_properties_basic(buf, offset): """Decode basic properties.""" properties = {} @@ -507,8 +504,7 @@ return self.properties[name] raise AttributeError(name) - def _load_properties(self, class_id, buf, offset=0, - classes=PROPERTY_CLASSES, unpack_from=unpack_from): + def _load_properties(self, class_id, buf, offset): """Load AMQP properties. Given the raw bytes containing the property-flags and property-list @@ -516,7 +512,7 @@ stored in this object as an attribute named 'properties'. """ # Read 16-bit shorts until we get one with a low bit set to zero - props, offset = classes[class_id](buf, offset) + props, offset = PROPERTY_CLASSES[class_id](buf, offset) self.properties = props return offset diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp/transport.py new/amqp-2.6.0/amqp/transport.py --- old/amqp-2.5.2/amqp/transport.py 2019-08-14 17:32:29.000000000 +0200 +++ new/amqp-2.6.0/amqp/transport.py 2020-06-01 07:25:14.000000000 +0200 @@ -2,6 +2,7 @@ # Copyright (C) 2009 Barry Pederson <b...@barryp.org> from __future__ import absolute_import, unicode_literals +import os import errno import re import socket @@ -256,7 +257,15 @@ # so we know the size can be at most 2 * SIGNED_INT_MAX if size > SIGNED_INT_MAX: part1 = read(SIGNED_INT_MAX) - part2 = read(size - SIGNED_INT_MAX) + + try: + part2 = read(size - SIGNED_INT_MAX) + except (socket.timeout, socket.error, SSLError): + # In case this read times out, we need to make sure to not + # lose part1 when we retry the read + read_frame_buffer += part1 + raise + payload = b''.join([part1, part2]) else: payload = read(size) @@ -266,10 +275,22 @@ self._read_buffer = read_frame_buffer + self._read_buffer raise except (OSError, IOError, SSLError, socket.error) as exc: - # Don't disconnect for ssl read time outs - # http://bugs.python.org/issue10272 + if ( + isinstance(exc, socket.error) and os.name == 'nt' + and get_errno(exc) == errno.EWOULDBLOCK # noqa + ): + # On windows we can get a read timeout with a winsock error + # code instead of a proper socket.timeout() error, see + # https://github.com/celery/py-amqp/issues/320 + self._read_buffer = read_frame_buffer + self._read_buffer + raise socket.timeout() + if isinstance(exc, SSLError) and 'timed out' in str(exc): + # Don't disconnect for ssl read time outs + # http://bugs.python.org/issue10272 + self._read_buffer = read_frame_buffer + self._read_buffer raise socket.timeout() + if get_errno(exc) not in _UNAVAIL: self.connected = False raise diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/amqp.egg-info/PKG-INFO new/amqp-2.6.0/amqp.egg-info/PKG-INFO --- old/amqp-2.5.2/amqp.egg-info/PKG-INFO 2019-09-30 15:03:23.000000000 +0200 +++ new/amqp-2.6.0/amqp.egg-info/PKG-INFO 2020-06-01 08:15:08.000000000 +0200 @@ -1,143 +1,13 @@ Metadata-Version: 1.2 Name: amqp -Version: 2.5.2 +Version: 2.6.0 Summary: Low-level AMQP client for Python (fork of amqplib). Home-page: http://github.com/celery/py-amqp Author: Barry Pederson Author-email: pya...@celeryproject.org -Maintainer: Asif Saif Uddin, Omer Katz +Maintainer: Asif Saif Uddin, Matus Valo License: BSD -Description: ===================================================================== - Python AMQP 0.9.1 client library - ===================================================================== - - |build-status| |coverage| |license| |wheel| |pyversion| |pyimp| - - :Version: 2.5.2 - :Web: https://amqp.readthedocs.io/ - :Download: https://pypi.org/project/amqp/ - :Source: http://github.com/celery/py-amqp/ - :Keywords: amqp, rabbitmq - - About - ===== - - This is a fork of amqplib_ which was originally written by Barry Pederson. - It is maintained by the Celery_ project, and used by `kombu`_ as a pure python - alternative when `librabbitmq`_ is not available. - - This library should be API compatible with `librabbitmq`_. - - .. _amqplib: https://pypi.org/project/amqplib/ - .. _Celery: http://celeryproject.org/ - .. _kombu: https://kombu.readthedocs.io/ - .. _librabbitmq: https://pypi.org/project/librabbitmq/ - - Differences from `amqplib`_ - =========================== - - - Supports draining events from multiple channels (``Connection.drain_events``) - - Support for timeouts - - Channels are restored after channel error, instead of having to close the - connection. - - Support for heartbeats - - - ``Connection.heartbeat_tick(rate=2)`` must called at regular intervals - (half of the heartbeat value if rate is 2). - - Or some other scheme by using ``Connection.send_heartbeat``. - - Supports RabbitMQ extensions: - - Consumer Cancel Notifications - - by default a cancel results in ``ChannelError`` being raised - - but not if a ``on_cancel`` callback is passed to ``basic_consume``. - - Publisher confirms - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Exchange-to-exchange bindings: ``exchange_bind`` / ``exchange_unbind``. - - ``Channel.confirm_select()`` enables publisher confirms. - - ``Channel.events['basic_ack'].append(my_callback)`` adds a callback - to be called when a message is confirmed. This callback is then - called with the signature ``(delivery_tag, multiple)``. - - Authentication Failure Notifications - Instead of just closing the connection abruptly on invalid - credentials, py-amqp will raise an ``AccessRefused`` error - when connected to rabbitmq-server 3.2.0 or greater. - - Support for ``basic_return`` - - Uses AMQP 0-9-1 instead of 0-8. - - ``Channel.access_request`` and ``ticket`` arguments to methods - **removed**. - - Supports the ``arguments`` argument to ``basic_consume``. - - ``internal`` argument to ``exchange_declare`` removed. - - ``auto_delete`` argument to ``exchange_declare`` deprecated - - ``insist`` argument to ``Connection`` removed. - - ``Channel.alerts`` has been removed. - - Support for ``Channel.basic_recover_async``. - - ``Channel.basic_recover`` deprecated. - - Exceptions renamed to have idiomatic names: - - ``AMQPException`` -> ``AMQPError`` - - ``AMQPConnectionException`` -> ConnectionError`` - - ``AMQPChannelException`` -> ChannelError`` - - ``Connection.known_hosts`` removed. - - ``Connection`` no longer supports redirects. - - ``exchange`` argument to ``queue_bind`` can now be empty - to use the "default exchange". - - Adds ``Connection.is_alive`` that tries to detect - whether the connection can still be used. - - Adds ``Connection.connection_errors`` and ``.channel_errors``, - a list of recoverable errors. - - Exposes the underlying socket as ``Connection.sock``. - - Adds ``Channel.no_ack_consumers`` to keep track of consumer tags - that set the no_ack flag. - - Slightly better at error recovery - - Further - ======= - - - Differences between AMQP 0.8 and 0.9.1 - - http://www.rabbitmq.com/amqp-0-8-to-0-9-1.html - - - AMQP 0.9.1 Quick Reference - - http://www.rabbitmq.com/amqp-0-9-1-quickref.html - - - RabbitMQ Extensions - - http://www.rabbitmq.com/extensions.html - - - For more information about AMQP, visit - - http://www.amqp.org - - - For other Python client libraries see: - - http://www.rabbitmq.com/devtools.html#python-dev - - .. |build-status| image:: https://secure.travis-ci.org/celery/py-amqp.png?branch=master - :alt: Build status - :target: https://travis-ci.org/celery/py-amqp - - .. |coverage| image:: https://codecov.io/github/celery/py-amqp/coverage.svg?branch=master - :target: https://codecov.io/github/celery/py-amqp?branch=master - - .. |license| image:: https://img.shields.io/pypi/l/amqp.svg - :alt: BSD License - :target: https://opensource.org/licenses/BSD-3-Clause - - .. |wheel| image:: https://img.shields.io/pypi/wheel/amqp.svg - :alt: Python AMQP can be installed via wheel - :target: https://pypi.org/project/amqp/ - - .. |pyversion| image:: https://img.shields.io/pypi/pyversions/amqp.svg - :alt: Supported Python versions. - :target: https://pypi.org/project/amqp/ - - .. |pyimp| image:: https://img.shields.io/pypi/implementation/amqp.svg - :alt: Support Python implementations. - :target: https://pypi.org/project/amqp/ - - +Description: UNKNOWN Keywords: amqp rabbitmq cloudamqp messaging Platform: any Classifier: Development Status :: 5 - Production/Stable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/docs/includes/introduction.txt new/amqp-2.6.0/docs/includes/introduction.txt --- old/amqp-2.5.2/docs/includes/introduction.txt 2019-09-30 14:58:33.000000000 +0200 +++ new/amqp-2.6.0/docs/includes/introduction.txt 2020-06-01 07:49:30.000000000 +0200 @@ -1,4 +1,4 @@ -:Version: 2.5.2 +:Version: 2.6.0 :Web: https://amqp.readthedocs.io/ :Download: https://pypi.org/project/amqp/ :Source: http://github.com/celery/py-amqp/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/requirements/test.txt new/amqp-2.6.0/requirements/test.txt --- old/amqp-2.5.2/requirements/test.txt 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/requirements/test.txt 2020-06-01 07:25:14.000000000 +0200 @@ -1,4 +1,4 @@ case>=1.3.1 -pytest>=3.0 +pytest>=3.0,<=5.3.5 pytest-sugar>=0.9.1 pytest-rerunfailures>=6.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/setup.py new/amqp-2.6.0/setup.py --- old/amqp-2.5.2/setup.py 2019-08-14 17:48:25.000000000 +0200 +++ new/amqp-2.6.0/setup.py 2020-06-01 08:15:02.000000000 +0200 @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import codecs import io import os import re @@ -81,11 +82,11 @@ # -*- Long Description -*- -if os.path.exists('README.rst'): - with io.open('README.rst', encoding='utf-8') as fp: - long_description = fp.read() -else: - long_description = 'See https://pypi.org/project/amqp/' +def long_description(): + try: + return codecs.open('README.rst', 'r', 'utf-8').read() + except IOError: + return 'Long description error: Missing README.rst file' # -*- %%% -*- @@ -103,10 +104,34 @@ sys.exit(pytest.main(pytest_args)) +if os.environ.get("CELERY_ENABLE_SPEEDUPS"): + setup_requires=['Cython'] + ext_modules = [ + setuptools.Extension( + 'amqp.serialization', + ["amqp/serialization.py"], + ), + setuptools.Extension( + 'amqp.basic_message', + ["amqp/basic_message.py"], + ), + setuptools.Extension( + 'amqp.method_framing', + ["amqp/method_framing.py"], + ), + setuptools.Extension( + 'amqp.abstract_channel', + ["amqp/abstract_channel.py"], + ), + ] +else: + setup_requires = [] + ext_modules = [] + + setuptools.setup( name=NAME, packages=setuptools.find_packages(exclude=['ez_setup', 't', 't.*']), - long_description=long_description, version=meta['version'], description=meta['doc'], keywords='amqp rabbitmq cloudamqp messaging', @@ -119,7 +144,9 @@ classifiers=classifiers, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", install_requires=reqs('default.txt'), + setup_requires=setup_requires, tests_require=reqs('test.txt'), cmdclass={'test': pytest}, zip_safe=False, + ext_modules = ext_modules, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/t/integration/test_integration.py new/amqp-2.6.0/t/integration/test_integration.py --- old/amqp-2.5.2/t/integration/test_integration.py 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/t/integration/test_integration.py 2020-06-01 07:25:14.000000000 +0200 @@ -123,7 +123,7 @@ self.items = items def __eq__(self, other): - values, offset = loads(self.argsig, other) + values, offset = loads(self.argsig, other, 0) return tuple(values) == tuple(self.items) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/t/integration/test_rmq.py new/amqp-2.6.0/t/integration/test_rmq.py --- old/amqp-2.5.2/t/integration/test_rmq.py 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/t/integration/test_rmq.py 2020-06-01 07:25:14.000000000 +0200 @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals import os +import ssl import pytest @@ -8,14 +9,41 @@ from case import ANY, Mock -@pytest.fixture +def get_connection( + hostname, port, vhost, use_tls=False, keyfile=None, certfile=None): + host = '%s:%s' % (hostname, port) + if use_tls: + return amqp.Connection(host=host, vhost=vhost, ssl={ + 'keyfile': keyfile, + 'certfile': certfile + } + ) + else: + return amqp.Connection(host=host, vhost=vhost) + + +@pytest.fixture(params=['plain', 'tls']) def connection(request): - host = '%s:%s' % ( - os.environ.get('RABBITMQ_HOST', 'localhost'), - os.environ.get('RABBITMQ_5672_TCP', '5672') - ) - vhost = getattr(request.config, "slaveinput", {}).get("slaveid", None) - return amqp.Connection(host=host, vhost=vhost) + # this fixture yields plain connections to broker and TLS encrypted + if request.param == 'plain': + return get_connection( + hostname=os.environ.get('RABBITMQ_HOST', 'localhost'), + port=os.environ.get('RABBITMQ_5672_TCP', '5672'), + vhost=getattr( + request.config, "slaveinput", {} + ).get("slaveid", None), + ) + elif request.param == 'tls': + return get_connection( + hostname=os.environ.get('RABBITMQ_HOST', 'localhost'), + port=os.environ.get('RABBITMQ_5671_TCP', '5671'), + vhost=getattr( + request.config, "slaveinput", {} + ).get("slaveid", None), + use_tls=True, + keyfile='t/certs/client_key.pem', + certfile='t/certs/client_certificate.pem' + ) @pytest.mark.env('rabbitmq') @@ -26,6 +54,23 @@ @pytest.mark.env('rabbitmq') +@pytest.mark.flaky(reruns=5, reruns_delay=2) +def test_tls_connect_fails(): + # testing that wrong client key/certificate yields SSLError + # when encrypted connection is used + connection = get_connection( + hostname=os.environ.get('RABBITMQ_HOST', 'localhost'), + port=os.environ.get('RABBITMQ_5671_TCP', '5671'), + vhost='/', + use_tls=True, + keyfile='t/certs/client_key_broken.pem', + certfile='t/certs/client_certificate_broken.pem' + ) + with pytest.raises(ssl.SSLError): + connection.connect() + + +@pytest.mark.env('rabbitmq') class test_rabbitmq_operations(): @pytest.fixture(autouse=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/t/unit/test_serialization.py new/amqp-2.6.0/t/unit/test_serialization.py --- old/amqp-2.5.2/t/unit/test_serialization.py 2019-07-12 07:25:19.000000000 +0200 +++ new/amqp-2.6.0/t/unit/test_serialization.py 2020-06-01 07:25:14.000000000 +0200 @@ -3,6 +3,7 @@ from datetime import datetime from decimal import Decimal from math import ceil +import pickle import pytest @@ -25,6 +26,7 @@ @pytest.mark.parametrize('descr,frame,expected,cast', [ ('S', b's8thequick', 'thequick', None), + ('S', b'S\x00\x00\x00\x03\xc0\xc0\x00', b'\xc0\xc0\x00', None), ('x', b'x\x00\x00\x00\x09thequick\xffIGNORED', b'thequick\xff', None), ('b', b'b' + pack('>B', True), True, None), ('B', b'B' + pack('>b', 123), 123, None), @@ -36,12 +38,12 @@ ('f', b'f' + pack('>f', 33.3), 34.0, ceil), ]) def test_read_item(self, descr, frame, expected, cast): - actual = _read_item(frame)[0] + actual = _read_item(frame, 0)[0] actual = cast(actual) if cast else actual assert actual == expected def test_read_item_V(self): - assert _read_item(b'V')[0] is None + assert _read_item(b'V', 0)[0] is None def test_roundtrip(self): format = b'bobBlLbsbSTx' @@ -51,7 +53,7 @@ datetime(2015, 3, 13, 10, 23), b'thequick\xff' ]) - y = loads(format, x) + y = loads(format, x, 0) assert [ True, 32, False, 3415, 4513134, 13241923419, True, 'thequickbrownfox', False, 'jumpsoverthelazydog', @@ -63,18 +65,18 @@ x = dumps(format, [ {'a': -2147483649, 'b': 2147483648}, # celery/celery#3121 ]) - y = loads(format, x) + y = loads(format, x, 0) assert y[0] == [{ 'a': -2147483649, 'b': 2147483648, # celery/celery#3121 }] def test_loads_unknown_type(self): with pytest.raises(FrameSyntaxError): - loads('y', 'asdsad') + loads('y', 'asdsad', 0) def test_float(self): - assert (int(loads(b'fb', dumps(b'fb', [32.31, False]))[0][0] * 100) == - 3231) + data = int(loads(b'fb', dumps(b'fb', [32.31, False]), 0)[0][0] * 100) + assert(data == 3231) def test_table(self): table = { @@ -85,7 +87,19 @@ 1, True, 'bar' ] } - assert loads(b'F', dumps(b'F', [table]))[0][0] == table + assert loads(b'F', dumps(b'F', [table]), 0)[0][0] == table + + def test_table__unknown_type(self): + table = { + 'foo': object(), + 'bar': 'baz', + 'nil': None, + 'array': [ + 1, True, 'bar' + ] + } + with pytest.raises(FrameSyntaxError): + dumps(b'F', [table]) def test_array(self): array = [ @@ -99,7 +113,7 @@ expected = list(array) expected[6] = _ANY() - assert expected == loads('A', dumps('A', [array]))[0][0] + assert expected == loads('A', dumps('A', [array]), 0)[0][0] def test_array_unknown_type(self): with pytest.raises(FrameSyntaxError): @@ -109,14 +123,14 @@ expected = [50, "quick", "fox", True, False, False, True, True, {"prop1": True}] buf = dumps('BssbbbbbF', expected) - actual, _ = loads('BssbbbbbF', buf) + actual, _ = loads('BssbbbbbF', buf, 0) assert actual == expected def test_sixteen_bitflags(self): expected = [True, False] * 8 format = 'b' * len(expected) buf = dumps(format, expected) - actual, _ = loads(format, buf) + actual, _ = loads(format, buf, 0) assert actual == expected @@ -128,12 +142,13 @@ def test_getattr(self): self.g.properties['foo'] = 30 - with pytest.raises(AttributeError): - self.g.__setstate__ assert self.g.foo == 30 with pytest.raises(AttributeError): self.g.bar + def test_pickle(self): + pickle.loads(pickle.dumps(self.g)) + def test_load_properties(self): m = Message() m.properties = { @@ -157,7 +172,7 @@ } s = m._serialize_properties() m2 = Message() - m2._load_properties(m2.CLASS_ID, s) + m2._load_properties(m2.CLASS_ID, s, 0) assert m2.properties == m.properties def test_load_properties__some_missing(self): @@ -176,7 +191,7 @@ } s = m._serialize_properties() m2 = Message() - m2._load_properties(m2.CLASS_ID, s) + m2._load_properties(m2.CLASS_ID, s, 0) def test_inbound_header(self): m = Message() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-2.5.2/t/unit/test_transport.py new/amqp-2.6.0/t/unit/test_transport.py --- old/amqp-2.5.2/t/unit/test_transport.py 2019-08-14 17:32:29.000000000 +0200 +++ new/amqp-2.6.0/t/unit/test_transport.py 2020-06-01 07:25:14.000000000 +0200 @@ -3,6 +3,7 @@ import errno import socket import struct +import os import pytest @@ -715,3 +716,39 @@ with pytest.raises(IOError, match=r'.*Server unexpectedly closed connection.*'): self.t._read(64) + + def test_read_frame__windowstimeout(self, monkeypatch): + """Make sure BlockingIOError on Windows properly saves off partial reads. + + See https://github.com/celery/py-amqp/issues/320 + """ + + self.t._quick_recv = Mock() + + self.t._quick_recv.side_effect = [ + pack('>BHI', 1, 1, 16), + socket.error( + 10035, + "A non-blocking socket operation could " + "not be completed immediately" + ), + b'thequickbrownfox', + b'\xce' + ] + + monkeypatch.setattr(os, 'name', 'nt') + monkeypatch.setattr(errno, 'EWOULDBLOCK', 10035) + + assert len(self.t._read_buffer) == 0 + + with pytest.raises(socket.timeout): + self.t.read_frame() + + assert len(self.t._read_buffer) == 7 + + frame_type, channel, payload = self.t.read_frame() + + assert len(self.t._read_buffer) == 0 + assert frame_type == 1 + assert channel == 1 + assert payload == b'thequickbrownfox'