Skip to main content

Process isolation for gevent applications — run any object in a clean subprocess, call methods transparently via ZMQ IPC.

Project description

gisolate

Gevent has hurt me a thousand times, yet I keep coming back for more. This library is proof of that love.

Process isolation for gevent applications. Run any object in a clean subprocess, call its methods transparently via ZMQ IPC.

Why

gevent's monkey.patch_all() replaces stdlib modules globally. Some libraries (database drivers, native async frameworks, etc.) break under monkey-patching. gisolate spawns a clean child process — no monkey-patching — and proxies method calls over ZMQ, so incompatible code runs in isolation while your gevent app stays cooperative.

Install

pip install gisolate

Requires Python 3.12+.

Quick Start

ProcessProxy — persistent child process

Proxy method calls to an object living in an isolated subprocess:

import gevent.monkey
gevent.monkey.patch_all()

from gisolate import ProcessProxy

# Define a factory (must be importable / picklable)
def create_client():
    from some_native_lib import Client
    return Client(host="localhost")

# Option 1: inline
proxy = ProcessProxy.create(create_client, timeout=30)
result = proxy.query("SELECT 1")  # runs in child process
proxy.shutdown()

# Option 2: subclass
class ClientProxy(ProcessProxy):
    client_factory = staticmethod(create_client)
    timeout = 30

with ClientProxy() as proxy:
    result = proxy.query("SELECT 1")

run_in_subprocess — one-shot call

Run a single function in a subprocess and get the result:

from gisolate import run_in_subprocess

def heavy_compute(n):
    return sum(range(n))

result = run_in_subprocess(heavy_compute, args=(10_000_000,), timeout=60)

ProcessBridge — cross-process RPC

ZMQ-based RPC bridge for server/client architectures. Server side uses gevent, client side uses asyncio:

from gisolate import ProcessBridge

# Server (gevent side)
server = ProcessBridge("ipc:///tmp/rpc.sock", mode="server")
_ = server.address  # starts listening

# Client (asyncio side)
import asyncio

async def main():
    client = ProcessBridge("ipc:///tmp/rpc.sock", mode="client")
    result = await client.call(lambda x, y: x + y, 3, 4, timeout=5)
    print(result)  # 7
    client.close()

asyncio.run(main())
server.close()

ThreadLocalProxy — per-thread instances

Thread-local proxy using unpatched threading.local for true isolation in gevent.threadpool:

from gisolate import ThreadLocalProxy

proxy = ThreadLocalProxy(create_client)
proxy.query("SELECT 1")  # each real OS thread gets its own instance

Child Process Modes

patch_kwargs Child process runtime
None (default) asyncio event loop
dict gevent with patch_all(**patch_kwargs)
# Child uses asyncio (default)
proxy = ProcessProxy.create(factory)

# Child uses gevent with selective patching
proxy = ProcessProxy.create(factory, patch_kwargs={"thread": False, "os": False})

API Reference

ProcessProxy

  • ProcessProxy.create(factory, *, timeout=24, mp_context=None, patch_kwargs=None) — create a proxy without subclassing
  • proxy.<method>(*args, **kwargs) — transparently call any method on the remote object
  • proxy.restart_process() — kill and restart child process
  • proxy.shutdown() — gracefully stop child process
  • Supports context manager (with statement)
  • Thread-safe: usable from greenlets and native threads

run_in_subprocess(target, args=(), kwargs=None, *, timeout=3600, mp_context=None)

Run a function in an isolated subprocess. Blocks with gevent-safe polling.

ProcessBridge(address, mode=None)

  • bridge.address — IPC address (starts server if needed)
  • await bridge.call(func, *args, timeout=60, **kwargs) — async RPC call (client mode)
  • bridge.close() — cleanup resources

ThreadLocalProxy(factory)

Transparent proxy delegating attribute access to a per-thread instance.

shutdown_hub()

Explicitly stop the internal gevent hub loop. Registered via atexit automatically.

set_default_mp_context(ctx) / get_default_mp_context()

Configure the default multiprocessing context for all proxies (default: "spawn").

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gisolate-0.1.1.tar.gz (52.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gisolate-0.1.1-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file gisolate-0.1.1.tar.gz.

File metadata

  • Download URL: gisolate-0.1.1.tar.gz
  • Upload date:
  • Size: 52.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for gisolate-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6dda7e0f00f743e48dcbe4523a89fcdfc5652a3629315118b6fb1e8e9402f5cb
MD5 5457a060a41b80392048ca05841b6174
BLAKE2b-256 4b714af56defe13e9ce4819b1fedc65e77c11cfd84d1486bc5eb83c292af333a

See more details on using hashes here.

File details

Details for the file gisolate-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: gisolate-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 16.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for gisolate-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c9f74f34f649531abc1bfbfec9b2a28d4fd7d4603d49af295b9a2040e095a58d
MD5 acc6c350e5092521fab2ee92f36de973
BLAKE2b-256 a033ec287b29611faec40ac2cd2e7cc8ee5bc7184a64787549d1e410730b1cf3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page