Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-tinyrpc for openSUSE:Factory 
checked in at 2022-10-01 17:44:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-tinyrpc (Old)
 and      /work/SRC/openSUSE:Factory/.python-tinyrpc.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-tinyrpc"

Sat Oct  1 17:44:11 2022 rev:10 rq:1007454 version:1.1.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-tinyrpc/python-tinyrpc.changes    
2020-03-11 18:54:55.331663502 +0100
+++ /work/SRC/openSUSE:Factory/.python-tinyrpc.new.2275/python-tinyrpc.changes  
2022-10-01 17:44:36.873829230 +0200
@@ -1,0 +2,8 @@
+Sat Oct  1 13:51:06 UTC 2022 - Dirk M??ller <[email protected]>
+
+- update to 1.1.4:
+  * fix undesired sharing of ID generators between protocol instances  
+  * add timeout to ZmqClientTransport
+  * changed zmq client timeout to seconds
+
+-------------------------------------------------------------------

Old:
----
  1.0.4.tar.gz

New:
----
  1.1.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-tinyrpc.spec ++++++
--- /var/tmp/diff_new_pack.hliV8u/_old  2022-10-01 17:44:37.293829993 +0200
+++ /var/tmp/diff_new_pack.hliV8u/_new  2022-10-01 17:44:37.301830008 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-tinyrpc
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-tinyrpc
-Version:        1.0.4
+Version:        1.1.4
 Release:        0
 Summary:        A modular transport and protocol neutral RPC library
 License:        MIT
@@ -28,6 +28,7 @@
 BuildRequires:  %{python_module Werkzeug}
 BuildRequires:  %{python_module gevent}
 BuildRequires:  %{python_module msgpack}
+BuildRequires:  %{python_module pika >= 1.2.0}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module pyzmq}
 BuildRequires:  %{python_module requests}

