mrkeyoor.com_
Sun 20 Sept 17:51 UTC
PyPIWeb Backendupdated 20 Sept 2026

gevent review

gevent 26.8.0 schedules greenlets on a libev or libuv loop so synchronous Python code can make progress while another call waits for I/O. Its monkey module swaps standard socket, SSL, threading, subprocess, and related objects for cooperative equivalents. Pools, bounded queues, events, timeouts, DNS resolvers, and a WSGI server fill out the runtime. The current release adds Python 3.15 wheels and repairs deadlocks around ThreadPoolExecutor and fork, missed semaphore wakeups from native threads, and subprocess cleanup that could hide the original exception.

Verdict

gevent 26.8.0 installed in 0.3 seconds, used 11 MB, and imported in 0.16 seconds in our sandbox with no audit findings. Keep it for a measured synchronous I/O stack; prefer explicit async code for a new service or any runtime dominated by native blocking calls, CPU work, or Windows.

We installed it

Lab card: what happened when we installed geventScreenshot of gevent documentation
Install✓ · 0.3s4 packages on disk · 11 MB
Importimport gevent in 0.16s · compiled extensions · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does gevent install cleanly?

Yes. In a fresh container with an empty cache, pip install gevent finished in 0.3s, leaving 4 packages and 11 MB on disk. pip-audit reported no known vulnerabilities.

What does gevent need to run?

Python >=3.10, and a platform wheel with compiled extensions. In our run import gevent succeeded in 0.16s.

gevent or eventlet: which should you use?

eventlet: Use it only to maintain software already coupled to Eventlet's green-thread APIs. gevent 26.8.0 installed in 0.3 seconds, used 11 MB, and imported in 0.16 seconds in our sandbox with no audit findings.

When should you not use gevent?

You are starting a new async service. asyncio, AnyIO, or Trio exposes suspension and cancellation in source code instead of replacing imported standard-library functions.

API stability4/5spawn, joinall, Pool, Queue, Event, Timeout, and monkey.patch_all remain familiar across long-lived gevent applications. Interpreter support changes sooner than those calls: version 26.4 removed Python 3.9, 26.5 announced future removal of Python 3.10 and PyPy, and the 26.7 notes schedule keyword-only patch_all arguments for early 2027. Existing code is steady, but platform and startup assumptions still require release-by-release review.
Docs4/5gevent.org explains hubs, greenlets, patching, pools, queues, events, subprocesses, resolvers, monitoring, WSGI serving, and configuration with API-level detail. Its changelog names exact concurrency failures and affected functions. Documentation cannot settle the hardest adoption question, which is whether every third-party C extension in one application yields correctly; that still requires driver-specific evidence and a realistic load test.
Maintenance5/5The repository is unarchived, has 6,447 stars and 137 open issues and pull requests, and was pushed on 2026-08-10. Version 26.8.0 shipped that day with CPython 3.15 wheels and fixes at difficult runtime boundaries: locks across fork, ThreadPoolExecutor shutdown, native-thread semaphore notification, and interrupted subprocess cleanup. The work is active, though its interpreter support announcements need attention before Python upgrades.
Ecosystem4/5The catalog records 11,348,146 weekly downloads. Gunicorn supports a gevent worker, and gevent supplies cooperative sockets, SSL, DNS, subprocesses, synchronization objects, queues, and WSGI serving. Its reach is weaker among newer libraries that publish asyncio clients first. Native synchronous drivers may need an adapter such as psycogreen or isolation in a thread pool, which makes compatibility a property of the whole dependency graph.

Use it if

  • An established synchronous WSGI service needs more concurrent socket work without rewriting every call site to async and await.
  • Gunicorn's gevent worker is already part of the stack and every database, DNS, HTTP, and queue client has been tested under patching.
  • A crawler or proxy spends most of its lifetime waiting and can limit outstanding calls with Pool or a bounded Queue.
  • Legacy functions need an outside deadline even though their own signatures have no timeout or cancellation parameter.
Skip it if

Setup reality

We installed gevent 26.8.0 in a clean, unprivileged Python 3.12 Bookworm container. It completed in 0.3 seconds, left 4 packages, and occupied 11 MB. Its metadata declares 23 direct requirements across core dependencies, environment markers, and extras. The wheel contains compiled .so files, requires Python 3.10 or newer, lacks py.typed, and imported in 0.16 seconds. pip-audit returned zero known vulnerabilities.

There is no account or application config file. Import order is the setup trap. gevent.monkey.patch_all() must run before code imports and caches socket, ssl, threading, or subprocess objects. When the entrypoint cannot be edited, python -m gevent.monkey can patch before loading the script. The changelog warns that patch_all arguments will become keyword-only in an early-2027 release, so named arguments are the safer form today.

Only Python-level calls covered by gevent become cooperative. A native database or network client needs its own compatibility evidence; psycopg2, for example, commonly uses psycogreen. Put unknown blocking I/O in gevent.threadpool until load tests show safe behavior. CPU work belongs in another process. Even tiny greenlets consume memory, descriptors, and upstream capacity, so cap them with Pool and slow producers with Queue(maxsize=...).

