Search code examples
pythonwindowsserializationpyro

Pyro4 Serialize Error : unsupported serialized class


I'm new to Pyro4 and need to get into it, and what better way is there than trying out the examples contained in the Pyro4 package? So my first move was to simply make one work, it's the example "warehouse". The idea is to have a person visit the warehouse and choose items to take or store. The file "warehouse.py" contains the Pyro deamon information while "visit.py" makes the interaction between person and warehouse work.

The nameserver (in my case: Hodor, which is started by pyro4-ns -n Hodorand has an entry in my hosts file in C:\Windows\System32\drivers\etc) and "warehouse.py" must be running in two seperate terminal windows. Every time I start the communication via "visit.py" in a third terminal window, at first it works and asks me if I want to store something. If I do, I get the error message:
"Pyro4.errors.SerializeError: unsupported serialized class: person.Visitor"

  File "visit.py", line 10, in <module>
    janet.visit(warehouse)
  File "C:\Users\RickJames\Pyro_other_machine\person.py", line 14, in visit
    self.retrieve(warehouse)
  File "C:\Users\RickJames\Pyro_other_machine\person.py", line 25, in retrieve
    warehouse.take(self, item)
  File "C:\Python34\lib\site-packages\Pyro4\core.py", line 171, in __call__
    return self.__send(self.__name, args, kwargs)
  File "C:\Python34\lib\site-packages\Pyro4\core.py", line 428, in _pyroInvoke
    raise data
Pyro4.errors.SerializeError: unsupported serialized class: person.Visitor

Here is "warehouse.py":

from __future__ import print_function
import Pyro4
import person


class Warehouse(object):
    def __init__(self):
        self.contents=["chair","bike","flashlight","laptop","couch"]

    def list_contents(self):
        return self.contents

    def take(self, person, item):
        self.contents.remove(item)
        print("{0} took the {1}.".format(person.name, item))

    def store(self, person, item):
        self.contents.append(item)
        print("{0} stored the {1}.".format(person.name, item))


def main():
    warehouse=Warehouse()
    Pyro4.Daemon.serveSimple(
            {
                warehouse: "warehouse"
            },
            host = "Hodor",
            ns=True)

if __name__=="__main__":
    main()

"person.py":

from __future__ import print_function
import sys

if sys.version_info<(3,0):
    input=raw_input


class Visitor(object):
    def __init__(self, name):
        self.name=name
    def visit(self, warehouse):
        print("This is {0}.".format(self.name))
        self.deposit(warehouse)
        self.retrieve(warehouse)
        print("Thank you, come again!")
    def deposit(self, warehouse):
        print("The warehouse contains:", warehouse.list_contents())
        item=input("Type a thing you want to store (or empty): ").strip()
        if item:
            warehouse.store(self, item)
    def retrieve(self, warehouse):
        print("The warehouse contains:", warehouse.list_contents())
        item=input("Type something you want to take (or empty): ").strip()
        if item:
            warehouse.take(self, item)

and finally "visit.py":

import sys
import Pyro4
import Pyro4.util
from person import Visitor

warehouse=Pyro4.Proxy("PYRONAME:warehouse")
janet=Visitor("Janet")
henry=Visitor("Henry")
janet.visit(warehouse)
henry.visit(warehouse)

I know that by default Pyro4 uses the serializer "serpent". I tried to switch to 'marshal','json', 'dill' and 'pickle', but none of them solved my problem. Also, since I read about all the security problems with some serializers, I'd like to keep "serpent".


Solution

  • Your source files are wrong. Did you type them in yourself? Where did you get them from? Because the ones you show us here are very old versions of those example files (3 years old!) Those old versions indeed won't work anymore with recent versions of Pyro4 due to some changes to the serializer mechanism that have been made since then.

    The most important reason why your code doesn't work anymore is because in your 'Visitor' class, in the deposit and retrieve methods, the code is passing a Visitor instance to the Warehouse pyro service. This is not possible unless you're telling Pyro how to serialize that. Since this is meant to be a simple example, that complexity is not included here but you can read about it in Pyro's docs. The correct version of the example only passes the person's name to the warehouse service thereby avoiding the issue altogether.

    The correct versions can be copied from the current Pyro4 documentation, or is provided for you in the examples/warehouse/phase3 folder of the Pyro4 source distribution.

    For completeness, the correct versions are as follows. "person.py":

    from __future__ import print_function
    import sys
    
    if sys.version_info < (3, 0):
        input = raw_input
    
    
    class Person(object):
        def __init__(self, name):
            self.name = name
    
        def visit(self, warehouse):
            print("This is {0}.".format(self.name))
            self.deposit(warehouse)
            self.retrieve(warehouse)
            print("Thank you, come again!")
    
        def deposit(self, warehouse):
            print("The warehouse contains:", warehouse.list_contents())
            item = input("Type a thing you want to store (or empty): ").strip()
            if item:
                warehouse.store(self.name, item)
    
        def retrieve(self, warehouse):
            print("The warehouse contains:", warehouse.list_contents())
            item = input("Type something you want to take (or empty): ").strip()
            if item:
                warehouse.take(self.name, item)
    

    Then, "visit.py":

    # This is the code that visits the warehouse.
    import sys
    
    import Pyro4
    import Pyro4.util
    from person import Person
    
    
    sys.excepthook = Pyro4.util.excepthook
    
    warehouse = Pyro4.Proxy("PYRONAME:example.warehouse")
    janet = Person("Janet")
    henry = Person("Henry")
    janet.visit(warehouse)
    henry.visit(warehouse)
    

    And finally, "warehouse.py":

    from __future__ import print_function
    import Pyro4
    
    
    @Pyro4.expose
    class Warehouse(object):
        def __init__(self):
            self.contents = ["chair", "bike", "flashlight", "laptop", "couch"]
    
        def list_contents(self):
            return self.contents
    
        def take(self, name, item):
            self.contents.remove(item)
            print("{0} took the {1}.".format(name, item))
    
        def store(self, name, item):
            self.contents.append(item)
            print("{0} stored the {1}.".format(name, item))
    
    
    def main():
        Pyro4.Daemon.serveSimple(
            {
                Warehouse: "example.warehouse"
            },
            ns=True)
    
    
    if __name__ == "__main__":
        main()
    

    These work fine with Pyro 4.45.