Merge
This commit is contained in:
58
NEWS
58
NEWS
@@ -1,13 +1,59 @@
|
||||
0.9.0
|
||||
=====
|
||||
0.8.11
|
||||
======
|
||||
|
||||
Merged many eventlet-twisted integration patches by Denis Bilenko. It is now possible to mix eventlet-using code with twisted-using code.
|
||||
Eventlet can now run on top of twisted reactor. Twisted-based hub is enabled automatically if
|
||||
twisted.internet.reactor is imported. It is also possible to "embed" eventlet into a twisted
|
||||
application via eventlet.twistedutil.join_reactor. See the examples for details.
|
||||
|
||||
Much of the core functionality has been refactored and cleaned up, including the removal of eventlet.greenlib. This means that it is now possible to use plain greenlets without modification in eventlet, and plain old subclasses of greenlet instead of the old eventlet.greenlib.GreenletContext. Calling eventlet.api.get_hub().switch() now checks to see whether the current greenlet has a "switch_out" method and calls it if so, providing the same functionality that the GreenletContext.swap_out used to. The swap_in behavior can be duplicated by overriding the switch method, and the finalize functionality can be duplicated by having a try: finally: block around the greenlet's main implementation. The eventlet.backdoor module has been ported to this new scheme, although it's signature had to change slightly so existing code that used the backdoor will have to be modified.
|
||||
A new package, eventlet.twistedutil, is added that makes integration of twisted and eventlet
|
||||
easier. It has block_on function that allows to wait for a Deferred to fire and it wraps
|
||||
twisted's Protocol in a synchronous interface. This is similar to and is inspired by Christopher
|
||||
Armstrong's corotwine library. Thanks to Dan Pascu for reviewing the package.
|
||||
|
||||
Monkey-patching is now no longer the default way to use eventlet. Instead, there are a series of standard library networking modules that have been patched to cooperate using the eventlet hub inside the eventlet.green module. For example, using "from eventlet.green import httplib" is equivalent to the old practice of doing "from eventlet import util; util.wrap_socket_with_coroutine_socket(); import httplib".
|
||||
Another new package, eventlet.green, was added to provide some of the standard modules
|
||||
that are fixed not to block other greenlets. This is an alternative to monkey-patching
|
||||
the socket, which is impossible to do if you are running twisted's reactor.
|
||||
The package includes socket, httplib, urllib2.
|
||||
|
||||
eventlet.httpd has been removed. Use eventlet.wsgi instead.
|
||||
Much of the core functionality has been refactored and cleaned up, including the removal
|
||||
of eventlet.greenlib. This means that it is now possible to use plain greenlets without
|
||||
modification in eventlet, and plain old subclasses of greenlet instead of the old
|
||||
eventlet.greenlib.GreenletContext. Calling eventlet.api.get_hub().switch() now checks to
|
||||
see whether the current greenlet has a "switch_out" method and calls it if so, providing the
|
||||
same functionality that the GreenletContext.swap_out used to. The swap_in behavior can be
|
||||
duplicated by overriding the switch method, and the finalize functionality can be duplicated
|
||||
by having a try: finally: block around the greenlet's main implementation. The eventlet.backdoor
|
||||
module has been ported to this new scheme, although it's signature had to change slightly so
|
||||
existing code that used the backdoor will have to be modified.
|
||||
|
||||
A number of bugs related to unproper scheduling of switch calls has been fixed.
|
||||
The fixed functions and classes include api.trampoline, api.sleep, coros.event,
|
||||
coros.semaphore, coros.queue.
|
||||
|
||||
Many methods of greenio.GreenSocket were fixed to make its behavior more like that of a regular
|
||||
socket. Thanks to Marcin Bachry for fixing GreenSocket.dup to preserve the timeout.
|
||||
|
||||
Added proc module which provides an easy way to subscribe to coroutine's results. This makes
|
||||
it easy to wait() for a single greenlet to complete or to waitall() for several greenlets.
|
||||
|
||||
wsgi.py now supports chunked transfer requests (patch by Mike Barton)
|
||||
|
||||
The following modules were deprecated or removed because they were broken:
|
||||
hubs.nginx, hubs.libev, support.pycurls, support.twisteds, cancel method of coros.event class
|
||||
|
||||
The following classes are still present but will be removed in the future version:
|
||||
- channel.channel (use coros.Channel)
|
||||
- coros.CoroutinePool (use pool.Pool)
|
||||
|
||||
Python 2.3 is no longer supported.
|
||||
|
||||
A number of tests was added along with a script to run all of them for all the configurations.
|
||||
The script generates an html page with the results.
|
||||
|
||||
Thanks to Brian Brunswick for investigation of popen4 badness (eventlet.process)
|
||||
Thanks to Marcus Cavanaugh for pointing out some coros.queue(0) bugs.
|
||||
|
||||
The twisted integration as well as many other improvements were funded by AG Projects (http://ag-projects.com), thanks!
|
||||
|
||||
0.8.x
|
||||
=====
|
||||
|
@@ -111,7 +111,6 @@ def tcp_server(listensocket, server, *args, **kw):
|
||||
\*\*kw
|
||||
The keyword arguments to pass to *server*.
|
||||
"""
|
||||
print "tcpserver spawning %s on %s" % (server, listensocket.getsockname())
|
||||
try:
|
||||
try:
|
||||
while True:
|
||||
|
@@ -1,16 +1,23 @@
|
||||
from eventlet import coros
|
||||
|
||||
class channel(coros.Queue):
|
||||
|
||||
def __init__(self):
|
||||
coros.queue.__init__(self, 0)
|
||||
class channel(coros.Channel):
|
||||
|
||||
def receive(self):
|
||||
return self.wait()
|
||||
|
||||
@property
|
||||
def balance(self):
|
||||
return self.sem.balance
|
||||
return len(self.items) - len(self._waiters)
|
||||
|
||||
import warnings
|
||||
warnings.warn("channel is deprecated; use coros.queue(0) which behaves the same", DeprecationWarning, stacklevel=2)
|
||||
warnings.warn("channel.py is deprecated by coros.Channel", DeprecationWarning, stacklevel=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from eventlet import proc, api
|
||||
c = channel()
|
||||
proc.spawn(c.send, 'X')
|
||||
proc.spawn(c.send, 'Y')
|
||||
assert c.wait() == 'X', c
|
||||
assert c.wait() == 'Y', c
|
||||
assert api.with_timeout(1, c.wait, timeout_value='hello') == 'hello', c
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# @author Donovan Preston
|
||||
#
|
||||
# Copyright (c) 2007, Linden Research, Inc.
|
||||
# Copyright (c) 2008-2009, AG Projects
|
||||
# Copyright (c) 2009, Denis Bilenko
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
|
@@ -1,97 +0,0 @@
|
||||
# Copyright (c) 2005-2009, Donovan Preston
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Takes an exception object and returns a dictionary suitable for use debugging the exception later.
|
||||
"""
|
||||
|
||||
import linecache
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
def format_exc(exc=None):
|
||||
if exc is None:
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
else:
|
||||
exc_type, exc_value, exc_tb = exc
|
||||
|
||||
frames = []
|
||||
while exc_tb is not None:
|
||||
f = exc_tb.tb_frame
|
||||
|
||||
frames.append((
|
||||
f.f_code.co_name,
|
||||
f.f_code.co_filename,
|
||||
exc_tb.tb_lineno,
|
||||
[(key, value) for (key, value)
|
||||
in f.f_locals.items() if key != '__builtins__'],
|
||||
[(key, value) for (key, value)
|
||||
in f.f_globals.items() if key != '__builtins__']))
|
||||
exc_tb = exc_tb.tb_next
|
||||
|
||||
stack_trace = []
|
||||
|
||||
result = {
|
||||
'error': True,
|
||||
'stack-trace': stack_trace,
|
||||
'description': str(exc_type) + ": " + str(exc_value),
|
||||
'text-exception': ''.join(
|
||||
traceback.format_exception(
|
||||
exc_type, exc_value, exc_tb))
|
||||
|
||||
}
|
||||
|
||||
for method, filename, lineno, local_vars, global_vars in frames:
|
||||
code = []
|
||||
vars_dict = {}
|
||||
stack_trace.append(
|
||||
{'filename': filename,
|
||||
'lineno': lineno,
|
||||
'method': method,
|
||||
'code': code,
|
||||
#'vars': vars_dict})
|
||||
})
|
||||
|
||||
code_text = ''
|
||||
for line_number in range(lineno-2, lineno+2):
|
||||
line = linecache.getline(filename, line_number)
|
||||
code.append({'lineno': line_number, 'line': line})
|
||||
code_text += line
|
||||
|
||||
# "self"
|
||||
for name, var in local_vars:
|
||||
if name == 'self' and hasattr(var, '__dict__'):
|
||||
vars_dict['self'] = dict([
|
||||
(key, value) for (key, value) in var.__dict__.items()
|
||||
if re.search(
|
||||
r'\Wself.%s\W' % (re.escape(key),), code_text)])
|
||||
break
|
||||
|
||||
# Local and global vars
|
||||
vars_dict['locals'] = dict(
|
||||
[(name, var) for (name, var) in local_vars
|
||||
if re.search(r'\W%s\W' % (re.escape(name),), code_text)])
|
||||
vars_dict['globals'] = dict(
|
||||
[(name, var) for (name, var) in global_vars
|
||||
if re.search(r'\W%s\W' % (re.escape(name),), code_text)])
|
||||
|
||||
return result
|
@@ -499,17 +499,12 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
self.server.site.handle_request(request)
|
||||
except ErrorResponse, err:
|
||||
request.response(code=err.code,
|
||||
reason_phrase=err.reason,
|
||||
headers=err.headers,
|
||||
body=err.body)
|
||||
finally:
|
||||
# clean up any timers that might have been left around by the handling code
|
||||
#api.get_hub().cancel_timers(api.getcurrent())
|
||||
pass
|
||||
self.server.site.handle_request(request)
|
||||
except ErrorResponse, err:
|
||||
request.response(code=err.code,
|
||||
reason_phrase=err.reason,
|
||||
headers=err.headers,
|
||||
body=err.body)
|
||||
|
||||
# throw an exception if it failed to write a body
|
||||
if not request.response_written():
|
||||
|
@@ -99,4 +99,4 @@ class channel(object):
|
||||
return self._send_tasklet(None, exc)
|
||||
|
||||
import warnings
|
||||
warnings.warn("channel is deprecated; use coros.queue(0) which behaves the same", DeprecationWarning, stacklevel=2)
|
||||
warnings.warn("channel is deprecated by coros.Channel", DeprecationWarning, stacklevel=2)
|
||||
|
Reference in New Issue
Block a user