++++++ 1.0.4.tar.gz -> 1.1.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/.github/workflows/python-tox.yml 
new/tinyrpc-1.1.4/.github/workflows/python-tox.yml
--- old/tinyrpc-1.0.4/.github/workflows/python-tox.yml  1970-01-01 
01:00:00.000000000 +0100
+++ new/tinyrpc-1.1.4/.github/workflows/python-tox.yml  2022-01-30 
14:56:51.000000000 +0100
@@ -0,0 +1,32 @@
+# This workflow will install tox and run tox for each version of Python 
defined in the matrix
+
+
+name: Unit tests
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install tox
+      run: |
+        python -m pip install --upgrade pip
+        python -m pip install tox
+    - name: Run tox
+      run: tox -e py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/.travis.yml 
new/tinyrpc-1.1.4/.travis.yml
--- old/tinyrpc-1.0.4/.travis.yml       2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/.travis.yml       1970-01-01 01:00:00.000000000 +0100
@@ -1,17 +0,0 @@
-language: python
-
-python:
-  - 3.6
-
-matrix:
-    include:
-        - python: 3.7
-          dist: xenial
-          sudo: true
-
-install:
-  - pip install tox-travis
-
-script:
-  - tox
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/.vscode/settings.json 
new/tinyrpc-1.1.4/.vscode/settings.json
--- old/tinyrpc-1.0.4/.vscode/settings.json     1970-01-01 01:00:00.000000000 
+0100
+++ new/tinyrpc-1.1.4/.vscode/settings.json     2022-01-30 14:56:51.000000000 
+0100
@@ -0,0 +1,9 @@
+{
+    "restructuredtext.confPath": "${workspaceFolder}/docs"
+    "files.watcherExclude": {
+        "**/.git/objects/**": true,
+        "**/.git/subtree-cache/**": true,
+        "**/node_modules/*/**": true,
+        ".tox/**": true
+    }
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/README.rst new/tinyrpc-1.1.4/README.rst
--- old/tinyrpc-1.0.4/README.rst        2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/README.rst        2022-01-30 14:56:51.000000000 +0100
@@ -3,8 +3,8 @@
 
 .. image:: https://readthedocs.org/projects/tinyrpc/badge/?version=latest
     :target: https://tinyrpc.readthedocs.io/en/latest
-.. image:: https://travis-ci.org/mbr/tinyrpc.svg?branch=master
-    :target: https://travis-ci.org/mbr/tinyrpc
+.. image:: 
https://github.com/mbr/tinyrpc/actions/workflows/python-tox.yml/badge.svg
+    :target: https://github.com/mbr/tinyrpc/actions/workflows/python-tox.yml
 .. image:: https://badge.fury.io/py/tinyrpc.svg
     :target: https://pypi.org/project/tinyrpc/
 
@@ -128,6 +128,8 @@
 +------------+-------------------------------------------------------+
 | jsonext    | optional in JSONRPCProtocol                           |
 +------------+-------------------------------------------------------+
+| rabbitmq   | RabbitMQServerTransport, RabbitMQClientTransport      |
++------------+-------------------------------------------------------+
 | websocket  | WSServerTransport                                     |
 +------------+-------------------------------------------------------+
 | wsgi       | WsgiServerTransport                                   |
@@ -135,6 +137,11 @@
 | zmq        | ZmqServerTransport, ZmqClientTransport                |
 +------------+-------------------------------------------------------+
 
+New in version 1.1.0
+--------------------
+
+Tinyrpc supports RabbitMQ has transport medium.
+
 New in version 1.0.4
 --------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/docs/conf.py 
new/tinyrpc-1.1.4/docs/conf.py
--- old/tinyrpc-1.0.4/docs/conf.py      2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/docs/conf.py      2022-01-30 14:56:51.000000000 +0100
@@ -42,16 +42,16 @@
 
 # General information about the project.
 project = u'tinyrpc'
-copyright = u'2013 - 2019, Marc Brinkmann, Leo Noordergraaf'
+copyright = u'2013 - 2021, Marc Brinkmann, Leo Noordergraaf'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '1.0'
+version = '1.1'
 # The full version, including alpha/beta/rc tags.
-release = '1.0.4'
+release = '1.1.4'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/docs/index.rst 
new/tinyrpc-1.1.4/docs/index.rst
--- old/tinyrpc-1.0.4/docs/index.rst    2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/docs/index.rst    2022-01-30 14:56:51.000000000 +0100
@@ -63,6 +63,8 @@
 +------------+-------------------------------------------------------+
 | msgpack    | required by MSGPACKRPCProtocol                        |
 +------------+-------------------------------------------------------+
+| rabbitmq   | RabbitMQServerTransport, RabbitMQClientTransport      |
++------------+-------------------------------------------------------+
 | websocket  | WSServerTransport, HttpWebSocketClientTransport       |
 +------------+-------------------------------------------------------+
 | wsgi       | WsgiServerTransport                                   |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/docs/protocols.rst 
new/tinyrpc-1.1.4/docs/protocols.rst
--- old/tinyrpc-1.0.4/docs/protocols.rst        2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/docs/protocols.rst        2022-01-30 14:56:51.000000000 
+0100
@@ -88,6 +88,38 @@
     :member-order: bysource
 
 
+ID Generators
+-------------
+
+By default, the :py:class:`~tinyrpc.protocols.jsonrpc.JSONRPCProtocol` 
+and :py:class:`~tinyrpc.protocols.msgpackrpc.MSGPACKRPCProtocol` classes
+generates ids as sequential integers starting at 1.
+If alternative id generation is needed, you may supply your own
+generator.
+
+Example
+-------
+
+The following example shows how to use alternative id generators in a protocol
+that supports them.
+
+.. code-block:: python
+
+    from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
+    
+    def collatz_generator():
+        """A sample generator for demonstration purposes ONLY."""
+        n = 27
+        while True:
+            if n % 2 != 0:
+                n = 3*n + 1
+            else:
+                n = n / 2
+            yield n
+
+    rpc = JSONRPCProtocol(id_generator=collatz_generator())
+
+
 Supported protocols
 -------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/docs/transports.rst 
new/tinyrpc-1.1.4/docs/transports.rst
--- old/tinyrpc-1.0.4/docs/transports.rst       2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/docs/transports.rst       2022-01-30 14:56:51.000000000 
+0100
@@ -94,6 +94,21 @@
     :show-inheritance:
     :member-order: bysource
 
+RabbitMQ
+~~~~~~~~
+
+.. autoclass:: tinyrpc.transports.rabbitmq.RabbitMQServerTransport
+    :members:
+    :noindex:
+    :show-inheritance:
+    :member-order: bysource
+
+.. autoclass:: tinyrpc.transports.rabbitmq.RabbitMQClientTransport
+    :members:
+    :noindex:
+    :show-inheritance:
+    :member-order: bysource
+
 WebSocket
 ~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/optional_features.pip 
new/tinyrpc-1.1.4/optional_features.pip
--- old/tinyrpc-1.0.4/optional_features.pip     2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/optional_features.pip     2022-01-30 14:56:51.000000000 
+0100
@@ -7,4 +7,4 @@
 pyzmq
 jsonext
 msgpack
-
+pika
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/requirements.txt 
new/tinyrpc-1.1.4/requirements.txt
--- old/tinyrpc-1.0.4/requirements.txt  2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/requirements.txt  2022-01-30 14:56:51.000000000 +0100
@@ -1,21 +1,11 @@
-atomicwrites==1.1.5
-attrs==18.1.0
-coverage==4.5.1
-gevent==1.3.4
+gevent==21.1.2
 gevent-websocket==0.10.1
-greenlet==0.4.13
-more-itertools==4.2.0
-msgpack==0.6.2
-packaging==17.1
-pluggy==0.6.0
-py==1.5.4
-pyparsing==2.2.0
-pytest==3.6.3
-pytest-cov==2.5.1
-pyzmq==17.1.0
-requests>=2.20.0
-six==1.11.0
-tox==3.1.2
-virtualenv==16.0.0
-Werkzeug==0.15.5
-zmq==0.0.0
+msgpack==1.0.2
+pika==1.2.0
+pytest==6.2.4
+pytest-cov==2.11.1
+pyzmq==22.0.3
+requests==2.25.1
+six==1.16.0
+Werkzeug==2.0.0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/setup.py new/tinyrpc-1.1.4/setup.py
--- old/tinyrpc-1.0.4/setup.py  2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/setup.py  2022-01-30 14:56:51.000000000 +0100
@@ -9,7 +9,7 @@
 
 setup(
     name='tinyrpc',
-    version='1.0.4',
+    version='1.1.4',
     description='A small, modular, transport and protocol neutral RPC '
                 'library that, among other things, supports JSON-RPC and zmq.',
     long_description=read('README.rst'),
@@ -30,6 +30,7 @@
         'websocket': ['gevent-websocket'],
         'wsgi': ['werkzeug'],
         'zmq': ['pyzmq'],
-        'jsonext': ['jsonext']
+        'jsonext': ['jsonext'],
+        'rabbitmq': ['pika']
     }
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_client.py 
new/tinyrpc-1.1.4/tests/test_client.py
--- old/tinyrpc-1.0.4/tests/test_client.py      2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_client.py      2022-01-30 14:56:51.000000000 
+0100
@@ -129,7 +129,7 @@
         assert isinstance(args[0], RPCErrorResponse)
         assert args[0].error == 'foo'
         print(mock_protocol.mock_calls)
-#        mock_protocol.raise_error.assert_called_with('foo')
+        mock_protocol.raise_error.assert_called_with(error_response)
 
 
 def test_client_raises_indirect_error_replies(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_dispatch.py 
new/tinyrpc-1.1.4/tests/test_dispatch.py
--- old/tinyrpc-1.0.4/tests/test_dispatch.py    2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_dispatch.py    2022-01-30 14:56:51.000000000 
+0100
@@ -1,8 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-import _compat
-from six.moves.mock import Mock, MagicMock
+from unittest.mock import Mock
 import pytest
 import inspect
 
@@ -21,7 +20,7 @@
     return RPCDispatcher()
 
 
[email protected]()
+
 def mock_request(method='subtract', args=None, kwargs=None):
     mock_request = Mock(RPCRequest)
     mock_request.method = method
@@ -30,6 +29,9 @@
 
     return mock_request
 
[email protected](name="mock_request")
+def mock_request_fixture():
+    return mock_request()
 
 def test_function_decorating_without_paramters(dispatch):
     @dispatch.public
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_jsonrpc.py 
new/tinyrpc-1.1.4/tests/test_jsonrpc.py
--- old/tinyrpc-1.0.4/tests/test_jsonrpc.py     2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_jsonrpc.py     2022-01-30 14:56:51.000000000 
+0100
@@ -107,12 +107,25 @@
     ),
 ])
 def test_good_reply_samples(prot, data, id, result):
