Port the backdoor to a plain greenlet subclass, add checking for a switch_out method in Hub.switch to duplicate the old GreenletContext.swap_out functionality, and remove greenlib. Yay, less complexity
This commit is contained in:
11
NEWS
11
NEWS
@@ -1,3 +1,14 @@
|
|||||||
|
0.9.0
|
||||||
|
=====
|
||||||
|
|
||||||
|
Merged many eventlet-twisted integration patches by Denis Bilenko. It is now possible to mix eventlet-using code with twisted-using code.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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".
|
||||||
|
|
||||||
|
eventlet.httpd has been removed. Use eventlet.wsgi instead.
|
||||||
|
|
||||||
0.8.x
|
0.8.x
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@@ -114,6 +114,7 @@ def tcp_server(listensocket, server, *args, **kw):
|
|||||||
\*\*kw
|
\*\*kw
|
||||||
The keyword arguments to pass to *server*.
|
The keyword arguments to pass to *server*.
|
||||||
"""
|
"""
|
||||||
|
print "tcpserver spawning %s on %s" % (server, listensocket.getsockname())
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Copyright (c) 2005-2006, Bob Ippolito
|
Copyright (c) 2005-2006, Bob Ippolito
|
||||||
Copyright (c) 2007, Linden Research, Inc.
|
Copyright (c) 2007, Linden Research, Inc.
|
||||||
|
Copyright (c) 2008, Donovan Preston
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
@@ -23,10 +25,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
from code import InteractiveConsole
|
from code import InteractiveConsole
|
||||||
from eventlet import greenlib
|
from eventlet import greenlib
|
||||||
|
|
||||||
|
from eventlet import api
|
||||||
|
from eventlet.support import greenlets
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sys.ps1
|
sys.ps1
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -36,8 +42,11 @@ try:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
sys.ps2 = '... '
|
sys.ps2 = '... '
|
||||||
|
|
||||||
class SocketConsole(greenlib.GreenletContext):
|
|
||||||
def __init__(self, desc):
|
class SocketConsole(greenlets.greenlet):
|
||||||
|
def __init__(self, desc, hostport, locals):
|
||||||
|
self.hostport = hostport
|
||||||
|
self.locals = locals
|
||||||
# mangle the socket
|
# mangle the socket
|
||||||
self.desc = desc
|
self.desc = desc
|
||||||
readline = desc.readline
|
readline = desc.readline
|
||||||
@@ -53,6 +62,24 @@ class SocketConsole(greenlib.GreenletContext):
|
|||||||
self.old[key] = getattr(desc, key)
|
self.old[key] = getattr(desc, key)
|
||||||
setattr(desc, key, value)
|
setattr(desc, key, value)
|
||||||
|
|
||||||
|
greenlets.greenlet.__init__(self)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
console = InteractiveConsole(self.locals)
|
||||||
|
console.interact()
|
||||||
|
finally:
|
||||||
|
self.switch_out()
|
||||||
|
self.finalize()
|
||||||
|
|
||||||
|
def switch(self, *args, **kw):
|
||||||
|
self.saved = sys.stdin, sys.stderr, sys.stdout
|
||||||
|
sys.stdin = sys.stdout = sys.stderr = self.desc
|
||||||
|
greenlets.greenlet.switch(self, *args, **kw)
|
||||||
|
|
||||||
|
def switch_out(self):
|
||||||
|
sys.stdin, sys.stderr, sys.stdout = self.saved
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
# restore the state of the socket
|
# restore the state of the socket
|
||||||
for key in self.fixups:
|
for key in self.fixups:
|
||||||
@@ -65,23 +92,26 @@ class SocketConsole(greenlib.GreenletContext):
|
|||||||
self.fixups.clear()
|
self.fixups.clear()
|
||||||
self.old.clear()
|
self.old.clear()
|
||||||
self.desc = None
|
self.desc = None
|
||||||
|
print "backdoor closed to %s:%s" % self.hostport
|
||||||
|
|
||||||
def swap_in(self):
|
|
||||||
self.saved = sys.stdin, sys.stderr, sys.stdout
|
|
||||||
sys.stdin = sys.stdout = sys.stderr = self.desc
|
|
||||||
|
|
||||||
def swap_out(self):
|
def backdoor_server(server, locals=None):
|
||||||
sys.stdin, sys.stderr, sys.stdout = self.saved
|
print "backdoor listening on %s:%s" % server.getsockname()
|
||||||
|
|
||||||
def backdoor((conn, addr), locals=None):
|
|
||||||
host, port = addr
|
|
||||||
print "backdoor to %s:%s" % (host, port)
|
|
||||||
fl = conn.makeGreenFile("rw")
|
|
||||||
fl.newlines = '\n'
|
|
||||||
ctx = SocketConsole(fl)
|
|
||||||
ctx.register()
|
|
||||||
try:
|
try:
|
||||||
console = InteractiveConsole(locals)
|
try:
|
||||||
console.interact()
|
while True:
|
||||||
|
(conn, (host, port)) = server.accept()
|
||||||
|
print "backdoor connected to %s:%s" % (host, port)
|
||||||
|
fl = conn.makeGreenFile("rw")
|
||||||
|
fl.newlines = '\n'
|
||||||
|
greenlet = SocketConsole(fl, (host, port), locals)
|
||||||
|
hub = api.get_hub()
|
||||||
|
hub.schedule_call_global(0, greenlet.switch)
|
||||||
|
hub.switch()
|
||||||
|
except socket.error, e:
|
||||||
|
# Broken pipe means it was shutdown
|
||||||
|
if e[0] != 32:
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
ctx.unregister()
|
server.close()
|
||||||
|
|
||||||
|
@@ -1,335 +0,0 @@
|
|||||||
"""\
|
|
||||||
@file greenlib.py
|
|
||||||
@author Bob Ippolito
|
|
||||||
|
|
||||||
Copyright (c) 2005-2006, Bob Ippolito
|
|
||||||
Copyright (c) 2007, Linden Research, Inc.
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
from eventlet.support import greenlets as greenlet
|
|
||||||
|
|
||||||
from eventlet import tls
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'switch', 'kill', 'tracked_greenlets',
|
|
||||||
'greenlet_id', 'greenlet_dict', 'GreenletContext',
|
|
||||||
'tracked_greenlet',
|
|
||||||
]
|
|
||||||
|
|
||||||
try:
|
|
||||||
reversed
|
|
||||||
except NameError:
|
|
||||||
def reversed(something):
|
|
||||||
for x in something[::-1]:
|
|
||||||
yield x
|
|
||||||
|
|
||||||
_threadlocal = tls.local()
|
|
||||||
|
|
||||||
def tracked_greenlet():
|
|
||||||
"""
|
|
||||||
Returns a greenlet that has a greenlet-local dictionary and can be
|
|
||||||
used with GreenletContext and enumerated with tracked_greenlets
|
|
||||||
"""
|
|
||||||
return greenlet.greenlet(greenlet_body)
|
|
||||||
|
|
||||||
class GreenletContextManager(object):
|
|
||||||
"""
|
|
||||||
Per-thread manager for GreenletContext. Created lazily on registration
|
|
||||||
"""
|
|
||||||
def __new__(cls, *args, **kw):
|
|
||||||
dct = greenlet_dict()
|
|
||||||
self = dct.get('greenlet_context', None)
|
|
||||||
if self is not None:
|
|
||||||
return self
|
|
||||||
self = super(GreenletContextManager, cls).__new__(cls, *args, **kw)
|
|
||||||
dct['greenlet_context'] = self
|
|
||||||
self.contexts = []
|
|
||||||
return self
|
|
||||||
|
|
||||||
def add_context(self, ctx):
|
|
||||||
fn = getattr(ctx, '_swap_in', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
self.contexts.append(ctx)
|
|
||||||
|
|
||||||
def remove_context(self, ctx):
|
|
||||||
try:
|
|
||||||
idx = self.contexts.index(ctx)
|
|
||||||
except ValueError:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
del self.contexts[idx]
|
|
||||||
fn = getattr(ctx, '_swap_out', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
fn = getattr(ctx, '_finalize', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def swap_in(self):
|
|
||||||
for ctx in self.contexts:
|
|
||||||
fn = getattr(ctx, '_swap_in', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def swap_out(self):
|
|
||||||
for ctx in reversed(self.contexts):
|
|
||||||
fn = getattr(ctx, '_swap_out', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
for ctx in reversed(self.contexts):
|
|
||||||
fn = getattr(ctx, '_swap_out', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
fn = getattr(ctx, '_finalize', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
del self.contexts[:]
|
|
||||||
try:
|
|
||||||
del greenlet_dict()['greenlet_context']
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class GreenletContext(object):
|
|
||||||
"""
|
|
||||||
A context manager to be triggered when a specific tracked greenlet is
|
|
||||||
swapped in, swapped out, or finalized.
|
|
||||||
|
|
||||||
To use, subclass and override the swap_in, swap_out, and/or finalize
|
|
||||||
methods, for example::
|
|
||||||
|
|
||||||
import greenlib
|
|
||||||
from greenlib import greenlet_id, tracked_greenlet, switch
|
|
||||||
|
|
||||||
class NotifyContext(greenlib.GreenletContext):
|
|
||||||
|
|
||||||
def swap_in(self):
|
|
||||||
print "swap_in"
|
|
||||||
|
|
||||||
def swap_out(self):
|
|
||||||
print "swap_out"
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
print "finalize"
|
|
||||||
|
|
||||||
def another_greenlet():
|
|
||||||
print "another_greenlet"
|
|
||||||
|
|
||||||
def notify_demo():
|
|
||||||
print "starting"
|
|
||||||
NotifyContext().register()
|
|
||||||
switch(tracked_greenlet(), (another_greenlet,))
|
|
||||||
print "finishing"
|
|
||||||
# we could have kept the NotifyContext object
|
|
||||||
# to unregister it here but finalization of all
|
|
||||||
# contexts is implicit when the greenlet returns
|
|
||||||
|
|
||||||
t = tracked_greenlet()
|
|
||||||
switch(t, (notify_demo,))
|
|
||||||
|
|
||||||
The output should be:
|
|
||||||
|
|
||||||
starting
|
|
||||||
swap_in
|
|
||||||
swap_out
|
|
||||||
another_greenlet
|
|
||||||
swap_in
|
|
||||||
finishing
|
|
||||||
swap_out
|
|
||||||
finalize
|
|
||||||
|
|
||||||
"""
|
|
||||||
_balance = 0
|
|
||||||
|
|
||||||
def _swap_in(self):
|
|
||||||
if self._balance != 0:
|
|
||||||
raise RuntimeError("balance != 0: %r" % (self._balance,))
|
|
||||||
self._balance = self._balance + 1
|
|
||||||
fn = getattr(self, 'swap_in', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def _swap_out(self):
|
|
||||||
if self._balance != 1:
|
|
||||||
raise RuntimeError("balance != 1: %r" % (self._balance,))
|
|
||||||
self._balance = self._balance - 1
|
|
||||||
fn = getattr(self, 'swap_out', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def register(self):
|
|
||||||
GreenletContextManager().add_context(self)
|
|
||||||
|
|
||||||
def unregister(self):
|
|
||||||
GreenletContextManager().remove_context(self)
|
|
||||||
|
|
||||||
def _finalize(self):
|
|
||||||
fn = getattr(self, 'finalize', None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
|
|
||||||
def kill(g):
|
|
||||||
"""
|
|
||||||
Kill the given greenlet if it is alive by sending it a GreenletExit.
|
|
||||||
|
|
||||||
Note that of any other exception is raised, it will pass-through!
|
|
||||||
"""
|
|
||||||
if not g:
|
|
||||||
return
|
|
||||||
kill_exc = greenlet.GreenletExit()
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
g.parent = greenlet.getcurrent()
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
switch(g, exc=kill_exc)
|
|
||||||
except SwitchingToDeadGreenlet:
|
|
||||||
pass
|
|
||||||
except greenlet.GreenletExit, e:
|
|
||||||
if e is not kill_exc:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def tracked_greenlets():
|
|
||||||
"""
|
|
||||||
Return a list of greenlets tracked in this thread. Tracked greenlets
|
|
||||||
use greenlet_body() to ensure that they have greenlet-local storage.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return _threadlocal.greenlets.keys()
|
|
||||||
except AttributeError:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def greenlet_id(self=None):
|
|
||||||
"""
|
|
||||||
Get the id of the current tracked greenlet, returns None if the
|
|
||||||
greenlet is not tracked.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
d = greenlet_dict(self)
|
|
||||||
except RuntimeError:
|
|
||||||
return None
|
|
||||||
return d['greenlet_id']
|
|
||||||
|
|
||||||
def greenlet_dict(self=None):
|
|
||||||
"""
|
|
||||||
Return the greenlet local storage for this greenlet. Raises RuntimeError
|
|
||||||
if this greenlet is not tracked.
|
|
||||||
"""
|
|
||||||
if self is None:
|
|
||||||
self = greenlet.getcurrent()
|
|
||||||
try:
|
|
||||||
return _threadlocal.greenlets[self]
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
raise RuntimeError("greenlet %r is not tracked" % (self,))
|
|
||||||
|
|
||||||
def _greenlet_context(dct=None):
|
|
||||||
if dct is None:
|
|
||||||
try:
|
|
||||||
dct = greenlet_dict()
|
|
||||||
except RuntimeError:
|
|
||||||
return None
|
|
||||||
return dct.get('greenlet_context', None)
|
|
||||||
|
|
||||||
def _greenlet_context_call(name, dct=None):
|
|
||||||
ctx = _greenlet_context(dct)
|
|
||||||
fn = getattr(ctx, name, None)
|
|
||||||
if fn is not None:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
def greenlet_body(value, exc):
|
|
||||||
"""
|
|
||||||
Track the current greenlet during the execution of the given callback,
|
|
||||||
normally you would use tracked_greenlet() to get a greenlet that uses this.
|
|
||||||
|
|
||||||
Greenlets using this body must be greenlib.switch()'ed to
|
|
||||||
"""
|
|
||||||
from eventlet import api
|
|
||||||
if exc is not None:
|
|
||||||
if isinstance(exc, tuple):
|
|
||||||
raise exc[0], exc[1], exc[2]
|
|
||||||
raise exc
|
|
||||||
cb, args = value[0], value[1:]
|
|
||||||
try:
|
|
||||||
greenlets = _threadlocal.greenlets
|
|
||||||
except AttributeError:
|
|
||||||
greenlets = _threadlocal.greenlets = {}
|
|
||||||
else:
|
|
||||||
if greenlet.getcurrent() in greenlets:
|
|
||||||
raise RuntimeError("greenlet_body can not be called recursively!")
|
|
||||||
try:
|
|
||||||
greenlet_id = _threadlocal.next_greenlet_id.next()
|
|
||||||
except AttributeError:
|
|
||||||
greenlet_id = 1
|
|
||||||
_threadlocal.next_greenlet_id = itertools.count(2)
|
|
||||||
cur = greenlet.getcurrent()
|
|
||||||
greenlets[cur] = {'greenlet_id': greenlet_id}
|
|
||||||
try:
|
|
||||||
return cb(*args)
|
|
||||||
finally:
|
|
||||||
_greenlet_context_call('finalize')
|
|
||||||
greenlets.pop(cur, None)
|
|
||||||
api.get_hub().cancel_timers(cur, quiet=True)
|
|
||||||
|
|
||||||
|
|
||||||
class SwitchingToDeadGreenlet(RuntimeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def switch(other=None, value=None, exc=None):
|
|
||||||
"""
|
|
||||||
Switch to another greenlet, passing value or exception
|
|
||||||
"""
|
|
||||||
self = greenlet.getcurrent()
|
|
||||||
if other is None:
|
|
||||||
other = self.parent
|
|
||||||
if other is None:
|
|
||||||
other = self
|
|
||||||
if not (other or hasattr(other, 'run')):
|
|
||||||
raise SwitchingToDeadGreenlet("Switching to dead greenlet %r %r %r" % (other, value, exc))
|
|
||||||
_greenlet_context_call('swap_out')
|
|
||||||
sys.exc_clear() # don't pass along exceptions to the other coroutine
|
|
||||||
try:
|
|
||||||
rval = other.switch(value, exc)
|
|
||||||
if not rval or not other:
|
|
||||||
res, exc = rval, None
|
|
||||||
else:
|
|
||||||
res, exc = rval
|
|
||||||
except:
|
|
||||||
res, exc = None, sys.exc_info()
|
|
||||||
_greenlet_context_call('swap_in')
|
|
||||||
# *NOTE: we don't restore exc_info, so don't switch inside an
|
|
||||||
# exception handler and then call sys.exc_info() or use bare
|
|
||||||
# raise. Instead, explicitly save off the exception before
|
|
||||||
# switching. We need an extension that allows us to restore the
|
|
||||||
# exception state at this point because vanilla Python doesn't
|
|
||||||
# allow that.
|
|
||||||
if isinstance(exc, tuple):
|
|
||||||
typ, exc, tb = exc
|
|
||||||
raise typ, exc, tb
|
|
||||||
elif exc is not None:
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
return res
|
|
@@ -120,6 +120,10 @@ class BaseHub(object):
|
|||||||
self.switch()
|
self.switch()
|
||||||
|
|
||||||
def switch(self):
|
def switch(self):
|
||||||
|
cur = greenlet.getcurrent()
|
||||||
|
switch_out = getattr(cur, 'switch_out', None)
|
||||||
|
if switch_out is not None:
|
||||||
|
switch_out()
|
||||||
if self.greenlet.dead:
|
if self.greenlet.dead:
|
||||||
self.greenlet = greenlet.greenlet(self.run)
|
self.greenlet = greenlet.greenlet(self.run)
|
||||||
try:
|
try:
|
||||||
|
Reference in New Issue
Block a user