Pyvider RPC Plugin
Project description
๐๐ pyvider.rpcplugin
High-performance, type-safe RPC plugin framework for Python.
Modern gRPC-based plugin architecture with async support, mTLS security, and comprehensive transport options.
Build lightning-fast, secure RPC plugins! pyvider.rpcplugin provides a complete framework for creating high-performance RPC-based plugins with built-in security, async support, and production-ready patterns. Perfect for microservices, plugin architectures, and inter-process communication.
๐ค Why pyvider.rpcplugin?
โก Performance-First
- Async-native with full
asynciointegration for maximum concurrency - Efficient transports - Unix domain sockets for local IPC, TCP for network communication
- Zero-copy protocols - Protocol Buffers with optimized serialization
- High throughput - Handle 10,000+ requests/second with low latency
๐ Security-Focused
- Built-in mTLS - Mutual TLS authentication with certificate management
- Process Isolation - Plugins run in separate processes, protecting the host application from plugin crashes or resource exhaustion.
- Transport encryption - Secure communication over any network
- Magic cookie validation - Handshake verification for trusted connections
- Certificate utilities - Easy cert generation and rotation
๐ ๏ธ Developer Experience
- Modern Python 3.13+ with complete type annotations and
attrsintegration - Factory functions - Simple APIs for common plugin patterns
- Comprehensive logging - Integrated with
pyvider.telemetryfor observability - Rich error handling - Detailed exceptions with context and recovery guidance
๐๏ธ Production Ready
- Robust configuration - Environment variables, file-based, and programmatic setup
- Graceful shutdown - Clean resource cleanup and connection termination
- Health monitoring - Built-in health checks and status reporting
- Battle-tested - Used in production environments with high reliability
๐ Quick Start
Installation
# With uv (recommended)
uv add pyvider-rpcplugin
# With pip
pip install pyvider-rpcplugin
Hello, RPC World!
Create your first RPC plugin in minutes. This example demonstrates a client application launching a simple plugin server (as a separate Python script/executable) and connecting to it.
Note on mTLS: For simplicity, these examples run with mTLS disabled (PLUGIN_AUTO_MTLS=False is set by example_utils.configure_for_example()). In production, mTLS is highly recommended. See examples/05_security_mtls.py for mTLS setup.
1. The Plugin Server Executable (examples/00_dummy_server.py)
This script acts as a minimal executable plugin. When run, it starts an RPC server using a basic protocol and a simple handler. It's designed to be launched by a client process.
# Condensed from examples/00_dummy_server.py
import asyncio
# Imports for pyvider.rpcplugin, logger, and example_utils are present in the actual file
# from example_utils import configure_for_example
# configure_for_example() # Sets up paths and default config (like disabling mTLS)
from pyvider.rpcplugin import plugin_protocol, plugin_server
from pyvider.telemetry import logger
class DummyHandler: # Basic handler
async def NoOp(self, request, context):
logger.info("DummyHandler: NoOp called (should not occur in this example)")
return {}
async def main():
# configure_for_example() is called at module level.
# It sets default magic cookie (key & value) and disables mTLS.
# The launching client is expected to set the correct magic cookie environment variable.
protocol = plugin_protocol() # Basic protocol
server = plugin_server(protocol=protocol, handler=DummyHandler())
logger.info("Dummy server starting (will print handshake string to stdout)...")
await server.serve() # Prints handshake, then serves until stopped by client
logger.info("Dummy server stopped.")
# if __name__ == "__main__":
# asyncio.run(main())
You typically don't run 00_dummy_server.py directly in this scenario; it's launched by the client. If you were to run it independently (e.g., for direct testing with 01b_direct_client_connection.py), it would start and wait for connections.
2. The Client Application (examples/01_quick_start.py)
This script acts as the client (or host application). It uses plugin_client to launch the 00_dummy_server.py script as a subprocess and then connects to it.
# Condensed from examples/01_quick_start.py
import asyncio
import sys
from pathlib import Path
# Imports for pyvider.rpcplugin, logger, and example_utils are present
# from example_utils import configure_for_example
# configure_for_example() # Sets up paths and default config
from pyvider.rpcplugin import plugin_client
from pyvider.telemetry import logger
async def main():
# configure_for_example() called at module level.
example_dir = Path(__file__).resolve().parent
dummy_server_executable = example_dir / "00_dummy_server.py"
dummy_server_command = [sys.executable, str(dummy_server_executable)]
client = None
try:
logger.info(f"Client launching plugin: {' '.join(dummy_server_command)}")
# plugin_client reads its config (e.g., magic cookie key/value from
# configure_for_example) and sets the required environment variable
# (e.g., PYVIDER_PLUGIN_MAGIC_COOKIE) for the dummy_server_command process.
client = plugin_client(command=dummy_server_command)
await client.start() # Starts the server executable and connects via handshake
logger.info("Client connected to dummy_server plugin successfully!")
# The dummy_server uses a basic protocol with no custom RPC methods.
# If it defined services (e.g., via .proto files), you would create a
# gRPC stub here using client.grpc_channel and make calls:
# stub = YourServiceStub(client.grpc_channel)
# response = await stub.YourMethod(YourRequest())
await asyncio.sleep(2) # Keep connection alive for a moment
except Exception as e:
logger.error(f"Client error: {e}", exc_info=True)
finally:
if client and client.is_started:
await client.close() # Stops the server executable too
logger.info("Client closed, dummy_server plugin stopped.")
elif client: # If client was created but not started
await client.close()
# if __name__ == "__main__":
# asyncio.run(main())
To run this Quick Start example:
python examples/01_quick_start.py
This setup demonstrates the typical plugin model where the client (host application) manages the lifecycle of the plugin executable. The plugin_client handles launching the process, the handshake (including magic cookie authentication and transport negotiation), and establishing the gRPC connection.
๐งฉ Core Concepts & Use Cases
What is a Plugin Architecture?
A plugin architecture allows you to extend a core application with modular, independently developed components called plugins. These plugins run as separate processes, communicating with the main application via well-defined interfaces (often RPC). Key benefits include:
- Modularity: Features can be developed, deployed, and scaled independently.
- Isolation: A crash in one plugin doesn't necessarily bring down the core application.
- Flexibility: Different plugins can even be written in different languages (though
pyvider.rpcpluginfocuses on Python plugins). - Security: Plugins can be sandboxed, limiting their access to system resources.
pyvider.rpcplugin provides the tools to build and manage the RPC communication layer for such an architecture in Python.
When to Use pyvider.rpcplugin?
Consider pyvider.rpcplugin when:
- You need to extend your Python application with out-of-process plugins for enhanced modularity or security isolation.
- You're building a system where components (plugins) communicate via efficient, type-safe RPC, especially if they are on the same machine (leveraging Unix domain sockets) or across a network (using TCP with mTLS).
- High-performance Inter-Process Communication (IPC) is critical.
- You value clear service contracts defined via Protocol Buffers (protobufs).
- End-to-end security with mutual TLS (mTLS) is a requirement for plugin communication.
- You want to standardize plugin communication with features like magic cookie authentication and common configuration patterns.
- You prefer an async-native framework that integrates well with Python's
asyncio.
It simplifies the complexities of setting up and managing gRPC connections, security, and configuration for these scenarios.
Comparison to Alternatives
While many RPC and IPC mechanisms exist, pyvider.rpcplugin offers a specific set of advantages for plugin architectures:
- vs. Direct gRPC: Building directly with
grpciogives you full control but requires significant boilerplate for server/client setup, security handshakes, transport management, and configuration, especially in a plugin context.pyvider.rpcpluginautomates these, providing pre-configured patterns for mTLS, magic cookie authentication, and transport negotiation, letting you focus on your service logic. - vs.
go-plugin: HashiCorp'sgo-pluginis a mature framework for Go that heavily inspiredpyvider.rpcplugin.pyvider.rpcpluginaims to bring a similar level of robustness and ease-of-use to the Python ecosystem, leveraging Python-specific features likeasyncioand rich type hinting. If you're familiar withgo-plugin's concepts, you'll findpyvider.rpcpluginfollows similar patterns for secure, versioned RPC. - vs. Other Python RPC Libraries (e.g., RPyC, Pyro5, Nameko):
- Many traditional Python RPC libraries are excellent for general-purpose RPC.
pyvider.rpcplugindifferentiates itself by being built on gRPC, offering high performance and leveraging Protocol Buffers for service definitions, which is beneficial for type safety and cross-language interop (if the host application isn't Python).- Its primary focus is on the out-of-process, secure plugin model, with built-in mTLS and handshake mechanisms tailored for this.
- Some alternatives might be simpler for basic Python-to-Python RPC but may not offer the same level of performance, security features for untrusted plugins, or the strict contract definitions of gRPC.
Client Connection Scenarios
There are two main ways a client connects:
1. Connecting to an Independently Running Server
If you have a pyvider.rpcplugin server already running (e.g., started manually or by another process), you can connect to it directly using grpc.aio if you know its transport address (like a Unix socket path or TCP host/port).
See examples/01b_direct_client_connection.py for a runnable example. Here's a condensed version:
# Condensed from examples/01b_direct_client_connection.py
import asyncio
import grpc # For grpc.aio.insecure_channel
async def run_direct_client(socket_path): # socket_path from running server
target = f"unix:{socket_path}"
channel = None
try:
# Assumes insecure connection; for mTLS use grpc.aio.secure_channel()
channel = grpc.aio.insecure_channel(target)
await asyncio.wait_for(channel.channel_ready(), timeout=5.0)
print(f"Successfully connected directly to server at {target}")
# Use channel with your gRPC stubs here
except Exception as e:
print(f"Direct connection error: {e}")
finally:
if channel: await channel.close()
# To run this, you'd need a server running on 'socket_path'
# For example:
# server_socket = "/tmp/my_independent_server.sock" # Get this from your server's output
# asyncio.run(run_direct_client(server_socket))
2. Client Launching and Connecting to a Plugin Executable
This is the typical model for plugin architectures, where the host application (client) manages the lifecycle of the plugin executable. The plugin_client factory is used for this.
The "Hello, RPC World!" example earlier demonstrates this pattern using examples/00_dummy_server.py as the executable and examples/01_quick_start.py as the client that launches it. Here's a conceptual reminder:
# Conceptual, see "Hello, RPC World!" or examples/01_quick_start.py for full version
from pyvider.rpcplugin import plugin_client
async def call_executable_plugin():
# The 'command' launches your plugin server executable.
# plugin_client handles handshake and connection.
client = plugin_client(command=["python", "./path_to_your_plugin_server_script.py"])
try:
await client.start() # Launches the executable and connects
# Now client.grpc_channel is available to use with your gRPC stubs
# stub = YourServiceStub(client.grpc_channel)
# response = await stub.YourMethod(YourRequest(data="..."))
# print(f"Response from plugin: {response.message}")
print("Client connected to executable plugin. Use client.grpc_channel.")
except Exception as e:
print(f"Error with executable plugin: {e}")
finally:
if client:
await client.close() # Shuts down the plugin executable too
๐ฏ Key Features
๐ Multiple Transport Options
# Unix Domain Sockets (fastest for local IPC)
server = plugin_server(protocol, handler, transport="unix")
# TCP Sockets (network communication)
server = plugin_server(protocol, handler, transport="tcp", host="0.0.0.0", port=50051)
# The server will listen on the specified transport.
# Client-side configuration can then specify its preferred transports to connect with.
# (Refer to HandshakeConfig and client configuration for details on negotiation)
๐ mTLS Security Made Simple
from pyvider.rpcplugin import configure
# Configure mTLS with certificates
configure(
PLUGIN_AUTO_MTLS=True,
PLUGIN_SERVER_CERT="path/to/server.crt",
PLUGIN_SERVER_KEY="path/to/server.key",
PLUGIN_CLIENT_CERT="path/to/client.crt",
PLUGIN_CLIENT_KEY="path/to/client.key"
)
# Certificates are automatically validated during TLS handshakes.
# Secure rotation of certificates is a complex operational process that typically
# involves out-of-band updates to certificate files and graceful restarts;
# this library focuses on using the configured certificates for mTLS.
โ๏ธ Flexible Configuration
from pyvider.rpcplugin import RPCPluginConfig
# Programmatic configuration
config = RPCPluginConfig()
config.set("PLUGIN_LOG_LEVEL", "DEBUG")
config.set("PLUGIN_HANDSHAKE_TIMEOUT", 30.0)
# Environment variable support
# PLUGIN_MAGIC_COOKIE=my-secret-cookie
# PLUGIN_AUTO_MTLS=true
# PLUGIN_SERVER_TRANSPORTS=unix,tcp
See docs/configuration.md for full details on all configuration options and methods.
๐ Built-in Observability
from pyvider.telemetry import logger
# Rich logging with context
logger.info(
"RPC call completed",
domain="rpc",
action="call",
status="success",
method="SayHello",
duration_ms=23.5,
client_id="client-123"
)
# Automatic performance metrics
# Transport connection monitoring
# Error tracking and alerting
๐ Complete Examples
Explore our comprehensive example suite:
- 01_quick_start.py - Basic server and client setup
- 02_server_setup.py - Advanced server configuration
- 03_client_connection.py - Robust client patterns
- 04_transport_options.py - Unix vs TCP transports
- 05_security_mtls.py - mTLS certificate setup
- 06_async_patterns.py - Async best practices
- 07_error_handling.py - Robust error management
- 08_production_config.py - Production deployment
- 09_custom_protocols.py - Advanced protocol patterns
- 10_performance_tuning.py - Optimization techniques
Run any example:
cd examples/
python 01_quick_start.py
๐๏ธ Architecture
graph TB
Client[Client Application] --> Transport[Transport Layer]
Transport --> |Unix Socket| UDS[Unix Domain Socket]
Transport --> |TCP| TCP[TCP Socket]
Transport --> |mTLS| TLS[TLS Encryption]
UDS --> Protocol[Protocol Layer]
TCP --> Protocol
TLS --> Protocol
Protocol --> |gRPC| Services[Service Handlers]
Services --> Business[Business Logic]
subgraph "Security Layer"
Auth[Magic Cookie Auth]
Cert[Certificate Management]
Valid[Validation Pipeline]
end
Protocol --> Auth
TLS --> Cert
Services --> Valid
Core Components
- ๐ Transport Layer - Unix sockets, TCP, with automatic negotiation
- ๐ Protocol Layer - gRPC with Protocol Buffers serialization
- ๐ Security Layer - mTLS, certificate management, authentication
- โ๏ธ Configuration - Environment, file, and programmatic configuration
- ๐ Observability - Comprehensive logging and metrics integration
๐ Performance
pyvider.rpcplugin is designed for high-performance scenarios:
| Metric | Unix Socket | TCP (localhost) | TCP (network) |
|---|---|---|---|
| Throughput | 50K+ req/s | 25K+ req/s | 10K+ req/s |
| Latency (p99) | <1ms | <2ms | <10ms |
| Memory Usage | ~50MB | ~75MB | ~100MB |
| CPU Overhead | <5% | <10% | <15% |
Benchmarks on modern hardware (8-core, 16GB RAM) with 1KB payloads
Performance Tips
# Optimize for high throughput
server = plugin_server(
protocol=protocol,
handler=handler,
transport="unix", # Fastest for local IPC
config={
"PLUGIN_CONNECTION_TIMEOUT": 60.0,
"PLUGIN_HANDSHAKE_TIMEOUT": 10.0
# Note: Detailed gRPC server options (like max workers) are typically
# configured directly on the grpc.aio.server if not exposed by RPCPluginServer.
}
)
# For client-side optimization, manage client instances appropriately.
# If connecting to multiple different plugin executables, create separate
# RPCPluginClient instances for each.
# client = plugin_client(server_path="./path_to_your_plugin_executable")
# await client.start()
# # ... use client ...
# await client.close()
๐ง Advanced Usage
Custom Transport Implementation
from pyvider.rpcplugin.transport.base import RPCPluginTransport
class CustomTransport(RPCPluginTransport):
async def listen(self) -> str:
# Implement custom server listening logic
pass
async def connect(self, endpoint: str) -> None:
# Implement custom client connection logic
pass
# To use a custom transport, instantiate RPCPluginServer directly:
# custom_transport_instance = CustomTransport()
# server = RPCPluginServer(protocol=protocol, handler=handler, transport=custom_transport_instance)
# rather than using the plugin_server() factory.
Protocol Factories
from pyvider.rpcplugin import create_basic_protocol
# For testing and development
protocol = create_basic_protocol()
# Production protocol with custom validation
# Note: 'service_pb2' and 'add_ServiceServicer_to_server' are typically
# generated by grpc_tools from a your_service.proto definition.
protocol = plugin_protocol(
service_name="ProductionService",
descriptor_module=service_pb2, # e.g., from your_service_pb2.py
servicer_add_fn=add_ServiceServicer_to_server # e.g., from your_service_pb2_grpc.py
)
Production Configuration
from pyvider.rpcplugin import configure
# Production-ready setup
configure(
PLUGIN_MAGIC_COOKIE_VALUE="production-secret-key", # Or PLUGIN_MAGIC_COOKIE
PLUGIN_PROTOCOL_VERSIONS=[1], # Server announces its supported versions
# For server transport capabilities (server announces what it can do):
PLUGIN_SERVER_TRANSPORTS=["unix", "tcp"],
# For client transport preferences (client announces what it prefers):
# PLUGIN_CLIENT_TRANSPORTS=["unix"],
PLUGIN_AUTO_MTLS=True,
PLUGIN_HANDSHAKE_TIMEOUT=30.0,
PLUGIN_CONNECTION_TIMEOUT=300.0,
# Certificate paths for mTLS (server-side configuration)
PLUGIN_SERVER_CERT="/etc/ssl/certs/server.crt", # Server's own certificate
PLUGIN_SERVER_KEY="/etc/ssl/private/server.key", # Server's own private key
PLUGIN_CLIENT_ROOT_CERTS="/etc/ssl/certs/ca.crt" # CA cert(s) to verify client certificates
# Ensure these paths are accessible by the plugin process
)
๐ Troubleshooting
Common Issues
Connection refused errors:
# Check if server is running and endpoint is correct
server_info = await server.get_status()
logger.info("Server status", **server_info)
Certificate validation failures:
# Verify certificate chain and expiration
from pyvider.rpcplugin.crypto.certificate import verify_certificate_chain
valid = verify_certificate_chain(cert_path, ca_path)
Performance bottlenecks:
# Enable detailed performance logging
configure(PLUGIN_LOG_LEVEL="DEBUG")
# Monitor metrics for bottleneck identification
See docs/troubleshooting.md for comprehensive debugging guides.
๐ Documentation
- ๐ API Reference - Complete class and method documentation
- ๐๏ธ Architecture Guide - Design patterns and best practices
- ๐ Security Guide - mTLS setup and certificate management
- ๐ Performance Guide - Optimization and tuning recommendations
- ๐ Troubleshooting - Common issues and solutions
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone and setup development environment
git clone https://github.com/provide-io/pyvider-rpcplugin.git
cd pyvider-rpcplugin
# Install with development dependencies
uv sync --all-groups
# Run tests
uv run pytest
# Run type checking
uv run mypy src/
# Format code
uv run ruff format src/ tests/
๐ License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
๐ Acknowledgements
pyvider.rpcplugin builds upon these excellent open-source libraries:
grpcio- High-performance RPC frameworkattrs- Powerful data classes and configurationstructlog- Structured logging foundationcryptography- Modern cryptographic recipes
๐ค Development Transparency
AI-Assisted Development Notice: This project was developed with significant AI assistance for code generation and implementation. While AI tools performed much of the heavy lifting for writing code, documentation, and tests, all architectural decisions, design patterns, functionality requirements, and final verification were made by human developers.
Human Oversight Includes:
- RPC architecture and protocol design decisions
- Security model and mTLS implementation strategy
- API design and developer experience specifications
- Performance requirements and optimization targets
- Testing strategy and reliability requirements
- Production deployment patterns and operational requirements
AI Assistance Includes:
- gRPC service implementation and protocol buffer integration
- Transport layer implementation (Unix/TCP sockets)
- Certificate management and mTLS automation
- Configuration system and environment variable handling
- Example scripts and documentation generation
- Test case generation and error handling implementation
This approach allows us to leverage AI capabilities for productivity while maintaining human control over critical architectural decisions and production reliability standards.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyvider_rpcplugin-0.0.11.tar.gz.
File metadata
- Download URL: pyvider_rpcplugin-0.0.11.tar.gz
- Upload date:
- Size: 93.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd27aa3e7c8ff7f45521ad11c3ad2a6c813ad3e31982d20698db2224aa844f58
|
|
| MD5 |
c848384cf3461e8ad1188bed3540bed9
|
|
| BLAKE2b-256 |
b86bf10638181706f0f3ea411ba79ad49390ddad4ae13456a464dd2afc3ecbd7
|
File details
Details for the file pyvider_rpcplugin-0.0.11-py3-none-any.whl.
File metadata
- Download URL: pyvider_rpcplugin-0.0.11-py3-none-any.whl
- Upload date:
- Size: 101.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2225c8b046792a3d2ab35aca9f294c15be3e8cf7faa361c1efd07da053341d76
|
|
| MD5 |
337230d8aa4ecf9d2445647b87a162f2
|
|
| BLAKE2b-256 |
d0710d2a5e56cde5d310bb63e5c3311edc65a018dd3f8fe7197be34fb457ee52
|