Doc improvements, removed docs for coros and proc modules.

This commit is contained in:
Ryan Williams
2010-01-18 15:42:15 -08:00
parent 9d07716c35
commit d2fa28ecdc
18 changed files with 120 additions and 72 deletions

View File

@@ -7,6 +7,7 @@ Contributors
------------
* Denis Bilenko, AG Projects
* Mike Barton
* R. Tyler Ballance
* Chet Murthy
* radix
@@ -24,13 +25,11 @@ Linden Lab Contributors
Thanks To
---------
* AdamKG, giving the hint that invalid argument errors were introduced post-0.9.0
* Michael Barton, 100-continue patch, content-length bugfixes for wsgi, reviews of wsgi logic
* gholt, wsgi patches for accepting a custom pool, and returning 400 if content-length is invalid
* Luke Tucker, bug report regarding wsgi + webob
* Chuck Thier, reporting a bug in processes.py
* Brantley Harris, reporting bug #4
* Taso Du Val, reproing an exception squelching bug, saving children's lives ;-)
* R. Tyler Ballance, bug report on tpool on Windows, help with improving corolocal module, profile performance report, suggestion that fixed tpool on Windows, quick reporting of wsgi bug, sharing production experiences, patch to support keepalive-disabling
* Sergey Shepelev, PEP 8 police :-), reporting bug #5
* Luci Stanescu, for reporting twisted hub bug
* Marcus Cavanaugh, for test case code that has been incredibly useful in tracking down bugs

View File

@@ -7,13 +7,16 @@ Module Reference
modules/api
modules/backdoor
modules/corolocal
modules/coros
modules/debug
modules/db_pool
modules/event
modules/greenio
modules/greenpool
modules/greenthread
modules/pools
modules/processes
modules/proc
modules/queue
modules/saranwrap
modules/semaphore
modules/util
modules/wsgi

5
doc/modules/debug.rst Normal file
View File

@@ -0,0 +1,5 @@
:mod:`debug` -- Debugging tools for Eventlet
==================================================
.. automodule:: eventlet.debug
:members:

5
doc/modules/event.rst Normal file
View File

@@ -0,0 +1,5 @@
:mod:`event` -- Cross-greenthread primitive
==================================================
.. automodule:: eventlet.event
:members:

View File

@@ -3,4 +3,3 @@
.. automodule:: eventlet.greenio
:members:
:undoc-members:

View File

@@ -0,0 +1,5 @@
:mod:`greenthread` -- Green Thread Implementation
==================================================
.. automodule:: eventlet.greenthread
:members:

View File

@@ -1,6 +0,0 @@
:mod:`proc` -- Advanced coroutine control
==========================================
.. automodule:: eventlet.proc
:members:
:undoc-members:

View File

@@ -3,4 +3,3 @@
.. automodule:: eventlet.processes
:members:
:undoc-members:

5
doc/modules/queue.rst Normal file
View File

@@ -0,0 +1,5 @@
:mod:`queue` -- Queue class
========================================
.. automodule:: eventlet.queue
:members:

View File

@@ -18,7 +18,6 @@ The objects so wrapped behave as if they are resident in the current process spa
.. automodule:: eventlet.saranwrap
:members:
:undoc-members:
Underlying Protocol

View File

@@ -1,6 +1,6 @@
:mod:`coros` -- Coroutine communication patterns
:mod:`semaphore` -- Semaphore classes
==================================================
.. automodule:: eventlet.coros
.. automodule:: eventlet.semaphore
:members:
:undoc-members:

View File

