Introduction

gevent is a coroutine-based Python networking library.

Features include:

  • Fast event loop based on libev (epoll on Linux, kqueue on FreeBSD, select on Mac OS X).
  • Lightweight execution units based on greenlet.
  • API that re-uses concepts from the Python standard library (e.g. gevent.event.Event, gevent.queue.Queue).
  • Cooperative socket and ssl modules.
  • Ability to use standard library and 3rd party modules written for standard blocking sockets (gevent.monkey).
  • DNS queries performed through threadpool (default) or through c-ares (enabled via GEVENT_RESOLVER=ares env var).
  • TCP/UDP/HTTP servers
  • Subprocess support (through gevent.subprocess)
  • Thread pools

Installation and Requirements

gevent 1.1 runs on Python 2 and Python 3. Version 2.7 of Python 2 is supported, and versions 3.3, 3.4, and 3.5 of Python 3 are supported. (Users of older versions of Python 2 need to install gevent 1.0.x (2.5) or 1.1.x (2.6); Python 3 is not supported by 1.0.) gevent requires the greenlet library.

Note

For Python 3.3, version 3.3.5 or newer is required to use the gevent’s SSL support due to bugs in the standard library of older versions. For Python 2, 2.7.9 or newer is recommended for the best SSL support; 2.7.8 is also tested although it offers a less-secure SSL module.

gevent 1.1 also runs on PyPy 2.6.1 and above, although 5.0 or above is strongly recommended. On PyPy, there are no external dependencies.

gevent is tested on Windows, OS X, and Linux, and should run on most other Unix-like operating systems (e.g., FreeBSD, Solaris, etc.)

Note

gevent does not run on PyPy on Windows because the CFFI backend does not build.

Note

On Windows, gevent is limited to a maximum of 1024 open sockets due to limitations in libev.

gevent and greenlet can both be installed with pip, e.g., pip install gevent. On Windows and OS X, both gevent and greenlet are distributed as binary wheels, so no C compiler is required (so long as pip is at least version 8.0). On Linux or for Mac OS X variants without pre-built wheels or if wheel installation is disabled, a C compiler (Xcode on OS X) and the Python development package are required. cffi can optionally be installed to build the CFFI backend in addition to the Cython backend on CPython.

Development instructions can be found on PyPI.

Common Installation Issues

The following are some common installation problems and solutions for those compiling gevent from source.

  • Some Linux distributions are now mounting their temporary directories with the noexec option. This can cause a standard pip install gevent to fail with an error like cannot run C compiled programs. One fix is to mount the temporary directory without that option. Another may be to use the --build option to pip install to specify another directory. See issue #570 and issue #612 for examples.
  • Also check for conflicts with environment variables like CFLAGS. For example, see Library Updates.
  • Users of a recent SmartOS release may need to customize the CPPFLAGS (the environment variable containing the default options for the C preprocessor) if they are using the libev shipped with gevent. See Operating Systems for more information.

Example

The following example shows how to run tasks concurrently.

>>> import gevent
>>> from gevent import socket
>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']
>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
>>> gevent.joinall(jobs, timeout=2)
>>> [job.value for job in jobs]
['74.125.79.106', '208.77.188.166', '82.94.164.162']

After the jobs have been spawned, gevent.joinall() waits for them to complete, allowing up to 2 seconds. The results are then collected by checking the value property. The gevent.socket.gethostbyname() function has the same interface as the standard socket.gethostbyname() but it does not block the whole interpreter and thus lets the other greenlets proceed with their requests unhindered.

Monkey patching

The example above used gevent.socket for socket operations. If the standard socket module was used the example would have taken 3 times longer to complete because the DNS requests would be sequential (serialized). Using the standard socket module inside greenlets makes gevent rather pointless, so what about existing modules and packages that are built on top of socket (including the standard library modules like urllib)?

That’s where monkey patching comes in. The functions in gevent.monkey carefully replace functions and classes in the standard socket module with their cooperative counterparts. That way even the modules that are unaware of gevent can benefit from running in a multi-greenlet environment.

>>> from gevent import monkey; monkey.patch_socket()
>>> import urllib2 # it's usable from multiple greenlets now

See examples/concurrent_download.py

Beyond sockets

Of course, there are several other parts of the standard library that can block the whole interpreter and result in serialized behavior. gevent provides cooperative versions of many of those as well. They can be patched independently through individual functions, but most programs using monkey patching will want to patch the entire recommended set of modules using the gevent.monkey.patch_all() function:

>>> from gevent import monkey; monkey.patch_all()
>>> import subprocess # it's usable from multiple greenlets now

Tip

When monkey patching, it is recommended to do so as early as possible in the lifetime of the process. If possible, monkey patching should be the first lines executed. Monkey patching later, especially if native threads have been created, atexit or signal handlers have been installed, or sockets have been created, may lead to unpredictable results including unexpected LoopExit errors.

Event loop

Instead of blocking and waiting for socket operations to complete (a technique known as polling), gevent arranges for the operating system to deliver an event letting it know when, for example, data has arrived to be read from the socket. Having done that, gevent can move on to running another greenlet, perhaps one that itself now has an event ready for it. This repeated process of registering for events and reacting to them as they arrive is the event loop.

Unlike other network libraries, though in a similar fashion as eventlet, gevent starts the event loop implicitly in a dedicated greenlet. There’s no reactor that you must call a run() or dispatch() function on. When a function from gevent’s API wants to block, it obtains the gevent.hub.Hub instance — a special greenlet that runs the event loop — and switches to it (it is said that the greenlet yielded control to the Hub). If there’s no Hub instance yet, one is automatically created.

Tip

