Updated green psycopg support, added a test for it (which hasn't been run because I don't have that particular branch installed yet.
This commit is contained in:
@@ -45,7 +45,7 @@ Monkeypatching the Standard Library
|
|||||||
The other way of greening an application is simply to monkeypatch the standard
|
The other way of greening an application is simply to monkeypatch the standard
|
||||||
library. This has the disadvantage of appearing quite magical, but the advantage of avoiding the late-binding problem.
|
library. This has the disadvantage of appearing quite magical, but the advantage of avoiding the late-binding problem.
|
||||||
|
|
||||||
.. function:: eventlet.patcher.monkey_patch(os=None, select=None, socket=None, thread=None, time=None)
|
.. function:: eventlet.patcher.monkey_patch(os=None, select=None, socket=None, thread=None, time=None, psycopg=None)
|
||||||
|
|
||||||
This function monkeypatches the key system modules by replacing their key elements with green equivalents. If no arguments are specified, everything is patched::
|
This function monkeypatches the key system modules by replacing their key elements with green equivalents. If no arguments are specified, everything is patched::
|
||||||
|
|
||||||
@@ -60,3 +60,11 @@ library. This has the disadvantage of appearing quite magical, but the advantag
|
|||||||
eventlet.monkey_patch(socket=True, select=True)
|
eventlet.monkey_patch(socket=True, select=True)
|
||||||
|
|
||||||
It is important to call :func:`~eventlet.patcher.monkey_patch` as early in the lifetime of the application as possible. Try to do it as one of the first lines in the main module. The reason for this is that sometimes there is a class that inherits from a class that needs to be greened -- e.g. a class that inherits from socket.socket -- and inheritance is done at import time, so therefore the monkeypatching should happen before the derived class is defined. It's safe to call monkey_patch multiple times.
|
It is important to call :func:`~eventlet.patcher.monkey_patch` as early in the lifetime of the application as possible. Try to do it as one of the first lines in the main module. The reason for this is that sometimes there is a class that inherits from a class that needs to be greened -- e.g. a class that inherits from socket.socket -- and inheritance is done at import time, so therefore the monkeypatching should happen before the derived class is defined. It's safe to call monkey_patch multiple times.
|
||||||
|
|
||||||
|
The psycopg monkeypatching relies on Daniele Varrazzo's green psycopg2 branch; see `the announcement <https://lists.secondlife.com/pipermail/eventletdev/2010-April/000800.html>`_ for more information.
|
||||||
|
|
||||||
|
.. function:: eventlet.patcher.is_monkey_patched(module)
|
||||||
|
|
||||||
|
Returns whether or not the specified module is currently monkeypatched. *module* can either be the module itself or the module's name.
|
||||||
|
|
||||||
|
Based entirely off the name of the module, so if you import a module some other way than with the import keyword (including :func:`~eventlet.patcher.import_patched`), is_monkey_patched might not be correct about that particular module.
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['inject', 'import_patched', 'monkey_patch']
|
__all__ = ['inject', 'import_patched', 'monkey_patch', 'is_monkey_patched']
|
||||||
|
|
||||||
__exclude = set(('__builtins__', '__file__', '__name__'))
|
__exclude = set(('__builtins__', '__file__', '__name__'))
|
||||||
|
|
||||||
@@ -171,9 +171,14 @@ def monkey_patch(**on):
|
|||||||
try:
|
try:
|
||||||
from eventlet.support import psycopg2_patcher
|
from eventlet.support import psycopg2_patcher
|
||||||
psycopg2_patcher.make_psycopg_green()
|
psycopg2_patcher.make_psycopg_green()
|
||||||
|
already_patched['psycopg'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
# note that if we get an importerror from trying to
|
||||||
|
# monkeypatch psycopg, we will continually retry it
|
||||||
|
# whenever monkey_patch is called; this should not be a
|
||||||
|
# performance problem but it allows is_monkey_patched to
|
||||||
|
# tell us whether or not we succeeded
|
||||||
pass
|
pass
|
||||||
already_patched['psycopg'] = True
|
|
||||||
|
|
||||||
for name, mod in modules_to_patch:
|
for name, mod in modules_to_patch:
|
||||||
orig_mod = sys.modules.get(name)
|
orig_mod = sys.modules.get(name)
|
||||||
@@ -182,6 +187,17 @@ def monkey_patch(**on):
|
|||||||
if patched_attr is not None:
|
if patched_attr is not None:
|
||||||
setattr(orig_mod, attr_name, patched_attr)
|
setattr(orig_mod, attr_name, patched_attr)
|
||||||
|
|
||||||
|
def is_monkey_patched(module):
|
||||||
|
"""Returns True if the given module is monkeypatched currently, False if
|
||||||
|
not. *module* can be either the module itself or its name.
|
||||||
|
|
||||||
|
Based entirely off the name of the module, so if you import a
|
||||||
|
module some other way than with the import keyword (including
|
||||||
|
import_patched), this might not be correct about that particular
|
||||||
|
module."""
|
||||||
|
return module in already_patched or \
|
||||||
|
getattr(module, '__name__', None) in already_patched
|
||||||
|
|
||||||
def _green_os_modules():
|
def _green_os_modules():
|
||||||
from eventlet.green import os
|
from eventlet.green import os
|
||||||
return [('os', os)]
|
return [('os', os)]
|
||||||
|
41
tests/patcher_psycopg_test.py
Normal file
41
tests/patcher_psycopg_test.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from tests import patcher_test
|
||||||
|
|
||||||
|
psycopg_test_file = """
|
||||||
|
import sys
|
||||||
|
import eventlet
|
||||||
|
eventlet.monkey_patch()
|
||||||
|
from eventlet import patcher
|
||||||
|
if not patcher.is_monkey_patched('psycopg'):
|
||||||
|
print "Psycopg not monkeypatched"
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
count = [0]
|
||||||
|
def tick(totalseconds, persecond):
|
||||||
|
for i in xrange(totalseconds*persecond):
|
||||||
|
count[0] += 1
|
||||||
|
eventlet.sleep(1.0/persecond)
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
def fetch(num, secs):
|
||||||
|
conn = psycopg2.connect()
|
||||||
|
cur = conn.cursor()
|
||||||
|
for i in range(num):
|
||||||
|
cur.execute("select pg_sleep(%s)", (secs,))
|
||||||
|
|
||||||
|
f = eventlet.spawn(fetch, 2, 1)
|
||||||
|
t = eventlet.spawn(tick, 2, 100)
|
||||||
|
f.wait()
|
||||||
|
assert count[0] > 150
|
||||||
|
print "done"
|
||||||
|
"""
|
||||||
|
|
||||||
|
class PatchingPsycopg(patcher_test.Patcher):
|
||||||
|
def test_psycopg_pached(self):
|
||||||
|
self.write_to_tempfile("psycopg_patcher", psycopg_test_file)
|
||||||
|
output, lines = self.launch_subprocess('psycopg_patcher.py')
|
||||||
|
if lines[0].startswith('Psycopg not monkeypatched'):
|
||||||
|
print "Can't test psycopg2 patching; it's not installed."
|
||||||
|
return
|
||||||
|
# if there's anything wrong with the test program it'll have a stack trace
|
||||||
|
self.assert_(lines[0].startswith('done'), repr(output))
|
||||||
|
|
@@ -154,32 +154,40 @@ patcher.monkey_patch(finagle=True)
|
|||||||
self.assert_('finagle' in lines[-2], repr(output))
|
self.assert_('finagle' in lines[-2], repr(output))
|
||||||
|
|
||||||
|
|
||||||
def assert_boolean_logic(self, call, expected):
|
def assert_boolean_logic(self, call, expected, not_expected=''):
|
||||||
|
expected_list = ", ".join(['"%s"' % x for x in expected.split(',') if len(x)])
|
||||||
|
not_expected_list = ", ".join(['"%s"' % x for x in not_expected.split(',') if len(x)])
|
||||||
new_mod = """
|
new_mod = """
|
||||||
from eventlet import patcher
|
from eventlet import patcher
|
||||||
%s
|
%s
|
||||||
|
for mod in [%s]:
|
||||||
|
assert patcher.is_monkey_patched(mod), mod
|
||||||
|
for mod in [%s]:
|
||||||
|
assert not patcher.is_monkey_patched(mod), mod
|
||||||
print "already_patched", ",".join(sorted(patcher.already_patched.keys()))
|
print "already_patched", ",".join(sorted(patcher.already_patched.keys()))
|
||||||
""" % call
|
""" % (call, expected_list, not_expected_list)
|
||||||
self.write_to_tempfile("newmod", new_mod)
|
self.write_to_tempfile("newmod", new_mod)
|
||||||
output, lines = self.launch_subprocess('newmod.py')
|
output, lines = self.launch_subprocess('newmod.py')
|
||||||
ap = 'already_patched'
|
ap = 'already_patched'
|
||||||
self.assert_(lines[0].startswith(ap), repr(output))
|
self.assert_(lines[0].startswith(ap), repr(output))
|
||||||
patched_modules = lines[0][len(ap):].strip()
|
patched_modules = lines[0][len(ap):].strip()
|
||||||
|
# psycopg might or might not be patched based on installed modules
|
||||||
|
patched_modules.replace("psycopg,", "")
|
||||||
self.assertEqual(patched_modules, expected,
|
self.assertEqual(patched_modules, expected,
|
||||||
"Logic:%s\nExpected: %s != %s" %(call, expected,
|
"Logic:%s\nExpected: %s != %s" %(call, expected,
|
||||||
patched_modules))
|
patched_modules))
|
||||||
|
|
||||||
def test_boolean(self):
|
def test_boolean(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch()",
|
self.assert_boolean_logic("patcher.monkey_patch()",
|
||||||
'os,psycopg,select,socket,thread,time')
|
'os,select,socket,thread,time')
|
||||||
|
|
||||||
def test_boolean_all(self):
|
def test_boolean_all(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(all=True)",
|
self.assert_boolean_logic("patcher.monkey_patch(all=True)",
|
||||||
'os,psycopg,select,socket,thread,time')
|
'os,select,socket,thread,time')
|
||||||
|
|
||||||
def test_boolean_all_single(self):
|
def test_boolean_all_single(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(all=True, socket=True)",
|
self.assert_boolean_logic("patcher.monkey_patch(all=True, socket=True)",
|
||||||
'os,psycopg,select,socket,thread,time')
|
'os,select,socket,thread,time')
|
||||||
|
|
||||||
def test_boolean_all_negative(self):
|
def test_boolean_all_negative(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(all=False, "\
|
self.assert_boolean_logic("patcher.monkey_patch(all=False, "\
|
||||||
@@ -197,12 +205,12 @@ print "already_patched", ",".join(sorted(patcher.already_patched.keys()))
|
|||||||
|
|
||||||
def test_boolean_negative(self):
|
def test_boolean_negative(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(socket=False)",
|
self.assert_boolean_logic("patcher.monkey_patch(socket=False)",
|
||||||
'os,psycopg,select,thread,time')
|
'os,select,thread,time')
|
||||||
|
|
||||||
def test_boolean_negative2(self):
|
def test_boolean_negative2(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(socket=False,"\
|
self.assert_boolean_logic("patcher.monkey_patch(socket=False,"\
|
||||||
"time=False)",
|
"time=False)",
|
||||||
'os,psycopg,select,thread')
|
'os,select,thread')
|
||||||
|
|
||||||
def test_conflicting_specifications(self):
|
def test_conflicting_specifications(self):
|
||||||
self.assert_boolean_logic("patcher.monkey_patch(socket=False, "\
|
self.assert_boolean_logic("patcher.monkey_patch(socket=False, "\
|
||||||
|
Reference in New Issue
Block a user