@@ -1,9 +1,10 @@
"""The debug module contains utilities and functions for better
debugging Eventlet-powered applications."""
__all__ = ['spew', 'unspew', 'hub_listener_stacks',
'hub_exceptions', 'tpool_exceptions']
import os
__all__ = ['spew', 'unspew', 'format_hub_listeners', 'hub_listener_stacks',
'hub_exceptions', 'tpool_exceptions']
class Spew(object):
"""
@@ -60,15 +61,27 @@ def unspew():
sys.settrace(None)
def format_hub_listeners():
""" Returns a formatted string of the current listeners on the current
hub. This can be useful in determining what's going on in the event system,
especially when used in conjunction with :func:`hub_listener_stacks`.
"""
from eventlet import hubs
hub = hubs.get_hub()
result = ['READERS:']
for l in hub.get_readers():
result.append(repr(l))
result.append('WRITERS:')
for l in hub.get_writers():
result.append(repr(l))
return os.linesep.join(result)
def hub_listener_stacks(state):
"""Toggles whether or not the hub records the stack when clients register
listeners on file descriptors. This can be useful when trying to figure
out what the hub is up to at any given moment. To inspect the stacks
of the current listeners, you have to iterate over them::
from eventlet import hubs
for reader in hubs.get_hub().get_readers():
print reader
of the current listeners, call :func:`format_hub_listeners` at critical
junctures in the application logic.
"""
from eventlet import hubs
hubs.get_hub().set_debug_listeners(state)

View File

@@ -13,20 +13,22 @@ class Event(object):
"""An abstraction where an arbitrary number of coroutines
can wait for one event from another.
Events differ from channels in two ways:
Events are similar to a Queue that can only hold one item, but differ
in two important ways:
1. calling :meth:`send` does not unschedule the current coroutine
1. calling :meth:`send` never unschedules the current greenthread
2. :meth:`send` can only be called once; use :meth:`reset` to prepare the
event for another :meth:`send`
They are ideal for communicating return values between coroutines.
They are good for communicating results between coroutines.
>>> from eventlet import coros, api
>>> evt = coros.Event()
>>> from eventlet import event
>>> import eventlet
>>> evt = event.Event()
>>> def baz(b):
... evt.send(b + 1)
...
>>> _ = api.spawn(baz, 3)
>>> _ = eventlet.spawn_n(baz, 3)
>>> evt.wait()
4
"""
@@ -43,8 +45,8 @@ class Event(object):
""" Reset this event so it can be used to send again.
Can only be called after :meth:`send` has been called.
>>> from eventlet import coros
>>> evt = coros.Event()
>>> from eventlet import event
>>> evt = event.Event()
>>> evt.send(1)
>>> evt.reset()
>>> evt.send(2)
@@ -103,14 +105,15 @@ class Event(object):
Returns the value the other coroutine passed to
:meth:`send`.
>>> from eventlet import coros, api
>>> evt = coros.Event()
>>> from eventlet import event
>>> import eventlet
>>> evt = event.Event()
>>> def wait_on():
... retval = evt.wait()
... print "waited for", retval
>>> _ = api.spawn(wait_on)
>>> _ = eventlet.spawn(wait_on)
>>> evt.send('result')
>>> api.sleep(0)
>>> eventlet.sleep(0)
waited for result
Returns immediately if the event has already
@@ -134,17 +137,18 @@ class Event(object):
"""Makes arrangements for the waiters to be woken with the
result and then returns immediately to the parent.
>>> from eventlet import coros, api
>>> evt = coros.Event()
>>> from eventlet import event
>>> import eventlet
>>> evt = event.Event()
>>> def waiter():
... print 'about to wait'
... result = evt.wait()
... print 'waited for', result
>>> _ = api.spawn(waiter)
>>> api.sleep(0)
>>> _ = eventlet.spawn(waiter)
>>> eventlet.sleep(0)
about to wait
>>> evt.send('a')
>>> api.sleep(0)
>>> eventlet.sleep(0)
waited for a
It is an error to call :meth:`send` multiple times on the same event.
@@ -175,5 +179,6 @@ class Event(object):
waiter.throw(*exc)
def send_exception(self, *args):
"""Same as :meth:`send`, but sends an exception to waiters."""
# the arguments and the same as for greenlet.throw
return self.send(None, args)

View File

@@ -27,12 +27,12 @@ class GreenPool(object):
self.no_coros_running = event.Event()
def resize(self, new_size):
""" Change the max number of coroutines doing work at any given time.
""" Change the max number of greenthreads doing work at any given time.
If resize is called when there are more than *new_size*
coroutines already working on tasks, they will be allowed to complete
but no new tasks will be allowed to get launched until enough coroutines
finish their tasks to drop the overall quantity below *new_size*. Until
If resize is called when there are more than *new_size* greenthreads
already working on tasks, they will be allowed to complete but no new
tasks will be allowed to get launched until enough greenthreads finish
their tasks to drop the overall quantity below *new_size*. Until
then, the return value of free() will be negative.
"""
size_delta = new_size - self.size
@@ -40,15 +40,15 @@ class GreenPool(object):
self.size = new_size
def running(self):
""" Returns the number of coroutines that are currently executing
""" Returns the number of greenthreads that are currently executing
functions in the Parallel's pool."""
return len(self.coroutines_running)
def free(self):
""" Returns the number of coroutines available for use.
""" Returns the number of greenthreads available for use.
If zero or less, the next call to :meth:`spawn` will block the calling
coroutine until a slot becomes available."""
If zero or less, the next call to :meth:`spawn` or :meth:`spawn_n` will
block the calling greenthread until a slot becomes available."""
return self.sem.counter
def spawn(self, function, *args, **kwargs):
@@ -89,8 +89,8 @@ class GreenPool(object):
self._spawn_done(coro)
def spawn_n(self, func, *args, **kwargs):
""" Create a coroutine to run the *function*. Returns None; the results
of the function are not retrievable.
""" Create a greenthread to run the *function*. Returns None; the
results of the function are not retrievable.
"""
# if reentering an empty pool, don't try to wait on a coroutine freeing
# itself -- instead, just execute in the current coroutine
@@ -99,13 +99,14 @@ class GreenPool(object):
self._spawn_n_impl(func, args, kwargs)
else:
self.sem.acquire()
g = greenthread.spawn_n(self._spawn_n_impl, func, args, kwargs, coro=True)
g = greenthread.spawn_n(self._spawn_n_impl,
func, args, kwargs, coro=True)
if not self.coroutines_running:
self.no_coros_running = event.Event()
self.coroutines_running.add(g)
def waitall(self):
"""Waits until all coroutines in the pool are finished working."""
"""Waits until all greenthreads in the pool are finished working."""
self.no_coros_running.wait()
def _spawn_done(self, coro):
@@ -118,7 +119,7 @@ class GreenPool(object):
self.no_coros_running.send(None)
def waiting(self):
"""Return the number of coroutines waiting to spawn.
"""Return the number of greenthreads waiting to spawn.
"""
if self.sem.balance < 0:
return -self.sem.balance
@@ -194,7 +195,7 @@ class GreenPile(object):
return self
def next(self):
"""Wait for the next result, suspending the current coroutine until it
"""Wait for the next result, suspending the current greenthread until it
is available. Raises StopIteration when there are no more results."""
if self.counter == 0 and self.used:
raise StopIteration()

View File

@@ -30,8 +30,14 @@ def sleep(seconds=0):
def spawn(func, *args, **kwargs):
"""Create a green thread to run func(*args, **kwargs). Returns a
GreenThread object which you can use to get the results of the call.
"""Create a greenthread to run ``func(*args, **kwargs)``. Returns a
:class:`GreenThread` object which you can use to get the results of the
call.
Execution control returns immediately to the caller; the created greenthread
is merely scheduled to be run at the next available opportunity.
Use :func:`call_after_global` to arrange for greenthreads to be spawned
after a finite delay.
"""
hub = hubs.get_hub()
g = GreenThread(hub.greenlet)
@@ -46,9 +52,10 @@ def _main_wrapper(func, args, kwargs):
def spawn_n(func, *args, **kwargs):
"""Same as spawn, but returns a greenlet object from which it is not
possible to retrieve the results. This is slightly faster than spawn; it is
fastest if there are no keyword arguments."""
"""Same as :func:`spawn`, but returns a ``greenlet`` object from which it is
not possible to retrieve the results. This is slightly faster
than :func:`spawn` in all cases; it is fastest if there are no keyword
arguments."""
return _spawn_n(0, func, args, kwargs)[1]
@@ -58,23 +65,21 @@ def call_after_global(seconds, func, *args, **kwargs):
*seconds* may be specified as an integer, or a float if fractional seconds
are desired. The *function* will be called with the given *args* and
keyword arguments *kwargs*, and will be executed within the main loop's
coroutine.
Its return value is discarded. Any uncaught exception will be logged."""
keyword arguments *kwargs*, and will be executed within its own greenthread.
Its return value is discarded."""
return _spawn_n(seconds, func, args, kwargs)[0]
def call_after_local(seconds, function, *args, **kwargs):
"""Schedule *function* to be called after *seconds* have elapsed.
The function will NOT be called if the current greenlet has exited.
The function will NOT be called if the current greenthread has exited.
*seconds* may be specified as an integer, or a float if fractional seconds
are desired. The *function* will be called with the given *args* and
keyword arguments *kwargs*, and will be executed within the main loop's
coroutine.
keyword arguments *kwargs*, and will be executed within its own greenthread.
Its return value is discarded. Any uncaught exception will be logged.
Its return value is discarded.
"""
hub = hubs.get_hub()
g = greenlet.greenlet(_main_wrapper, parent=hub.greenlet)
@@ -180,7 +185,7 @@ def _spawn_n(seconds, func, args, kwargs):
class GreenThread(greenlet.greenlet):
"""The GreenThread class is a type of Greenlet which has the additional
property of having a retrievable result. Do not construct GreenThread
objects directly; call :func:greenthread.spawn to get one.
objects directly; call :func:`spawn` to get one.
"""
def __init__(self, parent):
greenlet.greenlet.__init__(self, self.main, parent)
@@ -188,20 +193,28 @@ class GreenThread(greenlet.greenlet):
def wait(self):
""" Returns the result of the main function of this GreenThread. If the
result is a normal return value, wait() returns it. If it raised
an exception, wait() will also raise an exception."""
result is a normal return value, :meth:`wait` returns it. If it raised
an exception, :meth:`wait` will raise the same exception (though the
stack trace will unavoidably contain some frames from within the
greenthread module)."""
return self._exit_event.wait()
def link(self, func, *curried_args, **curried_kwargs):
""" Set up a function to be called with the results of the GreenThread.
The function must have the following signature::
def func(gt, [curried args/kwargs]):
def func(gt, [curried args/kwargs]):
When the GreenThread finishes its run, it calls *func* with itself
and with the arguments supplied at link-time. If the function wants
to retrieve the result of the GreenThread, it should call wait()
on its first argument.
Note that *func* is called within execution context of
the GreenThread, so it is possible to interfere with other linked
functions by doing things like switching explicitly to another
greenthread.
"""
self._exit_funcs = getattr(self, '_exit_funcs', [])
self._exit_funcs.append((func, curried_args, curried_kwargs))

View File

@@ -3,7 +3,7 @@ import sys
import threading
_threadlocal = threading.local()
__all__ = ["use_hub"]
__all__ = ["use_hub", "get_hub", "get_default_hub"]
def get_default_hub():
"""Select the default hub implementation based on what multiplexing
@@ -56,6 +56,7 @@ def use_hub(mod=None):
if hasattr(_threadlocal, 'hub'):
del _threadlocal.hub
if isinstance(mod, str):
assert mod.strip(), "Need to specify a hub"
mod = __import__('eventlet.hubs.' + mod, globals(), locals(), ['Hub'])
if hasattr(mod, 'Hub'):
_threadlocal.Hub = mod.Hub

View File

@@ -47,6 +47,7 @@ def cooperative_wait(pobj, check_interval=0.01):
class Process(object):
"""Construct Process objects, then call read, and write on them."""
process_number = 0
def __init__(self, command, args, dead_callback=lambda:None):
self.process_number = self.process_number + 1

View File

@@ -18,6 +18,7 @@ class TestDebug(LimitedTestCase):
debug.tpool_exceptions(False)
debug.hub_listener_stacks(True)
debug.hub_listener_stacks(False)
debug.format_hub_listeners()
def test_hub_exceptions(self):
debug.hub_exceptions(True)