Version 26.8.0 fixes a lock deadlock involving ThreadPoolExecutor and fork, plus a wakeup failure when a hubless native thread released a semaphore. It also preserves the real exception when subprocess pipe cleanup re-enters communicate(). Those fixes do not make late patching safe. Apply patches while the process has one thread, and turn on the monitor thread when a stuck hub otherwise leaves little diagnostic evidence.

Patterns

Replace blocking standard modules early patch-before-imports

from gevent import monkey
monkey.patch_all()

import gevent
import requests

jobs = [gevent.spawn(requests.get, url) for url in urls]
gevent.joinall(jobs)

patch_all must precede imports that retain socket, SSL, thread, or subprocess objects. A late call can leave a mixture of blocking and cooperative references.

Read each task's outcome inspect-greenlet-failures

jobs = [gevent.spawn(fetch, item) for item in items]
gevent.joinall(jobs)

for job in jobs:
    if job.successful():
        consume(job.value)
    else:
        logger.error('fetch failed', exc_info=job.exception)

joinall waits for completion but does not automatically re-raise every child exception. Check successful(), value, and exception yourself.

Set a hard concurrency cap limit-parallel-io

from gevent.pool import Pool

pool = Pool(40)
responses = pool.map(fetch, urls)

The Pool limits active greenlets to 40. It protects file descriptors and the upstream service when the input list is much larger.

Bound a call that lacks a timeout enforce-deadline

from gevent import Timeout

try:
    with Timeout(5):
        payload = client.read()
except Timeout:
    payload = None

Timeout is delivered the next time this greenlet yields. It cannot interrupt a CPU loop or native call that never returns to the hub.

Return a sentinel after five seconds return-timeout-fallback

result = gevent.with_timeout(
    5,
    client.read,
    timeout_value=None,
)

None must be impossible as a successful result, or the caller cannot tell a timeout from valid data.

Slow the producer with a bounded queue apply-queue-backpressure

from gevent.queue import JoinableQueue

queue = JoinableQueue(maxsize=100)

def worker():
    while True:
        item = queue.get()
        try:
            handle(item)
        finally:
            queue.task_done()

workers = [gevent.spawn(worker) for _ in range(10)]
for item in source():
    queue.put(item)
queue.join()
gevent.killall(workers)

queue.put waits when 100 items are pending. task_done belongs in finally, or one failed item can make queue.join wait forever.

Pass a value or exception to a waiter share-one-result

from gevent.event import AsyncResult

outcome = AsyncResult()

def produce():
    try:
        outcome.set(compute())
    except Exception as exc:
        outcome.set_exception(exc)

gevent.spawn(produce)
value = outcome.get(timeout=10)

AsyncResult.get re-raises an exception supplied with set_exception, so failure is not mistaken for an empty result.

Run WSGI with gevent workers configure-gunicorn-worker

# gunicorn.conf.py
worker_class = 'gevent'
workers = 4
worker_connections = 1000

# gunicorn -c gunicorn.conf.py app:application

Gunicorn owns worker startup and patching here. The 1,000-connection setting is safe only after database pools and external clients have matching limits.

Cap handlers in the built-in WSGI server serve-wsgi-directly

from gevent.pool import Pool
from gevent.pywsgi import WSGIServer

server = WSGIServer(('0.0.0.0', 8080), app, spawn=Pool(500))
server.serve_forever()

Pool(500) bounds active handlers. A production deployment still needs process supervision, restart policy, TLS termination, and request limits.

Move an uncooperative call to real threads isolate-blocking-extension

from gevent.threadpool import ThreadPool

native_calls = ThreadPool(4)
result = native_calls.apply(client.call, (payload,))

This protects the hub when the extension performs blocking I/O. Threads do not speed up Python computation that holds the GIL.

Report a greenlet that stops yielding diagnose-blocked-hub

import gevent

gevent.config.monitor_thread = True
gevent.config.max_blocking_time = 0.5

from gevent.util import print_run_info
print_run_info()

The monitor runs in a native thread and can report stacks while the cooperative loop is stuck. Configure it before starting application work.

Read a subprocess cooperatively run-child-process

from gevent.subprocess import PIPE, Popen

process = Popen(command, stdout=PIPE, stderr=PIPE)
out, err = process.communicate(timeout=60)
if process.returncode != 0:
    raise RuntimeError(err.decode())

Use gevent.subprocess or patch before importing subprocess. Version 26.8.0 fixes exception handling when interrupted pipe readers are cleaned up.

Alternatives

PackageRegistryPick it when
eventletPyPIUse it only to maintain software already coupled to Eventlet's green-thread APIs.
anyioPyPIUse it for structured async code that can operate on asyncio or Trio backends.
uvloopPyPIUse it when the program already uses asyncio and only needs a libuv-based event loop.
trioPyPIUse it when nurseries and cancellation scopes should make task ownership explicit.

More web backend guides

urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.