diff --git a/eventlet/api.py b/eventlet/api.py index 1779f42..8100e7e 100644 --- a/eventlet/api.py +++ b/eventlet/api.py @@ -49,7 +49,7 @@ def tcp_listener(address, backlog=50): socket object on which one should call ``accept()`` to accept a connection on the newly bound socket. """ - warnings.warn("""eventlet.api.tcp_listener is deprecated. Please use eventlet.green.socket instead. See examples/echoserver.py for an example.""", + warnings.warn("""eventlet.api.tcp_listener is deprecated. Please use eventlet.listen instead.""", DeprecationWarning, stacklevel=2) from eventlet import greenio, util @@ -77,7 +77,7 @@ def connect_tcp(address, localaddr=None): Create a TCP connection to address ``(host, port)`` and return the socket. Optionally, bind to localaddr ``(host, port)`` first. """ - warnings.warn("""eventlet.api.connect_tcp is deprecated. Please use eventlet.green.socket instead. See examples/connect.py for an example.""", + warnings.warn("""eventlet.api.connect_tcp is deprecated. Please use eventlet.connect instead.""", DeprecationWarning, stacklevel=2) from eventlet import greenio, util diff --git a/eventlet/db_pool.py b/eventlet/db_pool.py index 61effe3..07b96ae 100644 --- a/eventlet/db_pool.py +++ b/eventlet/db_pool.py @@ -3,7 +3,6 @@ import sys import time from eventlet.pools import Pool -from eventlet.processes import DeadProcess from eventlet import timeout from eventlet import greenthread diff --git a/eventlet/debug.py b/eventlet/debug.py index 937e6af..5e44eeb 100644 --- a/eventlet/debug.py +++ b/eventlet/debug.py @@ -4,6 +4,8 @@ debugging Eventlet-powered applications.""" import os import sys import linecache +import string +import inspect __all__ = ['spew', 'unspew', 'format_hub_listeners', 'hub_listener_stacks', 'hub_exceptions', 'tpool_exceptions'] @@ -122,4 +124,4 @@ def tpool_exceptions(state): functions that are executed in it, in addition to raising them like it normally does.""" from eventlet import tpool - tpool.QUIET = not state \ No newline at end of file + tpool.QUIET = not state diff --git a/eventlet/greenio.py b/eventlet/greenio.py index bcf03d9..b95ab39 100644 --- a/eventlet/greenio.py +++ b/eventlet/greenio.py @@ -336,7 +336,7 @@ class GreenSocket(object): class GreenPipe(object): """ GreenPipe is a cooperatively-yielding wrapper around OS pipes. """ - newlines = '\r\n' + newlines = '\n' def __init__(self, fd): set_nonblocking(fd) self.fd = fd @@ -416,7 +416,7 @@ class GreenPipe(object): chunk, self.recvbuffer = buf[:found], buf[found:] return chunk checked = max(0, len(buf) - (len(terminator) - 1)) - d = self.fd.read(BUFFER_SIZE) + d = self._recv(BUFFER_SIZE) if not d: break buf += d @@ -428,7 +428,7 @@ class GreenPipe(object): chunk, self.recvbuffer = buf[:found], buf[found:] return chunk checked = len(buf) - d = self.fd.read(BUFFER_SIZE) + d = self._recv(BUFFER_SIZE) if not d: break buf += d @@ -574,4 +574,4 @@ def serve(sock, handle, concurrency=1000): connections until the existing ones complete. """ pass - \ No newline at end of file + diff --git a/eventlet/pools.py b/eventlet/pools.py index d79f7f3..73d8238 100644 --- a/eventlet/pools.py +++ b/eventlet/pools.py @@ -34,35 +34,41 @@ except ImportError: class Pool(object): """ - Pool is a base class that is meant to be subclassed. When subclassing, - define the :meth:`create` method to implement the desired resource. + Pool is a base class that implements resource limitation and construction. + It is meant to be subclassed. When subclassing, define only + the :meth:`create` method to implement the desired resource:: - When using the pool, if you do a get, you should **always** do a - :meth:`put`. + class MyPool(pools.Pool): + def create(self): + return MyObject() + + If using 2.5 or greater, the :meth:`item` method acts as a context manager; + that's the best way to use it:: + + with mypool.item() as thing: + thing.dostuff() + + If stuck on 2.4, the :meth:`get` and :meth:`put` methods are the preferred + nomenclature. Use a ``finally`` to ensure that nothing is leaked:: - The pattern is:: + thing = self.pool.get() + try: + thing.dostuff() + finally: + self.pool.put(thing) - thing = self.pool.get() - try: - thing.method() - finally: - self.pool.put(thing) - - The maximum size of the pool can be modified at runtime via the - :attr:`max_size` attribute. Adjusting this number does not affect existing - items checked out of the pool, nor on any waiters who are waiting for an - item to free up. Some indeterminate number of :meth:`get`/:meth:`put` - cycles will be necessary before the new maximum size truly matches the - actual operation of the pool. + The maximum size of the pool can be modified at runtime via + the :meth:`resize` method. + + Specifying a non-zero *min-size* argument pre-populates the pool with + *min_size* items. *max-size* sets a hard limit to the size of the pool -- + it cannot contain any more items than *max_size*, and if there are already + *max_size* items 'checked out' of the pool, the pool will cause any + greenthread calling :meth:`get` to cooperatively yield until an item + is :meth:`put` in. """ def __init__(self, min_size=0, max_size=4, order_as_stack=False): - """ Pre-populates the pool with *min_size* items. Sets a hard limit to - the size of the pool -- it cannot contain any more items than - *max_size*, and if there are already *max_size* items 'checked out' of - the pool, the pool will cause any getter to cooperatively yield until an - item is put in. - - *order_as_stack* governs the ordering of the items in the free pool. + """*order_as_stack* governs the ordering of the items in the free pool. If ``False`` (the default), the free items collection (of items that were created and were put back in the pool) acts as a round-robin, giving each item approximately equal utilization. If ``True``, the @@ -80,7 +86,8 @@ class Pool(object): self.free_items.append(self.create()) def get(self): - """Return an item from the pool, when one is available + """Return an item from the pool, when one is available. This may + cause the calling greenthread to block. """ if self.free_items: return self.free_items.popleft() @@ -89,12 +96,13 @@ class Pool(object): self.current_size += 1 return created return self.channel.get() - + if item_impl is not None: item = item_impl def put(self, item): - """Put an item back into the pool, when done + """Put an item back into the pool, when done. This may + cause the putting greenthread to block. """ if self.current_size > self.max_size: self.current_size -= 1 @@ -109,12 +117,19 @@ class Pool(object): self.free_items.append(item) def resize(self, new_size): - """Resize the pool + """Resize the pool to *new_size*. + + Adjusting this number does not affect existing items checked out of + the pool, nor on any greenthreads who are waiting for an item to free + up. Some indeterminate number of :meth:`get`/:meth:`put` + cycles will be necessary before the new maximum size truly matches + the actual operation of the pool. """ self.max_size = new_size def free(self): - """Return the number of free items in the pool. + """Return the number of free items in the pool. This corresponds + to the number of :meth:`get` calls needed to empty the pool. """ return len(self.free_items) + self.max_size - self.current_size @@ -124,7 +139,17 @@ class Pool(object): return max(0, self.channel.getting() - self.channel.putting()) def create(self): - """Generate a new pool item + """Generate a new pool item. This method must be overridden in order + for the pool to function. It accepts no arguments and returns a single + instance of whatever thing the pool is supposed to contain. + + In general, :meth:`create` is called whenever the pool exceeds its + previous high-water mark of concurrently-checked-out-items. In other + words, in a new pool with *min_size* of 0, the very first call + to :meth:`get` will result in a call to :meth:`create`. If the first + caller calls :meth:`put` before some other caller calls :meth:`get`, + then the first item will be returned, and :meth:`create` will not be + called a second time. """ raise NotImplementedError("Implement in subclass") diff --git a/eventlet/queue.py b/eventlet/queue.py index 3963381..1bf3aef 100644 --- a/eventlet/queue.py +++ b/eventlet/queue.py @@ -181,6 +181,15 @@ class LightQueue(object): def qsize(self): """Return the size of the queue.""" return len(self.queue) + + def resize(self, size): + """Resizes the queue's maximum size. + + If the size is increased, and there are putters waiting, they may be woken up.""" + if size > self.maxsize: + # Maybe wake some stuff up + self._schedule_unlock() + self.maxsize = size def putting(self): """Returns the number of greenthreads that are blocked waiting to put diff --git a/eventlet/support/greenlets.py b/eventlet/support/greenlets.py index 25bb60d..cb75667 100644 --- a/eventlet/support/greenlets.py +++ b/eventlet/support/greenlets.py @@ -4,6 +4,7 @@ try: GreenletExit = greenlet.GreenletExit greenlet = greenlet.greenlet except ImportError, e: + raise try: from py.magic import greenlet getcurrent = greenlet.getcurrent diff --git a/tests/debug_test.py b/tests/debug_test.py index 4afa349..db5e329 100644 --- a/tests/debug_test.py +++ b/tests/debug_test.py @@ -4,12 +4,90 @@ import eventlet from eventlet import debug from eventlet import api from tests import LimitedTestCase, main +from unittest import TestCase try: from cStringIO import StringIO except ImportError: from StringIO import StringIO +class TestSpew(TestCase): + def setUp(self): + self.orig_trace = sys.settrace + sys.settrace = self._settrace + self.tracer = None + + def tearDown(self): + sys.settrace = self.orig_trace + sys.stdout = sys.__stdout__ + + def _settrace(self, cb): + self.tracer = cb + + def test_spew(self): + debug.spew() + self.failUnless(isinstance(self.tracer, debug.Spew)) + + def test_unspew(self): + debug.spew() + debug.unspew() + self.failUnlessEqual(self.tracer, None) + + def test_line(self): + sys.stdout = StringIO() + s = debug.Spew() + f = sys._getframe() + s(f, "line", None) + lineno = f.f_lineno - 1 # -1 here since we called with frame f in the line above + output = sys.stdout.getvalue() + self.failUnless("debug_test:%i" % lineno in output, "Didn't find line %i in %s" % (lineno, output)) + self.failUnless("f=