From: Mike Bayer Date: Sun, 5 Dec 2010 17:51:24 +0000 (-0500) Subject: - move topological, queue into util X-Git-Tag: rel_0_7b1~207 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f9029597caf3d9d9e64e5a326508babfca60ebb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - move topological, queue into util - move function_named into test.lib.util - use @decorator for all decorators in test/ --- diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index db20acb90b..7ee633f3e5 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -12,7 +12,8 @@ organizes them in order of dependency, and executes. """ -from sqlalchemy import util, topological +from sqlalchemy import util +from sqlalchemy.util import topological from sqlalchemy.orm import attributes, interfaces from sqlalchemy.orm import util as mapperutil from sqlalchemy.orm.util import _state_mapper diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py index 58b3a39c1b..4ae6ec022f 100644 --- a/lib/sqlalchemy/pool.py +++ b/lib/sqlalchemy/pool.py @@ -20,7 +20,7 @@ SQLAlchemy connection pool. import weakref, time, threading from sqlalchemy import exc, log, event, events, interfaces, util -from sqlalchemy import queue as sqla_queue +from sqlalchemy.util import queue as sqla_queue from sqlalchemy.util import threading, pickle, memoized_property proxies = {} diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 638549e12c..757de37c40 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -1,4 +1,5 @@ -from sqlalchemy import exc, schema, topological, util, sql, types as sqltypes +from sqlalchemy import exc, schema, util, sql, types as sqltypes +from sqlalchemy.util import topological from sqlalchemy.sql import expression, operators, visitors from itertools import chain diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 4aaf41205c..aa150874fc 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -20,7 +20,7 @@ from langhelpers import iterate_attributes, class_hierarchy, \ portable_instancemethod, unbound_method_to_callable, \ getargspec_init, format_argspec_init, format_argspec_plus, \ get_func_kwargs, get_cls_kwargs, decorator, as_interface, \ - function_named, memoized_property, memoized_instancemethod, \ + memoized_property, memoized_instancemethod, \ reset_memoized, group_expirable_memoized_property, importlater, \ monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\ duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\ @@ -29,4 +29,3 @@ from langhelpers import iterate_attributes, class_hierarchy, \ from deprecations import warn_deprecated, warn_pending_deprecation, \ deprecated, pending_deprecation - diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index 5f00102c11..090f45212e 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -1,4 +1,5 @@ """Handle Python version/platform incompatibilities.""" + import sys # Py2K diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 68d9709f82..aac22ba309 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -371,20 +371,6 @@ def as_interface(obj, cls=None, methods=None, required=None): raise TypeError("dictionary does not contain required keys %s" % ', '.join(required - found)) -def function_named(fn, name): - """Return a function with a given __name__. - - Will assign to __name__ and return the original function if possible on - the Python implementation, otherwise a new function will be constructed. - - """ - try: - fn.__name__ = name - except TypeError: - fn = types.FunctionType(fn.func_code, fn.func_globals, name, - fn.func_defaults, fn.func_closure) - return fn - class memoized_property(object): """A read-only @property that is only evaluated once.""" diff --git a/lib/sqlalchemy/queue.py b/lib/sqlalchemy/util/queue.py similarity index 100% rename from lib/sqlalchemy/queue.py rename to lib/sqlalchemy/util/queue.py diff --git a/lib/sqlalchemy/topological.py b/lib/sqlalchemy/util/topological.py similarity index 100% rename from lib/sqlalchemy/topological.py rename to lib/sqlalchemy/util/topological.py diff --git a/test/base/test_dependency.py b/test/base/test_dependency.py index 6d4662be56..1853b4d52c 100644 --- a/test/base/test_dependency.py +++ b/test/base/test_dependency.py @@ -1,10 +1,9 @@ -import sqlalchemy.topological as topological +from sqlalchemy.util import topological from test.lib import TestBase from test.lib.testing import assert_raises, eq_ from test.lib.util import conforms_partial_ordering from sqlalchemy import exc, util - class DependencySortTest(TestBase): def assert_sort(self, tuples, allitems=None): diff --git a/test/lib/engines.py b/test/lib/engines.py index 0f3ccf2888..a57795f157 100644 --- a/test/lib/engines.py +++ b/test/lib/engines.py @@ -1,7 +1,8 @@ import sys, types, weakref from collections import deque from test.bootstrap import config -from sqlalchemy.util import function_named, callable +from test.lib.util import decorator +from sqlalchemy.util import callable from sqlalchemy import event import re import warnings @@ -44,42 +45,38 @@ testing_reaper = ConnectionKiller() def drop_all_tables(metadata): testing_reaper.close_all() metadata.drop_all() - -def assert_conns_closed(fn): - def decorated(*args, **kw): - try: - fn(*args, **kw) - finally: - testing_reaper.assert_all_closed() - return function_named(decorated, fn.__name__) - -def rollback_open_connections(fn): + +@decorator +def assert_conns_closed(fn, *args, **kw): + try: + fn(*args, **kw) + finally: + testing_reaper.assert_all_closed() + +@decorator +def rollback_open_connections(fn, *args, **kw): """Decorator that rolls back all open connections after fn execution.""" - def decorated(*args, **kw): - try: - fn(*args, **kw) - finally: - testing_reaper.rollback_all() - return function_named(decorated, fn.__name__) + try: + fn(*args, **kw) + finally: + testing_reaper.rollback_all() -def close_first(fn): +@decorator +def close_first(fn, *args, **kw): """Decorator that closes all connections before fn execution.""" - def decorated(*args, **kw): - testing_reaper.close_all() - fn(*args, **kw) - return function_named(decorated, fn.__name__) + + testing_reaper.close_all() + fn(*args, **kw) -def close_open_connections(fn): +@decorator +def close_open_connections(fn, *args, **kw): """Decorator that closes all connections after fn execution.""" - - def decorated(*args, **kw): - try: - fn(*args, **kw) - finally: - testing_reaper.close_all() - return function_named(decorated, fn.__name__) + try: + fn(*args, **kw) + finally: + testing_reaper.close_all() def all_dialects(exclude=None): import sqlalchemy.databases as d diff --git a/test/lib/profiling.py b/test/lib/profiling.py index f6c21bde85..eeb3901cb3 100644 --- a/test/lib/profiling.py +++ b/test/lib/profiling.py @@ -6,7 +6,7 @@ in a more fine-grained way than nose's profiling plugin. """ import os, sys -from test.lib.util import function_named, gc_collect +from test.lib.util import gc_collect, decorator from nose import SkipTest __all__ = 'profiled', 'function_call_count', 'conditional_call_count' @@ -38,34 +38,33 @@ def profiled(target=None, **target_opts): all_targets.add(target) filename = "%s.prof" % target - - def decorator(fn): - def profiled(*args, **kw): - if (target not in profile_config['targets'] and - not target_opts.get('always', None)): - return fn(*args, **kw) - - elapsed, load_stats, result = _profile( - filename, fn, *args, **kw) - - report = target_opts.get('report', profile_config['report']) - if report: - sort_ = target_opts.get('sort', profile_config['sort']) - limit = target_opts.get('limit', profile_config['limit']) - print "Profile report for target '%s' (%s)" % ( - target, filename) - - stats = load_stats() - stats.sort_stats(*sort_) - if limit: - stats.print_stats(limit) - else: - stats.print_stats() - #stats.print_callers() - os.unlink(filename) - return result - return function_named(profiled, fn.__name__) - return decorator + + @decorator + def decorate(fn, *args, **kw): + if (target not in profile_config['targets'] and + not target_opts.get('always', None)): + return fn(*args, **kw) + + elapsed, load_stats, result = _profile( + filename, fn, *args, **kw) + + report = target_opts.get('report', profile_config['report']) + if report: + sort_ = target_opts.get('sort', profile_config['sort']) + limit = target_opts.get('limit', profile_config['limit']) + print "Profile report for target '%s' (%s)" % ( + target, filename) + + stats = load_stats() + stats.sort_stats(*sort_) + if limit: + stats.print_stats(limit) + else: + stats.print_stats() + #stats.print_callers() + os.unlink(filename) + return result + return decorate def function_call_count(count=None, versions={}, variance=0.05): """Assert a target for a test case's function call count. @@ -109,35 +108,34 @@ def function_call_count(count=None, versions={}, variance=0.05): if count is None: return lambda fn: fn + + @decorator + def decorate(fn, *args, **kw): + try: + filename = "%s.prof" % fn.__name__ - def decorator(fn): - def counted(*args, **kw): - try: - filename = "%s.prof" % fn.__name__ + elapsed, stat_loader, result = _profile( + filename, fn, *args, **kw) - elapsed, stat_loader, result = _profile( - filename, fn, *args, **kw) + stats = stat_loader() + calls = stats.total_calls - stats = stat_loader() - calls = stats.total_calls + stats.sort_stats('calls', 'cumulative') + stats.print_stats() + #stats.print_callers() + deviance = int(count * variance) + if (calls < (count - deviance) or + calls > (count + deviance)): + raise AssertionError( + "Function call count %s not within %s%% " + "of expected %s. (Python version %s)" % ( + calls, (variance * 100), count, py_version)) - stats.sort_stats('calls', 'cumulative') - stats.print_stats() - #stats.print_callers() - deviance = int(count * variance) - if (calls < (count - deviance) or - calls > (count + deviance)): - raise AssertionError( - "Function call count %s not within %s%% " - "of expected %s. (Python version %s)" % ( - calls, (variance * 100), count, py_version)) - - return result - finally: - if os.path.exists(filename): - os.unlink(filename) - return function_named(counted, fn.__name__) - return decorator + return result + finally: + if os.path.exists(filename): + os.unlink(filename) + return decorate def conditional_call_count(discriminator, categories): """Apply a function call count conditionally at runtime. @@ -153,17 +151,15 @@ def conditional_call_count(discriminator, categories): have a function count penalty not seen in the full suite, due to lazy initialization in the DB-API, SA, etc. """ - - def decorator(fn): - def at_runtime(*args, **kw): - criteria = categories.get(discriminator(), None) - if criteria is None: - return fn(*args, **kw) - - rewrapped = function_call_count(*criteria)(fn) - return rewrapped(*args, **kw) - return function_named(at_runtime, fn.__name__) - return decorator + @decorator + def decorate(fn, *args, **kw): + criteria = categories.get(discriminator(), None) + if criteria is None: + return fn(*args, **kw) + + rewrapped = function_call_count(*criteria)(fn) + return rewrapped(*args, **kw) + return decorate def _profile(filename, fn, *args, **kw): diff --git a/test/lib/testing.py b/test/lib/testing.py index 8f4838d0a2..6a2c62959e 100644 --- a/test/lib/testing.py +++ b/test/lib/testing.py @@ -10,7 +10,7 @@ from cStringIO import StringIO from test.bootstrap import config from test.lib import assertsql, util as testutil -from sqlalchemy.util import function_named, py3k +from sqlalchemy.util import py3k, decorator from engines import drop_all_tables from sqlalchemy import exc as sa_exc, util, types as sqltypes, schema, \ @@ -47,44 +47,39 @@ def fails_if(callable_, reason=None): docstring = getattr(callable_, '__doc__', None) or callable_.__name__ description = docstring.split('\n')[0] - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if not callable_(): - return fn(*args, **kw) + @decorator + def decorate(fn, *args, **kw): + if not callable_(): + return fn(*args, **kw) + else: + try: + fn(*args, **kw) + except Exception, ex: + print ("'%s' failed as expected (condition: %s): %s " % ( + fn.__name__, description, str(ex))) + return True else: - try: - fn(*args, **kw) - except Exception, ex: - print ("'%s' failed as expected (condition: %s): %s " % ( - fn_name, description, str(ex))) - return True - else: - raise AssertionError( - "Unexpected success for '%s' (condition: %s)" % - (fn_name, description)) - return function_named(maybe, fn_name) + raise AssertionError( + "Unexpected success for '%s' (condition: %s)" % + (fn.__name__, description)) return decorate - -def future(fn): +@decorator +def future(fn, *args, **kw): """Mark a test as expected to unconditionally fail. Takes no arguments, omit parens when using as a decorator. """ - fn_name = fn.__name__ - def decorated(*args, **kw): - try: - fn(*args, **kw) - except Exception, ex: - print ("Future test '%s' failed as expected: %s " % ( - fn_name, str(ex))) - return True - else: - raise AssertionError( - "Unexpected success for future test '%s'" % fn_name) - return function_named(decorated, fn_name) + try: + fn(*args, **kw) + except Exception, ex: + print ("Future test '%s' failed as expected: %s " % ( + fn.__name__, str(ex))) + return True + else: + raise AssertionError( + "Unexpected success for future test '%s'" % fn.__name__) def db_spec(*dbs): dialects = set([x for x in dbs if '+' not in x]) @@ -110,25 +105,23 @@ def fails_on(dbs, reason): """ spec = db_spec(dbs) - - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if not spec(config.db): - return fn(*args, **kw) + + @decorator + def decorate(fn, *args, **kw): + if not spec(config.db): + return fn(*args, **kw) + else: + try: + fn(*args, **kw) + except Exception, ex: + print ("'%s' failed as expected on DB implementation " + "'%s+%s': %s" % ( + fn.__name__, config.db.name, config.db.driver, reason)) + return True else: - try: - fn(*args, **kw) - except Exception, ex: - print ("'%s' failed as expected on DB implementation " - "'%s+%s': %s" % ( - fn_name, config.db.name, config.db.driver, reason)) - return True - else: - raise AssertionError( - "Unexpected success for '%s' on DB implementation '%s+%s'" % - (fn_name, config.db.name, config.db.driver)) - return function_named(maybe, fn_name) + raise AssertionError( + "Unexpected success for '%s' on DB implementation '%s+%s'" % + (fn.__name__, config.db.name, config.db.driver)) return decorate def fails_on_everything_except(*dbs): @@ -140,24 +133,22 @@ def fails_on_everything_except(*dbs): spec = db_spec(*dbs) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if spec(config.db): - return fn(*args, **kw) + @decorator + def decorate(fn, *args, **kw): + if spec(config.db): + return fn(*args, **kw) + else: + try: + fn(*args, **kw) + except Exception, ex: + print ("'%s' failed as expected on DB implementation " + "'%s+%s': %s" % ( + fn.__name__, config.db.name, config.db.driver, str(ex))) + return True else: - try: - fn(*args, **kw) - except Exception, ex: - print ("'%s' failed as expected on DB implementation " - "'%s+%s': %s" % ( - fn_name, config.db.name, config.db.driver, str(ex))) - return True - else: - raise AssertionError( - "Unexpected success for '%s' on DB implementation '%s+%s'" % - (fn_name, config.db.name, config.db.driver)) - return function_named(maybe, fn_name) + raise AssertionError( + "Unexpected success for '%s' on DB implementation '%s+%s'" % + (fn.__name__, config.db.name, config.db.driver)) return decorate def crashes(db, reason): @@ -169,19 +160,17 @@ def crashes(db, reason): """ carp = _should_carp_about_exclusion(reason) spec = db_spec(db) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if spec(config.db): - msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( - fn_name, config.db.name, config.db.driver, reason) - print msg - if carp: - print >> sys.stderr, msg - return True - else: - return fn(*args, **kw) - return function_named(maybe, fn_name) + @decorator + def decorate(fn, *args, **kw): + if spec(config.db): + msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( + fn.__name__, config.db.name, config.db.driver, reason) + print msg + if carp: + print >> sys.stderr, msg + return True + else: + return fn(*args, **kw) return decorate def _block_unconditionally(db, reason): @@ -194,37 +183,33 @@ def _block_unconditionally(db, reason): """ carp = _should_carp_about_exclusion(reason) spec = db_spec(db) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if spec(config.db): - msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( - fn_name, config.db.name, config.db.driver, reason) - print msg - if carp: - print >> sys.stderr, msg - return True - else: - return fn(*args, **kw) - return function_named(maybe, fn_name) + @decorator + def decorate(fn, *args, **kw): + if spec(config.db): + msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( + fn.__name__, config.db.name, config.db.driver, reason) + print msg + if carp: + print >> sys.stderr, msg + return True + else: + return fn(*args, **kw) return decorate def only_on(dbs, reason): carp = _should_carp_about_exclusion(reason) spec = db_spec(*util.to_list(dbs)) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if spec(config.db): - return fn(*args, **kw) - else: - msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( - fn_name, config.db.name, config.db.driver, reason) - print msg - if carp: - print >> sys.stderr, msg - return True - return function_named(maybe, fn_name) + @decorator + def decorate(fn, *args, **kw): + if spec(config.db): + return fn(*args, **kw) + else: + msg = "'%s' unsupported on DB implementation '%s+%s': %s" % ( + fn.__name__, config.db.name, config.db.driver, reason) + print msg + if carp: + print >> sys.stderr, msg + return True return decorate def exclude(db, op, spec, reason): @@ -241,19 +226,17 @@ def exclude(db, op, spec, reason): """ carp = _should_carp_about_exclusion(reason) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if _is_excluded(db, op, spec): - msg = "'%s' unsupported on DB %s version '%s': %s" % ( - fn_name, config.db.name, _server_version(), reason) - print msg - if carp: - print >> sys.stderr, msg - return True - else: - return fn(*args, **kw) - return function_named(maybe, fn_name) + @decorator + def decorate(fn, *args, **kw): + if _is_excluded(db, op, spec): + msg = "'%s' unsupported on DB %s version '%s': %s" % ( + fn.__name__, config.db.name, _server_version(), reason) + print msg + if carp: + print >> sys.stderr, msg + return True + else: + return fn(*args, **kw) return decorate def _should_carp_about_exclusion(reason): @@ -312,19 +295,17 @@ def skip_if(predicate, reason=None): reason = reason or predicate.__name__ carp = _should_carp_about_exclusion(reason) - def decorate(fn): - fn_name = fn.__name__ - def maybe(*args, **kw): - if predicate(): - msg = "'%s' skipped on DB %s version '%s': %s" % ( - fn_name, config.db.name, _server_version(), reason) - print msg - if carp: - print >> sys.stderr, msg - return True - else: - return fn(*args, **kw) - return function_named(maybe, fn_name) + @decorator + def decorate(fn, *args, **kw): + if predicate(): + msg = "'%s' skipped on DB %s version '%s': %s" % ( + fn.__name__, config.db.name, _server_version(), reason) + print msg + if carp: + print >> sys.stderr, msg + return True + else: + return fn(*args, **kw) return decorate def emits_warning(*messages): @@ -339,26 +320,26 @@ def emits_warning(*messages): # and may work on non-CPython if they keep to the spirit of # warnings.showwarning's docstring. # - update: jython looks ok, it uses cpython's module - def decorate(fn): - def safe(*args, **kw): - # todo: should probably be strict about this, too - filters = [dict(action='ignore', - category=sa_exc.SAPendingDeprecationWarning)] - if not messages: - filters.append(dict(action='ignore', - category=sa_exc.SAWarning)) - else: - filters.extend(dict(action='ignore', - message=message, - category=sa_exc.SAWarning) - for message in messages) - for f in filters: - warnings.filterwarnings(**f) - try: - return fn(*args, **kw) - finally: - resetwarnings() - return function_named(safe, fn.__name__) + + @decorator + def decorate(fn, *args, **kw): + # todo: should probably be strict about this, too + filters = [dict(action='ignore', + category=sa_exc.SAPendingDeprecationWarning)] + if not messages: + filters.append(dict(action='ignore', + category=sa_exc.SAWarning)) + else: + filters.extend(dict(action='ignore', + message=message, + category=sa_exc.SAWarning) + for message in messages) + for f in filters: + warnings.filterwarnings(**f) + try: + return fn(*args, **kw) + finally: + resetwarnings() return decorate def emits_warning_on(db, *warnings): @@ -370,21 +351,20 @@ def emits_warning_on(db, *warnings): """ spec = db_spec(db) - def decorate(fn): - def maybe(*args, **kw): - if isinstance(db, basestring): - if not spec(config.db): - return fn(*args, **kw) - else: - wrapped = emits_warning(*warnings)(fn) - return wrapped(*args, **kw) + @decorator + def decorate(fn, *args, **kw): + if isinstance(db, basestring): + if not spec(config.db): + return fn(*args, **kw) else: - if not _is_excluded(*db): - return fn(*args, **kw) - else: - wrapped = emits_warning(*warnings)(fn) - return wrapped(*args, **kw) - return function_named(maybe, fn.__name__) + wrapped = emits_warning(*warnings)(fn) + return wrapped(*args, **kw) + else: + if not _is_excluded(*db): + return fn(*args, **kw) + else: + wrapped = emits_warning(*warnings)(fn) + return wrapped(*args, **kw) return decorate def assert_warnings(fn, warnings): @@ -411,32 +391,30 @@ def uses_deprecated(*messages): verbiage emitted by the sqlalchemy.util.deprecated decorator. """ - - def decorate(fn): - def safe(*args, **kw): - # todo: should probably be strict about this, too - filters = [dict(action='ignore', - category=sa_exc.SAPendingDeprecationWarning)] - if not messages: - filters.append(dict(action='ignore', - category=sa_exc.SADeprecationWarning)) - else: - filters.extend( - [dict(action='ignore', - message=message, - category=sa_exc.SADeprecationWarning) - for message in - [ (m.startswith('//') and - ('Call to deprecated function ' + m[2:]) or m) - for m in messages] ]) - - for f in filters: - warnings.filterwarnings(**f) - try: - return fn(*args, **kw) - finally: - resetwarnings() - return function_named(safe, fn.__name__) + @decorator + def decorate(fn, *args, **kw): + # todo: should probably be strict about this, too + filters = [dict(action='ignore', + category=sa_exc.SAPendingDeprecationWarning)] + if not messages: + filters.append(dict(action='ignore', + category=sa_exc.SADeprecationWarning)) + else: + filters.extend( + [dict(action='ignore', + message=message, + category=sa_exc.SADeprecationWarning) + for message in + [ (m.startswith('//') and + ('Call to deprecated function ' + m[2:]) or m) + for m in messages] ]) + + for f in filters: + warnings.filterwarnings(**f) + try: + return fn(*args, **kw) + finally: + resetwarnings() return decorate def testing_warn(msg, stacklevel=3): @@ -569,24 +547,24 @@ def fixture(table, columns, *rows): for column_values in rows]) table.append_ddl_listener('after-create', onload) -def provide_metadata(fn): +@decorator +def provide_metadata(fn, *args, **kw): """Provides a bound MetaData object for a single test, drops it afterwards.""" - def maybe(*args, **kw): - metadata = schema.MetaData(db) - context = dict(fn.func_globals) - context['metadata'] = metadata - # jython bug #1034 - rebound = types.FunctionType( - fn.func_code, context, fn.func_name, fn.func_defaults, - fn.func_closure) - try: - return rebound(*args, **kw) - finally: - metadata.drop_all() - return function_named(maybe, fn.__name__) - -def resolve_artifact_names(fn): + metadata = schema.MetaData(db) + context = dict(fn.func_globals) + context['metadata'] = metadata + # jython bug #1034 + rebound = types.FunctionType( + fn.func_code, context, fn.func_name, fn.func_defaults, + fn.func_closure) + try: + return rebound(*args, **kw) + finally: + metadata.drop_all() + +@decorator +def resolve_artifact_names(fn, *args, **kw): """Decorator, augment function globals with tables and classes. Swaps out the function's globals at execution time. The 'global' statement @@ -601,17 +579,15 @@ def resolve_artifact_names(fn): # Also: it's lame that CPython accepts a dict-subclass for globals, but # only calls dict methods. That would allow 'global' to pass through to # the func_globals. - def resolved(*args, **kwargs): - self = args[0] - context = dict(fn.func_globals) - for source in self._artifact_registries: - context.update(getattr(self, source)) - # jython bug #1034 - rebound = types.FunctionType( - fn.func_code, context, fn.func_name, fn.func_defaults, - fn.func_closure) - return rebound(*args, **kwargs) - return function_named(resolved, fn.func_name) + self = args[0] + context = dict(fn.func_globals) + for source in self._artifact_registries: + context.update(getattr(self, source)) + # jython bug #1034 + rebound = types.FunctionType( + fn.func_code, context, fn.func_name, fn.func_defaults, + fn.func_closure) + return rebound(*args, **kw) class adict(dict): """Dict keys available as attributes. Shadows.""" diff --git a/test/lib/util.py b/test/lib/util.py index e5277f0768..b8cc05a818 100644 --- a/test/lib/util.py +++ b/test/lib/util.py @@ -1,4 +1,4 @@ -from sqlalchemy.util import jython, function_named, defaultdict +from sqlalchemy.util import jython, defaultdict, decorator import gc import time @@ -105,3 +105,22 @@ def all_partial_orderings(tuples, elements): return iter(_all_orderings(elements)) + +def function_named(fn, name): + """Return a function with a given __name__. + + Will assign to __name__ and return the original function if possible on + the Python implementation, otherwise a new function will be constructed. + + This function should be phased out as much as possible + in favor of @decorator. Tests that "generate" many named tests + should be modernized. + + """ + try: + fn.__name__ = name + except TypeError: + fn = types.FunctionType(fn.func_code, fn.func_globals, name, + fn.func_defaults, fn.func_closure) + return fn + diff --git a/test/orm/_base.py b/test/orm/_base.py index 4ccc101574..345009d400 100644 --- a/test/orm/_base.py +++ b/test/orm/_base.py @@ -6,7 +6,6 @@ import sqlalchemy.exceptions as sa_exc from test.lib import config, testing from test.lib.testing import resolve_artifact_names, adict from test.lib.engines import drop_all_tables -from sqlalchemy.util import function_named from test.lib.entities import BasicEntity, ComparableEntity Entity = BasicEntity diff --git a/test/orm/inheritance/test_abc_polymorphic.py b/test/orm/inheritance/test_abc_polymorphic.py index fb229003b7..58569ab9c0 100644 --- a/test/orm/inheritance/test_abc_polymorphic.py +++ b/test/orm/inheritance/test_abc_polymorphic.py @@ -2,7 +2,7 @@ from sqlalchemy import * from sqlalchemy import util from sqlalchemy.orm import * -from sqlalchemy.util import function_named +from test.lib.util import function_named from test.orm import _base, _fixtures from test.lib.schema import Table, Column diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 544aa1abea..d6b9d89f4f 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -7,7 +7,6 @@ from sqlalchemy.orm import exc as orm_exc, attributes from test.lib.assertsql import AllOf, CompiledSQL from test.lib import testing, engines -from sqlalchemy.util import function_named from test.orm import _base, _fixtures from test.lib.schema import Table, Column diff --git a/test/orm/inheritance/test_magazine.py b/test/orm/inheritance/test_magazine.py index 307c54a9cc..e38a1ec33a 100644 --- a/test/orm/inheritance/test_magazine.py +++ b/test/orm/inheritance/test_magazine.py @@ -2,7 +2,7 @@ from sqlalchemy import * from sqlalchemy.orm import * from test.lib import testing -from sqlalchemy.util import function_named +from test.lib.util import function_named from test.orm import _base from test.lib.schema import Table, Column diff --git a/test/orm/inheritance/test_polymorph.py b/test/orm/inheritance/test_polymorph.py index 8539baa765..7e31c476ba 100644 --- a/test/orm/inheritance/test_polymorph.py +++ b/test/orm/inheritance/test_polymorph.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import * from sqlalchemy.orm import exc as orm_exc from sqlalchemy import exc as sa_exc from test.lib import Column, testing -from sqlalchemy.util import function_named +from test.lib.util import function_named from test.orm import _fixtures, _base class Person(_fixtures.Base): diff --git a/test/orm/inheritance/test_polymorph2.py b/test/orm/inheritance/test_polymorph2.py index 030b931a56..2a9341692d 100644 --- a/test/orm/inheritance/test_polymorph2.py +++ b/test/orm/inheritance/test_polymorph2.py @@ -8,7 +8,7 @@ from sqlalchemy import util from sqlalchemy.orm import * from test.lib import TestBase, AssertsExecutionResults, testing -from sqlalchemy.util import function_named +from test.lib.util import function_named from test.orm import _base, _fixtures from test.lib.testing import eq_ from test.lib.schema import Table, Column diff --git a/test/orm/test_attributes.py b/test/orm/test_attributes.py index 395911b644..c0481f96b4 100644 --- a/test/orm/test_attributes.py +++ b/test/orm/test_attributes.py @@ -7,8 +7,8 @@ from test.lib import * from test.lib.testing import eq_, ne_, assert_raises from test.orm import _base from test.lib.util import gc_collect, all_partial_orderings -from sqlalchemy.util import cmp, jython -from sqlalchemy import event, topological +from sqlalchemy.util import cmp, jython, topological +from sqlalchemy import event # global for pickling tests MyTest = None diff --git a/test/orm/test_dynamic.py b/test/orm/test_dynamic.py index c89503278d..0958d60dd3 100644 --- a/test/orm/test_dynamic.py +++ b/test/orm/test_dynamic.py @@ -7,7 +7,6 @@ from test.lib.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, Query, attributes from sqlalchemy.orm.dynamic import AppenderMixin from test.lib.testing import eq_, AssertsCompiledSQL, assert_raises_message -from sqlalchemy.util import function_named from test.orm import _base, _fixtures diff --git a/test/orm/test_instrumentation.py b/test/orm/test_instrumentation.py index 4b37013502..f7ba025e06 100644 --- a/test/orm/test_instrumentation.py +++ b/test/orm/test_instrumentation.py @@ -7,29 +7,26 @@ from sqlalchemy.orm import mapper, relationship, create_session, \ from test.lib.schema import Table from test.lib.schema import Column from test.lib.testing import eq_, ne_ -from sqlalchemy.util import function_named +from test.lib.util import decorator from test.orm import _base +@decorator +def modifies_instrumentation_finders(fn, *args, **kw): + pristine = instrumentation.instrumentation_finders[:] + try: + fn(*args, **kw) + finally: + del instrumentation.instrumentation_finders[:] + instrumentation.instrumentation_finders.extend(pristine) -def modifies_instrumentation_finders(fn): - def decorated(*args, **kw): - pristine = instrumentation.instrumentation_finders[:] +def with_lookup_strategy(strategy): + @decorator + def decorate(fn, *args, **kw): try: - fn(*args, **kw) + instrumentation._install_lookup_strategy(strategy) + return fn(*args, **kw) finally: - del instrumentation.instrumentation_finders[:] - instrumentation.instrumentation_finders.extend(pristine) - return function_named(decorated, fn.func_name) - -def with_lookup_strategy(strategy): - def decorate(fn): - def wrapped(*args, **kw): - try: - instrumentation._install_lookup_strategy(strategy) - return fn(*args, **kw) - finally: - instrumentation._install_lookup_strategy(sa.util.symbol('native')) - return function_named(wrapped, fn.func_name) + instrumentation._install_lookup_strategy(sa.util.symbol('native')) return decorate