From 124e84e88ce31b3c12d0eb9eb3d6a099ef028bc8 Mon Sep 17 00:00:00 2001 From: nat Date: Thu, 20 Mar 2008 15:17:02 -0400 Subject: [PATCH] Fold in some api function documentation from Donovan's preliminary guide. Fix reStructuredText syntax on a couple others. --- eventlet/api.py | 165 ++++++++++++++++++++++++++++++++++++------------ makedoc | 3 + 2 files changed, 128 insertions(+), 40 deletions(-) create mode 100755 makedoc diff --git a/eventlet/api.py b/eventlet/api.py index 42487cb..dbffb7e 100644 --- a/eventlet/api.py +++ b/eventlet/api.py @@ -55,17 +55,18 @@ __all__ = [ class TimeoutError(Exception): + """Exception raised if an asynchronous operation times out""" pass _threadlocal = tls.local() def tcp_listener(address): """ - Listen on the given (ip, port) address with a TCP socket. - Returns a socket object which one should call accept() on to + Listen on the given (ip, port) *address* with a TCP socket. + Returns a socket object on which one should call ``accept()`` to accept a connection on the newly bound socket. - Generally, the returned socket will be passed to tcp_server, + Generally, the returned socket will be passed to ``tcp_server()``, which accepts connections forever and spawns greenlets for each incoming connection. """ @@ -75,13 +76,16 @@ def tcp_listener(address): return socket def ssl_listener(address, certificate, private_key): - """Listen on the given (ip, port) address with a TCP socket that + """Listen on the given (ip, port) *address* with a TCP socket that can do SSL. - Returns a socket object which one should call accept() on to + *certificate* and *private_key* should be the filenames of the appropriate + certificate and private key files to use with the SSL socket. + + Returns a socket object on which one should call ``accept()`` to accept a connection on the newly bound socket. - Generally, the returned socket will be passed to tcp_server, + Generally, the returned socket will be passed to ``tcp_server()``, which accepts connections forever and spawns greenlets for each incoming connection. """ @@ -103,20 +107,17 @@ def connect_tcp(address): def tcp_server(listensocket, server, *args, **kw): """ Given a socket, accept connections forever, spawning greenlets - and executing "server" for each new incoming connection. - When listensocket is closed, the tcp_server greenlet will end. + and executing *server* for each new incoming connection. + When *listensocket* is closed, the ``tcp_server()`` greenlet will end. - listensocket: - The socket to accept connections from. - - server: + listensocket + The socket from which to accept connections. + server The callable to call when a new connection is made. - - *args: - The arguments to pass to the call to server. - - **kw: - The keyword arguments to pass to the call to server. + \*args + The positional arguments to pass to *server*. + \*\*kw + The keyword arguments to pass to *server*. """ try: try: @@ -129,7 +130,19 @@ def tcp_server(listensocket, server, *args, **kw): finally: listensocket.close() -def trampoline(fd, read=None, write=None, timeout=None): +def trampoline(fd, read=False, write=False, timeout=None): + """Suspend the current coroutine until the given socket object or file + descriptor is ready to *read*, ready to *write*, or the specified + *timeout* elapses, depending on arguments specified. + + To wait for *fd* to be ready to read, pass *read* ``=True``; ready to + write, pass *write* ``=True``. To specify a timeout, pass the *timeout* + argument in seconds. + + If the specified *timeout* elapses before the socket is ready to read or + write, ``TimeoutError`` will be raised instead of ``trampoline()`` + returning normally. + """ t = None hub = get_hub() self = greenlet.getcurrent() @@ -164,21 +177,43 @@ def _spawn(g): greenlib.switch(g) -def spawn(cb, *args, **kw): +def spawn(function, *args, **kwds): + """Create a new coroutine, or cooperative thread of control, within which + to execute *function*. + + The *function* will be called with the given *args* and keyword arguments + *kwds* and will remain in control unless it cooperatively yields by + calling a socket method or ``sleep()``. + + ``spawn()`` returns control to the caller immediately, and *function* will + be called in a future main loop iteration. + + An uncaught exception in *function* or any child will terminate the new + coroutine with a log message. + """ # killable t = None g = greenlib.tracked_greenlet() t = get_hub().schedule_call(0, _spawn, g) - greenlib.switch(g, (_spawn_startup, cb, args, kw, t.cancel)) + greenlib.switch(g, (_spawn_startup, function, args, kwds, t.cancel)) return g kill = greenlib.kill -def call_after(seconds, cb, *args, **kw): +def call_after(seconds, function, *args, **kwds): + """Schedule *function* to be called after *seconds* have elapsed. + + *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 *kwds*, and will be executed within the main loop's + coroutine. + + Its return value is discarded. Any uncaught exception will be logged. + """ # cancellable def startup(): g = greenlib.tracked_greenlet() - greenlib.switch(g, (_spawn_startup, cb, args, kw)) + greenlib.switch(g, (_spawn_startup, function, args, kwds)) greenlib.switch(g) return get_hub().schedule_call(seconds, startup) @@ -188,14 +223,14 @@ def with_timeout(seconds, func, *args, **kwds): function fails to return before the timeout, cancel it and return a flag value. - *seconds* + seconds (int or float) seconds before timeout occurs - *func* + func the callable to execute with a timeout; must be one of the functions that implicitly or explicitly yields - *\*args*, *\*\*kwds* + \*args, \*\*kwds (positional, keyword) arguments to pass to *func* - *timeout_value=* + timeout_value= value to return if timeout occurs (default None) **Returns**: @@ -209,9 +244,10 @@ def with_timeout(seconds, func, *args, **kwds): **Example**:: data = with_timeout(30, httpc.get, 'http://www.google.com/', timeout_value="") - # Here data is either the result of the get() call, or the empty string if - # it took too long to return. Any exception raised by the get() call is - # passed through to the caller. + + Here *data* is either the result of the ``get()`` call, or the empty string if + it took too long to return. Any exception raised by the ``get()`` call is + passed through to the caller. """ # Recognize a specific keyword argument, while also allowing pass-through # of any other keyword arguments accepted by func. Use pop() so we don't @@ -226,11 +262,36 @@ def with_timeout(seconds, func, *args, **kwds): finally: timeout.cancel() -def exc_after(seconds, exc): - return call_after(seconds, switch, getcurrent(), None, exc) +def exc_after(seconds, exception_object): + """Schedule *exception_object* to be raised into the current coroutine + after *seconds* have elapsed. + + This only works if the current coroutine is yielding, and is generally + used to set timeouts after which a network operation or series of + operations will be canceled. + + Returns a timer object with a ``cancel()`` method which should be used to + prevent the exception if the operation completes successfully. + + See also ``with_timeout()`` that encapsulates the idiom below. + + Example:: + + def read_with_timeout(): + timer = api.exc_after(30, RuntimeError()) + try: + httpc.get('http://www.google.com/') + except RuntimeError: + print "Timed out!" + else: + timer.cancel() + """ + return call_after(seconds, switch, getcurrent(), None, exception_object) -def get_default_hub(): +def get_default_hub(): + """ + """ try: import eventlet.hubs.libevent return eventlet.hubs.libevent @@ -247,6 +308,8 @@ def get_default_hub(): def use_hub(mod=None): + """ + """ if mod is None: mod = get_default_hub() if hasattr(_threadlocal, 'hub'): @@ -257,6 +320,8 @@ def use_hub(mod=None): _threadlocal.Hub = mod def get_hub(): + """ + """ try: hub = _threadlocal.hub except AttributeError: @@ -268,9 +333,19 @@ def get_hub(): return hub -def sleep(timeout=0): +def sleep(seconds=0): + """Yield control to another eligible coroutine until at least *seconds* have + elapsed. + + *seconds* may be specified as an integer, or a float if fractional seconds + are desired. Calling sleep with *seconds* of 0 is the canonical way of + expressing a cooperative yield. For example, if one is looping over a + large list performing an expensive calculation without calling any socket + methods, it's a good idea to call ``sleep(0)`` occasionally; otherwise + nothing else will run. + """ hub = get_hub() - hub.schedule_call(timeout, greenlib.switch, greenlet.getcurrent()) + hub.schedule_call(seconds, greenlib.switch, greenlet.getcurrent()) hub.switch() @@ -280,6 +355,8 @@ GreenletExit = greenlet.GreenletExit class Spew(object): + """ + """ def __init__(self, trace_names=None): self.trace_names = trace_names @@ -317,19 +394,27 @@ class Spew(object): def spew(trace_names=None): + """ + """ sys.settrace(Spew(trace_names)) def unspew(): + """ + """ sys.settrace(None) def named(name): - """Return an object given its name. The name uses a module-like -syntax, eg: - os.path.join - or - mulib.mu.Resource + """Return an object given its name. + + The name uses a module-like syntax, eg:: + + os.path.join + + or:: + + mulib.mu.Resource """ toimport = name obj = None diff --git a/makedoc b/makedoc new file mode 100755 index 0000000..178c6cb --- /dev/null +++ b/makedoc @@ -0,0 +1,3 @@ +dir="$(dirname "$0")" +epydoc -o "$dir/html" --graph classtree --docformat=restructuredtext "$dir/eventlet/" || exit $? +open "$dir/html/index.html"