diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-07-08 13:39:56 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-07-08 13:39:56 -0400 |
| commit | 02a81707dc8b7c4d69551cad195fb16ca6955df1 (patch) | |
| tree | feda05ad7e0ce7bef057b9ee9d55d3273d8d008a /lib/sqlalchemy/util | |
| parent | db68ecff12f790fd129f03b8676b317fa17e5f28 (diff) | |
| download | sqlalchemy-02a81707dc8b7c4d69551cad195fb16ca6955df1.tar.gz | |
- create a new system where we can decorate an event method
with @_legacy_signature, will inspect incoming listener functions
to see if they match an older signature, will wrap into a newer sig
- add an event listen argument named=True, will send all args as
kw args so that event listeners can be written with **kw, any combination
of names
- add a doc system to events that writes out the various calling styles
for a given event, produces deprecation messages automatically.
a little concerned that it's a bit verbose but will look at it up
on RTD for awhile to get a feel.
- change the calling signature for bulk update/delete events - we have
the BulkUD object right there, and there's at least six or seven things
people might want to see, so just send the whole BulkUD in
[ticket:2775]
Diffstat (limited to 'lib/sqlalchemy/util')
| -rw-r--r-- | lib/sqlalchemy/util/__init__.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/compat.py | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/deprecations.py | 40 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 16 |
4 files changed, 57 insertions, 16 deletions
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 739caefe0..5d9c4d49e 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -10,7 +10,7 @@ from .compat import callable, cmp, reduce, \ raise_from_cause, text_type, string_types, int_types, binary_type, \ quote_plus, with_metaclass, print_, itertools_filterfalse, u, ue, b,\ unquote_plus, b64decode, b64encode, byte_buffer, itertools_filter,\ - StringIO + StringIO, inspect_getargspec from ._collections import KeyedTuple, ImmutableContainer, immutabledict, \ Properties, OrderedProperties, ImmutableProperties, OrderedDict, \ @@ -30,10 +30,11 @@ from .langhelpers import iterate_attributes, class_hierarchy, \ duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\ classproperty, set_creation_order, warn_exception, warn, NoneType,\ constructor_copy, methods_equivalent, chop_traceback, asint,\ - generic_repr, counter, PluginLoader, hybridmethod, safe_reraise + generic_repr, counter, PluginLoader, hybridmethod, safe_reraise,\ + get_callable_argspec from .deprecations import warn_deprecated, warn_pending_deprecation, \ - deprecated, pending_deprecation + deprecated, pending_deprecation, inject_docstring_text # things that used to be not always available, # but are now as of current support Python versions diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index d866534ab..a89762b4e 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -22,7 +22,7 @@ pypy = hasattr(sys, 'pypy_version_info') win32 = sys.platform.startswith('win') cpython = not pypy and not jython # TODO: something better for this ? - +import collections next = next if py3k: @@ -33,6 +33,9 @@ else: except ImportError: import pickle +ArgSpec = collections.namedtuple("ArgSpec", + ["args", "varargs", "keywords", "defaults"]) + if py3k: import builtins @@ -43,6 +46,10 @@ if py3k: from io import BytesIO as byte_buffer + def inspect_getargspec(func): + return ArgSpec( + *inspect_getfullargspec(func)[0:4] + ) string_types = str, binary_type = bytes @@ -87,6 +94,7 @@ if py3k: else: from inspect import getargspec as inspect_getfullargspec + inspect_getargspec = inspect_getfullargspec from urllib import quote_plus, unquote_plus from urlparse import parse_qsl import ConfigParser as configparser diff --git a/lib/sqlalchemy/util/deprecations.py b/lib/sqlalchemy/util/deprecations.py index e0dc168db..c315d2da6 100644 --- a/lib/sqlalchemy/util/deprecations.py +++ b/lib/sqlalchemy/util/deprecations.py @@ -107,17 +107,37 @@ def _decorate_with_warning(func, wtype, message, docstring_header=None): doc = func.__doc__ is not None and func.__doc__ or '' if docstring_header is not None: docstring_header %= dict(func=func.__name__) - docs = doc and doc.expandtabs().split('\n') or [] - indent = '' - for line in docs[1:]: - text = line.lstrip() - if text: - indent = line[0:len(line) - len(text)] - break - point = min(len(docs), 1) - docs.insert(point, '\n' + indent + docstring_header.rstrip()) - doc = '\n'.join(docs) + + doc = inject_docstring_text(doc, docstring_header, 1) decorated = warned(func) decorated.__doc__ = doc return decorated + +import textwrap + +def _dedent_docstring(text): + split_text = text.split("\n", 1) + if len(split_text) == 1: + return text + else: + firstline, remaining = split_text + if not firstline.startswith(" "): + return firstline + "\n" + textwrap.dedent(remaining) + else: + return textwrap.dedent(text) + +def inject_docstring_text(doctext, injecttext, pos): + doctext = _dedent_docstring(doctext or "") + lines = doctext.split('\n') + injectlines = textwrap.dedent(injecttext).split("\n") + if injectlines[0]: + injectlines.insert(0, "") + + blanks = [num for num, line in enumerate(lines) if not line.strip()] + blanks.insert(0, 0) + + inject_pos = blanks[min(pos, len(blanks) - 1)] + + lines = lines[0:inject_pos] + injectlines + lines[inject_pos:] + return "\n".join(lines) diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 1ff868e01..c91178a75 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -211,8 +211,20 @@ def get_func_kwargs(func): """ - return inspect.getargspec(func)[0] - + return compat.inspect_getargspec(func)[0] + +def get_callable_argspec(fn, no_self=False): + if isinstance(fn, types.FunctionType): + return compat.inspect_getargspec(fn) + elif isinstance(fn, types.MethodType) and no_self: + spec = compat.inspect_getargspec(fn.__func__) + return compat.ArgSpec(spec.args[1:], spec.varargs, spec.keywords, spec.defaults) + elif hasattr(fn, '__func__'): + return compat.inspect_getargspec(fn.__func__) + elif hasattr(fn, '__call__'): + return get_callable_argspec(fn.__call__) + else: + raise ValueError("Can't inspect function: %s" % fn) def format_argspec_plus(fn, grouped=True): """Returns a dictionary of formatted, introspected function arguments. |
