Remote Python Call (RPyC)

repository·master·Indexed 23 days ago

https://github.com/tomerfiliba-org/rpyc

A transparent and symmetric distributed computing library for Python. RPyC enables bidirectional communication between clients and servers, supporting synchronous and asynchronous remote procedure calls. Key features include the ability to subclass rpyc.Service for custom APIs, the use of rpyc.async_ for non-blocking calls, and various server implementations such as ThreadedServer and OneShotServer. It supports complex patterns like server-side callbacks and session management via token objects.

Tokens
24.1K
Snippets
52
Records
133
Agent score
81%

What's inside rpyc

  1. Understand the Simple Echo Server behavior

    master
    The Simple Echo Server demo demonstrates a single-use connection pattern. The client opens a connection, sends a message, and then the connection is terminated. Because the server is implemented using OneShotServer, the server process will automatically shut down once the client has finished its interaction.
  2. What is RPyC classic mode?

    master
    Classic mode is a legacy operating mode where the server is completely under the control of the client. Unlike the modern service-oriented mode where servers expose specific, restricted services, a client in classic mode can connect to a server and manipulate it without predefined restrictions. In current versions of RPyC, classic mode is implemented as a rpyc.core.service.SlaveService. It is particularly useful for testing environments where unrestricted access is desired.
  3. What is RPyC and how does it work?

    master

    RPyC (Remote Python Call) is a transparent, symmetric library for remote procedure calls, clustering, and distributed computing.

    It uses object-proxying to overcome physical boundaries between processes and computers. This allows remote objects to be manipulated as if they were local, meaning existing code can often work seamlessly with both local and remote objects without modification.

    Key characteristics include:

    • Symmetric Protocol: Both client and server can serve requests, enabling the server to invoke callbacks on the client side.
    • Platform Agnostic: Works across different architectures (32/64 bit, little/big endian) and operating systems (Windows, Linux, Solaris, Mac).
    • Low Overhead: Uses a compact binary protocol and requires no complex setup like name servers or URL-mapping.
    • Security: Employs a capability-based security model and integrates with TLS/SSL and SSH.
  4. Implement secure remote services using RPyC

    master

    RPyC is a service-oriented library where a service is defined as a class that exposes a specific set of remote functions and objects.

    To implement a service, create a class containing the methods you wish to expose to clients. RPyC uses a capability-based security model. Instead of granting broad permissions (like file system access), you can pass specific objects (like an open file handle) to a client. This allows the client to perform operations on that specific object (e.g., read(), write()) without having access to the rest of the file system.

    By default, RPyC prevents the use of getattr on remote objects except for "allowed attributes."

  5. Understand RPyC security risks and best practices

    master

    RPyC is designed with a capability-based security model, but improper usage can create back-doors.

    Key Security Principles:

    • Avoid Internet Exposure: Do not expose RPyC servers openly over the Internet. Use them only over secure local networks where peers are trusted.
    • Use SSL: To mitigate network-level risks, use RPyC over a secure connection (SSL).
    • Beware of Object Traversal: If you expose an object that holds a reference to a sensitive module (like sys), a client might traverse that reference to gain access to all imported modules.
    • Avoid allow_public_attrs if untrusted: Enabling allow_public_attrs can allow clients to bypass intended restrictions and reach dangerous objects.
    • Classic Mode Warning: SlaveService (Classic Mode) is intentionally insecure and exposes everything to the client. Use it only for testing in isolated environments.
  6. Achieve parallel execution via multiprocessing

    master

    Because of the Global Interpreter Lock (GIL) in CPython, CPU-bound programs require multiple processes rather than threads to utilize multicore CPUs.

    RPyC simplifies multiprocessing by allowing you to treat RPyC-connected processes as if they were part of "one big process." A common pattern is to have a "master" process spawn multiple worker processes and distribute the workload between them using RPyC connections.

  7. Build a distributed computation platform with RPyC

    master

    RPyC provides the underlying mechanism for distributed computing and clustering. It is architecture-agnostic, supports both synchronous and asynchronous invocation, and treats clients and servers symmetrically.

    While RPyC is not a full-featured distributed computing framework itself, it can be used to build one. A framework built on RPyC would typically handle:

    • Node membership (nodes joining or leaving the cluster)
    • Workload balancing and node failure handling
    • Result collection from workers
    • Object and code migration based on runtime profiling
  8. Understand RPyC Client-Side Components

    master

    Clients connect to services using various tools:

    • Connection Factories: General-purpose factories for establishing connections over different transports like pipes, sockets, SSL, SSH, or TLSlite.
    • Classic-mode Factories: Specialized factories and utilities for RPyC's classic mode.
    • Helpers: Utility functions for common tasks, including timed, async_, buffiter, and BgServingThread.
  9. Security and timeouts in Zero-Deploy RPyC

    master

    Security

    Zero-deploy leverages SSH for both authentication and transport security:

    • Authentication: The RPyC server runs under the permissions of the SSH user. Connecting as an unprivileged user ensures the RPyC process is restricted.
    • Encryption: All communication is routed through an SSH tunnel, ensuring data is encrypted in transit.

    Timeouts

    When calling server.close(timeout=...), you can specify a timeout in seconds.

    • If the subprocess communication takes longer than the specified timeout after the termination signal is sent, a TimeoutExpired exception is raised.
    • The default value is None (infinite).
  10. Understand RPyC Serialization (Brine and Vinegar)

    master

    RPyC uses two primary serialization mechanisms for data transfer:

    • Brine: The standard "over-the-wire" encoding format. It is a simple and fast serialization format designed for immutable data types such as numbers, strings, and tuples.
    • Vinegar: A configurable serializer specifically for exceptions. It extracts exception details and converts them into a brine-friendly format so they can be transmitted across the wire.
  11. Understanding Boxing: By Value vs. By Reference

    master

    Boxing is the serialization mechanism RPyC uses to transfer objects across a connection. It uses two distinct strategies depending on the object type:

    • By Value: Used for simple, immutable Python objects (e.g., str, int, tuple). The actual value is copied to the other side. Since the value cannot change, it is safe to duplicate.
    • By Reference: Used for all other objects. Instead of the object itself, a "reference" is passed. This allows changes made to the proxy object on one side to be reflected on the actual object on the other side. This strategy is essential for passing "location-aware" objects like files or OS resources.

    On the receiving end, unboxing occurs: by-value data is deserialized into local objects, while by-reference data is converted into object proxies (also called netrefs).

  12. Share data between clients using ThreadedServer

    master

    When using ThreadedServer, multiple clients can interact with the server simultaneously. Because ThreadedServer provides a concurrency illusion, any data shared between clients must be thread-safe to prevent data races and maintain invariants.

    In the sharing/server.py implementation, a constant named THREAD_SAFE is used to toggle between thread-safe and unsafe function calls. When developing shared-state applications, ensure your logic accounts for this concurrency.