Hello community,

here is the log from the commit of package python-Pyro4 for openSUSE:Factory 
checked in at 2019-03-19 09:58:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-Pyro4 (Old)
 and      /work/SRC/openSUSE:Factory/.python-Pyro4.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-Pyro4"

Tue Mar 19 09:58:09 2019 rev:4 rq:682266 version:4.75

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-Pyro4/python-Pyro4.changes        
2019-01-24 14:03:11.700066573 +0100
+++ /work/SRC/openSUSE:Factory/.python-Pyro4.new.28833/python-Pyro4.changes     
2019-03-19 09:58:11.808107676 +0100
@@ -1,0 +2,12 @@
+Wed Mar  6 09:29:20 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Update to 4.75:
+  * include LICENSE file in distribution
+  * avoid decode error when dealing with memoryview annotations
+  * serpent 1.27 required to avoid regression in previous version
+  * fixed marshal serializer problem that prevented it to even call register() 
in the name server.
+  * msgpack, json and marshal serializers now understand how to serialize 
array.array the same way serpent already did
+  * fixed distributed-mandelbrot example to actually run multiple concurrent 
calculations.
+  * missing API method doc added on NameServer.count()
+
+-------------------------------------------------------------------

Old:
----
  Pyro4-4.72.tar.gz

New:
----
  Pyro4-4.75.tar.gz

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

Other differences:
------------------
++++++ python-Pyro4.spec ++++++
--- /var/tmp/diff_new_pack.coxbTU/_old  2019-03-19 09:58:12.956107215 +0100
+++ /var/tmp/diff_new_pack.coxbTU/_new  2019-03-19 09:58:12.996107198 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-Pyro4
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 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
@@ -18,36 +18,36 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-Pyro4
-Version:        4.72
+Version:        4.75
 Release:        0
 Summary:        Distributed object middleware for Python (RPC)
 License:        MIT
 Group:          Development/Languages/Python
-Url:            https://github.com/irmen/Pyro4
+URL:            https://github.com/irmen/Pyro4
 Source:         
https://files.pythonhosted.org/packages/source/P/Pyro4/Pyro4-%{version}.tar.gz
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
+Requires:       python-serpent >= 1.27
+Requires(post): update-alternatives
+Requires(postun): update-alternatives
+Recommends:     ca-certificates
+Recommends:     python-cloudpickle >= 0.4.0
+Recommends:     python-dill >= 0.2.6
+Recommends:     python-msgpack-python >= 0.5.2
+BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module cloudpickle >= 0.4.0}
 BuildRequires:  %{python_module dill >= 0.2.6}
-BuildRequires:  %{python_module msgpack-python >= 0.4.6}
-BuildRequires:  %{python_module serpent >= 1.24}
+BuildRequires:  %{python_module msgpack-python >= 0.5.2}
+BuildRequires:  %{python_module pytest}
+BuildRequires:  %{python_module serpent >= 1.27}
 BuildRequires:  ca-certificates
 BuildRequires:  python-selectors34
 # /SECTION
-Requires:       python-serpent >= 1.24
-Recommends:     ca-certificates
-Recommends:     python-cloudpickle >= 0.4.0
-Recommends:     python-dill >= 0.2.6
-Recommends:     python-msgpack-python >= 0.4.6
 %ifpython2
 Requires:       python-selectors34
 %endif
-BuildArch:      noarch
-Requires(post):   update-alternatives
-Requires(postun):  update-alternatives
-
 %python_subpackages
 
 %description
@@ -61,10 +61,8 @@
 building distributed applications. Pyro is a pure Python library and
 runs on many different platforms and Python versions.
 
-
 %prep
 %setup -q -n Pyro4-%{version}
-sed -i 's/\r$//' LICENSE
 
 %build
 %python_build
@@ -79,6 +77,27 @@
 %python_clone -a %{buildroot}%{_bindir}/pyro4-nsc
 %python_clone -a %{buildroot}%{_bindir}/pyro4-test-echoserver
 
+%check
+# testContextAndSock missing cert fixtures 
https://github.com/irmen/Pyro4/issues/216
+# socket tests require at least lo interface thus skip them
+skip="testContextAndSock"
+skip+=" or testGetIP or testAutoClean"
+skip+=" or testBroadcast or testBCstart"
+skip+=" or testStartNSfunc or testStartNSfunc"
+skip+=" or testResolveWrongHmac"
+skip+=" or testResolveAsymmetricHmacUsage"
+skip+=" or testResolve"
+skip+=" or testPyroname"
+skip+=" or testLookupAndRegister"
+skip+=" or testBCLookup0000"
+skip+=" or testRefuseDottedNames"
+skip+=" or testMulti"
+skip+=" or testLookupUnixsockParsing"
+skip+=" or testLookupInvalidHmac"
+skip+=" or testLookupAndRegister"
+skip+=" or testDaemonPyroObj"
+%python_expand PYTHONPATH=%{buildroot}%{$python_sitelib}:./tests/PyroTests 
py.test-%{$python_bin_suffix} -v -k "not ($skip)"
+
 %post
 %{python_install_alternative pyro4-check-config pyro4-flameserver 
pyro4-httpgateway pyro4-ns pyro4-nsc pyro4-test-echoserver}
 

++++++ Pyro4-4.72.tar.gz -> Pyro4-4.75.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/MANIFEST.in new/Pyro4-4.75/MANIFEST.in
--- old/Pyro4-4.72/MANIFEST.in  2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/MANIFEST.in  2018-08-16 09:50:01.000000000 +0200
@@ -1,5 +1,7 @@
 include LICENSE
 include MANIFEST.in
+include README.md
+include tox.ini
 recursive-include tests *
 recursive-include examples *
 recursive-include docs *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/PKG-INFO new/Pyro4-4.75/PKG-INFO