+    # assume the protocol is awaiting a response for
+    # a request with `id`
+    prot._pending_replies = [id]
+    
     reply = prot.parse_reply(data)
 
     assert reply.unique_id == id
     assert reply.result == result
 
 
[email protected](('data'), [
+    """{"jsonrpc": "2.0", "result": 19, "id": 9001}"""
+])
+def test_unsolicited_reply_raises_error(prot, data):
+    prot._pending_replies = [4]
+    with pytest.raises(InvalidReplyError):
+        reply = prot.parse_reply(data)
+
+
 @pytest.mark.parametrize(('exc', 'code', 'message'), [
     (JSONRPCParseError, -32700, 'Parse error'),
     (JSONRPCInvalidRequestError, -32600, 'Invalid Request'),
@@ -230,7 +243,8 @@
 
 def test_jsonrpc_spec_v2_example1(prot):
     # reset id counter
-    prot._id_counter = 0
+    from tinyrpc.protocols import default_id_generator
+    prot._id_generator = default_id_generator(1)
 
     request = prot.create_request('subtract', [42, 23])
 
@@ -264,7 +278,8 @@
 
 def test_jsonrpc_spec_v2_example2(prot):
     # reset id counter
-    prot._id_counter = 2
+    from tinyrpc.protocols import default_id_generator
+    prot._id_generator = default_id_generator(3)
 
     request = prot.create_request('subtract',
                                   kwargs={'subtrahend': 23, 'minuend': 42})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_msgpackrpc.py 
new/tinyrpc-1.1.4/tests/test_msgpackrpc.py
--- old/tinyrpc-1.0.4/tests/test_msgpackrpc.py  2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_msgpackrpc.py  2022-01-30 14:56:51.000000000 
+0100
@@ -210,7 +210,8 @@
 
 def test_jsonrpc_spec_v2_example1(prot):
     # reset id counter
-    prot._id_counter = 0
+    from tinyrpc.protocols import default_id_generator
+    prot._id_generator = default_id_generator(1)
 
     request = prot.create_request("subtract", [42, 23])
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_protocols.py 
new/tinyrpc-1.1.4/tests/test_protocols.py
--- old/tinyrpc-1.0.4/tests/test_protocols.py   2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_protocols.py   2022-01-30 14:56:51.000000000 
+0100
@@ -58,3 +58,10 @@
     parsed = protocol.parse_reply(err_rep.serialize())
 
     assert hasattr(parsed, 'error')
+
+def test_default_id_generator():
+    from tinyrpc.protocols import default_id_generator
+    g = default_id_generator(1)
+    assert next(g) == 1
+    assert next(g) == 2
+    assert next(g) == 3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_rabbitmq_transport.py 
new/tinyrpc-1.1.4/tests/test_rabbitmq_transport.py
--- old/tinyrpc-1.0.4/tests/test_rabbitmq_transport.py  1970-01-01 
01:00:00.000000000 +0100
+++ new/tinyrpc-1.1.4/tests/test_rabbitmq_transport.py  2022-01-30 
14:56:51.000000000 +0100
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import pytest
+from unittest.mock import patch
+
+from tinyrpc.transports.rabbitmq import RabbitMQServerTransport, 
RabbitMQClientTransport
+
+FAKE_REQUEST_MSG = b'a fake request message'
+FAKE_RESPONSE_MSG = b'a fake response message'
+FAKE_MESSAGE_DATA = b'some fake message data'
+TEST_QUEUE = 'test_queue'
+TEST_ROUTE = 'test_route'
+
+class DummyBlockingConnection:
+    class DummyChannel:
+        class GenericObject(object):
+            pass
+
+        def __init__(self):
+            self.properties = self.GenericObject()
+            self.properties.reply_to = "reply_to"
+            self.properties.correlation_id = "correlation_id"
+
+        def queue_declare(self, *args, **kwargs):
+            result = self.GenericObject()
+            result.method = self.GenericObject()
+            result.method.queue = "queue_id"
+            return result
+
+        def basic_consume(self, on_message_callback, *args, **kwargs):
+            self.on_message_callback = on_message_callback
+
+        def basic_publish(self, properties, *args, **kwargs):
+            self.properties = properties
+
+        def basic_ack(self, *args, **kwargs):
+            pass
+
+    def __init__(self, *args, **kwargs):
+        pass
+
+    def channel(self):
+        self.channel = self.DummyChannel()
+        return self.channel
+
+    def process_data_events(self):
+        fake_response = FAKE_MESSAGE_DATA
+        method = self.DummyChannel.GenericObject()
+        method.delivery_tag = "delivery_tag"
+        self.channel.on_message_callback(self.channel, method, 
self.channel.properties, fake_response)
+
[email protected]
+def dummy_blockingconnection():
+    return DummyBlockingConnection()
+
[email protected]
+def rabbitmq_server(dummy_blockingconnection):
+    return RabbitMQServerTransport(dummy_blockingconnection, TEST_QUEUE)
+
[email protected]
+def rabbitmq_client(dummy_blockingconnection):
+    return RabbitMQClientTransport(dummy_blockingconnection, TEST_ROUTE)
+
+@patch('pika.BlockingConnection', DummyBlockingConnection)
+def test_can_create_rabbitmq_server():
+    RabbitMQServerTransport.create("localhost", TEST_QUEUE)
+
+@patch('pika.BlockingConnection', DummyBlockingConnection)
+def test_can_create_rabbitmq_client():
+    RabbitMQClientTransport.create("localhost", TEST_ROUTE)
+
+def test_server_can_receive_message(rabbitmq_server):
+    context, message = rabbitmq_server.receive_message()
+    assert context
+    assert message == FAKE_MESSAGE_DATA
+
+def test_server_can_send_reply(rabbitmq_server):
+    context, message = rabbitmq_server.receive_message()
+    assert context
+    assert message == FAKE_MESSAGE_DATA
+    rabbitmq_server.send_reply(context, FAKE_RESPONSE_MSG)
+
+def test_client_can_send_message(rabbitmq_client):
+    response = rabbitmq_client.send_message(FAKE_REQUEST_MSG, 
expect_reply=False)
+    assert response is None
+
+def test_client_can_send_message_and_get_reply(rabbitmq_client):
+    response = rabbitmq_client.send_message(FAKE_REQUEST_MSG, 
expect_reply=True)
+    assert response == FAKE_MESSAGE_DATA
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tests/test_server.py 
new/tinyrpc-1.1.4/tests/test_server.py
--- old/tinyrpc-1.0.4/tests/test_server.py      2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tests/test_server.py      2022-01-30 14:56:51.000000000 
+0100
@@ -2,8 +2,7 @@
 # -*- coding: utf-8 -*-
 
 import pytest
-import _compat
-from six.moves.mock import Mock, call
+from unittest.mock import Mock, call
 
 from tinyrpc.server import RPCServer
 from tinyrpc.transports import ServerTransport
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/client.py 
new/tinyrpc-1.1.4/tinyrpc/client.py
--- old/tinyrpc-1.0.4/tinyrpc/client.py 2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/client.py 2022-01-30 14:56:51.000000000 +0100
@@ -49,7 +49,7 @@
         tport = self.transport if transport is None else transport
 
         # sends ...
-        reply = tport.send_message(req.serialize())
+        reply = tport.send_message(req.serialize(), expect_reply=(not one_way))
 
         if one_way:
             # ... and be done
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/dispatch/__init__.py 
new/tinyrpc-1.1.4/tinyrpc/dispatch/__init__.py
--- old/tinyrpc-1.0.4/tinyrpc/dispatch/__init__.py      2019-11-03 
14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/dispatch/__init__.py      2022-01-30 
14:56:51.000000000 +0100
@@ -10,13 +10,24 @@
 """
 
 import inspect
-from typing import Callable, Any, Dict, List, Union
+from typing import Callable, Any, Dict, List, Optional, TypeVar, Union, 
overload
 
 from tinyrpc import RPCRequest, RPCResponse, RPCBatchRequest, RPCBatchResponse
 from .. import exc
 
 
-def public(name: str = None) -> Callable:
+T = TypeVar("T")
+
+
+@overload
+def public(name: Callable[..., T]) -> Callable[..., T]:
+    ...
+
+@overload
+def public(name: Optional[str] = None) -> Callable[[Callable[..., T]], 
Callable[..., T]]:
+    ...
+
+def public(name = None):
     # noinspection SpellCheckingInspection
     """Decorator. Mark a method as eligible for registration by a dispatcher.
 
@@ -67,7 +78,15 @@
         self.method_map = {}
         self.subdispatchers = {}
 
-    def public(self, name: str = None) -> Callable:
+    @overload
+    def public(self, name: Callable[..., T]) -> Callable[..., T]:
+        ...
+
+    @overload
+    def public(self, name: Optional[str] = None) -> Callable[[Callable[..., 
T]], Callable[..., T]]:
+        ...
+
+    def public(self, name = None):
         """Convenient decorator.
 
         Allows easy registering of functions to this dispatcher. Example:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/exc.py 
new/tinyrpc-1.1.4/tinyrpc/exc.py
--- old/tinyrpc-1.0.4/tinyrpc/exc.py    2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/exc.py    2022-01-30 14:56:51.000000000 +0100
@@ -34,6 +34,10 @@
     could not be parsed into a response."""
 
 
+class UnexpectedIDError (InvalidReplyError, ABC):
+    """A reply received contained an invalid unique identifier."""
+
+
 class MethodNotFoundError(RPCError, ABC):
     """The desired method was not found."""
 
@@ -44,3 +48,6 @@
 
 class ServerError(RPCError, ABC):
     """An internal error in the RPC system occurred."""
+
+class TimeoutError(Exception):
+    """No reply received within the timeout period."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/protocols/__init__.py 
new/tinyrpc-1.1.4/tinyrpc/protocols/__init__.py
--- old/tinyrpc-1.0.4/tinyrpc/protocols/__init__.py     2019-11-03 
14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/protocols/__init__.py     2022-01-30 
14:56:51.000000000 +0100
@@ -5,7 +5,8 @@
 Defines the abstract base classes from which a protocol definition must be 
constructed.
 """
 from abc import ABC
-from typing import Any, List, Dict, Union, Optional
+from typing import Any, Generator, List, Dict, Union, Optional
+import itertools
 
 from tinyrpc import exc
 
@@ -324,3 +325,16 @@
         :rtype: :py:class:`RPCBatchRequest`
         """
         raise NotImplementedError()
+
+
+def default_id_generator(start: int = 1) -> Generator[int, None, None]:
+    """Generates sequential integers from `start`.
+
+    e.g. 1, 2, 3, .. 9, 10, 11, ...
+
+    :param start: The first value to start with.`
+    :type start: int
+    :return: A generator that yields a sequence of integers.
+    :rtype: :py:class:`Generator[int, None, None]`
+    """
+    return itertools.count(start)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/protocols/jsonrpc.py 
new/tinyrpc-1.1.4/tinyrpc/protocols/jsonrpc.py
--- old/tinyrpc-1.0.4/tinyrpc/protocols/jsonrpc.py      2019-11-03 
14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/protocols/jsonrpc.py      2022-01-30 
14:56:51.000000000 +0100
@@ -12,12 +12,14 @@
 
 import json
 import sys
-from typing import Dict, Any, Union, Optional, List, Tuple, Callable
+from tinyrpc.exc import UnexpectedIDError
+from typing import Dict, Any, Union, Optional, List, Tuple, Callable, Generator
 
+from . import default_id_generator
 from .. import (
     RPCBatchProtocol, RPCRequest, RPCResponse, RPCErrorResponse,
     InvalidRequestError, MethodNotFoundError, InvalidReplyError, RPCError,
-    RPCBatchRequest, RPCBatchResponse, InvalidParamsError
+    RPCBatchRequest, RPCBatchResponse, InvalidParamsError,
 )
 
 if 'jsonext' in sys.modules:
@@ -168,11 +170,12 @@
     ) -> None:
         if isinstance(error, JSONRPCErrorResponse):
             super(JSONRPCError, self).__init__(error.error)
+            self.message = error.error
             self._jsonrpc_error_code = error._jsonrpc_error_code
             if hasattr(error, 'data'):
                 self.data = error.data
         else:
-            super(JSONRPCError, self).__init__()
+            super(JSONRPCError, self).__init__(error.message)
             self.message = error['message']
             self._jsonrpc_error_code = error['code']
             if 'data' in error:
@@ -244,7 +247,7 @@
 
         :type: Any type that can be serialized by the protocol.
 
-    .. py:attribute:: _json_rpc_error
+    .. py:attribute:: _jsonrpc_error_code
 
         The numeric error code.
 
@@ -504,13 +507,18 @@
     _ALLOWED_REPLY_KEYS = sorted(['id', 'jsonrpc', 'error', 'result'])
     _ALLOWED_REQUEST_KEYS = sorted(['id', 'jsonrpc', 'method', 'params'])
 
-    def __init__(self, *args, **kwargs) -> None:
+    def __init__(
+            self,
+            id_generator: Optional[Generator[object, None, None]] = None,
+            *args,
+            **kwargs
+    ) -> None:
         super(JSONRPCProtocol, self).__init__(*args, **kwargs)
-        self._id_counter = 0
+        self._id_generator = id_generator or default_id_generator()
+        self._pending_replies = []
 
-    def _get_unique_id(self) -> int:
-        self._id_counter += 1
-        return self._id_counter
+    def _get_unique_id(self) -> object:
+        return next(self._id_generator)
 
     def request_factory(self) -> 'JSONRPCRequest':
         """Factory for request objects.
@@ -567,10 +575,13 @@
 
         if not one_way:
             request.unique_id = self._get_unique_id()
+            self._pending_replies.append(request.unique_id)
 
         request.method = method
-        request.args = args
-        request.kwargs = kwargs
+        if args is not None:
+            request.args = args
+        if kwargs is not None:
+            request.kwargs = kwargs
 
         return request
 
@@ -626,6 +637,12 @@
             response.result = rep.get('result', None)
 
         response.unique_id = rep['id']
+        if response.unique_id not in self._pending_replies:
+            raise UnexpectedIDError(
+                'Reply id does not correspond to any sent requests.'
+            )
+        else:
+            self._pending_replies.remove(response.unique_id)
 
         return response
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/protocols/msgpackrpc.py 
new/tinyrpc-1.1.4/tinyrpc/protocols/msgpackrpc.py
--- old/tinyrpc-1.0.4/tinyrpc/protocols/msgpackrpc.py   2019-11-03 
14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/protocols/msgpackrpc.py   2022-01-30 
14:56:51.000000000 +0100
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+from . import default_id_generator
 from .. import (
     RPCError,
     RPCErrorResponse,
@@ -15,7 +16,7 @@
 import msgpack
 import six
 
-from typing import Any, Dict, List, Optional, Tuple, Union
+from typing import Any, Dict, List, Optional, Tuple, Union, Generator
 
 
 class FixedErrorMessageMixin(object):
@@ -236,13 +237,17 @@
 class MSGPACKRPCProtocol(RPCProtocol):
     """MSGPACKRPC protocol implementation."""
 
-    def __init__(self, *args, **kwargs):
+    def __init__(
+            self,
+            id_generator: Optional[Generator[object, None, None]] = None,
+            *args,
+            **kwargs
+    ) -> None:
         super(MSGPACKRPCProtocol, self).__init__(*args, **kwargs)
-        self._id_counter = 0
+        self._id_generator = id_generator or default_id_generator()
 
     def _get_unique_id(self):
-        self._id_counter += 1
-        return self._id_counter
+        return next(self._id_generator)
 
     def request_factory(self) -> "MSGPACKRPCRequest":
         """Factory for request objects.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/transports/rabbitmq.py 
new/tinyrpc-1.1.4/tinyrpc/transports/rabbitmq.py
--- old/tinyrpc-1.0.4/tinyrpc/transports/rabbitmq.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/transports/rabbitmq.py    2022-01-30 
14:56:51.000000000 +0100
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from typing import Tuple, Any
+
+import pika
+
+from . import ServerTransport, ClientTransport
+
+
+class RabbitMQServerTransport(ServerTransport):
+    """Server transport based on a :py:class:`pika.BlockingConnection`.
+
+    The transport assumes a RabbitMQ topology has already been established.
+
+    :param connection: A :py:class:`pika.BlockingConnection` instance.
+    :param queue: The RabbitMQ queue to consume messages from.
+    :param exchange: The RabbitMQ exchange to use.
+    """
+
+    def __init__(self, connection: pika.BlockingConnection, queue: str, 
exchange: str = '') -> None:
+        self.connection = connection
+        self.queue = queue
+        self.exchange = exchange
+
+        self.channel = self.connection.channel()
+        self.channel.queue_declare(queue=self.queue)
+        self.channel.basic_consume(queue=self.queue, 
on_message_callback=self.on_receive)
+        self.message_received = False
+
+    def receive_message(self) -> Tuple[Any, bytes]:
+        while not self.message_received:
+            self.connection.process_data_events()
+        return self.context, self.message
+
+    def send_reply(self, context: Any, reply: bytes) -> None:
+        ch, method, props = context
+        ch.basic_publish(exchange=self.exchange,
+                     routing_key=props.reply_to,
+                     properties=pika.BasicProperties(correlation_id = 
props.correlation_id),
+                     body=reply)
+        ch.basic_ack(delivery_tag=method.delivery_tag)
+        self.message_received = False # message processed, reset status
+
+    def on_receive(self, ch, method, props, body):
+        self.context = (ch, method, props)
+        self.message = body
+        self.message_received = True
+
+    @classmethod
+    def create(cls, host: str, queue: str, exchange: str = '') -> 
'RabbitMQServerTransport':
+        """Create new server transport.
+
+        Instead of creating the BlockingConnection yourself, you can call this 
function and
+        pass in the host name, queue, and exchange.
+
+        :param host: The host clients will connect to.
+        :param queue: The RabbitMQ queue to consume messages from.
+        :param exchange: The RabbitMQ exchange to use.
+        """
+        connection = pika.BlockingConnection(pika.ConnectionParameters(host))
+        return cls(connection, queue, exchange)
+
+
+class RabbitMQClientTransport(ClientTransport):
+    """Client transport based on a :py:class:`pika.BlockingConnection`.
+
+    The transport assumes a RabbitMQ topology has already been established.
+
+    :param connection: A :py:class:`pika.BlockingConnection` instance.
+    :param routing_key: The RabbitMQ routing key to direct messages.
+    :param exchange: The RabbitMQ exchange to use.
+    """
+
+    def __init__(self, connection: pika.BlockingConnection, routing_key: str, 
exchange: str = '') -> None:
+        self.connection = connection
+        self.routing_key = routing_key
+        self.exchange = exchange
+        self._id_counter = 1000
+
+        self.channel = self.connection.channel()
+        qd_result = self.channel.queue_declare(queue='', exclusive=True)
+        self.callback_queue = qd_result.method.queue
+
+        self.channel.basic_consume(
+            queue=self.callback_queue,
+            on_message_callback=self.on_response,
+            auto_ack=True
+        )
+
+    def _get_unique_id(self) -> int:
+        self._id_counter += 1
+        return self._id_counter
+
+    def send_message(self, message: bytes, expect_reply: bool = True) -> bytes:
+        self.response_data = None
+        self.corr_id = str(self._get_unique_id())
+        self.channel.basic_publish(
+            exchange=self.exchange,
+            routing_key=self.routing_key,
+            properties=pika.BasicProperties(
+                reply_to=self.callback_queue,
+                correlation_id=self.corr_id,
+            ),
+            body=message)
+
+        if expect_reply:
+            while self.response_data is None:
+                self.connection.process_data_events()
+            return self.response_data
+
+    def on_response(self, ch, method, props, body):
+        if self.corr_id == props.correlation_id:
+            self.response_data = body
+
+    @classmethod
+    def create(cls, host: str, routing_key: str, exchange: str = '') -> 
'RabbitMQClientTransport':
+        """Create new client transport.
+
+        Instead of creating the BlockingConnection yourself, you can call this 
function and
+        pass in the host name, routing key, and exchange.
+
+        :param host: The host clients will connect to.
+        :param routing_key: The RabbitMQ routing key to direct messages.
+        :param exchange: The RabbitMQ exchange to use.
+        """
+        connection = pika.BlockingConnection(pika.ConnectionParameters(host))
+        return cls(connection, routing_key, exchange)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/transports/wsgi.py 
new/tinyrpc-1.1.4/tinyrpc/transports/wsgi.py
--- old/tinyrpc-1.0.4/tinyrpc/transports/wsgi.py        2019-11-03 
14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tinyrpc/transports/wsgi.py        2022-01-30 
14:56:51.000000000 +0100
@@ -72,6 +72,10 @@
             'Content-Type, X-Requested-With, Accept, Origin'
         }
 
+        post_headers = {
+            'Content-Type': 'application/json'
+        }
+
         if request.method == 'OPTIONS':
             response = Response(headers=access_control_headers)
 
@@ -84,8 +88,11 @@
 
             self.messages.put((context, msg))
 
+            # collect and combine all headers
+            response_headers = dict(**access_control_headers, **post_headers)
+
             # ...and send the reply
-            response = Response(context.get(), headers=access_control_headers)
+            response = Response(context.get(), headers=response_headers)
         else:
             # nothing else supported at the moment
             response = Response('Only POST supported', 405)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tinyrpc/transports/zmq.py 
new/tinyrpc-1.1.4/tinyrpc/transports/zmq.py
--- old/tinyrpc-1.0.4/tinyrpc/transports/zmq.py 2019-11-03 14:42:07.000000000 
+0100
+++ new/tinyrpc-1.1.4/tinyrpc/transports/zmq.py 2022-01-30 14:56:51.000000000 
+0100
@@ -3,11 +3,12 @@
 
 from __future__ import absolute_import  # needed for zmq import
 
-from typing import Tuple, Any
+from typing import Tuple, Any, Dict
 
 import zmq
 
 from . import ServerTransport, ClientTransport
+from .. import exc
 
 
 class ZmqServerTransport(ServerTransport):
@@ -50,19 +51,36 @@
 
     :param socket: A :py:const:`zmq.REQ` socket instance, connected to the
                    server socket.
+    :param timeout: An optional float. When set it defines the time period
+                    in seconds to wait for a reply.
+                    It will generate a :py:class:`exc.TimeoutError` exception
+                    if no reply was received in time.
     """
 
-    def __init__(self, socket: zmq.Socket) -> None:
+    def __init__(self, socket: zmq.Socket, timeout: float = None) -> None:
         self.socket = socket
+        self.timeout = timeout
 
     def send_message(self, message: bytes, expect_reply: bool = True) -> bytes:
         self.socket.send(message)
 
+        # zmq contains a state machine preventing a new request
+        # until the previous one is answered, so always receive
+        if self.timeout is None:
+            reply = self.socket.recv()
+        else:
+            poller = zmq.Poller()
+            poller.register(self.socket, zmq.POLLIN)
+            ready = dict(poller.poll(int(self.timeout * 1000)))
+            if ready.get(self.socket) == zmq.POLLIN:
+                reply = self.socket.recv()
+            else:
+                raise exc.TimeoutError()
         if expect_reply:
-            return self.socket.recv()
+            return reply
 
     @classmethod
-    def create(cls, zmq_context: zmq.Context, endpoint: str) -> 
'ZmqClientTransport':
+    def create(cls, zmq_context: zmq.Context, endpoint: str, timeout: float = 
None) -> 'ZmqClientTransport':
         """Create new client transport.
 
         Instead of creating the socket yourself, you can call this function and
@@ -73,7 +91,8 @@
 
         :param zmq_context: A 0mq context.
         :param endpoint: The endpoint the server is bound to.
+        :param timeout: Optional period in seconds to wait for reply
         """
         socket = zmq_context.socket(zmq.REQ)
         socket.connect(endpoint)
-        return cls(socket)
+        return cls(socket, timeout)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tinyrpc-1.0.4/tox.ini new/tinyrpc-1.1.4/tox.ini
--- old/tinyrpc-1.0.4/tox.ini   2019-11-03 14:42:07.000000000 +0100
+++ new/tinyrpc-1.1.4/tox.ini   2022-01-30 14:56:51.000000000 +0100
@@ -1,19 +1,9 @@
 [tox]
-envlist = py37
+#envlist = py38
+envlist = py34, py35, py36, py37, py38, py39
 
 [testenv]
-;usedevelop = true
-;deps=
-;    six
-;    pytest
-;    pytest-cov
-;    werkzeug
-;    greenlet
-;    gevent-websocket
-;    gevent
-;    requests==2.4.3
-;    zmq
 deps = -rrequirements.txt
 commands=
     pytest -rs
-;    pytest --cov=tinyrpc/ --cov-report=term --cov-report=html
+    pytest --cov=tinyrpc/ --cov-report=term --cov-report=html

Reply via email to