Permalink
Browse files

Added signature checking.

Added warnings.
  • Loading branch information...
1 parent facb927 commit fa87e837be844dd9caf074eafe49c60ebe7823e3 @fschulze committed Dec 13, 2008
Showing with 48 additions and 3 deletions.
  1. +44 −3 mr/monkey/__init__.py
  2. +4 −0 mr/monkey/tests/test_monkey.py
View
@@ -1,30 +1,71 @@
from copy import copy
from sys import modules
+import warnings
+import inspect
+import sha
+
+
+class MonkeyException(Exception):
+ """An exception for errors on applying monkey patches."""
+
+
+class MonkeyWarning(RuntimeWarning):
+ """A warning for applied monkey patches."""
+
+class MonkeySignatureWarning(RuntimeWarning):
+ """A warning about missing signatures for monkey patches."""
def funcinfo(func):
""" returns the container (class or module) and name of a function """
if hasattr(func, 'im_class'):
container = func.im_class
dottedname = '.'.join((container.__module__,
- container.__name__, func.__name__))
+ container.__name__,
+ func.__name__))
return container, dottedname, func.__name__
else:
container = modules[func.__module__]
dottedname = '.'.join((func.__module__, func.__name__))
return container, dottedname, func.__name__
-def replace(target, replacement):
+def verify(target, signatures):
+ if signatures is None:
+ return
container, dottedname, funcname = funcinfo(target)
+ try:
+ code = inspect.getsource(target)
+ except IOError:
+ code = target.func_code
+ signature = sha.new(code).hexdigest()
+
+ if signature not in signatures:
+ warnings.warn(
+ "%s is not a valid signature for %s." % (signature, dottedname),
+ MonkeySignatureWarning,
+ stacklevel=3)
+
+
+def replace(target, replacement, signatures=()):
+ verify(target, signatures)
+ container, dottedname, funcname = funcinfo(target)
+ if getattr(target, '__mr_monkey_info__', False):
+ raise MonkeyException("Trying to apply monkey patch %s twice." %
+ dottedname)
+ else:
+ warnings.warn('Wrapping %s.' % dottedname,
+ MonkeyWarning,
+ stacklevel=2)
replacement.__doc__ = target.__doc__
info = replacement.__mr_monkey_info__ = dict(
original = target,
)
setattr(container, funcname, replacement)
-def wrap(target, handler):
+def wrap(target, handler, signatures=()):
+ verify(target, signatures)
def wrapper(*args, **kwargs):
return handler(target, *args, **kwargs)
replace(target, wrapper)
@@ -1,13 +1,17 @@
from unittest import TestCase, defaultTestLoader
+import warnings
from mr.monkey import replace, wrap
+from mr.monkey import MonkeyWarning, MonkeySignatureWarning
import mock
class MonkeyTests(TestCase):
def setUp(self):
reload(mock)
+ warnings.filterwarnings('ignore', category=MonkeyWarning)
+ warnings.filterwarnings('ignore', category=MonkeySignatureWarning)
def testReplace(self):
self.assertEqual(mock.foo(), (1, 2, 3))

0 comments on commit fa87e83

Please sign in to comment.