Each operating system thread has its own Hub. This makes it possible to use the gevent blocking API from multiple threads (with care).

The event loop provided by libev uses the fastest polling mechanism available on the system by default. Please read the libev documentation for more information.

The Libev API is available under the gevent.core module. Note that the callbacks supplied to the libev API are run in the Hub greenlet and thus cannot use the synchronous gevent API. It is possible to use the asynchronous API there, like gevent.spawn() and gevent.event.Event.set().

Cooperative multitasking

The greenlets all run in the same OS thread and are scheduled cooperatively. This means that until a particular greenlet gives up control, (by calling a blocking function that will switch to the Hub), other greenlets won’t get a chance to run. This is typically not an issue for an I/O bound app, but one should be aware of this when doing something CPU intensive, or when calling blocking I/O functions that bypass the libev event loop.

Tip

Even some apparently cooperative functions, like gevent.sleep(), can temporarily take priority over waiting I/O operations in some circumstances.

Synchronizing access to objects shared across the greenlets is unnecessary in most cases (because yielding control is usually explict), thus traditional synchronization devices like the BoundedSemaphore, RLock and Semaphore classes, although present, aren’t used very often. Other abstractions from threading and multiprocessing remain useful in the cooperative world:

Lightweight pseudothreads

New greenlets are spawned by creating a Greenlet instance and calling its start method. (The gevent.spawn() function is a shortcut that does exactly that). The start method schedules a switch to the greenlet that will happen as soon as the current greenlet gives up control. If there is more than one active greenlet, they will be executed one by one, in an undefined order as they each give up control to the Hub.

If there is an error during execution it won’t escape the greenlet’s boundaries. An unhandled error results in a stacktrace being printed, annotated by the failed function’s signature and arguments:

>>> gevent.spawn(lambda : 1/0)
>>> gevent.sleep(1)
Traceback (most recent call last):
 ...
ZeroDivisionError: integer division or modulo by zero
<Greenlet at 0x7f2ec3a4e490: <function <lambda...>> failed with ZeroDivisionError

The traceback is asynchronously printed to sys.stderr when the greenlet dies.

Greenlet instances have a number of useful methods:

  • join – waits until the greenlet exits;
  • kill – interrupts greenlet’s execution;
  • get – returns the value returned by greenlet or re-raises the exception that killed it.

It is possible to customize the string printed after the traceback by subclassing the Greenlet class and redefining its __str__ method.

To subclass a gevent.Greenlet, override its gevent.Greenlet._run() method and call Greenlet.__init__(self) in __init__:

class MyNoopGreenlet(Greenlet):

    def __init__(self, seconds):
        Greenlet.__init__(self)
        self.seconds = seconds

    def _run(self):
        gevent.sleep(self.seconds)

    def __str__(self):
        return 'MyNoopGreenlet(%s)' % self.seconds

Greenlets can be killed synchronously from another greenlet. Killing will resume the sleeping greenlet, but instead of continuing execution, a GreenletExit will be raised.

>>> g = MyNoopGreenlet(4)
>>> g.start()
>>> g.kill()
>>> g.dead
True

The gevent.greenlet.GreenletExit exception and its subclasses are handled differently than other exceptions. Raising GreenletExit is not considered an exceptional situation, so the traceback is not printed. The GreenletExit is returned by get as if it were returned by the greenlet, not raised.

The kill method can accept a custom exception to be raised:

>>> g = MyNoopGreenlet.spawn(5) # spawn() creates a Greenlet and starts it
>>> g.kill(Exception("A time to kill"))
Traceback (most recent call last):
 ...
Exception: A time to kill
MyNoopGreenlet(5) failed with Exception

The kill can also accept a timeout argument specifying the number of seconds to wait for the greenlet to exit. Note that kill cannot guarantee that the target greenlet will not ignore the exception (i.e., it might catch it), thus it’s a good idea always to pass a timeout to kill (otherwise, the greenlet doing the killing will remain blocked forever).

Tip

The exact timing at which an exception is raised within a target greenlet as the result of kill is not defined. See that function’s documentation for more details.

Caution

Use care when killing greenlets, especially arbitrary greenlets spawned by a library or otherwise executing code you are not familiar with. If the code being executed is not prepared to deal with exceptions, object state may be corrupted. For example, if it has acquired a Lock but does not use a finally block to release it, killing the greenlet at the wrong time could result in the lock being permanently locked:

def func():
    # DON'T DO THIS
    lock.acquire()
    socket.sendall(data) # This could raise many exceptions, including GreenletExit
    lock.release()

This document describes a similar situation for threads.

Timeouts

Many functions in the gevent API are synchronous, blocking the current greenlet until the operation is done. For example, kill waits until the target greenlet is dead before returning [1]. Many of those functions can be made asynchronous by passing the keyword argument block=False.

Furthermore, many of the synchronous functions accept a timeout argument, which specifies a limit on how long the function can block (examples include gevent.event.Event.wait(), gevent.Greenlet.join(), gevent.Greenlet.kill(), gevent.event.AsyncResult.get(), and many more).

The socket and SSLObject instances can also have a timeout, set by the settimeout method.

When these are not enough, the Timeout class can be used to add timeouts to arbitrary sections of (cooperative, yielding) code.

Futher reading

To limit concurrency, use the gevent.pool.Pool class (see example: dns_mass_resolve.py).

Gevent comes with TCP/SSL/HTTP/WSGI servers. See Implementing servers.

External resources

Gevent for working Python developer is a comprehensive tutorial.

Footnotes

[1]This was not the case before 0.13.0, kill method in 0.12.2 and older was asynchronous by default.

Next page: What’s new in gevent 1.1