legacy.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # event/legacy.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """Routines to handle adaption of legacy call signatures,
  8. generation of deprecation notes and docstrings.
  9. """
  10. from .. import util
  11. def _legacy_signature(since, argnames, converter=None):
  12. def leg(fn):
  13. if not hasattr(fn, '_legacy_signatures'):
  14. fn._legacy_signatures = []
  15. fn._legacy_signatures.append((since, argnames, converter))
  16. return fn
  17. return leg
  18. def _wrap_fn_for_legacy(dispatch_collection, fn, argspec):
  19. for since, argnames, conv in dispatch_collection.legacy_signatures:
  20. if argnames[-1] == "**kw":
  21. has_kw = True
  22. argnames = argnames[0:-1]
  23. else:
  24. has_kw = False
  25. if len(argnames) == len(argspec.args) \
  26. and has_kw is bool(argspec.keywords):
  27. if conv:
  28. assert not has_kw
  29. def wrap_leg(*args):
  30. return fn(*conv(*args))
  31. else:
  32. def wrap_leg(*args, **kw):
  33. argdict = dict(zip(dispatch_collection.arg_names, args))
  34. args = [argdict[name] for name in argnames]
  35. if has_kw:
  36. return fn(*args, **kw)
  37. else:
  38. return fn(*args)
  39. return wrap_leg
  40. else:
  41. return fn
  42. def _indent(text, indent):
  43. return "\n".join(
  44. indent + line
  45. for line in text.split("\n")
  46. )
  47. def _standard_listen_example(dispatch_collection, sample_target, fn):
  48. example_kw_arg = _indent(
  49. "\n".join(
  50. "%(arg)s = kw['%(arg)s']" % {"arg": arg}
  51. for arg in dispatch_collection.arg_names[0:2]
  52. ),
  53. " ")
  54. if dispatch_collection.legacy_signatures:
  55. current_since = max(since for since, args, conv
  56. in dispatch_collection.legacy_signatures)
  57. else:
  58. current_since = None
  59. text = (
  60. "from sqlalchemy import event\n\n"
  61. "# standard decorator style%(current_since)s\n"
  62. "@event.listens_for(%(sample_target)s, '%(event_name)s')\n"
  63. "def receive_%(event_name)s("
  64. "%(named_event_arguments)s%(has_kw_arguments)s):\n"
  65. " \"listen for the '%(event_name)s' event\"\n"
  66. "\n # ... (event handling logic) ...\n"
  67. )
  68. if len(dispatch_collection.arg_names) > 3:
  69. text += (
  70. "\n# named argument style (new in 0.9)\n"
  71. "@event.listens_for("
  72. "%(sample_target)s, '%(event_name)s', named=True)\n"
  73. "def receive_%(event_name)s(**kw):\n"
  74. " \"listen for the '%(event_name)s' event\"\n"
  75. "%(example_kw_arg)s\n"
  76. "\n # ... (event handling logic) ...\n"
  77. )
  78. text %= {
  79. "current_since": " (arguments as of %s)" %
  80. current_since if current_since else "",
  81. "event_name": fn.__name__,
  82. "has_kw_arguments": ", **kw" if dispatch_collection.has_kw else "",
  83. "named_event_arguments": ", ".join(dispatch_collection.arg_names),
  84. "example_kw_arg": example_kw_arg,
  85. "sample_target": sample_target
  86. }
  87. return text
  88. def _legacy_listen_examples(dispatch_collection, sample_target, fn):
  89. text = ""
  90. for since, args, conv in dispatch_collection.legacy_signatures:
  91. text += (
  92. "\n# legacy calling style (pre-%(since)s)\n"
  93. "@event.listens_for(%(sample_target)s, '%(event_name)s')\n"
  94. "def receive_%(event_name)s("
  95. "%(named_event_arguments)s%(has_kw_arguments)s):\n"
  96. " \"listen for the '%(event_name)s' event\"\n"
  97. "\n # ... (event handling logic) ...\n" % {
  98. "since": since,
  99. "event_name": fn.__name__,
  100. "has_kw_arguments": " **kw"
  101. if dispatch_collection.has_kw else "",
  102. "named_event_arguments": ", ".join(args),
  103. "sample_target": sample_target
  104. }
  105. )
  106. return text
  107. def _version_signature_changes(dispatch_collection):
  108. since, args, conv = dispatch_collection.legacy_signatures[0]
  109. return (
  110. "\n.. versionchanged:: %(since)s\n"
  111. " The ``%(event_name)s`` event now accepts the \n"
  112. " arguments ``%(named_event_arguments)s%(has_kw_arguments)s``.\n"
  113. " Listener functions which accept the previous argument \n"
  114. " signature(s) listed above will be automatically \n"
  115. " adapted to the new signature." % {
  116. "since": since,
  117. "event_name": dispatch_collection.name,
  118. "named_event_arguments": ", ".join(dispatch_collection.arg_names),
  119. "has_kw_arguments": ", **kw" if dispatch_collection.has_kw else ""
  120. }
  121. )
  122. def _augment_fn_docs(dispatch_collection, parent_dispatch_cls, fn):
  123. header = ".. container:: event_signatures\n\n"\
  124. " Example argument forms::\n"\
  125. "\n"
  126. sample_target = getattr(parent_dispatch_cls, "_target_class_doc", "obj")
  127. text = (
  128. header +
  129. _indent(
  130. _standard_listen_example(
  131. dispatch_collection, sample_target, fn),
  132. " " * 8)
  133. )
  134. if dispatch_collection.legacy_signatures:
  135. text += _indent(
  136. _legacy_listen_examples(
  137. dispatch_collection, sample_target, fn),
  138. " " * 8)
  139. text += _version_signature_changes(dispatch_collection)
  140. return util.inject_docstring_text(fn.__doc__,
  141. text,
  142. 1
  143. )