diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-07-26 14:21:58 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-07-26 14:21:58 -0400 |
| commit | 4505425a38b079a8e2a59fdbe31bc033de25e871 (patch) | |
| tree | d0dd695935aee0e26f8cd9ca36bc39f6df7e66ef /lib/sqlalchemy/event/legacy.py | |
| parent | 550141b14c8e165218cd32c27d91541eeee86d2a (diff) | |
| download | sqlalchemy-4505425a38b079a8e2a59fdbe31bc033de25e871.tar.gz | |
- Removal of event listeners is now implemented. The feature is
provided via the :func:`.event.remove` function.
[ticket:2268]
- reorganization of event.py module into a package; with the addition of the
docstring work as well as the new registry for removal, there's a lot more code now.
the package separates concerns and provides a top-level doc for each subsection
of functionality
- the remove feature works by providing the EventKey object which associates
the user-provided arguments to listen() with a global, weak-referencing registry.
This registry stores a collection of _ListenerCollection and _DispatchDescriptor
objects associated with each set of arguments, as well as the wrapped function
which was applied to that collection. The EventKey can then be recreated for
a removal, all the _ListenerCollection and _DispatchDescriptor objects are located,
and the correct wrapped function is removed from each one.
Diffstat (limited to 'lib/sqlalchemy/event/legacy.py')
| -rw-r--r-- | lib/sqlalchemy/event/legacy.py | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/lib/sqlalchemy/event/legacy.py b/lib/sqlalchemy/event/legacy.py new file mode 100644 index 000000000..cd84031c4 --- /dev/null +++ b/lib/sqlalchemy/event/legacy.py @@ -0,0 +1,150 @@ +"""Routines to handle adaption of legacy call signatures, +generation of deprecation notes and docstrings. + +""" + +from .. import util + +def _legacy_signature(since, argnames, converter=None): + def leg(fn): + if not hasattr(fn, '_legacy_signatures'): + fn._legacy_signatures = [] + fn._legacy_signatures.append((since, argnames, converter)) + return fn + return leg + +def _wrap_fn_for_legacy(dispatch_descriptor, fn, argspec): + for since, argnames, conv in dispatch_descriptor.legacy_signatures: + if argnames[-1] == "**kw": + has_kw = True + argnames = argnames[0:-1] + else: + has_kw = False + + if len(argnames) == len(argspec.args) \ + and has_kw is bool(argspec.keywords): + + if conv: + assert not has_kw + def wrap_leg(*args): + return fn(*conv(*args)) + else: + def wrap_leg(*args, **kw): + argdict = dict(zip(dispatch_descriptor.arg_names, args)) + args = [argdict[name] for name in argnames] + if has_kw: + return fn(*args, **kw) + else: + return fn(*args) + return wrap_leg + else: + return fn + +def _indent(text, indent): + return "\n".join( + indent + line + for line in text.split("\n") + ) + +def _standard_listen_example(dispatch_descriptor, sample_target, fn): + example_kw_arg = _indent( + "\n".join( + "%(arg)s = kw['%(arg)s']" % {"arg": arg} + for arg in dispatch_descriptor.arg_names[0:2] + ), + " ") + if dispatch_descriptor.legacy_signatures: + current_since = max(since for since, args, conv + in dispatch_descriptor.legacy_signatures) + else: + current_since = None + text = ( + "from sqlalchemy import event\n\n" + "# standard decorator style%(current_since)s\n" + "@event.listens_for(%(sample_target)s, '%(event_name)s')\n" + "def receive_%(event_name)s(%(named_event_arguments)s%(has_kw_arguments)s):\n" + " \"listen for the '%(event_name)s' event\"\n" + "\n # ... (event handling logic) ...\n" + ) + + if len(dispatch_descriptor.arg_names) > 2: + text += ( + + "\n# named argument style (new in 0.9)\n" + "@event.listens_for(%(sample_target)s, '%(event_name)s', named=True)\n" + "def receive_%(event_name)s(**kw):\n" + " \"listen for the '%(event_name)s' event\"\n" + "%(example_kw_arg)s\n" + "\n # ... (event handling logic) ...\n" + ) + + text %= { + "current_since": " (arguments as of %s)" % + current_since if current_since else "", + "event_name": fn.__name__, + "has_kw_arguments": " **kw" if dispatch_descriptor.has_kw else "", + "named_event_arguments": ", ".join(dispatch_descriptor.arg_names), + "example_kw_arg": example_kw_arg, + "sample_target": sample_target + } + return text + +def _legacy_listen_examples(dispatch_descriptor, sample_target, fn): + text = "" + for since, args, conv in dispatch_descriptor.legacy_signatures: + text += ( + "\n# legacy calling style (pre-%(since)s)\n" + "@event.listens_for(%(sample_target)s, '%(event_name)s')\n" + "def receive_%(event_name)s(%(named_event_arguments)s%(has_kw_arguments)s):\n" + " \"listen for the '%(event_name)s' event\"\n" + "\n # ... (event handling logic) ...\n" % { + "since": since, + "event_name": fn.__name__, + "has_kw_arguments": " **kw" if dispatch_descriptor.has_kw else "", + "named_event_arguments": ", ".join(args), + "sample_target": sample_target + } + ) + return text + +def _version_signature_changes(dispatch_descriptor): + since, args, conv = dispatch_descriptor.legacy_signatures[0] + return ( + "\n.. versionchanged:: %(since)s\n" + " The ``%(event_name)s`` event now accepts the \n" + " arguments ``%(named_event_arguments)s%(has_kw_arguments)s``.\n" + " Listener functions which accept the previous argument \n" + " signature(s) listed above will be automatically \n" + " adapted to the new signature." % { + "since": since, + "event_name": dispatch_descriptor.__name__, + "named_event_arguments": ", ".join(dispatch_descriptor.arg_names), + "has_kw_arguments": ", **kw" if dispatch_descriptor.has_kw else "" + } + ) + +def _augment_fn_docs(dispatch_descriptor, parent_dispatch_cls, fn): + header = ".. container:: event_signatures\n\n"\ + " Example argument forms::\n"\ + "\n" + + sample_target = getattr(parent_dispatch_cls, "_target_class_doc", "obj") + text = ( + header + + _indent( + _standard_listen_example( + dispatch_descriptor, sample_target, fn), + " " * 8) + ) + if dispatch_descriptor.legacy_signatures: + text += _indent( + _legacy_listen_examples( + dispatch_descriptor, sample_target, fn), + " " * 8) + + text += _version_signature_changes(dispatch_descriptor) + + return util.inject_docstring_text(fn.__doc__, + text, + 1 + ) |
