{"id":8515,"library":"pyro5","title":"Pyro5 Remote Object Communication Library","description":"Pyro5 is a Python library for remote object communication, enabling client-server interactions across different processes or machines. It's the fifth major version of the Pyro project, emphasizing ease of use and flexibility. As of version 5.16, the project has entered a low maintenance mode, with active development paused unless critical bugs are reported.","status":"maintenance","version":"5.16","language":"en","source_language":"en","source_url":"https://github.com/irmen/Pyro5","tags":["rpc","distributed-computing","networking","serialization","inter-process-communication"],"install":[{"cmd":"pip install pyro5","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Used for object serialization.","package":"serpent"}],"imports":[{"symbol":"Daemon","correct":"from Pyro5.api import Daemon"},{"symbol":"expose","correct":"from Pyro5.api import expose"},{"symbol":"locate_ns","correct":"from Pyro5.api import locate_ns"},{"symbol":"Proxy","correct":"from Pyro5.api import Proxy"},{"symbol":"URI","correct":"from Pyro5.api import URI"},{"note":"While 'Pyro5.server' is a submodule, its core components like Daemon are usually accessed via the unified 'Pyro5.api' entry point. Direct import was also problematic in older versions (v5.12).","wrong":"import Pyro5.server","symbol":"Pyro5.server","correct":"from Pyro5.api import Daemon"}],"quickstart":{"code":"import Pyro5.api\nimport time\nimport threading\n\n# --- Server side (run in one terminal) ---\n@Pyro5.api.expose\nclass MyServer:\n    def greet(self, name):\n        return f\"Hello, {name}! This is Pyro5.\"\n\n    def echo(self, message):\n        return f\"Server received: {message}\"\n\n    def get_time(self):\n        return time.time()\n\ndef run_server():\n    # Start a Name Server in a separate terminal: python -m Pyro5.nameserver\n    # Or, start one programmatically if not using a shared nameserver\n    # For simplicity, we'll try to connect to one assumed to be running on localhost\n    try:\n        ns = Pyro5.api.locate_ns(host=\"localhost\")\n    except Pyro5.errors.NamingError:\n        print(\"No Name Server found. Please start one: python -m Pyro5.nameserver\")\n        return\n\n    daemon = Pyro5.api.Daemon(host=\"localhost\")\n    uri = daemon.register(MyServer, \"example.myserver\") # Register with a name\n    ns.register(\"example.myserver\", uri) # Register the URI with the Name Server\n    print(f\"Server URI: {uri}\")\n    print(\"Server ready. Object registered as 'example.myserver'.\")\n    print(\"Starting event loop (Ctrl+C to stop)...\")\n    daemon.requestLoop()\n\n# --- Client side (run in another terminal) ---\ndef run_client():\n    # Locate the Name Server to find the object\n    try:\n        ns = Pyro5.api.locate_ns(host=\"localhost\")\n    except Pyro5.errors.NamingError:\n        print(\"No Name Server found. Cannot connect to server.\")\n        return\n\n    uri = ns.lookup(\"example.myserver\") # Look up the object's URI by its name\n\n    # Create a proxy to the remote object\n    with Pyro5.api.Proxy(uri) as server_proxy:\n        print(f\"Greeting: {server_proxy.greet('Pyro User')}\")\n        print(f\"Echo: {server_proxy.echo('This is a test message.')}\")\n        print(f\"Remote time: {server_proxy.get_time()}\")\n\nif __name__ == '__main__':\n    # This quickstart demonstrates client-server. \n    # For actual usage, run server and client in separate processes.\n    print(\"--- Pyro5 Quickstart ---\")\n    print(\"To run, first start the Pyro Name Server in a *separate* terminal:\")\n    print(\"  python -m Pyro5.nameserver\n\")\n    print(\"Then run this script. It will run the server, wait, then the client.\")\n    print(\"Or, copy the server part to 'server.py' and client to 'client.py' and run them separately.\")\n\n    # Simplified running for quickstart; normally they are separate processes\n    server_thread = threading.Thread(target=run_server)\n    server_thread.daemon = True # Allow main program to exit even if server is running\n    server_thread.start()\n\n    print(\"\\nWaiting for server to start and register...\")\n    time.sleep(2) # Give server a moment to start and register\n\n    print(\"\\n--- Running Client ---\")\n    run_client()\n    print(\"\\nClient finished.\")\n    # The server thread will continue running until Ctrl+C if it was main, \n    # but here it's a daemon thread so it will exit with main.\n","lang":"python","description":"This quickstart demonstrates a basic Pyro5 client-server interaction. It defines a server class, registers it with a Pyro Name Server, and then a client looks up the server and calls its methods. \n\nBefore running, ensure you start the Pyro Name Server in a *separate* terminal using `python -m Pyro5.nameserver`. This is crucial for name lookup. \n\nThe provided code attempts to run both server and client within the same script using threads for convenience, but in typical usage, they are separate processes, potentially on different machines."},"warnings":[{"fix":"Ensure your environment uses Python 3.10 or newer. Upgrade your Python interpreter if necessary.","message":"Pyro5 has aggressively dropped support for older Python versions. As of v5.16, only Python 3.10 and newer are supported.","severity":"breaking","affected_versions":"v5.12, v5.13, v5.15, v5.16"},{"fix":"Be aware that new features or rapid bug fixes are unlikely. Consider contributing or forking the project if significant changes are required for your application.","message":"The Pyro5 project is in a 'super low maintenance mode' as of v5.16. While critical bug fixes might be applied, active development of new features or significant changes is not planned.","severity":"deprecated","affected_versions":"v5.16+"},{"fix":"Explicitly apply `@expose` to methods in each inherited class where remote access is desired.","message":"The `@expose` decorator on a class does not automatically expose methods inherited from its base classes. Each class in the inheritance hierarchy must explicitly use `@expose` on its methods or class to make them remotely accessible.","severity":"gotcha","affected_versions":"All 5.x versions (behavior clarified in v5.16 docs)"},{"fix":"Explicitly specify the `host` parameter (e.g., `'0.0.0.0'` for all interfaces, `'localhost'`, or a specific IP address) when creating `Daemon` or calling `daemon.serve()` to ensure desired binding behavior.","message":"The `Daemon.serve()` method's `host` parameter no longer defaults to an empty string `''` but `None`. This can change binding behavior, especially for IPv6 or specific interface binding.","severity":"breaking","affected_versions":"v5.14+"},{"fix":"Upgrade to Pyro5 v5.13.1 or newer if using Python 3.10+ and relying on exposed static/class methods.","message":"Using `@expose` on `staticmethod` or `classmethod` in Python 3.10+ caused issues due to API changes in Python. While fixed in v5.13.1, older minor versions could be affected.","severity":"gotcha","affected_versions":"v5.13 and possibly earlier versions when run on Python 3.10+"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Always use `from Pyro5.api import ...` for accessing core Pyro5 classes and functions. For example, `from Pyro5.api import Daemon, expose, locate_ns, Proxy`.","cause":"Attempting to import `server` from the top-level `Pyro5` package. Core components like `Daemon` are primarily exposed through `Pyro5.api`.","error":"ImportError: cannot import name 'server' from 'Pyro5'"},{"fix":"1. Ensure the Pyro Name Server is running (`python -m Pyro5.nameserver`). 2. Verify the server registered its object correctly (e.g., `ns.register('name', uri)`). 3. Confirm the client is using the exact same name for lookup and connecting to the correct Name Server host/port.","cause":"The Pyro Name Server cannot find the registered object. This typically means the server object was not registered, the Name Server isn't running, or the client is looking up a different name, host, or port.","error":"Pyro5.errors.NamingError: no such object with name \"example.myserver\""},{"fix":"Ensure the remote class itself is decorated with `@Pyro5.api.expose` (which exposes all public methods), or if not, that each specific method intended for remote calls is decorated with `@Pyro5.api.expose`. Remember that methods inherited from base classes also need explicit `@expose` if the base class isn't itself exposed.","cause":"The method `my_method` on the remote object was not exposed for remote calls. Pyro5 requires methods or classes to be explicitly exposed.","error":"AttributeError: remote object has no attribute 'my_method'"},{"fix":"Ensure the object passed to `daemon.register()` is properly set up for exposure (e.g., its class or methods are `@expose`d). Check the Daemon's host and port configuration to ensure it can bind correctly and generate a reachable URI. For example, explicitly set `Daemon(host='0.0.0.0')` if you need to be reachable from other machines.","cause":"This error occurs when an object is registered with the Daemon, but there's a problem generating or associating a valid URI with it, often due to configuration issues or the object not being suitable for exposure.","error":"Pyro5.errors.DaemonError: no valid URI for the given object: <object at ...>"}]}