From: Jason Kirtland Date: Wed, 31 Oct 2007 23:49:09 +0000 (+0000) Subject: Added command line options to add tripwires for __hash__, __eq__ and __nonzero__... X-Git-Tag: rel_0_4_1~83 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=ad9f8b8158ebd5509eb98c45179f1187f52c96d0;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Added command line options to add tripwires for __hash__, __eq__ and __nonzero__ on mapped classes. --- diff --git a/test/testlib/__init__.py b/test/testlib/__init__.py index d30e4fc287..29b258c9f8 100644 --- a/test/testlib/__init__.py +++ b/test/testlib/__init__.py @@ -5,6 +5,7 @@ Load after sqlalchemy imports to use instrumented stand-ins like Table. import testlib.config from testlib.schema import Table, Column +from testlib.orm import mapper import testlib.testing as testing from testlib.testing import PersistTest, AssertMixin, ORMTest, SQLCompileTest import testlib.profiling as profiling @@ -12,6 +13,7 @@ import testlib.engines as engines __all__ = ('testing', + 'mapper', 'Table', 'Column', 'PersistTest', 'AssertMixin', 'ORMTest', 'SQLCompileTest', 'profiling', 'engines') diff --git a/test/testlib/config.py b/test/testlib/config.py index a4306e9b4b..36db872eb4 100644 --- a/test/testlib/config.py +++ b/test/testlib/config.py @@ -74,7 +74,7 @@ def _start_coverage(option, opt_str, value, parser): atexit.register(_stop) coverage.erase() coverage.start() - + def _list_dbs(*args): print "Available --db options (use --dburi to override)" for macro in sorted(file_config.options('db')): @@ -114,6 +114,12 @@ opt("--enginestrategy", action="callback", type="string", opt("--reversetop", action="store_true", dest="reversetop", default=False, help="Reverse the collection ordering for topological sorts (helps " "reveal dependency issues)") +opt("--unhashable", action="store_true", dest="unhashable", default=False, + help="Disallow SQLAlchemy from performing a hash() on mapped test objects.") +opt("--noncomparable", action="store_true", dest="noncomparable", default=False, + help="Disallow SQLAlchemy from performing == on mapped test objects.") +opt("--truthless", action="store_true", dest="truthless", default=False, + help="Disallow SQLAlchemy from truth-evaluating mapped test objects.") opt("--serverside", action="callback", callback=_server_side_cursors, help="Turn on server side cursors for PG") opt("--mysql-engine", action="store", dest="mysql_engine", default=None, diff --git a/test/testlib/orm.py b/test/testlib/orm.py new file mode 100644 index 0000000000..3a10b08629 --- /dev/null +++ b/test/testlib/orm.py @@ -0,0 +1,49 @@ +import testbase +from testlib import config +import inspect +orm = None + +__all__ = 'mapper', + + +def _make_blocker(method_name, fallback): + def method(self, *args, **kw): + frame_r = None + try: + frame_r = inspect.stack()[1] + module = frame_r[0].f_globals.get('__name__', '') + + type_ = type(self) + + if not module.startswith('sqlalchemy'): + supermeth = getattr(super(type_, self), method_name, None) + if supermeth is None or supermeth.im_func 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])) + finally: + del frame_r + method.__name__ = method_name + return method + +def mapper(type_, *args, **kw): + global orm + if orm is None: + from sqlalchemy import orm + + forbidden = [ + ('__hash__', 'unhashable', None), + ('__eq__', 'noncomparable', lambda s, x, y: x is y), + ('__nonzero__', 'truthless', lambda s: 1), ] + + if type_.__bases__ == (object,): + for method_name, option, fallback in forbidden: + if (getattr(config.options, option, False) and + method_name not in type_.__dict__): + setattr(type_, method_name, _make_blocker(method_name, fallback)) + + return orm.mapper(type_, *args, **kw)