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:
Ryan Williams
2010-04-09 15:00:46 -07:00
parent b76a8c76c0
commit 6ed6d01905
4 changed files with 84 additions and 11 deletions

View File

@@ -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.

View File

@@ -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)]

View 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))

View File

@@ -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, "\