Tweaked the way monkey_patch works so that it'll work better if called after the modules it patches are imported. Added major patcher docs overall. Added blog link to the main page.
This commit is contained in:
@@ -71,7 +71,7 @@ The file :ref:`echo server example <echo_server_example>` contains a somewhat mo
|
||||
Primary API
|
||||
===========
|
||||
|
||||
The design goal for Eventlet's API is simplicity and readability. You should be able to read its code and understand what it's doing. Fewer lines of code are preferred over excessively clever implementations. Like Python itself, there should be only one right way to do something with Eventlet!
|
||||
The design goal for Eventlet's API is simplicity and readability. You should be able to read its code and understand what it's doing. Fewer lines of code are preferred over excessively clever implementations. `Like Python itself <http://www.python.org/dev/peps/pep-0020/>`_, there should be one, and only one obvious way to do it in Eventlet!
|
||||
|
||||
Though Eventlet has many modules, much of the most-used stuff is accessible simply by doing ``import eventlet``
|
||||
|
||||
@@ -100,39 +100,3 @@ Though Eventlet has many modules, much of the most-used stuff is accessible simp
|
||||
Queues are a fundamental construct for communicating data between execution units. Eventlet's Queue class is used to communicate between greenthreads, and provides a bunch of useful features for doing that. See :class:`queue.Queue` for more details.
|
||||
|
||||
These are the basic primitives of Eventlet; there are a lot more out there in the other Eventlet modules; check out the :doc:`modules`.
|
||||
|
||||
|
||||
Green Libraries
|
||||
----------------
|
||||
|
||||
The package ``eventlet.green`` contains libraries that have the same interfaces as common standard ones, but they are modified to behave well with green threads. This can be preferable than monkeypatching in many circumstances, because it may be necessary to interoperate with some module that needs the standard libraries unmolested, or simply because it's good engineering practice to be able to understand how a file behaves based simply on its contents.
|
||||
|
||||
To use green libraries, simply import the desired module from ``eventlet.green``::
|
||||
|
||||
from eventlet.green import socket
|
||||
from eventlet.green import threading
|
||||
from eventlet.green import asyncore
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
Monkeypatching the Standard Library
|
||||
----------------------------------------
|
||||
|
||||
.. automethod:: eventlet.util::wrap_socket_with_coroutine_socket
|
||||
|
||||
Eventlet's socket object, whose implementation can be found in the
|
||||
:mod:`eventlet.greenio` module, is designed to match the interface of the
|
||||
standard library :mod:`socket` object. However, it is often useful to be able to
|
||||
use existing code which uses :mod:`socket` directly without modifying it to use the eventlet apis. To do this, one must call :func:`~eventlet.util.wrap_socket_with_coroutine_socket`. It is only necessary
|
||||
to do this once, at the beginning of the program, and it should be done before
|
||||
any socket objects which will be used are created.
|
||||
|
||||
.. automethod:: eventlet.util::wrap_select_with_coroutine_select
|
||||
|
||||
Some code which is written in a multithreaded style may perform some tricks,
|
||||
such as calling :mod:`select` with only one file descriptor and a timeout to
|
||||
prevent the operation from being unbounded. For this specific situation there
|
||||
is :func:`~eventlet.util.wrap_select_with_coroutine_select`; however it's
|
||||
always a good idea when trying any new library with eventlet to perform some
|
||||
tests to ensure eventlet is properly able to multiplex the operations.
|
||||
|
@@ -4,30 +4,35 @@ Examples
|
||||
Here are a bunch of small example programs that use Eventlet. All of these examples can be found in the ``examples`` directory of a source copy of Eventlet.
|
||||
|
||||
.. _web_crawler_example:
|
||||
|
||||
Web Crawler
|
||||
------------
|
||||
|
||||
.. literalinclude:: ../examples/webcrawler.py
|
||||
|
||||
.. _wsgi_server_example:
|
||||
|
||||
WSGI Server
|
||||
------------
|
||||
|
||||
.. literalinclude:: ../examples/wsgi.py
|
||||
|
||||
.. _echo_server_example:
|
||||
|
||||
Echo Server
|
||||
-----------
|
||||
|
||||
.. literalinclude:: ../examples/echoserver.py
|
||||
|
||||
.. _socket_connect_example:
|
||||
|
||||
Socket Connect
|
||||
--------------
|
||||
|
||||
.. literalinclude:: ../examples/connect.py
|
||||
|
||||
.. _chat_server_example:
|
||||
|
||||
Multi-User Chat Server
|
||||
-----------------------
|
||||
|
||||
|
@@ -8,7 +8,7 @@ Eventlet is different from other event-based frameworks out there because it doe
|
||||
Simple Example
|
||||
-------------------
|
||||
|
||||
This is a simple web crawler that fetches a bunch of urls concurrently.
|
||||
This is a simple web crawler that fetches a bunch of urls concurrently::
|
||||
|
||||
urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
|
||||
"https://wiki.secondlife.com/w/images/secondlife.jpg",
|
||||
@@ -23,7 +23,7 @@ This is a simple web crawler that fetches a bunch of urls concurrently.
|
||||
pool = eventlet.GreenPool()
|
||||
for body in pool.imap(fetch, urls):
|
||||
print "got body", len(body)
|
||||
|
||||
|
||||
|
||||
Contents
|
||||
=========
|
||||
@@ -32,6 +32,7 @@ Contents
|
||||
:maxdepth: 2
|
||||
|
||||
basic_usage
|
||||
patching
|
||||
examples
|
||||
ssl
|
||||
threading
|
||||
|
@@ -1,6 +0,0 @@
|
||||
:mod:`api` -- General purpose functions
|
||||
==========================================
|
||||
|
||||
.. automodule:: eventlet.api
|
||||
:members:
|
||||
:undoc-members:
|
@@ -1,5 +0,0 @@
|
||||
:mod:`greenio` -- Cooperative network primitives
|
||||
=================================================
|
||||
|
||||
.. automodule:: eventlet.greenio
|
||||
:members:
|
49
doc/patching.rst
Normal file
49
doc/patching.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
Greening The World
|
||||
==================
|
||||
|
||||
One of the challenges of writing a library like Eventlet is that the built-in networking libraries don't natively support the sort of cooperative yielding that we need. What we must do instead is patch standard library modules in certain key places so that they do cooperatively yield. We've in the past considered doing this automatically upon importing Eventlet, but have decided against that course of action because it is un-Pythonic to change the behavior of module A simply by importing module B.
|
||||
|
||||
Therefore, the application using Eventlet must explicitly green the world for itself, using one or both of the convenient methods provided.
|
||||
|
||||
Import Green
|
||||
--------------
|
||||
|
||||
The first way of greening an application is to import networking-related libraries from the ``eventlet.green`` package. It contains libraries that have the same interfaces as common standard ones, but they are modified to behave well with green threads. Using this method is a good engineering practice, because the true dependencies are apparent in every file::
|
||||
|
||||
from eventlet.green import socket
|
||||
from eventlet.green import threading
|
||||
from eventlet.green import asyncore
|
||||
|
||||
This works best if every library can be imported green in this manner. If ``eventlet.green`` lacks a module (for example, non-python-standard modules), then the :mod:`eventlet.patcher` module can come to the rescue. It provides a function, :func:`eventlet.patcher.import_patched`, that greens any module on import.
|
||||
|
||||
.. function:: eventlet.patcher.import_patched(module_name, *additional_modules, **kw_additional_modules)
|
||||
|
||||
Imports a module in a greened manner, so that the module's use of networking libraries like socket will use Eventlet's green versions instead. The only required argument is the name of the module to be imported::
|
||||
|
||||
from eventlet import patcher
|
||||
httplib2 = patcher.import_patched('httplib2')
|
||||
|
||||
Under the hood, it works by temporarily swapping out the "normal" versions of the libraries in sys.modules for an eventlet.green equivalent. When the import of the to-be-patched module completes, the state of sys.modules is restored. Therefore, if the patched module contains the statement 'import socket', import_patched will have it reference eventlet.green.socket. One weakness of this approach is that it doesn't work for late binding (i.e. imports that happen during runtime). Late binding of imports is fortunately rarely done (it's slow and against `PEP-8 <http://www.python.org/dev/peps/pep-0008/>`_), so in most cases import_patched will work just fine.
|
||||
|
||||
One other aspect of import_patched is the ability to specify exactly which modules are patched. Doing so may provide a slight performance benefit since only the needed modules are imported, whereas import_patched with no arguments imports a bunch of modules in case they're needed. The *additional_modules* and *kw_additional_modules* arguments are both sequences of name/module pairs. Either or both can be used::
|
||||
|
||||
from eventlet.green import socket
|
||||
from eventlet.green import SocketServer
|
||||
BaseHTTPServer = patcher.import_patched('BaseHTTPServer',
|
||||
('socket', socket),
|
||||
('SocketServer', SocketServer))
|
||||
BaseHTTPServer = patcher.import_patched('BaseHTTPServer',
|
||||
socket=socket, SocketServer=SocketServer)
|
||||
|
||||
|
||||
Monkeypatching the Standard Library
|
||||
----------------------------------------
|
||||
|
||||
The other way of greening an application is simply to monkeypatch the standard
|
||||
library. This has the disadvantage of appearing quite magical, but the advantage of avoiding the late-binding problem.
|
||||
|
||||
.. function:: eventlet.patcher.monkey_patch(os=True, select=True, socket=True, thread=True, time=True)
|
||||
|
||||
By default, this function monkeypatches the key system modules by replacing their key elements with green equivalents. The keyword arguments afford some control over which modules are patched, in case that's important. For almost all of them, they patch the single module of the same name (e.g. time=True means that the time module is patched [time.sleep is patched by eventlet.sleep]). The exceptions to this rule are *socket*, which also patches the :mod:`ssl` module if present; and *thread*, which patches both :mod:`thread` and :mod:`Queue`.
|
||||
|
||||
It is important to call :func:`eventlet.patcher.monkey_patch` as early in the lifetime of the application as possible. Try to do it as one of the first lines in the main module. The reason for this is that sometimes there is a class that inherits from a class that needs to be greened -- e.g. a class that inherits from socket.socket -- and inheritance is done at import time, so therefore the monkeypatching should happen before the module that has the derived class is imported.
|
@@ -100,11 +100,12 @@ easy_install eventlet
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="">Links</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference external" href="http://blog.eventlet.net/">Blog</a></li>
|
||||
<li><a class="reference external" href="doc/">Documentation</a></li>
|
||||
<li><a class="reference external" href="https://lists.secondlife.com/pipermail/eventletdev/">Mailing List Archives</a></li>
|
||||
<li><a class="reference external" href="http://eventlet.net/hudson/">Continuous Builds</a></li>
|
||||
<li><a class="reference external" href="http://eventlet.net/hudson/">Automated Builds</a></li>
|
||||
<li><a class="reference external" href="http://bitbucket.org/which_linden/eventlet/issues/new/">Bug Report Form</a></li>
|
||||
<li><a class="reference external" href="irc://kubrick.freenode.net/#eventlet">irc channel</a></li>
|
||||
<li><a class="reference external" href="irc://chat.freenode.net/#eventlet">irc channel</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,6 +2,8 @@ from eventlet import queue
|
||||
|
||||
__all__ = ['Empty', 'Full', 'LifoQueue', 'PriorityQueue', 'Queue']
|
||||
|
||||
__patched__ = ['LifoQueue', 'PriorityQueue', 'Queue']
|
||||
|
||||
# these classes exist to paper over the major operational difference between
|
||||
# eventlet.queue.Queue and the stdlib equivalents
|
||||
class Queue(queue.Queue):
|
||||
|
@@ -6,6 +6,8 @@ from eventlet import greenio
|
||||
from eventlet import greenthread
|
||||
from eventlet import hubs
|
||||
|
||||
__patched__ = ['fdopen', 'read', 'write', 'wait', 'waitpid']
|
||||
|
||||
for var in dir(os_orig):
|
||||
exec "%s = os_orig.%s" % (var, var)
|
||||
|
||||
|
@@ -5,6 +5,8 @@ error = __select.error
|
||||
from eventlet.api import getcurrent
|
||||
from eventlet.hubs import get_hub
|
||||
|
||||
__patched__ = ['select']
|
||||
|
||||
def get_fileno(obj):
|
||||
# The purpose of this function is to exactly replicate
|
||||
# the behavior of the select module when confronted with
|
||||
|
@@ -10,6 +10,9 @@ import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
__patched__ = ['fromfd', 'socketpair', 'gethostbyname', 'create_connection',
|
||||
'ssl', 'socket']
|
||||
|
||||
def fromfd(*args):
|
||||
return socket(__socket.fromfd(*args))
|
||||
|
||||
|
@@ -13,6 +13,7 @@ orig_socket = __import__('socket')
|
||||
socket = orig_socket.socket
|
||||
timeout_exc = orig_socket.timeout
|
||||
|
||||
__patched__ = ['SSLSocket', 'wrap_socket', 'sslwrap_simple']
|
||||
|
||||
class GreenSSLSocket(__ssl.SSLSocket):
|
||||
""" This is a green version of the SSLSocket class from the ssl module added
|
||||
|
@@ -4,6 +4,9 @@ from eventlet.support import greenlets as greenlet
|
||||
from eventlet.api import spawn
|
||||
from eventlet.semaphore import Semaphore as LockType
|
||||
|
||||
__patched__ = ['get_ident', 'start_new_thread', 'start_new', 'allocate_lock',
|
||||
'allocate', 'exit', 'interrupt_main', 'stack_size', '_local']
|
||||
|
||||
error = __thread.error
|
||||
|
||||
def get_ident(gr=None):
|
||||
|
@@ -1,4 +1,5 @@
|
||||
__time = __import__('time')
|
||||
for var in dir(__time):
|
||||
exec "%s = __time.%s" % (var, var)
|
||||
__patched__ = ['sleep']
|
||||
from eventlet.api import sleep
|
||||
|
@@ -1,17 +1,33 @@
|
||||
import sys
|
||||
|
||||
|
||||
__all__ = ['inject', 'import_patched', 'monkey_patch']
|
||||
|
||||
__exclude = set(('__builtins__', '__file__', '__name__'))
|
||||
|
||||
|
||||
def inject(module_name, new_globals, *additional_modules):
|
||||
"""Base method for "injecting" greened modules into an imported module. It
|
||||
imports the module specified in *module_name*, arranging things so
|
||||
that the already-imported modules in *additional_modules* are used when
|
||||
*module_name* makes its imports.
|
||||
|
||||
*new_globals* is either None or a globals dictionary that gets populated
|
||||
with the contents of the *module_name* module. This is useful when creating
|
||||
a "green" version of some other module.
|
||||
|
||||
*additional_modules* should be a collection of two-element tuples, of the
|
||||
form (<name>, <module>). If it's not specified, a default selection of
|
||||
name/module pairs is used, which should cover all use cases but may be
|
||||
slower because there are inevitably redundant or unnecessary imports.
|
||||
"""
|
||||
if not additional_modules:
|
||||
# supply some defaults
|
||||
additional_modules = (
|
||||
_green_os_modules() +
|
||||
_green_select_modules() +
|
||||
_green_socket_modules() +
|
||||
_green_thread_modules())
|
||||
_green_thread_modules() +
|
||||
_green_time_modules())
|
||||
|
||||
## Put the specified modules in sys.modules for the duration of the import
|
||||
saved = {}
|
||||
@@ -50,6 +66,12 @@ def inject(module_name, new_globals, *additional_modules):
|
||||
|
||||
|
||||
def import_patched(module_name, *additional_modules, **kw_additional_modules):
|
||||
"""Imports a module in a way that ensures that the module uses "green"
|
||||
versions of the standard library modules, so that everything works
|
||||
nonblockingly.
|
||||
|
||||
The only required argument is the name of the module to be imported.
|
||||
"""
|
||||
return inject(
|
||||
module_name,
|
||||
None,
|
||||
@@ -76,7 +98,14 @@ def patch_function(func, *additional_modules):
|
||||
return patched
|
||||
|
||||
|
||||
def monkey_patch(os=True, select=True, socket=True, thread=True):
|
||||
def monkey_patch(os=True, select=True, socket=True, thread=True, time=True):
|
||||
"""Globally patches certain system modules to be greenthread-friendly.
|
||||
|
||||
The keyword arguments afford some control over which modules are patched.
|
||||
For almost all of them, they patch the single module of the same name. The
|
||||
exceptions are socket, which also patches the ssl module if present; and
|
||||
thread, which patches thread and Queue.
|
||||
"""
|
||||
modules_to_patch = []
|
||||
if os:
|
||||
modules_to_patch += _green_os_modules()
|
||||
@@ -86,13 +115,18 @@ def monkey_patch(os=True, select=True, socket=True, thread=True):
|
||||
modules_to_patch += _green_socket_modules()
|
||||
if thread:
|
||||
modules_to_patch += _green_thread_modules()
|
||||
if time:
|
||||
modules_to_patch += _green_time_modules()
|
||||
for name, mod in modules_to_patch:
|
||||
sys.modules[name] = mod
|
||||
for attr in mod.__patched__:
|
||||
patched_attr = getattr(mod, attr, None)
|
||||
if patched_attr is not None:
|
||||
setattr(sys.modules[name], attr, patched_attr)
|
||||
|
||||
def _green_os_modules():
|
||||
from eventlet.green import os
|
||||
return [('os', os)]
|
||||
|
||||
|
||||
def _green_select_modules():
|
||||
from eventlet.green import select
|
||||
return [('select', select)]
|
||||
@@ -109,4 +143,7 @@ def _green_thread_modules():
|
||||
from eventlet.green import Queue
|
||||
from eventlet.green import thread
|
||||
return [('Queue', Queue), ('thread', thread)]
|
||||
|
||||
|
||||
def _green_time_modules():
|
||||
from eventlet.green import time
|
||||
return [('time', time)]
|
||||
|
@@ -92,7 +92,7 @@ from eventlet import patcher
|
||||
patcher.monkey_patch()
|
||||
import socket
|
||||
import urllib
|
||||
print "newmod", socket, urllib.socket.socket
|
||||
print "newmod", socket.socket, urllib.socket.socket
|
||||
"""
|
||||
self.write_to_tempfile("newmod", new_mod)
|
||||
python_path = os.pathsep.join(sys.path + [self.tempdir])
|
||||
@@ -104,5 +104,4 @@ print "newmod", socket, urllib.socket.socket
|
||||
output = p.communicate()
|
||||
lines = output[0].split("\n")
|
||||
self.assert_(lines[0].startswith('newmod'))
|
||||
self.assert_('eventlet.green.socket' in lines[0])
|
||||
self.assert_('GreenSocket' in lines[0])
|
||||
self.assertEqual(lines[0].count('GreenSocket'), 2)
|
Reference in New Issue
Block a user