]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
More improvements to testlib's mapper decorator
authorJason Kirtland <jek@discorporate.us>
Fri, 2 Nov 2007 00:27:40 +0000 (00:27 +0000)
committerJason Kirtland <jek@discorporate.us>
Fri, 2 Nov 2007 00:27:40 +0000 (00:27 +0000)
test/testlib/orm.py

index 3a10b0862925d29af716dd3804295abc2aa94f4e..f7d761b36bb75955bbb6bd6e42194f8d11b3eaf0 100644 (file)
@@ -1,32 +1,91 @@
 import testbase
 from testlib import config
-import inspect
+import inspect, re
 orm = None
 
 __all__ = 'mapper',
 
 
+_whitespace = re.compile(r'^(\s+)')
+
+def _find_pragma(lines, current):
+    m = _whitespace.match(lines[current])
+    basis = m and m.group() or ''
+
+    for line in reversed(lines[0:current]):
+        if 'testlib.pragma' in line:
+            return line
+        m = _whitespace.match(line)
+        indent = m and m.group() or ''
+
+        # simplistic detection:
+
+        # >> # testlib.pragma foo
+        # >> center_line()
+        if indent == basis:
+            break
+        # >> # testlib.pragma foo
+        # >> if fleem:
+        # >>     center_line()
+        if line.endswith(':'):
+            break
+    return None
+
 def _make_blocker(method_name, fallback):
+    """Creates tripwired variant of a method, raising when called.
+
+    To excempt an invocation from blockage, there are two options.
+
+    1) add a pragma in a comment::
+
+        # testlib.pragma exempt:methodname
+        offending_line()
+
+    2) add a magic cookie to the function's namespace::
+        __sa_baremethodname_exempt__ = True
+        ...
+        offending_line()
+        another_offending_lines()
+
+    The second is useful for testing and development.
+    """
+
+    if method_name.startswith('__') and method_name.endswith('__'):
+        frame_marker = '__sa_%s_exempt__' % method_name[2:-2]
+    else:
+        frame_marker = '__sa_%s_exempt__' % method_name
+    pragma_marker = 'exempt:' + method_name
+
     def method(self, *args, **kw):
         frame_r = None
         try:
-            frame_r = inspect.stack()[1]
-            module = frame_r[0].f_globals.get('__name__', '')
+            frame = inspect.stack()[1][0]
+            frame_r = inspect.getframeinfo(frame, 9)
+
+            module = frame.f_globals.get('__name__', '')
 
             type_ = type(self)
 
-            if not module.startswith('sqlalchemy'):
+            pragma = _find_pragma(*frame_r[3:5])
+
+            exempt = (
+                (not module.startswith('sqlalchemy')) or
+                (pragma and pragma_marker in pragma) or
+                (frame_marker in frame.f_locals))
+
+            if exempt:
                 supermeth = getattr(super(type_, self), method_name, None)
-                if supermeth is None or supermeth.im_func is method:
+                if (supermeth is None or
+                    getattr(supermeth, 'im_func', None) is method):
                     return fallback(self, *args, **kw)
                 else:
                     return supermeth(*args, **kw)
             else:
                 raise AssertionError(
                     "%s.%s called in %s, line %s in %s" % (
-                    type_.__name__, method_name, module, frame_r[2], frame_r[3]))
+                    type_.__name__, method_name, module, frame_r[1], frame_r[2]))
         finally:
-            del frame_r
+            del frame
     method.__name__ = method_name
     return method
 
@@ -36,8 +95,8 @@ def mapper(type_, *args, **kw):
         from sqlalchemy import orm
 
     forbidden = [
-        ('__hash__', 'unhashable', None),
-        ('__eq__', 'noncomparable', lambda s, x, y: x is y),
+        ('__hash__', 'unhashable', lambda s: id(s)),
+        ('__eq__', 'noncomparable', lambda s, o: s is o),
         ('__nonzero__', 'truthless', lambda s: 1), ]
 
     if type_.__bases__ == (object,):