--- old/Pyro4-4.72/PKG-INFO     2018-05-16 21:22:35.000000000 +0200
+++ new/Pyro4-4.75/PKG-INFO     2019-01-19 13:16:41.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Pyro4
-Version: 4.72
+Version: 4.75
 Summary: distributed object middleware for Python (RPC)
 Home-page: http://pyro4.readthedocs.io
 Author: Irmen de Jong
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/README.md new/Pyro4-4.75/README.md
--- old/Pyro4-4.72/README.md    1970-01-01 01:00:00.000000000 +0100
+++ new/Pyro4-4.75/README.md    2018-09-04 00:11:22.000000000 +0200
@@ -0,0 +1,29 @@
+[![saythanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/irmen)
+[![Build 
Status](https://travis-ci.org/irmen/Pyro4.svg?branch=master)](https://travis-ci.org/irmen/Pyro4)
+[![Latest 
Version](https://img.shields.io/pypi/v/Pyro4.svg)](https://pypi.python.org/pypi/Pyro4/)
+[![Anaconda-Server 
Badge](https://anaconda.org/conda-forge/pyro4/badges/version.svg)](https://anaconda.org/conda-forge/pyro4)
+[![Code Quality: 
Python](https://img.shields.io/lgtm/grade/python/g/irmen/Pyro4.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/irmen/Pyro4/context:python)
+[![Total 
Alerts](https://img.shields.io/lgtm/alerts/g/irmen/Pyro4.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/irmen/Pyro4/alerts)
+
+PYRO - Python Remote Objects
+============================
+
+Pyro enables you to build applications in which objects can talk
+to each other over the network, with minimal programming effort.
+You can just use normal Python method calls to call objects on
+other machines. Pyro is a pure Python library so it
+runs on many different platforms and Python versions.
+
+
+Documentation can be found online at: http://pyro4.readthedocs.io
+(or unformatted here in the repo at: docs/source/intro.rst)
+
+
+This software is copyright (c) by Irmen de Jong ([email protected]).
+
+This software is released under the MIT software license.
+This license, including disclaimer, is available in the 'LICENSE' file.
+
+
+----
+_Are you interested in the most recent cutting edge Pyro version? Go try out 
Pyro5!  https://github.com/irmen/Pyro5_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/_static/css/customize.css 
new/Pyro4-4.75/docs/source/_static/css/customize.css
--- old/Pyro4-4.72/docs/source/_static/css/customize.css        1970-01-01 
01:00:00.000000000 +0100
+++ new/Pyro4-4.75/docs/source/_static/css/customize.css        2018-09-04 
00:08:33.000000000 +0200
@@ -0,0 +1,14 @@
+.wy-nav-content {
+    max-width: 1000px;
+}
+
+/* override table width restrictions */
+.wy-table-responsive table td, .wy-table-responsive table th {
+    white-space: normal;
+}
+
+.wy-table-responsive {
+    margin-bottom: 24px;
+    max-width: 100%;
+    overflow: visible;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/changelog.rst 
new/Pyro4-4.75/docs/source/changelog.rst
--- old/Pyro4-4.72/docs/source/changelog.rst    2018-05-16 21:14:37.000000000 
+0200
+++ new/Pyro4-4.75/docs/source/changelog.rst    2019-01-19 13:14:23.000000000 
+0100
@@ -2,6 +2,28 @@
 Change Log
 **********
 
+**Pyro 4.75**
+
+- fixed distributed-mandelbrot example to actually run multiple concurrent 
calculations.
+- CI build process now using more modern Python versions.
+- missing API method doc added on NameServer.count()
+
+
+**Pyro 4.74**
+
+- serpent 1.27 required to avoid regression in previous version
+- fixed marshal serializer problem that prevented it to even call register() 
in the name server.
+  Its dumpsCall is now able to use the class_to_dict conversion for 
unmarshallable types
+  (in simple situations, not recursively).  Previously, you would get a 
ValueError: unmarshallable object.
+- msgpack, json and marshal serializers now understand how to serialize 
array.array the same way serpent already did
+
+
+**Pyro 4.73**
+
+- include LICENSE file in distribution
+- avoid decode error when dealing with memoryview annotations
+
+
 **Pyro 4.72**
 
 - (source files: normalized line endings to LF)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/clientcode.rst 
new/Pyro4-4.75/docs/source/clientcode.rst
--- old/Pyro4-4.72/docs/source/clientcode.rst   2018-05-16 21:14:37.000000000 
+0200
+++ new/Pyro4-4.75/docs/source/clientcode.rst   2018-08-16 09:50:01.000000000 
+0200
@@ -216,9 +216,12 @@
 Changing the way your custom classes are (de)serialized
 -------------------------------------------------------
 
-.. note::
-    The information in this paragraph is not relevant when using the pickle, 
cloudpickle or dill serialization protocols,
-    they have their own ways of serializing custom classes.
+.. sidebar::
+    Applicability
+
+    The information in this paragraph does not apply to the pickle, 
cloudpickle or dill serialization protocols.
+    They have their own ways of serializing custom classes.
+
 
 By default, custom classes are serialized into a dict.
 They are not deserialized back into instances of your custom class. This 
avoids possible security issues.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/conf.py 
new/Pyro4-4.75/docs/source/conf.py
--- old/Pyro4-4.72/docs/source/conf.py  2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/docs/source/conf.py  2018-09-04 00:09:27.000000000 +0200
@@ -229,6 +229,8 @@
 ]
 
 def setup(app):
+    # add custom css
+    app.add_stylesheet("css/customize.css")
     from sphinx.ext.autodoc import cut_lines
     # skip the copyright line in every module docstring (last line of 
docstring)
     app.connect('autodoc-process-docstring', cut_lines(pre=0, post=1, 
what=['module']))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/index.rst 
new/Pyro4-4.75/docs/source/index.rst
--- old/Pyro4-4.72/docs/source/index.rst        2018-05-16 21:14:37.000000000 
+0200
+++ new/Pyro4-4.75/docs/source/index.rst        2018-09-04 00:37:46.000000000 
+0200
@@ -20,11 +20,10 @@
 Pyro can be found on Pypi as `Pyro4 <http://pypi.python.org/pypi/Pyro4/>`_.  
Source on Github: https://github.com/irmen/Pyro4
 
 
-Contents
---------
 
 .. toctree::
    :maxdepth: 2
+   :caption: Contents of this manual:
    
    intro.rst
    install.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/install.rst 
new/Pyro4-4.75/docs/source/install.rst
--- old/Pyro4-4.72/docs/source/install.rst      2018-05-16 21:14:37.000000000 
+0200
+++ new/Pyro4-4.75/docs/source/install.rst      2018-08-16 09:50:01.000000000 
+0200
@@ -62,7 +62,7 @@
 Third party libraries that Pyro4 uses
 -------------------------------------
 
-`serpent <https://pypi.python.org/pypi/serpent>`_ - required, 1.24 or newer
+`serpent <https://pypi.python.org/pypi/serpent>`_ - required, 1.27 or newer
     Should be installed automatically when you install Pyro4.
 
 `selectors34 <https://pypi.python.org/pypi/selectors34>`_ - required on Python 
3.3 or older
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/docs/source/servercode.rst 
new/Pyro4-4.75/docs/source/servercode.rst
--- old/Pyro4-4.72/docs/source/servercode.rst   2018-05-16 21:14:37.000000000 
+0200
+++ new/Pyro4-4.75/docs/source/servercode.rst   2018-08-16 09:50:01.000000000 
+0200
@@ -44,14 +44,13 @@
   and decorate that with ``@expose``, if you want to provide a remotely 
accessible attribute.
 - classes as a whole (exposing a class has the effect of exposing every 
nonprivate method and property of the class automatically)
 
-.. sidebar:: private members
+Anything that isn't decorated with ``@expose`` is not remotely accessible.
 
+.. important:: **Private methods and attributes**:
     In the spirit of being secure by default, Pyro doesn't allow remote access 
to anything of your class unless
-    explicitly told to do so. It will never allow remote access to 'private' 
members
-    (where private means that the name starts with a single or double 
underscore,
-    with a special exception for the regular 'dunder' names with double 
underscores such as ``__len__``)
-
-Anything that isn't decorated with ``@expose`` is not remotely accessible.
+    explicitly told to do so. It will never allow remote access to 'private' 
methods and attributes
+    (where 'private' means that their name starts with a single or double 
underscore).
+    There's a special exception for the regular 'dunder' names with double 
underscores such as ``__len__`` though.
 
 Here's a piece of example code that shows how a partially exposed Pyro class 
may look like::
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/examples/batchedcalls/client.py 
new/Pyro4-4.75/examples/batchedcalls/client.py
--- old/Pyro4-4.72/examples/batchedcalls/client.py      2018-05-16 
21:14:37.000000000 +0200
+++ new/Pyro4-4.75/examples/batchedcalls/client.py      2018-08-16 
09:50:01.000000000 +0200
@@ -99,6 +99,6 @@
         divisor -= 1
         # this will raise the proper zerodivision exception once we're about
         # to process the batch result from the divide by 0 call.
-except Exception:
-    print("An error occurred during the batch! (expected)")
+except ZeroDivisionError:
+    print("A divide by zero error occurred during the batch! (expected)")
     print("".join(getPyroTraceback()))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/examples/blob-dispatch/Readme.txt 
new/Pyro4-4.75/examples/blob-dispatch/Readme.txt
--- old/Pyro4-4.72/examples/blob-dispatch/Readme.txt    1970-01-01 
01:00:00.000000000 +0100
+++ new/Pyro4-4.75/examples/blob-dispatch/Readme.txt    2018-08-16 
09:50:01.000000000 +0200
@@ -0,0 +1,15 @@
+This shows how you can pass through serialized arguments unchanged via 
Pyro4.core.SerializedBlob.
+The idea is that you tell Pyro to NOT serialize/deserialize particular message 
contents,
+because you'll be doing that yourself once it reaches the destination. This 
avoids a lot of
+serializer overhead (which is quite expensive).
+
+This way it is possible to make efficient dispatchers/proxies/routing services 
for Pyro,
+where only the actual receiving server at the end, deserializes the package 
once.
+
+
+Run this example by:
+
+- make sure a Pyro name server is running.
+- start a dispatcher from the dispatcher directory
+- start one or more listeners from listeners/main.py
+- start the client.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Pyro4-4.72/examples/distributed-mandelbrot/Readme.txt 
new/Pyro4-4.75/examples/distributed-mandelbrot/Readme.txt
--- old/Pyro4-4.72/examples/distributed-mandelbrot/Readme.txt   2018-05-16 
21:14:37.000000000 +0200
+++ new/Pyro4-4.75/examples/distributed-mandelbrot/Readme.txt   2018-10-30 
01:24:47.000000000 +0100
@@ -1,8 +1,13 @@
 These examples are about calculating the Mandelbrot fractal set (z=z^2+c).
 
+
+NOTE: use the "launch_servers.sh" shell script to launch the name server
+      and a reasonable number of Pyro mandelbrot server processes.
+
+
 First, a few notes:
 - The ascii animation runs at 100x40 resolution so make sure your console 
window is large enough.
-- The maximum iteration count is set to a quite high value to make the 
calculcations
+- The maximum iteration count is set to a quite high value to make the 
calculations
   more time consuming. If you want you can change both maxiter values in 
server.py down
   to something more reasonable such as 256.
 - try using Pypy instead of CPython to improve the speed dramatically
@@ -10,8 +15,6 @@
 
 The 'normal' code simply runs the calculation in a single Python process.
 It calculates every frame of the animation in one go and prints it to the 
screen.
-On my computer it runs at about 2.5 frames per second using CPython 3.5
-This is in a windows console and will only use one CPU core.
 
 
 The 'client_asciizoom' program uses Pyro to offload the calculations to 
whatever
@@ -23,15 +26,10 @@
 will just call every server once per frame. The calls will return a bunch of 
resulting lines
 that are merged into the final animation frame, which is then printed to the 
screen.
 
-On my machine with 4 cpu cores, when starting 4 mandelbrot servers using 
CPython 3.5,
-the animation now runs at about 7 fps instead of the previous 2.5. It uses all 
4 cores of the machine
-at about 80% load. I guess the rest is I/O time spent printing the frames to 
the console
-(something that is not fast on windows).
 
+The graphics version is interesting too because it actually creates a nice 
picture!
 
 
-The graphics version is interesting too because it actually creates a nice 
picture!
-The single core normal version takes 84 seconds on my machine. The Pyro
-version only takes 22 seconds, when using 4 mandelbrot calculation servers.
-It uses all 4 cores at nearly 100%, and indeed is almost 4 times faster.
-It submits a single line to a mandelbrot server per call.
+On my 8c/16t cpu the speedup of the distributed calculation of the graphical 
picture
+is massive. The normal single core version takes 22 seconds, while the 
distributed version
+only takes 2.6 seconds (and utilizes all cores of the cpu for nearly 100%).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Pyro4-4.72/examples/distributed-mandelbrot/client_graphics.py 
new/Pyro4-4.75/examples/distributed-mandelbrot/client_graphics.py
--- old/Pyro4-4.72/examples/distributed-mandelbrot/client_graphics.py   
2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/examples/distributed-mandelbrot/client_graphics.py   
2018-10-30 01:24:09.000000000 +0100
@@ -1,15 +1,8 @@
 # mandelbrot fractal,  z=z^2+c
-from __future__ import print_function, division
 import time
-try:
-    from queue import Queue, Empty
-except ImportError:
-    from Queue import Queue, Empty
-try:
-    import tkinter
-except ImportError:
-    import Tkinter as tkinter
-import Pyro4
+import tkinter
+from concurrent import futures
+from Pyro4 import Proxy, locateNS
 
 
 res_x = 1000
@@ -19,62 +12,40 @@
 class MandelWindow(object):
     def __init__(self):
         self.root = tkinter.Tk()
-        self.root.title("Mandelbrot (Pyro multi core version)")
+        self.root.title("Mandelbrot (Pyro multi CPU core version)")
         canvas = tkinter.Canvas(self.root, width=res_x, height=res_y, 
bg="#000000")
         canvas.pack()
         self.img = tkinter.PhotoImage(width=res_x, height=res_y)
         canvas.create_image((res_x/2, res_y/2), image=self.img, state="normal")
-        with Pyro4.locateNS() as ns:
+        with locateNS() as ns:
             mandels = ns.list(metadata_any={"class:mandelbrot_calc_color"})
             mandels = list(mandels.items())
         print("{0} mandelbrot calculation servers found.".format(len(mandels)))
         if not mandels:
             raise ValueError("launch at least one mandelbrot calculation 
server before starting this")
-        self.mandels = [Pyro4.Proxy(uri) for _, uri in mandels]
-        for m in self.mandels:
-            m._pyroAsync()   # set them to asynchronous mode
-        for proxy in self.mandels:
-            proxy._pyroBind()
-        self.lines = list(reversed(range(res_y)))
-        self.draw_data = Queue()
-        self.root.after(1000, self.draw_lines)
+        self.mandels = [uri for _, uri in mandels]
+        self.pool = futures.ThreadPoolExecutor(max_workers=len(self.mandels))
+        self.tasks = []
+        self.start_time = time.time()
+        for line in range(res_y):
+            self.tasks.append(self.calc_new_line(line))
+        self.root.after(100, self.draw_results)
         tkinter.mainloop()
 
-    def draw_lines(self):
-        # start by putting each of the found servers to work on a single line,
-        # the other lines will be done in turn when the results come back.
-        for _ in range(len(self.mandels)):
-            self.calc_new_line()
-        self.start_time = time.time()
-        self.draw_results()
-        
     def draw_results(self):
-        # we do the drawing of the results in the gui main thread
-        # otherwise strange things may happen such as freezes
-        try:
-            while True:
-                y, pixeldata = self.draw_data.get(block=False)
-                if pixeldata:
-                    self.img.put(pixeldata, (0, y))
-                else:
-                    # end reached
-                    duration = time.time() - self.start_time
-                    print("Calculation took: %.2f seconds" % duration)
-                    break
-        except Empty:
-            self.root.after(100, self.draw_results)
-
-    def calc_new_line(self):
-        y = self.lines.pop()
-        server = self.mandels[y % len(self.mandels)]  # round robin server 
selection
-        server.calc_photoimage_line(y, res_x, res_y).then(self.process_result)
-
-    def process_result(self, result):
-        self.draw_data.put(result)  # drawing should be done by the main gui 
thread
-        if self.lines:
-            self.calc_new_line()
-        else:
-            self.draw_data.put((None, None))  # end-sentinel
+        for task in futures.as_completed(self.tasks):
+            y, pixeldata = task.result()
+            self.img.put(pixeldata, (0, y))
+            self.root.update()
+        duration = time.time() - self.start_time
+        print("Calculation took: %.2f seconds" % duration)
+
+    def calc_new_line(self, y):
+        def line_task(server_uri, y):
+            with Proxy(server_uri) as calcproxy:
+                return calcproxy.calc_photoimage_line(y, res_x, res_y)
+        uri = self.mandels[y % len(self.mandels)]  # round robin server 
selection
+        return self.pool.submit(line_task, uri, y)
 
 
 if __name__ == "__main__":
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Pyro4-4.72/examples/distributed-mandelbrot/launch_servers.sh 
new/Pyro4-4.75/examples/distributed-mandelbrot/launch_servers.sh
--- old/Pyro4-4.72/examples/distributed-mandelbrot/launch_servers.sh    
1970-01-01 01:00:00.000000000 +0100
+++ new/Pyro4-4.75/examples/distributed-mandelbrot/launch_servers.sh    
2018-10-30 01:21:29.000000000 +0100
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+python -m Pyro4.naming &
+sleep 0.5
+
+NUM_CPUS=$(python -c "import os; print(os.cpu_count())")
+
+echo "Launching ${NUM_CPUS} mandelbrot server processes..."
+for id in $(seq 1 ${NUM_CPUS})
+do
+    python server.py ${id} &
+done
+
+sleep 1
+echo ""
+echo "Now start a client."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/examples/distributed-mandelbrot/server.py 
new/Pyro4-4.75/examples/distributed-mandelbrot/server.py
--- old/Pyro4-4.72/examples/distributed-mandelbrot/server.py    2018-05-16 
21:14:37.000000000 +0200
+++ new/Pyro4-4.75/examples/distributed-mandelbrot/server.py    2018-10-30 
01:18:48.000000000 +0100
@@ -1,18 +1,8 @@
 from __future__ import print_function, division
-import math
+import sys
 import Pyro4
 
 
-# A note about the abs(z) calls below not using abs(z),
-# but instead squaring the imaginary and real components itself:
-#
-# This is because using abs(z) triggers a performance issue on pypy on windows,
-# where it is much slower than it could have been. This seems to be an issue
-# with the hypot() function in Microsoft's 32 bits runtime library.
-# See bug report https://bitbucket.org/pypy/pypy/issues/2401
-# The problem doesn't occur on other Pypy implementations.
-
-
 @Pyro4.expose
 class Mandelbrot(object):
     maxiters = 500
@@ -40,7 +30,7 @@
     def iterations(self, z):
         c = z
         for n in range(self.maxiters):
-            if z.real*z.real + z.imag*z.imag > 4:      # abs(z) > 2
+            if abs(z) > 2:
                 return n
             z = z*z + c
         return self.maxiters
@@ -63,25 +53,31 @@
         zi *= res_y/res_x  # aspect correction
         z = complex(zr, zi)
         c = z
+        iters = 0
         for iters in range(self.maxiters+1):
-            if z.real*z.real + z.imag*z.imag > 4:      # abs(z) > 2
+            if abs(z) > 2:
                 break
             z = z*z + c
         if iters >= self.maxiters:
             return 0, 0, 0
-        abs_z = math.sqrt(z.real*z.real + z.imag*z.imag)     # abs(z)
         r = (iters+32) % 255
-        g = (iters - math.log(abs_z)) % 255
-        b = (abs_z*iters) % 255
+        g = iters % 255
+        b = (iters+40) % 255
         return int(r), int(g), int(b)
 
 
 if __name__ == "__main__":
+    # spawn a Pyro daemon process
+    # (can't use threads, because of the GIL)
+    if len(sys.argv) != 2:
+        raise SystemExit("give argument: server_id number")
+
+    server_id = int(sys.argv[1])
     with Pyro4.Daemon() as d:
-        uri_1 = d.register(Mandelbrot)
-        uri_2 = d.register(MandelbrotColorPixels)
         with Pyro4.locateNS() as ns:
-            ns.register(Mandelbrot._pyroId, uri_1, safe=True, 
metadata={"class:mandelbrot_calc"})
-            ns.register(MandelbrotColorPixels._pyroId, uri_2, safe=True, 
metadata={"class:mandelbrot_calc_color"})
-        print("Mandelbrot calculation server ready.")
+            mandel_server = d.register(Mandelbrot)
+            mandel_color_server = d.register(MandelbrotColorPixels)
+            ns.register("mandelbrot_"+str(server_id), mandel_server, 
safe=True, metadata={"class:mandelbrot_calc"})
+            ns.register("mandelbrot_color_"+str(server_id), 
mandel_color_server, safe=True, metadata={"class:mandelbrot_calc_color"})
+        print("Mandelbrot calculation server #{} ready.".format(server_id))
         d.requestLoop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/setup.cfg new/Pyro4-4.75/setup.cfg
--- old/Pyro4-4.72/setup.cfg    2018-05-16 21:22:35.000000000 +0200
+++ new/Pyro4-4.75/setup.cfg    2019-01-19 13:16:41.000000000 +0100
@@ -1,3 +1,7 @@
+[metadata]
+license = MIT
+license_file = LICENSE
+
 [bdist_rpm]
 doc_files = LICENSE
        docs/
@@ -14,7 +18,7 @@
 
 [pycodestyle]
 max-line-length = 140
-ignore = E402,E731
+ignore = E402,E731,W504
 exclude = .git,__pycache__,.tox,docs,tests,build,dist,examples
 
 [egg_info]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/setup.py new/Pyro4-4.75/setup.py
--- old/Pyro4-4.72/setup.py     2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/setup.py     2018-08-16 09:50:01.000000000 +0200
@@ -46,7 +46,7 @@
         "packages": ['Pyro4', 'Pyro4.socketserver', 'Pyro4.test', 
'Pyro4.utils'],
         "scripts": [],
         "platforms": "any",
-        "install_requires": ["serpent>=1.24"],
+        "install_requires": ["serpent>=1.27"],
         "extras_require": {
             ":python_version<'3.4'": ["selectors34"]
         },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4/constants.py 
new/Pyro4-4.75/src/Pyro4/constants.py
--- old/Pyro4-4.72/src/Pyro4/constants.py       2018-05-16 21:17:34.000000000 
+0200
+++ new/Pyro4-4.75/src/Pyro4/constants.py       2019-01-19 13:07:28.000000000 
+0100
@@ -5,7 +5,7 @@
 """
 
 # Pyro version
-VERSION = "4.72"
+VERSION = "4.75"
 
 # standard object name for the Daemon object
 DAEMON_NAME = "Pyro.Daemon"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4/core.py 
new/Pyro4-4.75/src/Pyro4/core.py
--- old/Pyro4-4.72/src/Pyro4/core.py    2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4/core.py    2018-08-16 09:50:01.000000000 +0200
@@ -466,7 +466,7 @@
                         return msg
                     data = serializer.deserializeData(msg.data, 
compressed=msg.flags & message.FLAGS_COMPRESSED)
                     if msg.flags & message.FLAGS_ITEMSTREAMRESULT:
-                        streamId = msg.annotations.get("STRM", b"").decode()
+                        streamId = bytes(msg.annotations.get("STRM", 
b"")).decode()
                         if not streamId:
                             raise errors.ProtocolError("result of call is an 
iterator, but the server is not configured to allow streaming")
                         return _StreamResultIterator(streamId, self)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4/naming.py 
new/Pyro4-4.75/src/Pyro4/naming.py
--- old/Pyro4-4.72/src/Pyro4/naming.py  2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4/naming.py  2018-12-02 20:45:18.000000000 +0100
@@ -79,6 +79,7 @@
         self.lock = threading.RLock()
 
     def count(self):
+        """Returns the number of name registrations."""
         return len(self.storage)
 
     def lookup(self, name, return_metadata=False):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Pyro4-4.72/src/Pyro4/socketserver/existingconnectionserver.py 
new/Pyro4-4.75/src/Pyro4/socketserver/existingconnectionserver.py
--- old/Pyro4-4.72/src/Pyro4/socketserver/existingconnectionserver.py   
2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4/socketserver/existingconnectionserver.py   
2018-09-03 12:21:49.000000000 +0200
@@ -8,7 +8,6 @@
 import socket
 import sys
 import logging
-import os
 import ssl
 from Pyro4 import socketutil, errors, util
 from Pyro4.configuration import config
@@ -81,8 +80,6 @@
         except (socket.error, errors.ConnectionClosedError, 
errors.SecurityError) as x:
             # client went away or caused a security error.
             # close the connection silently.
-            ex_t, ex_v, ex_tb = sys.exc_info()
-            tb = util.formatTraceback(ex_t, ex_v, ex_tb)
             try:
                 peername = self.conn.sock.getpeername()
                 log.debug("disconnected %s", peername)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4/util.py 
new/Pyro4-4.75/src/Pyro4/util.py
--- old/Pyro4-4.72/src/Pyro4/util.py    2018-05-16 21:14:37.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4/util.py    2018-09-03 12:27:25.000000000 +0200
@@ -4,6 +4,7 @@
 Pyro - Python Remote Objects.  Copyright by Irmen de Jong 
([email protected]).
 """
 
+import array
 import sys
 import zlib
 import uuid
@@ -225,7 +226,7 @@
         if serpent_too:
             try:
                 get_serializer_by_id(SerpentSerializer.serializer_id)
-                import serpent
+                import serpent      # @todo not needed?
 
                 def serpent_converter(obj, serializer, stream, level):
                     d = converter(obj)
@@ -243,7 +244,7 @@
             del cls.__custom_class_to_dict_registry[clazz]
         try:
             get_serializer_by_id(SerpentSerializer.serializer_id)
-            import serpent
+            import serpent          # @todo not needed?
             serpent.unregister_class(clazz)
         except errors.ProtocolError:
             pass
@@ -532,13 +533,12 @@
     serializer_id = 3  # never change this
 
     def dumpsCall(self, obj, method, vargs, kwargs):
+        vargs = [self.convert_obj_into_marshallable(value) for value in vargs]
+        kwargs = {key: self.convert_obj_into_marshallable(value) for key, 
value in kwargs.items()}
         return marshal.dumps((obj, method, vargs, kwargs))
 
     def dumps(self, data):
-        try:
-            return marshal.dumps(data)
-        except (ValueError, TypeError):
-            return marshal.dumps(self.class_to_dict(data))
+        return marshal.dumps(self.convert_obj_into_marshallable(data))
 
     if sys.platform == "cli":
         def loadsCall(self, data):
@@ -567,6 +567,22 @@
             data = self._convertToBytes(data)
             return self.recreate_classes(marshal.loads(data))
 
+    marshalable_types = (str, int, float, type(None), bool, complex, bytes, 
bytearray,
+                         tuple, set, frozenset, list, dict)
+    if sys.version_info < (3, 0):
+        marshalable_types += (unicode,)
+
+    def convert_obj_into_marshallable(self, obj):
+        if isinstance(obj, self.marshalable_types):
+            return obj
+        if isinstance(obj, array.array):
+            if obj.typecode == 'c':
+                return obj.tostring()
+            if obj.typecode == 'u':
+                return obj.tounicode()
+            return obj.tolist()
+        return self.class_to_dict(obj)
+
     @classmethod
     def class_to_dict(cls, obj):
         if isinstance(obj, uuid.UUID):
@@ -655,6 +671,12 @@
             return obj.isoformat()
         if isinstance(obj, decimal.Decimal):
             return str(obj)
+        if isinstance(obj, array.array):
+            if obj.typecode == 'c':
+                return obj.tostring()
+            if obj.typecode == 'u':
+                return obj.tounicode()
+            return obj.tolist()
         return self.class_to_dict(obj)
 
     @classmethod
@@ -707,6 +729,12 @@
             return str(obj)
         if isinstance(obj, numbers.Number):
             return msgpack.ExtType(0x31, str(obj).encode("ascii"))     # long
+        if isinstance(obj, array.array):
+            if obj.typecode == 'c':
+                return obj.tostring()
+            if obj.typecode == 'u':
+                return obj.tounicode()
+            return obj.tolist()
         return self.class_to_dict(obj)
 
     def object_hook(self, obj):
@@ -797,19 +825,22 @@
     else:
         ver = serpent.__version__
     ver = tuple(map(int, ver.split(".")))
-    if ver < (1, 24):  # serpent 1.24 required
-        raise RuntimeError("requires serpent 1.24 or better")
+    if ver < (1, 27):
+        raise RuntimeError("requires serpent 1.27 or later")
     _ser = SerpentSerializer()
     _serializers["serpent"] = _ser
     _serializers_by_id[_ser.serializer_id] = _ser
 except ImportError:
     log.warning("serpent serializer is not available")
-    pass
 try:
     import msgpack
-    _ser = MsgpackSerializer()
-    _serializers["msgpack"] = _ser
-    _serializers_by_id[_ser.serializer_id] = _ser
+    if msgpack.version < (0, 5, 2):
+        import warnings
+        warnings.warn("msgpack serializer unavailable. requires msgpack 
0.5.2+, found " + str(msgpack.version))
+    else:
+        _ser = MsgpackSerializer()
+        _serializers["msgpack"] = _ser
+        _serializers_by_id[_ser.serializer_id] = _ser
 except ImportError:
     pass
 del _ser
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4/utils/httpgateway.py 
new/Pyro4-4.75/src/Pyro4/utils/httpgateway.py
--- old/Pyro4-4.72/src/Pyro4/utils/httpgateway.py       2018-05-16 
21:14:37.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4/utils/httpgateway.py       2018-08-16 
09:50:01.000000000 +0200
@@ -117,7 +117,8 @@
     <a 
href="http://pyro4.readthedocs.io/en/stable/tipstricks.html#pyro-via-http-and-json";>Docs.</a>
 </p>
 </div>
-<p><em>Note: performance isn't a key concern here; it is a stateless server. 
It does a name lookup and uses a new Pyro proxy for each request.</em></p>
+<p><em>Note: performance isn't a key concern here; it is a stateless server.
+ It does a name lookup and uses a new Pyro proxy for each request.</em></p>
 <h2>Currently exposed contents of name server on {hostname}:</h2>
 <p>(Limited to 10 entries, exposed name pattern = '{ns_regex}')</p>
 {name_server_contents_list}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4.egg-info/PKG-INFO 
new/Pyro4-4.75/src/Pyro4.egg-info/PKG-INFO
--- old/Pyro4-4.72/src/Pyro4.egg-info/PKG-INFO  2018-05-16 21:22:35.000000000 
+0200
+++ new/Pyro4-4.75/src/Pyro4.egg-info/PKG-INFO  2019-01-19 13:16:41.000000000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Pyro4
-Version: 4.72
+Version: 4.75
 Summary: distributed object middleware for Python (RPC)
 Home-page: http://pyro4.readthedocs.io
 Author: Irmen de Jong
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4.egg-info/SOURCES.txt 
new/Pyro4-4.75/src/Pyro4.egg-info/SOURCES.txt
--- old/Pyro4-4.72/src/Pyro4.egg-info/SOURCES.txt       2018-05-16 
21:22:35.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4.egg-info/SOURCES.txt       2019-01-19 
13:16:41.000000000 +0100
@@ -1,7 +1,9 @@
 LICENSE
 MANIFEST.in
+README.md
 setup.cfg
 setup.py
+tox.ini
 contrib/init.d/pyro4-nsd
 docs/Makefile
 docs/make.bat
@@ -29,6 +31,7 @@
 docs/source/_static/pyro-large.png
 docs/source/_static/pyro.png
 docs/source/_static/tf_pyrotaunt.png
+docs/source/_static/css/customize.css
 docs/source/api/config.rst
 docs/source/api/constants.rst
 docs/source/api/core.rst
@@ -73,6 +76,7 @@
 examples/benchmark/client.py
 examples/benchmark/connections.py
 examples/benchmark/server.py
+examples/blob-dispatch/Readme.txt
 examples/blob-dispatch/client/client.py
 examples/blob-dispatch/client/customdata.py
 examples/blob-dispatch/dispatcher/dispatcher.py
@@ -122,6 +126,7 @@
 examples/distributed-mandelbrot/Readme.txt
 examples/distributed-mandelbrot/client_asciizoom.py
 examples/distributed-mandelbrot/client_graphics.py
+examples/distributed-mandelbrot/launch_servers.sh
 examples/distributed-mandelbrot/normal.py
 examples/distributed-mandelbrot/normal_graphics.py
 examples/distributed-mandelbrot/server.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/src/Pyro4.egg-info/requires.txt 
new/Pyro4-4.75/src/Pyro4.egg-info/requires.txt
--- old/Pyro4-4.72/src/Pyro4.egg-info/requires.txt      2018-05-16 
21:22:35.000000000 +0200
+++ new/Pyro4-4.75/src/Pyro4.egg-info/requires.txt      2019-01-19 
13:16:41.000000000 +0100
@@ -1,4 +1,4 @@
-serpent>=1.24
+serpent>=1.27
 
 [:python_version<'3.4']
 selectors34
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/tests/PyroTests/test_serialize.py 
new/Pyro4-4.75/tests/PyroTests/test_serialize.py
--- old/Pyro4-4.72/tests/PyroTests/test_serialize.py    2018-05-16 
21:14:37.000000000 +0200
+++ new/Pyro4-4.75/tests/PyroTests/test_serialize.py    2018-08-16 
09:50:01.000000000 +0200
@@ -4,6 +4,7 @@
 Pyro - Python Remote Objects.  Copyright by Irmen de Jong 
([email protected]).
 """
 
+import array
 import sys
 import collections
 import copy
@@ -28,7 +29,7 @@
     def setUp(self):
         self.previous_serializer = config.SERIALIZER
         config.SERIALIZER = self.SERIALIZER
-        self.ser = Pyro4.util.get_serializer(config.SERIALIZER)
+        self.serializer = Pyro4.util.get_serializer(config.SERIALIZER)
         config.REQUIRE_EXPOSE = True
 
     def tearDown(self):
@@ -36,30 +37,30 @@
 
     def testSerItself(self):
         s = Pyro4.util.get_serializer(config.SERIALIZER)
-        p, _ = self.ser.serializeData(s)
-        s2 = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(s)
+        s2 = self.serializer.deserializeData(p)
         self.assertEqual(s, s2)
         self.assertTrue(s == s2)
         self.assertFalse(s != s2)
 
     def testSerUnicode(self):
         data = unicode("x")
-        self.ser.serializeData(data)
-        self.ser.serializeCall(data, unicode("method"), [], {})
+        self.serializer.serializeData(data)
+        self.serializer.serializeCall(data, unicode("method"), [], {})
 
     def testSerCompression(self):
-        d1, c1 = self.ser.serializeData("small data", compress=True)
-        d2, c2 = self.ser.serializeData("small data", compress=False)
+        d1, c1 = self.serializer.serializeData("small data", compress=True)
+        d2, c2 = self.serializer.serializeData("small data", compress=False)
         self.assertFalse(c1)
         self.assertEqual(d1, d2)
         bigdata = "x" * 1000
-        d1, c1 = self.ser.serializeData(bigdata, compress=False)
-        d2, c2 = self.ser.serializeData(bigdata, compress=True)
+        d1, c1 = self.serializer.serializeData(bigdata, compress=False)
+        d2, c2 = self.serializer.serializeData(bigdata, compress=True)
         self.assertFalse(c1)
         self.assertTrue(c2)
         self.assertTrue(len(d2) < len(d1))
-        self.assertEqual(bigdata, self.ser.deserializeData(d1, 
compressed=False))
-        self.assertEqual(bigdata, self.ser.deserializeData(d2, 
compressed=True))
+        self.assertEqual(bigdata, self.serializer.deserializeData(d1, 
compressed=False))
+        self.assertEqual(bigdata, self.serializer.deserializeData(d2, 
compressed=True))
 
     def testSerErrors(self):
         e1 = Pyro4.errors.NamingError(unicode("x"))
@@ -69,19 +70,19 @@
         e3 = Pyro4.errors.ProtocolError(unicode("x"))
         if sys.platform == "cli":
             Pyro4.util.fixIronPythonExceptionForPickle(e1, True)
-        p, _ = self.ser.serializeData(e1)
-        e = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(e1)
+        e = self.serializer.deserializeData(p)
         if sys.platform == "cli":
             Pyro4.util.fixIronPythonExceptionForPickle(e, False)
         self.assertIsInstance(e, Pyro4.errors.NamingError)
         self.assertEqual(repr(orig_e), repr(e))
         self.assertEqual(["this is the remote traceback"], e._pyroTraceback, 
"remote traceback info should be present")
-        p, _ = self.ser.serializeData(e2)
-        e = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(e2)
+        e = self.serializer.deserializeData(p)
         self.assertIsInstance(e, Pyro4.errors.PyroError)
         self.assertEqual(repr(e2), repr(e))
-        p, _ = self.ser.serializeData(e3)
-        e = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(e3)
+        e = self.serializer.deserializeData(p)
         self.assertIsInstance(e, Pyro4.errors.ProtocolError)
         self.assertEqual(repr(e3), repr(e))
 
@@ -89,8 +90,8 @@
         ex = ZeroDivisionError("test error")
         ex._pyroTraceback = ["test traceback payload"]
         Pyro4.util.fixIronPythonExceptionForPickle(ex, True)  # hack for 
ironpython
-        data, compressed = self.ser.serializeData(ex)
-        ex2 = self.ser.deserializeData(data, compressed)
+        data, compressed = self.serializer.serializeData(ex)
+        ex2 = self.serializer.deserializeData(data, compressed)
         Pyro4.util.fixIronPythonExceptionForPickle(ex2, False)  # hack for 
ironpython
         self.assertEqual(ZeroDivisionError, type(ex2))
         self.assertTrue(hasattr(ex2, "_pyroTraceback"))
@@ -98,8 +99,8 @@
 
     def testSerCoreOffline(self):
         uri = Pyro4.core.URI("PYRO:[email protected]:4444")
-        p, _ = self.ser.serializeData(uri)
-        uri2 = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(uri)
+        uri2 = self.serializer.deserializeData(p)
         self.assertEqual(uri, uri2)
         self.assertEqual("PYRO", uri2.protocol)
         self.assertEqual("9999", uri2.object)
@@ -108,8 +109,8 @@
         self.assertIsNone(uri2.sockname)
 
         uri = Pyro4.core.URI("PYRO:12345@./u:/tmp/socketname")
-        p, _ = self.ser.serializeData(uri)
-        uri2 = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(uri)
+        uri2 = self.serializer.deserializeData(p)
         self.assertEqual(uri, uri2)
         self.assertEqual("PYRO", uri2.protocol)
         self.assertEqual("12345", uri2.object)
@@ -121,8 +122,8 @@
         proxy._pyroTimeout = 42
         proxy._pyroMaxRetries = 78
         self.assertIsNone(proxy._pyroConnection)
-        p, _ = self.ser.serializeData(proxy)
-        proxy2 = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(proxy)
+        proxy2 = self.serializer.deserializeData(p)
         self.assertIsNone(proxy._pyroConnection)
         self.assertIsNone(proxy2._pyroConnection)
         self.assertEqual(proxy2._pyroUri, proxy._pyroUri)
@@ -134,10 +135,10 @@
             self.skipTest("marshal can't serialize custom objects")
         uri1 = Pyro4.core.URI("PYRO:[email protected]:111")
         uri2 = Pyro4.core.URI("PYRO:[email protected]:222")
-        _ = self.ser.serializeData(uri1)
+        _ = self.serializer.serializeData(uri1)
         data = [uri1, uri2]
-        p, _ = self.ser.serializeData(data)
-        [u1, u2] = self.ser.deserializeData(p)
+        p, _ = self.serializer.serializeData(data)
+        [u1, u2] = self.serializer.deserializeData(p)
         self.assertEqual(uri1, u1)
         self.assertEqual(uri2, u2)
 
@@ -146,8 +147,8 @@
         # but only to support serializing Pyro objects.
         # The serialized form of a Daemon should be empty (and thus, useless)
         with Pyro4.core.Daemon(port=0) as daemon:
-            d, _ = self.ser.serializeData(daemon)
-            d2 = self.ser.deserializeData(d)
+            d, _ = self.serializer.serializeData(daemon)
+            d2 = self.serializer.deserializeData(d)
             self.assertTrue(len(d2.__dict__) == 0, "deserialized daemon should 
be empty")
             self.assertTrue("Pyro4.core.Daemon" in repr(d2))
             self.assertTrue("unusable" in repr(d2))
@@ -156,10 +157,10 @@
                 obj = pprint.PrettyPrinter(stream="dummy", width=42)
                 obj.name = "hello"
                 daemon.register(obj)
-                o, _ = self.ser.serializeData(obj)
+                o, _ = self.serializer.serializeData(obj)
                 if self.SERIALIZER in ("pickle", "cloudpickle", "dill"):
                     # only pickle, cloudpickle and dill can deserialize the 
PrettyPrinter class without the need of explicit deserialization function
-                    o2 = self.ser.deserializeData(o)
+                    o2 = self.serializer.deserializeData(o)
                     self.assertEqual("hello", o2.name)
                     self.assertEqual(42, o2._width)
             finally:
@@ -167,15 +168,15 @@
 
     def testPyroClasses(self):
         uri = Pyro4.core.URI("PYRO:object@host:4444")
-        s, c = self.ser.serializeData(uri)
-        x = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(uri)
+        x = self.serializer.deserializeData(s, c)
         self.assertIsInstance(x, Pyro4.core.URI)
         self.assertEqual(uri, x)
         self.assertTrue("Pyro4.core.URI" in repr(uri))
         self.assertEqual("PYRO:object@host:4444", str(uri))
         uri = Pyro4.core.URI("PYRO:12345@./u:/tmp/socketname")
-        s, c = self.ser.serializeData(uri)
-        x = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(uri)
+        x = self.serializer.deserializeData(s, c)
         self.assertIsInstance(x, Pyro4.core.URI)
         self.assertEqual(uri, x)
         proxy = Pyro4.core.Proxy(uri)
@@ -187,8 +188,8 @@
         proxy._pyroHandshake = "apples"
         proxy._pyroMaxRetries = 78
         proxy._pyroSerializer = "serializer"
-        s, c = self.ser.serializeData(proxy)
-        x = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(proxy)
+        x = self.serializer.deserializeData(s, c)
         self.assertIsInstance(x, Pyro4.core.Proxy)
         self.assertEqual(proxy._pyroUri, x._pyroUri)
         self.assertEqual(set("abc"), x._pyroAttrs)
@@ -202,16 +203,16 @@
         self.assertTrue("Pyro4.core.Proxy" in repr(x))
         self.assertTrue("Pyro4.core.Proxy" in str(x))
         daemon = Pyro4.core.Daemon()
-        s, c = self.ser.serializeData(daemon)
-        x = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(daemon)
+        x = self.serializer.deserializeData(s, c)
         self.assertIsInstance(x, Pyro4.core.Daemon)
         self.assertTrue("Pyro4.core.Daemon" in repr(x))
         self.assertTrue("unusable" in repr(x))
         self.assertTrue("Pyro4.core.Daemon" in str(x))
         self.assertTrue("unusable" in str(x))
         wrapper = Pyro4.futures._ExceptionWrapper(ZeroDivisionError("divided 
by zero"))
-        s, c = self.ser.serializeData(wrapper)
-        x = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(wrapper)
+        x = self.serializer.deserializeData(s, c)
         self.assertIsInstance(x, Pyro4.futures._ExceptionWrapper)
         self.assertEqual("divided by zero", str(x.exception))
         self.assertTrue("ExceptionWrapper" in repr(x))
@@ -272,15 +273,15 @@
     def testAutoProxyPartlyExposed(self):
         if self.SERIALIZER == "marshal":
             self.skipTest("marshal can't serialize custom objects")
-        self.ser.register_type_replacement(MyThingPartlyExposed, 
Pyro4.core.pyroObjectToAutoProxy)
+        self.serializer.register_type_replacement(MyThingPartlyExposed, 
Pyro4.core.pyroObjectToAutoProxy)
         t1 = MyThingPartlyExposed("1")
         t2 = MyThingPartlyExposed("2")
         with Pyro4.core.Daemon() as d:
             d.register(t1, "thingy1")
             d.register(t2, "thingy2")
             data = [t1, ["apple", t2]]
-            s, c = self.ser.serializeData(data)
-            data = self.ser.deserializeData(s, c)
+            s, c = self.serializer.serializeData(data)
+            data = self.serializer.deserializeData(s, c)
             self.assertEqual("apple", data[1][0])
             p1 = data[0]
             p2 = data[1][1]
@@ -295,15 +296,15 @@
     def testAutoProxyFullExposed(self):
         if self.SERIALIZER == "marshal":
             self.skipTest("marshal can't serialize custom objects")
-        self.ser.register_type_replacement(MyThingPartlyExposed, 
Pyro4.core.pyroObjectToAutoProxy)
+        self.serializer.register_type_replacement(MyThingPartlyExposed, 
Pyro4.core.pyroObjectToAutoProxy)
         t1 = MyThingFullExposed("1")
         t2 = MyThingFullExposed("2")
         with Pyro4.core.Daemon() as d:
             d.register(t1, "thingy1")
             d.register(t2, "thingy2")
             data = [t1, ["apple", t2]]
-            s, c = self.ser.serializeData(data)
-            data = self.ser.deserializeData(s, c)
+            s, c = self.serializer.serializeData(data)
+            data = self.serializer.deserializeData(s, c)
             self.assertEqual("apple", data[1][0])
             p1 = data[0]
             p2 = data[1][1]
@@ -318,19 +319,19 @@
     def testRegisterTypeReplacementSanity(self):
         if self.SERIALIZER == "marshal":
             self.skipTest("marshal can't serialize custom objects")
-        self.ser.register_type_replacement(int, lambda: None)
+        self.serializer.register_type_replacement(int, lambda: None)
         with self.assertRaises(ValueError):
-            self.ser.register_type_replacement(type, lambda: None)
+            self.serializer.register_type_replacement(type, lambda: None)
         with self.assertRaises(ValueError):
-            self.ser.register_type_replacement(42, lambda: None)
+            self.serializer.register_type_replacement(42, lambda: None)
 
     def testCustomClassFail(self):
         if self.SERIALIZER in ("pickle", "cloudpickle", "dill"):
             self.skipTest("pickle, cloudpickle and dill simply serialize 
custom classes")
         o = pprint.PrettyPrinter(stream="dummy", width=42)
-        s, c = self.ser.serializeData(o)
+        s, c = self.serializer.serializeData(o)
         try:
-            _ = self.ser.deserializeData(s, c)
+            _ = self.serializer.deserializeData(s, c)
             self.fail("error expected, shouldn't deserialize unknown class")
         except Pyro4.errors.ProtocolError:
             pass
@@ -341,22 +342,22 @@
         o = MyThingPartlyExposed("test")
         Pyro4.util.SerializerBase.register_class_to_dict(MyThingPartlyExposed, 
mything_dict)
         
Pyro4.util.SerializerBase.register_dict_to_class("CUSTOM-Mythingymabob", 
mything_creator)
-        s, c = self.ser.serializeData(o)
-        o2 = self.ser.deserializeData(s, c)
+        s, c = self.serializer.serializeData(o)
+        o2 = self.serializer.deserializeData(s, c)
         self.assertIsInstance(o2, MyThingPartlyExposed)
         self.assertEqual("test", o2.name)
         # unregister the deserializer
         
Pyro4.util.SerializerBase.unregister_dict_to_class("CUSTOM-Mythingymabob")
         try:
-            self.ser.deserializeData(s, c)
+            self.serializer.deserializeData(s, c)
             self.fail("must fail")
         except Pyro4.errors.ProtocolError:
             pass  # ok
         # unregister the serializer
         
Pyro4.util.SerializerBase.unregister_class_to_dict(MyThingPartlyExposed)
-        s, c = self.ser.serializeData(o)
+        s, c = self.serializer.serializeData(o)
         try:
-            self.ser.deserializeData(s, c)
+            self.serializer.deserializeData(s, c)
             self.fail("must fail")
         except Pyro4.errors.SerializeError as x:
             msg = str(x)
@@ -365,54 +366,56 @@
 
     def testData(self):
         data = [42, "hello"]
-        ser, compressed = self.ser.serializeData(data)
+        ser, compressed = self.serializer.serializeData(data)
         self.assertFalse(compressed)
-        data2 = self.ser.deserializeData(ser, compressed=False)
+        data2 = self.serializer.deserializeData(ser, compressed=False)
         self.assertEqual(data, data2)
 
     def testUnicodeData(self):
         data = u"euro\u20aclowbytes\u0000\u0001\u007f\u0080\u00ff"
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(data, data2)
 
     def testUUID(self):
         data = uuid.uuid1()
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         uuid_as_str = str(data)
         self.assertTrue(data2==data or data2==uuid_as_str)
 
     def testSet(self):
         data = {111, 222, 333}
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(data, data2)
 
     def testCircular(self):
         data = [42, "hello", Pyro4.core.Proxy("PYRO:dummy@dummy:4444")]
         data.append(data)
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed)
         self.assertIs(data2, data2[3])
         self.assertEqual(42, data2[0])
 
     def testCallPlain(self):
-        ser, compressed = self.ser.serializeCall("object", "method", "vargs", 
"kwargs")
+        ser, compressed = self.serializer.serializeCall("object", "method", 
("vargs1", "vargs2"), {"kwargs": 999})
         self.assertFalse(compressed)
-        obj, method, vargs, kwargs = self.ser.deserializeCall(ser, 
compressed=False)
+        obj, method, vargs, kwargs = self.serializer.deserializeCall(ser, 
compressed=False)
         self.assertEqual("object", obj)
         self.assertEqual("method", method)
-        self.assertEqual("vargs", vargs)
-        self.assertEqual("kwargs", kwargs)
+        self.assertTrue(len(vargs) == 2)
+        self.assertTrue(vargs[0] == "vargs1")
+        self.assertTrue(vargs[1] == "vargs2")
+        self.assertDictEqual({"kwargs": 999}, kwargs)
 
     def testCallPyroObjAsArg(self):
         if self.SERIALIZER == "marshal":
             self.skipTest("marshal can't serialize custom objects")
         uri = Pyro4.core.URI("PYRO:555@localhost:80")
-        ser, compressed = self.ser.serializeCall("object", "method", [uri], 
{"thing": uri})
+        ser, compressed = self.serializer.serializeCall("object", "method", 
[uri], {"thing": uri})
         self.assertFalse(compressed)
-        obj, method, vargs, kwargs = self.ser.deserializeCall(ser, 
compressed=False)
+        obj, method, vargs, kwargs = self.serializer.deserializeCall(ser, 
compressed=False)
         self.assertEqual("object", obj)
         self.assertEqual("method", method)
         self.assertEqual([uri], vargs)
@@ -422,9 +425,9 @@
         if self.SERIALIZER == "marshal":
             self.skipTest("marshal can't serialize custom objects")
         e = ZeroDivisionError("hello")
-        ser, compressed = self.ser.serializeCall("object", "method", [e], 
{"thing": e})
+        ser, compressed = self.serializer.serializeCall("object", "method", 
[e], {"thing": e})
         self.assertFalse(compressed)
-        obj, method, vargs, kwargs = self.ser.deserializeCall(ser, 
compressed=False)
+        obj, method, vargs, kwargs = self.serializer.deserializeCall(ser, 
compressed=False)
         self.assertEqual("object", obj)
         self.assertEqual("method", method)
         self.assertIsInstance(vargs, list)
@@ -435,25 +438,25 @@
 
     def testSerializeException(self):
         e = ZeroDivisionError()
-        d, c = self.ser.serializeData(e)
-        e2 = self.ser.deserializeData(d, c)
+        d, c = self.serializer.serializeData(e)
+        e2 = self.serializer.deserializeData(d, c)
         self.assertIsInstance(e2, ZeroDivisionError)
         self.assertEqual("", str(e2))
         e = ZeroDivisionError("hello")
-        d, c = self.ser.serializeData(e)
-        e2 = self.ser.deserializeData(d, c)
+        d, c = self.serializer.serializeData(e)
+        e2 = self.serializer.deserializeData(d, c)
         self.assertIsInstance(e2, ZeroDivisionError)
         self.assertEqual("hello", str(e2))
         e = ZeroDivisionError("hello", 42)
-        d, c = self.ser.serializeData(e)
-        e2 = self.ser.deserializeData(d, c)
+        d, c = self.serializer.serializeData(e)
+        e2 = self.serializer.deserializeData(d, c)
         self.assertIsInstance(e2, ZeroDivisionError)
         self.assertIn(str(e2), ("('hello', 42)", "(u'hello', 42)"))
         e.custom_attribute = 999
         if sys.platform == "cli":
             Pyro4.util.fixIronPythonExceptionForPickle(e, True)
-        ser, compressed = self.ser.serializeData(e)
-        e2 = self.ser.deserializeData(ser, compressed)
+        ser, compressed = self.serializer.serializeData(e)
+        e2 = self.serializer.deserializeData(ser, compressed)
         if sys.platform == "cli":
             Pyro4.util.fixIronPythonExceptionForPickle(e2, False)
         self.assertIsInstance(e2, ZeroDivisionError)
@@ -463,22 +466,22 @@
     def testSerializeSpecialException(self):
         self.assertIn("GeneratorExit", Pyro4.util.all_exceptions)
         e = GeneratorExit()
-        d, c = self.ser.serializeData(e)
-        e2 = self.ser.deserializeData(d, c)
+        d, c = self.serializer.serializeData(e)
+        e2 = self.serializer.deserializeData(d, c)
         self.assertIsInstance(e2, GeneratorExit)
 
     def testRecreateClasses(self):
-        self.assertEqual([1, 2, 3], self.ser.recreate_classes([1, 2, 3]))
+        self.assertEqual([1, 2, 3], self.serializer.recreate_classes([1, 2, 
3]))
         d = {"__class__": "invalid"}
         try:
-            self.ser.recreate_classes(d)
+            self.serializer.recreate_classes(d)
             self.fail("error expected")
         except Pyro4.errors.ProtocolError:
             pass  # ok
         d = {"__class__": "Pyro4.core.URI", "state": ['PYRO', '555', None, 
'localhost', 80]}
-        uri = self.ser.recreate_classes(d)
+        uri = self.serializer.recreate_classes(d)
         self.assertEqual(Pyro4.core.URI("PYRO:555@localhost:80"), uri)
-        number, uri = self.ser.recreate_classes([1, {"uri": d}])
+        number, uri = self.serializer.recreate_classes([1, {"uri": d}])
         self.assertEqual(1, number)
         self.assertEqual(Pyro4.core.URI("PYRO:555@localhost:80"), uri["uri"])
 
@@ -491,7 +494,7 @@
         config.PICKLE_PROTOCOL_VERSION = 2
         try:
             u = Pyro4.core.URI("PYRO:obj@localhost:1234")
-            d, compr = self.ser.serializeData(u)
+            d, compr = self.serializer.serializeData(u)
             self.assertFalse(compr)
             import pickletools
             d = pickletools.optimize(d)
@@ -506,75 +509,138 @@
         f2 = 9876543212345.12345678987654321
         f3 = 11223344.556677889988776655e33
         floats = [f1, f2, f3]
-        d, compr = self.ser.serializeData(floats)
-        v = self.ser.deserializeData(d, compr)
+        d, compr = self.serializer.serializeData(floats)
+        v = self.serializer.deserializeData(d, compr)
         self.assertEqual(floats, v, "float precision must not be compromised 
in any serializer")
 
     def testSourceByteTypes_deserialize(self):
         # uncompressed
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 3], 
{"kwarg": 42}, False)
-        ser, _ = self.ser.serializeData([4, 5, 6], False)
-        _, _, vargs, _ = self.ser.deserializeCall(bytearray(call_ser), False)
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3], {"kwarg": 42}, False)
+        ser, _ = self.serializer.serializeData([4, 5, 6], False)
+        _, _, vargs, _ = self.serializer.deserializeCall(bytearray(call_ser), 
False)
         self.assertEqual([1, 2, 3], vargs)
-        d = self.ser.deserializeData(bytearray(ser), False)
+        d = self.serializer.deserializeData(bytearray(ser), False)
         self.assertEqual([4, 5, 6], d)
         if sys.version_info < (3, 0):
-            _, _, vargs, _ = self.ser.deserializeCall(buffer(call_ser), False)
+            _, _, vargs, _ = self.serializer.deserializeCall(buffer(call_ser), 
False)
             self.assertEqual([1, 2, 3], vargs)
-            d = self.ser.deserializeData(buffer(ser), False)
+            d = self.serializer.deserializeData(buffer(ser), False)
             self.assertEqual([4, 5, 6], d)
         # compressed
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 
3]*100, {"kwarg": 42}, True)
-        ser, _ = self.ser.serializeData([4, 5, 6]*100, True)
-        _, _, vargs, _ = self.ser.deserializeCall(bytearray(call_ser), True)
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3] * 100, {"kwarg": 42}, True)
+        ser, _ = self.serializer.serializeData([4, 5, 6] * 100, True)
+        _, _, vargs, _ = self.serializer.deserializeCall(bytearray(call_ser), 
True)
         self.assertEqual(300, len(vargs))
-        d = self.ser.deserializeData(bytearray(ser), True)
+        d = self.serializer.deserializeData(bytearray(ser), True)
         self.assertEqual(300, len(d))
         if sys.version_info < (3, 0):
-            _, _, vargs, _ = self.ser.deserializeCall(buffer(call_ser), True)
+            _, _, vargs, _ = self.serializer.deserializeCall(buffer(call_ser), 
True)
             self.assertEqual(300, len(vargs))
-            d = self.ser.deserializeData(buffer(ser), True)
+            d = self.serializer.deserializeData(buffer(ser), True)
             self.assertEqual(300, len(d))
 
     @unittest.skipIf(sys.platform == "cli", "ironpython can't properly create 
memoryviews from serialized data")
     def testSourceByteTypes_deserialize_memoryview(self):
         # uncompressed
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 3], 
{"kwarg": 42}, False)
-        ser, _ = self.ser.serializeData([4, 5, 6], False)
-        _, _, vargs, _ = self.ser.deserializeCall(memoryview(call_ser), False)
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3], {"kwarg": 42}, False)
+        ser, _ = self.serializer.serializeData([4, 5, 6], False)
+        _, _, vargs, _ = self.serializer.deserializeCall(memoryview(call_ser), 
False)
         self.assertEqual([1, 2, 3], vargs)
-        d = self.ser.deserializeData(memoryview(ser), False)
+        d = self.serializer.deserializeData(memoryview(ser), False)
         self.assertEqual([4, 5, 6], d)
         # compressed
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 
3]*100, {"kwarg": 42}, True)
-        ser, _ = self.ser.serializeData([4, 5, 6]*100, True)
-        _, _, vargs, _ = self.ser.deserializeCall(memoryview(call_ser), True)
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3] * 100, {"kwarg": 42}, True)
+        ser, _ = self.serializer.serializeData([4, 5, 6] * 100, True)
+        _, _, vargs, _ = self.serializer.deserializeCall(memoryview(call_ser), 
True)
         self.assertEqual(300, len(vargs))
-        d = self.ser.deserializeData(memoryview(ser), True)
+        d = self.serializer.deserializeData(memoryview(ser), True)
         self.assertEqual(300, len(d))
 
     def testSourceByteTypes_loads(self):
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 3], 
{"kwarg": 42}, False)
-        ser, _ = self.ser.serializeData([4, 5, 6], False)
-        _, _, vargs, _ = self.ser.loadsCall(bytearray(call_ser))
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3], {"kwarg": 42}, False)
+        ser, _ = self.serializer.serializeData([4, 5, 6], False)
+        _, _, vargs, _ = self.serializer.loadsCall(bytearray(call_ser))
         self.assertEqual([1, 2, 3], vargs)
-        d = self.ser.loads(bytearray(ser))
+        d = self.serializer.loads(bytearray(ser))
         self.assertEqual([4, 5, 6], d)
         if sys.version_info < (3, 0):
-            _, _, vargs, _ = self.ser.loadsCall(buffer(call_ser))
+            _, _, vargs, _ = self.serializer.loadsCall(buffer(call_ser))
             self.assertEqual([1, 2, 3], vargs)
-            d = self.ser.loads(buffer(ser))
+            d = self.serializer.loads(buffer(ser))
             self.assertEqual([4, 5, 6], d)
 
     @unittest.skipIf(sys.platform == "cli", "ironpython can't properly create 
memoryviews from serialized data")
     def testSourceByteTypes_loads_memoryview(self):
-        call_ser, _ = self.ser.serializeCall("object", "method", [1, 2, 3], 
{"kwarg": 42}, False)
-        ser, _ = self.ser.serializeData([4, 5, 6], False)
-        _, _, vargs, _ = self.ser.loadsCall(memoryview(call_ser))
+        call_ser, _ = self.serializer.serializeCall("object", "method", [1, 2, 
3], {"kwarg": 42}, False)
+        ser, _ = self.serializer.serializeData([4, 5, 6], False)
+        _, _, vargs, _ = self.serializer.loadsCall(memoryview(call_ser))
         self.assertEqual([1, 2, 3], vargs)
-        d = self.ser.loads(memoryview(ser))
+        d = self.serializer.loads(memoryview(ser))
         self.assertEqual([4, 5, 6], d)
 
+    def testSerializeDumpsAndDumpsCall(self):
+        self.serializer.dumps(uuid.uuid4())
+        self.serializer.dumps(Pyro4.URI("PYRO:test@test:4444"))
+        self.serializer.dumps(Pyro4.Proxy("PYRONAME:foobar"))
+        self.serializer.dumpsCall("obj", "method", (1, 2, 3), {"arg1": 999})
+        self.serializer.dumpsCall("obj", "method", (1, 2, array.array('i', [1, 
2, 3])), {"arg1": 999})
+        self.serializer.dumpsCall("obj", "method", (1, 2, array.array('i', [1, 
2, 3])), {"arg1": array.array('i', [1, 2, 3])})
+        self.serializer.dumpsCall("obj", "method", (1, 2, 
Pyro4.URI("PYRO:test@test:4444")), {"arg1": 999})
+        self.serializer.dumpsCall("obj", "method", (1, 2, 
Pyro4.URI("PYRO:test@test:4444")), {"arg1": Pyro4.URI("PYRO:test@test:4444")})
+        self.serializer.dumpsCall("obj", "method", (1, 2, 
Pyro4.Proxy("PYRONAME:foobar")), {"arg1": 999})
+        self.serializer.dumpsCall("obj", "method", (1, 2, 
Pyro4.Proxy("PYRONAME:foobar")), {"arg1": Pyro4.Proxy("PYRONAME:foobar")})
+
+    def testArrays(self):
+        if sys.version_info < (3, 0):
+            a1 = array.array('c', b"hello")
+            ser = self.serializer.dumps(a1)
+            a2 = self.serializer.loads(ser)
+            if type(a2) is array.array:
+                self.assertEqual(a1, a2)
+            else:
+                self.assertEqual(b"hello", a2)
+        a1 = array.array('u', unicode("hello"))
+        ser = self.serializer.dumps(a1)
+        a2 = self.serializer.loads(ser)
+        if type(a2) is array.array:
+            self.assertEqual(a1, a2)
+        else:
+            self.assertEqual(unicode("hello"), a2)
+        a1 = array.array('h', [222, 333, 444, 555])
+        ser = self.serializer.dumps(a1)
+        a2 = self.serializer.loads(ser)
+        if type(a2) is array.array:
+            self.assertEqual(a1, a2)
+        else:
+            self.assertEqual([222, 333, 444, 555], a2)
+
+    def testArrays2(self):
+        if sys.version_info < (3, 0):
+            a1 = array.array('c', b"hello")
+            ser = self.serializer.dumpsCall("obj", "method", [a1], {})
+            a2 = self.serializer.loads(ser)
+            a2 = a2["params"][0] if self.SERIALIZER == "json" else a2[2][0]
+            if type(a2) is array.array:
+                self.assertEqual(a1, a2)
+            else:
+                self.assertEqual(b"hello", a2)
+        a1 = array.array('u', unicode("hello"))
+        ser = self.serializer.dumpsCall("obj", "method", [a1], {})
+        a2 = self.serializer.loads(ser)
+        a2 = a2["params"][0] if self.SERIALIZER == "json" else a2[2][0]
+        if type(a2) is array.array:
+            self.assertEqual(a1, a2)
+        else:
+            self.assertEqual(unicode("hello"), a2)
+        a1 = array.array('h', [222, 333, 444, 555])
+        ser = self.serializer.dumpsCall("obj", "method", [a1], {})
+        a2 = self.serializer.loads(ser)
+        a2 = a2["params"][0] if self.SERIALIZER == "json" else a2[2][0]
+        if type(a2) is array.array:
+            self.assertEqual(a1, a2)
+        else:
+            self.assertEqual([222, 333, 444, 555], a2)
+
 
 class SerializeTests_cloudpickle(SerializeTests_pickle):
     SERIALIZER = "cloudpickle"
@@ -585,15 +651,15 @@
 
     def testSerializeLambda(self):
         l = lambda x: x * x
-        ser, compressed = self.ser.serializeData(l)
-        l2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(l)
+        l2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(l2(3.), 9.)
 
     def testSerializeLocalFunction(self):
         def f(x):
             return x * x
-        ser, compressed = self.ser.serializeData(f)
-        f2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(f)
+        f2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(f2(3.), 9.)
 
 
@@ -619,15 +685,15 @@
 
     def testSerializeLambda(self):
         l = lambda x: x * x
-        ser, compressed = self.ser.serializeData(l)
-        l2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(l)
+        l2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(l2(3.), 9.)
 
     def testSerializeLocalFunction(self):
         def f(x):
             return x * x
-        ser, compressed = self.ser.serializeData(f)
-        f2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(f)
+        f2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(f2(3.), 9.)
 
 
@@ -641,8 +707,8 @@
     def testSet(self):
         # serpent serializes a set into a tuple on older python versions, so 
we override this
         data = {111, 222, 333}
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         if serpent.can_use_set_literals:
             self.assertEqual(data, data2)
         else:
@@ -651,8 +717,8 @@
     def testDeque(self):
         # serpent converts a deque into a primitive list
         deq = collections.deque([1, 2, 3, 4])
-        ser, compressed = self.ser.serializeData(deq)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(deq)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual([1, 2, 3, 4], data2)
 
     @unittest.skipIf(sys.version_info < (2, 7), "ordereddict is in Python 
2.7+")
@@ -665,15 +731,15 @@
             self.assertEqual("collections.OrderedDict", name)
             return collections.OrderedDict(values["items"])
         
Pyro4.util.SerializerBase.register_dict_to_class("collections.OrderedDict", 
recreate_OrderedDict)
-        ser, compressed = self.ser.serializeData(od)
+        ser, compressed = self.serializer.serializeData(od)
         self.assertIn(b"collections.OrderedDict", ser)
         self.assertIn(b"[('a',1),('b',2),('c',3)]", ser)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(od, data2)
 
     def testUriSerializationWithoutSlots(self):
         u = Pyro4.core.URI("PYRO:obj@localhost:1234")
-        d, compr = self.ser.serializeData(u)
+        d, compr = self.serializer.serializeData(u)
         self.assertFalse(compr)
         result1 = b"# serpent utf-8 
python3.2\n{'__class__':'Pyro4.core.URI','state':('PYRO','obj',None,'localhost',1234)}"
         result2 = b"# serpent utf-8 
python3.2\n{'state':('PYRO','obj',None,'localhost',1234),'__class__':'Pyro4.core.URI'}"
@@ -692,13 +758,13 @@
     def testSet(self):
         # json serializes a set into a list, so we override this
         data = {111, 222, 333}
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(list(data), data2)
 
     def testUriSerializationWithoutSlots(self):
         u = Pyro4.core.URI("PYRO:obj@localhost:1234")
-        d, compr = self.ser.serializeData(u)
+        d, compr = self.serializer.serializeData(u)
         self.assertFalse(compr)
         result1 = b'{"__class__": "Pyro4.core.URI", "state": ["PYRO", "obj", 
null, "localhost", 1234]}'
         result2 = b'{"state": ["PYRO", "obj", null, "localhost", 1234], 
"__class__": "Pyro4.core.URI"}'
@@ -727,8 +793,8 @@
     def testSet(self):
         # msgpack serializes a set into a list, so we override this
         data = {111, 222, 333}
-        ser, compressed = self.ser.serializeData(data)
-        data2 = self.ser.deserializeData(ser, compressed=compressed)
+        ser, compressed = self.serializer.serializeData(data)
+        data2 = self.serializer.deserializeData(ser, compressed=compressed)
         self.assertEqual(list(data), data2)
 
     @unittest.skip("msgpack is implementation dependent")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Pyro4-4.72/tox.ini new/Pyro4-4.75/tox.ini
--- old/Pyro4-4.72/tox.ini      1970-01-01 01:00:00.000000000 +0100
+++ new/Pyro4-4.75/tox.ini      2018-08-16 09:50:01.000000000 +0200
@@ -0,0 +1,11 @@
+[tox]
+envlist=py27,py34,py35,py36,py37,pypy
+
+[testenv]
+deps=-rtest_requirements.txt
+changedir=tests
+commands=python -bb -tt -E -Wall run_testsuite.py --tox
+
+[testenv:pypy3]
+# pypy3 doesn't have the -tt option
+commands=pypy3 -E -Wall -bb run_testsuite.py --tox


Reply via email to