summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-07-08 13:39:56 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-07-08 13:39:56 -0400
commit02a81707dc8b7c4d69551cad195fb16ca6955df1 (patch)
treefeda05ad7e0ce7bef057b9ee9d55d3273d8d008a /lib/sqlalchemy/util
parentdb68ecff12f790fd129f03b8676b317fa17e5f28 (diff)
downloadsqlalchemy-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__.py7
-rw-r--r--lib/sqlalchemy/util/compat.py10
-rw-r--r--lib/sqlalchemy/util/deprecations.py40
-rw-r--r--lib/sqlalchemy/util/langhelpers.py16
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.