diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-10-29 14:38:34 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-10-29 14:38:34 -0400 |
commit | 382950b70150434f124b6dc27df2b360e7d0331e (patch) | |
tree | 4dcbbc1e5f4aa24ffc3a1c522c3bd77cbeeaf0cd | |
parent | 98c1dcc6bcade313a254fe11e8efa3c5b5ad959e (diff) | |
parent | e31211c578854d63128a30c036e40eee5c43edc7 (diff) | |
download | sqlalchemy-382950b70150434f124b6dc27df2b360e7d0331e.tar.gz |
Merge branch 'pr204'
-rw-r--r-- | doc/build/changelog/changelog_11.rst | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/schema.py | 5 | ||||
-rw-r--r-- | lib/sqlalchemy/util/__init__.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 22 | ||||
-rw-r--r-- | test/base/test_utils.py | 69 | ||||
-rw-r--r-- | test/sql/test_defaults.py | 1 |
6 files changed, 106 insertions, 3 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index dcd43f28d..688818a2a 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -22,6 +22,16 @@ :version: 1.1.0b1 .. change:: + :tags: enhancement, schema + :pullreq: github:204 + + The default generation functions passed to :class:`.Column` objects + are now run through "update_wrapper", or an equivalent function + if a callable non-function is passed, so that introspection tools + preserve the name and docstring of the wrapped function. Pull + request courtesy hsum. + + .. change:: :tags: change, sql, mysql :tickets: 3216 diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index e20545962..25eb68f6f 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -1987,13 +1987,14 @@ class ColumnDefault(DefaultGenerator): try: argspec = util.get_callable_argspec(fn, no_self=True) except TypeError: - return lambda ctx: fn() + return util.wrap_callable(lambda ctx: fn(), fn) defaulted = argspec[3] is not None and len(argspec[3]) or 0 positionals = len(argspec[0]) - defaulted if positionals == 0: - return lambda ctx: fn() + return util.wrap_callable(lambda ctx: fn(), fn) + elif positionals == 1: return fn else: diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index ed968f168..36a81dbce 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -36,7 +36,7 @@ from .langhelpers import iterate_attributes, class_hierarchy, \ generic_repr, counter, PluginLoader, hybridproperty, hybridmethod, \ safe_reraise,\ get_callable_argspec, only_once, attrsetter, ellipses_string, \ - warn_limited, map_bits, MemoizedSlots, EnsureKWArgType + warn_limited, map_bits, MemoizedSlots, EnsureKWArgType, wrap_callable from .deprecations import warn_deprecated, warn_pending_deprecation, \ deprecated, pending_deprecation, inject_docstring_text diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 743afccfd..e9d4e09bc 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -1377,3 +1377,25 @@ class EnsureKWArgType(type): return fn(*arg) return update_wrapper(wrap, fn) + +def wrap_callable(wrapper, fn): + """Augment functools.update_wrapper() to work with objects with + a ``__call__()`` method. + + :param fn: + object with __call__ method + + """ + if hasattr(fn, '__name__'): + return update_wrapper(wrapper, fn) + else: + _f = wrapper + _f.__name__ = fn.__class__.__name__ + _f.__module__ = fn.__module__ + + if hasattr(fn.__call__, '__doc__') and fn.__call__.__doc__: + _f.__doc__ = fn.__call__.__doc__ + elif fn.__doc__: + _f.__doc__ = fn.__doc__ + + return _f diff --git a/test/base/test_utils.py b/test/base/test_utils.py index 8074de53e..c1027ec8e 100644 --- a/test/base/test_utils.py +++ b/test/base/test_utils.py @@ -313,6 +313,75 @@ class MemoizedAttrTest(fixtures.TestBase): eq_(canary.mock_calls, [mock.call.attr(), mock.call.method()]) +class WrapCallableTest(fixtures.TestBase): + def test_wrapping_update_wrapper_fn(self): + def my_fancy_default(): + """run the fancy default""" + return 10 + + c = util.wrap_callable(lambda: my_fancy_default, my_fancy_default) + + eq_(c.__name__, "my_fancy_default") + eq_(c.__doc__, "run the fancy default") + + def test_wrapping_update_wrapper_fn_nodocstring(self): + def my_fancy_default(): + return 10 + + c = util.wrap_callable(lambda: my_fancy_default, my_fancy_default) + eq_(c.__name__, "my_fancy_default") + eq_(c.__doc__, None) + + def test_wrapping_update_wrapper_cls(self): + class MyFancyDefault(object): + """a fancy default""" + + def __call__(self): + """run the fancy default""" + return 10 + + def_ = MyFancyDefault() + c = util.wrap_callable(lambda: def_(), def_) + + eq_(c.__name__, "MyFancyDefault") + eq_(c.__doc__, "run the fancy default") + + def test_wrapping_update_wrapper_cls_noclsdocstring(self): + class MyFancyDefault(object): + + def __call__(self): + """run the fancy default""" + return 10 + + def_ = MyFancyDefault() + c = util.wrap_callable(lambda: def_(), def_) + eq_(c.__name__, "MyFancyDefault") + eq_(c.__doc__, "run the fancy default") + + def test_wrapping_update_wrapper_cls_nomethdocstring(self): + class MyFancyDefault(object): + """a fancy default""" + + def __call__(self): + return 10 + + def_ = MyFancyDefault() + c = util.wrap_callable(lambda: def_(), def_) + eq_(c.__name__, "MyFancyDefault") + eq_(c.__doc__, "a fancy default") + + def test_wrapping_update_wrapper_cls_noclsdocstring_nomethdocstring(self): + class MyFancyDefault(object): + + def __call__(self): + return 10 + + def_ = MyFancyDefault() + c = util.wrap_callable(lambda: def_(), def_) + eq_(c.__name__, "MyFancyDefault") + eq_(c.__doc__, None) + + class ToListTest(fixtures.TestBase): def test_from_string(self): eq_( diff --git a/test/sql/test_defaults.py b/test/sql/test_defaults.py index fc0b2bb80..e21b21ab2 100644 --- a/test/sql/test_defaults.py +++ b/test/sql/test_defaults.py @@ -301,6 +301,7 @@ class DefaultTest(fixtures.TestBase): c = sa.ColumnDefault(fn) c.arg("context") + @testing.fails_on('firebird', 'Data type unknown') def test_standalone(self): c = testing.db.engine.contextual_connect() |