Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py: 56%
917 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1# util/langhelpers.py
2# Copyright (C) 2005-2022 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: https://www.opensource.org/licenses/mit-license.php
8"""Routines to help with the creation, loading and introspection of
9modules, classes, hierarchies, attributes, functions, and methods.
11"""
13import collections
14from functools import update_wrapper
15import hashlib
16import inspect
17import itertools
18import operator
19import re
20import sys
21import textwrap
22import types
23import warnings
25from . import _collections
26from . import compat
27from .. import exc
30def md5_hex(x):
31 if compat.py3k:
32 x = x.encode("utf-8")
33 m = hashlib.md5()
34 m.update(x)
35 return m.hexdigest()
38class safe_reraise(object):
39 """Reraise an exception after invoking some
40 handler code.
42 Stores the existing exception info before
43 invoking so that it is maintained across a potential
44 coroutine context switch.
46 e.g.::
48 try:
49 sess.commit()
50 except:
51 with safe_reraise():
52 sess.rollback()
54 """
56 __slots__ = ("warn_only", "_exc_info")
58 def __init__(self, warn_only=False):
59 self.warn_only = warn_only
61 def __enter__(self):
62 self._exc_info = sys.exc_info()
64 def __exit__(self, type_, value, traceback):
65 # see #2703 for notes
66 if type_ is None:
67 exc_type, exc_value, exc_tb = self._exc_info
68 self._exc_info = None # remove potential circular references
69 if not self.warn_only:
70 compat.raise_(
71 exc_value,
72 with_traceback=exc_tb,
73 )
74 else:
75 if not compat.py3k and self._exc_info and self._exc_info[1]:
76 # emulate Py3K's behavior of telling us when an exception
77 # occurs in an exception handler.
78 warn(
79 "An exception has occurred during handling of a "
80 "previous exception. The previous exception "
81 "is:\n %s %s\n" % (self._exc_info[0], self._exc_info[1])
82 )
83 self._exc_info = None # remove potential circular references
84 compat.raise_(value, with_traceback=traceback)
87def walk_subclasses(cls):
88 seen = set()
90 stack = [cls]
91 while stack:
92 cls = stack.pop()
93 if cls in seen:
94 continue
95 else:
96 seen.add(cls)
97 stack.extend(cls.__subclasses__())
98 yield cls
101def string_or_unprintable(element):
102 if isinstance(element, compat.string_types):
103 return element
104 else:
105 try:
106 return str(element)
107 except Exception:
108 return "unprintable element %r" % element
111def clsname_as_plain_name(cls):
112 return " ".join(
113 n.lower() for n in re.findall(r"([A-Z][a-z]+)", cls.__name__)
114 )
117def method_is_overridden(instance_or_cls, against_method):
118 """Return True if the two class methods don't match."""
120 if not isinstance(instance_or_cls, type):
121 current_cls = instance_or_cls.__class__
122 else:
123 current_cls = instance_or_cls
125 method_name = against_method.__name__
127 current_method = getattr(current_cls, method_name)
129 return current_method != against_method
132def decode_slice(slc):
133 """decode a slice object as sent to __getitem__.
135 takes into account the 2.5 __index__() method, basically.
137 """
138 ret = []
139 for x in slc.start, slc.stop, slc.step:
140 if hasattr(x, "__index__"):
141 x = x.__index__()
142 ret.append(x)
143 return tuple(ret)
146def _unique_symbols(used, *bases):
147 used = set(used)
148 for base in bases:
149 pool = itertools.chain(
150 (base,),
151 compat.itertools_imap(lambda i: base + str(i), range(1000)),
152 )
153 for sym in pool:
154 if sym not in used:
155 used.add(sym)
156 yield sym
157 break
158 else:
159 raise NameError("exhausted namespace for symbol base %s" % base)
162def map_bits(fn, n):
163 """Call the given function given each nonzero bit from n."""
165 while n:
166 b = n & (~n + 1)
167 yield fn(b)
168 n ^= b
171def decorator(target):
172 """A signature-matching decorator factory."""
174 def decorate(fn):
175 if not inspect.isfunction(fn) and not inspect.ismethod(fn):
176 raise Exception("not a decoratable function")
178 spec = compat.inspect_getfullargspec(fn)
179 env = {}
181 spec = _update_argspec_defaults_into_env(spec, env)
183 names = tuple(spec[0]) + spec[1:3] + (fn.__name__,)
184 targ_name, fn_name = _unique_symbols(names, "target", "fn")
186 metadata = dict(target=targ_name, fn=fn_name)
187 metadata.update(format_argspec_plus(spec, grouped=False))
188 metadata["name"] = fn.__name__
189 code = (
190 """\
191def %(name)s(%(args)s):
192 return %(target)s(%(fn)s, %(apply_kw)s)
193"""
194 % metadata
195 )
196 env.update({targ_name: target, fn_name: fn, "__name__": fn.__module__})
198 decorated = _exec_code_in_env(code, env, fn.__name__)
199 decorated.__defaults__ = getattr(fn, "__func__", fn).__defaults__
200 decorated.__wrapped__ = fn
201 return update_wrapper(decorated, fn)
203 return update_wrapper(decorate, target)
206def _update_argspec_defaults_into_env(spec, env):
207 """given a FullArgSpec, convert defaults to be symbol names in an env."""
209 if spec.defaults:
210 new_defaults = []
211 i = 0
212 for arg in spec.defaults:
213 if type(arg).__module__ not in ("builtins", "__builtin__"):
214 name = "x%d" % i
215 env[name] = arg
216 new_defaults.append(name)
217 i += 1
218 else:
219 new_defaults.append(arg)
220 elem = list(spec)
221 elem[3] = tuple(new_defaults)
222 return compat.FullArgSpec(*elem)
223 else:
224 return spec
227def _exec_code_in_env(code, env, fn_name):
228 exec(code, env)
229 return env[fn_name]
232def public_factory(target, location, class_location=None):
233 """Produce a wrapping function for the given cls or classmethod.
235 Rationale here is so that the __init__ method of the
236 class can serve as documentation for the function.
238 """
240 if isinstance(target, type):
241 fn = target.__init__
242 callable_ = target
243 doc = (
244 "Construct a new :class:`%s` object. \n\n"
245 "This constructor is mirrored as a public API function; "
246 "see :func:`sqlalchemy%s` "
247 "for a full usage and argument description."
248 % (
249 class_location if class_location else ".%s" % target.__name__,
250 location,
251 )
252 )
253 else:
254 fn = callable_ = target
255 doc = (
256 "This function is mirrored; see :func:`sqlalchemy%s` "
257 "for a description of arguments." % location
258 )
260 location_name = location.split(".")[-1]
261 spec = compat.inspect_getfullargspec(fn)
262 del spec[0][0]
263 metadata = format_argspec_plus(spec, grouped=False)
264 metadata["name"] = location_name
265 code = (
266 """\
267def %(name)s(%(args)s):
268 return cls(%(apply_kw)s)
269"""
270 % metadata
271 )
272 env = {
273 "cls": callable_,
274 "symbol": symbol,
275 "__name__": callable_.__module__,
276 }
277 exec(code, env)
278 decorated = env[location_name]
280 if hasattr(fn, "_linked_to"):
281 linked_to, linked_to_location = fn._linked_to
282 linked_to_doc = linked_to.__doc__
283 if class_location is None:
284 class_location = "%s.%s" % (target.__module__, target.__name__)
286 linked_to_doc = inject_docstring_text(
287 linked_to_doc,
288 ".. container:: inherited_member\n\n "
289 "This documentation is inherited from :func:`sqlalchemy%s`; "
290 "this constructor, :func:`sqlalchemy%s`, "
291 "creates a :class:`sqlalchemy%s` object. See that class for "
292 "additional details describing this subclass."
293 % (linked_to_location, location, class_location),
294 1,
295 )
296 decorated.__doc__ = linked_to_doc
297 else:
298 decorated.__doc__ = fn.__doc__
300 decorated.__module__ = "sqlalchemy" + location.rsplit(".", 1)[0]
301 if decorated.__module__ not in sys.modules:
302 raise ImportError(
303 "public_factory location %s is not in sys.modules"
304 % (decorated.__module__,)
305 )
307 if compat.py2k or hasattr(fn, "__func__"):
308 fn.__func__.__doc__ = doc
309 if not hasattr(fn.__func__, "_linked_to"):
310 fn.__func__._linked_to = (decorated, location)
311 else:
312 fn.__doc__ = doc
313 if not hasattr(fn, "_linked_to"):
314 fn._linked_to = (decorated, location)
316 return decorated
319class PluginLoader(object):
320 def __init__(self, group, auto_fn=None):
321 self.group = group
322 self.impls = {}
323 self.auto_fn = auto_fn
325 def clear(self):
326 self.impls.clear()
328 def load(self, name):
329 if name in self.impls:
330 return self.impls[name]()
332 if self.auto_fn:
333 loader = self.auto_fn(name)
334 if loader:
335 self.impls[name] = loader
336 return loader()
338 for impl in compat.importlib_metadata_get(self.group):
339 if impl.name == name:
340 self.impls[name] = impl.load
341 return impl.load()
343 raise exc.NoSuchModuleError(
344 "Can't load plugin: %s:%s" % (self.group, name)
345 )
347 def register(self, name, modulepath, objname):
348 def load():
349 mod = compat.import_(modulepath)
350 for token in modulepath.split(".")[1:]:
351 mod = getattr(mod, token)
352 return getattr(mod, objname)
354 self.impls[name] = load
357def _inspect_func_args(fn):
358 try:
359 co_varkeywords = inspect.CO_VARKEYWORDS
360 except AttributeError:
361 # https://docs.python.org/3/library/inspect.html
362 # The flags are specific to CPython, and may not be defined in other
363 # Python implementations. Furthermore, the flags are an implementation
364 # detail, and can be removed or deprecated in future Python releases.
365 spec = compat.inspect_getfullargspec(fn)
366 return spec[0], bool(spec[2])
367 else:
368 # use fn.__code__ plus flags to reduce method call overhead
369 co = fn.__code__
370 nargs = co.co_argcount
371 return (
372 list(co.co_varnames[:nargs]),
373 bool(co.co_flags & co_varkeywords),
374 )
377def get_cls_kwargs(cls, _set=None):
378 r"""Return the full set of inherited kwargs for the given `cls`.
380 Probes a class's __init__ method, collecting all named arguments. If the
381 __init__ defines a \**kwargs catch-all, then the constructor is presumed
382 to pass along unrecognized keywords to its base classes, and the
383 collection process is repeated recursively on each of the bases.
385 Uses a subset of inspect.getfullargspec() to cut down on method overhead,
386 as this is used within the Core typing system to create copies of type
387 objects which is a performance-sensitive operation.
389 No anonymous tuple arguments please !
391 """
392 toplevel = _set is None
393 if toplevel:
394 _set = set()
396 ctr = cls.__dict__.get("__init__", False)
398 has_init = (
399 ctr
400 and isinstance(ctr, types.FunctionType)
401 and isinstance(ctr.__code__, types.CodeType)
402 )
404 if has_init:
405 names, has_kw = _inspect_func_args(ctr)
406 _set.update(names)
408 if not has_kw and not toplevel:
409 return None
411 if not has_init or has_kw:
412 for c in cls.__bases__:
413 if get_cls_kwargs(c, _set) is None:
414 break
416 _set.discard("self")
417 return _set
420def get_func_kwargs(func):
421 """Return the set of legal kwargs for the given `func`.
423 Uses getargspec so is safe to call for methods, functions,
424 etc.
426 """
428 return compat.inspect_getfullargspec(func)[0]
431def get_callable_argspec(fn, no_self=False, _is_init=False):
432 """Return the argument signature for any callable.
434 All pure-Python callables are accepted, including
435 functions, methods, classes, objects with __call__;
436 builtins and other edge cases like functools.partial() objects
437 raise a TypeError.
439 """
440 if inspect.isbuiltin(fn):
441 raise TypeError("Can't inspect builtin: %s" % fn)
442 elif inspect.isfunction(fn):
443 if _is_init and no_self:
444 spec = compat.inspect_getfullargspec(fn)
445 return compat.FullArgSpec(
446 spec.args[1:],
447 spec.varargs,
448 spec.varkw,
449 spec.defaults,
450 spec.kwonlyargs,
451 spec.kwonlydefaults,
452 spec.annotations,
453 )
454 else:
455 return compat.inspect_getfullargspec(fn)
456 elif inspect.ismethod(fn):
457 if no_self and (_is_init or fn.__self__):
458 spec = compat.inspect_getfullargspec(fn.__func__)
459 return compat.FullArgSpec(
460 spec.args[1:],
461 spec.varargs,
462 spec.varkw,
463 spec.defaults,
464 spec.kwonlyargs,
465 spec.kwonlydefaults,
466 spec.annotations,
467 )
468 else:
469 return compat.inspect_getfullargspec(fn.__func__)
470 elif inspect.isclass(fn):
471 return get_callable_argspec(
472 fn.__init__, no_self=no_self, _is_init=True
473 )
474 elif hasattr(fn, "__func__"):
475 return compat.inspect_getfullargspec(fn.__func__)
476 elif hasattr(fn, "__call__"):
477 if inspect.ismethod(fn.__call__):
478 return get_callable_argspec(fn.__call__, no_self=no_self)
479 else:
480 raise TypeError("Can't inspect callable: %s" % fn)
481 else:
482 raise TypeError("Can't inspect callable: %s" % fn)
485def format_argspec_plus(fn, grouped=True):
486 """Returns a dictionary of formatted, introspected function arguments.
488 A enhanced variant of inspect.formatargspec to support code generation.
490 fn
491 An inspectable callable or tuple of inspect getargspec() results.
492 grouped
493 Defaults to True; include (parens, around, argument) lists
495 Returns:
497 args
498 Full inspect.formatargspec for fn
499 self_arg
500 The name of the first positional argument, varargs[0], or None
501 if the function defines no positional arguments.
502 apply_pos
503 args, re-written in calling rather than receiving syntax. Arguments are
504 passed positionally.
505 apply_kw
506 Like apply_pos, except keyword-ish args are passed as keywords.
507 apply_pos_proxied
508 Like apply_pos but omits the self/cls argument
510 Example::
512 >>> format_argspec_plus(lambda self, a, b, c=3, **d: 123)
513 {'args': '(self, a, b, c=3, **d)',
514 'self_arg': 'self',
515 'apply_kw': '(self, a, b, c=c, **d)',
516 'apply_pos': '(self, a, b, c, **d)'}
518 """
519 if compat.callable(fn):
520 spec = compat.inspect_getfullargspec(fn)
521 else:
522 spec = fn
524 args = compat.inspect_formatargspec(*spec)
526 apply_pos = compat.inspect_formatargspec(
527 spec[0], spec[1], spec[2], None, spec[4]
528 )
530 if spec[0]:
531 self_arg = spec[0][0]
533 apply_pos_proxied = compat.inspect_formatargspec(
534 spec[0][1:], spec[1], spec[2], None, spec[4]
535 )
537 elif spec[1]:
538 # I'm not sure what this is
539 self_arg = "%s[0]" % spec[1]
541 apply_pos_proxied = apply_pos
542 else:
543 self_arg = None
544 apply_pos_proxied = apply_pos
546 num_defaults = 0
547 if spec[3]:
548 num_defaults += len(spec[3])
549 if spec[4]:
550 num_defaults += len(spec[4])
551 name_args = spec[0] + spec[4]
553 if num_defaults:
554 defaulted_vals = name_args[0 - num_defaults :]
555 else:
556 defaulted_vals = ()
558 apply_kw = compat.inspect_formatargspec(
559 name_args,
560 spec[1],
561 spec[2],
562 defaulted_vals,
563 formatvalue=lambda x: "=" + x,
564 )
566 if spec[0]:
567 apply_kw_proxied = compat.inspect_formatargspec(
568 name_args[1:],
569 spec[1],
570 spec[2],
571 defaulted_vals,
572 formatvalue=lambda x: "=" + x,
573 )
574 else:
575 apply_kw_proxied = apply_kw
577 if grouped:
578 return dict(
579 args=args,
580 self_arg=self_arg,
581 apply_pos=apply_pos,
582 apply_kw=apply_kw,
583 apply_pos_proxied=apply_pos_proxied,
584 apply_kw_proxied=apply_kw_proxied,
585 )
586 else:
587 return dict(
588 args=args[1:-1],
589 self_arg=self_arg,
590 apply_pos=apply_pos[1:-1],
591 apply_kw=apply_kw[1:-1],
592 apply_pos_proxied=apply_pos_proxied[1:-1],
593 apply_kw_proxied=apply_kw_proxied[1:-1],
594 )
597def format_argspec_init(method, grouped=True):
598 """format_argspec_plus with considerations for typical __init__ methods
600 Wraps format_argspec_plus with error handling strategies for typical
601 __init__ cases::
603 object.__init__ -> (self)
604 other unreflectable (usually C) -> (self, *args, **kwargs)
606 """
607 if method is object.__init__:
608 args = "(self)" if grouped else "self"
609 proxied = "()" if grouped else ""
610 else:
611 try:
612 return format_argspec_plus(method, grouped=grouped)
613 except TypeError:
614 args = (
615 "(self, *args, **kwargs)"
616 if grouped
617 else "self, *args, **kwargs"
618 )
619 proxied = "(*args, **kwargs)" if grouped else "*args, **kwargs"
620 return dict(
621 self_arg="self",
622 args=args,
623 apply_pos=args,
624 apply_kw=args,
625 apply_pos_proxied=proxied,
626 apply_kw_proxied=proxied,
627 )
630def create_proxy_methods(
631 target_cls,
632 target_cls_sphinx_name,
633 proxy_cls_sphinx_name,
634 classmethods=(),
635 methods=(),
636 attributes=(),
637):
638 """A class decorator that will copy attributes to a proxy class.
640 The class to be instrumented must define a single accessor "_proxied".
642 """
644 def decorate(cls):
645 def instrument(name, clslevel=False):
646 fn = getattr(target_cls, name)
647 spec = compat.inspect_getfullargspec(fn)
648 env = {"__name__": fn.__module__}
650 spec = _update_argspec_defaults_into_env(spec, env)
651 caller_argspec = format_argspec_plus(spec, grouped=False)
653 metadata = {
654 "name": fn.__name__,
655 "apply_pos_proxied": caller_argspec["apply_pos_proxied"],
656 "apply_kw_proxied": caller_argspec["apply_kw_proxied"],
657 "args": caller_argspec["args"],
658 "self_arg": caller_argspec["self_arg"],
659 }
661 if clslevel:
662 code = (
663 "def %(name)s(%(args)s):\n"
664 " return target_cls.%(name)s(%(apply_kw_proxied)s)"
665 % metadata
666 )
667 env["target_cls"] = target_cls
668 else:
669 code = (
670 "def %(name)s(%(args)s):\n"
671 " return %(self_arg)s._proxied.%(name)s(%(apply_kw_proxied)s)" # noqa: E501
672 % metadata
673 )
675 proxy_fn = _exec_code_in_env(code, env, fn.__name__)
676 proxy_fn.__defaults__ = getattr(fn, "__func__", fn).__defaults__
677 proxy_fn.__doc__ = inject_docstring_text(
678 fn.__doc__,
679 ".. container:: class_bases\n\n "
680 "Proxied for the %s class on behalf of the %s class."
681 % (target_cls_sphinx_name, proxy_cls_sphinx_name),
682 1,
683 )
685 if clslevel:
686 proxy_fn = classmethod(proxy_fn)
688 return proxy_fn
690 def makeprop(name):
691 attr = target_cls.__dict__.get(name, None)
693 if attr is not None:
694 doc = inject_docstring_text(
695 attr.__doc__,
696 ".. container:: class_bases\n\n "
697 "Proxied for the %s class on behalf of the %s class."
698 % (
699 target_cls_sphinx_name,
700 proxy_cls_sphinx_name,
701 ),
702 1,
703 )
704 else:
705 doc = None
707 code = (
708 "def set_(self, attr):\n"
709 " self._proxied.%(name)s = attr\n"
710 "def get(self):\n"
711 " return self._proxied.%(name)s\n"
712 "get.__doc__ = doc\n"
713 "getset = property(get, set_)"
714 ) % {"name": name}
716 getset = _exec_code_in_env(code, {"doc": doc}, "getset")
718 return getset
720 for meth in methods:
721 if hasattr(cls, meth):
722 raise TypeError(
723 "class %s already has a method %s" % (cls, meth)
724 )
725 setattr(cls, meth, instrument(meth))
727 for prop in attributes:
728 if hasattr(cls, prop):
729 raise TypeError(
730 "class %s already has a method %s" % (cls, prop)
731 )
732 setattr(cls, prop, makeprop(prop))
734 for prop in classmethods:
735 if hasattr(cls, prop):
736 raise TypeError(
737 "class %s already has a method %s" % (cls, prop)
738 )
739 setattr(cls, prop, instrument(prop, clslevel=True))
741 return cls
743 return decorate
746def getargspec_init(method):
747 """inspect.getargspec with considerations for typical __init__ methods
749 Wraps inspect.getargspec with error handling for typical __init__ cases::
751 object.__init__ -> (self)
752 other unreflectable (usually C) -> (self, *args, **kwargs)
754 """
755 try:
756 return compat.inspect_getfullargspec(method)
757 except TypeError:
758 if method is object.__init__:
759 return (["self"], None, None, None)
760 else:
761 return (["self"], "args", "kwargs", None)
764def unbound_method_to_callable(func_or_cls):
765 """Adjust the incoming callable such that a 'self' argument is not
766 required.
768 """
770 if isinstance(func_or_cls, types.MethodType) and not func_or_cls.__self__:
771 return func_or_cls.__func__
772 else:
773 return func_or_cls
776def generic_repr(obj, additional_kw=(), to_inspect=None, omit_kwarg=()):
777 """Produce a __repr__() based on direct association of the __init__()
778 specification vs. same-named attributes present.
780 """
781 if to_inspect is None:
782 to_inspect = [obj]
783 else:
784 to_inspect = _collections.to_list(to_inspect)
786 missing = object()
788 pos_args = []
789 kw_args = _collections.OrderedDict()
790 vargs = None
791 for i, insp in enumerate(to_inspect):
792 try:
793 spec = compat.inspect_getfullargspec(insp.__init__)
794 except TypeError:
795 continue
796 else:
797 default_len = spec.defaults and len(spec.defaults) or 0
798 if i == 0:
799 if spec.varargs:
800 vargs = spec.varargs
801 if default_len:
802 pos_args.extend(spec.args[1:-default_len])
803 else:
804 pos_args.extend(spec.args[1:])
805 else:
806 kw_args.update(
807 [(arg, missing) for arg in spec.args[1:-default_len]]
808 )
810 if default_len:
811 kw_args.update(
812 [
813 (arg, default)
814 for arg, default in zip(
815 spec.args[-default_len:], spec.defaults
816 )
817 ]
818 )
819 output = []
821 output.extend(repr(getattr(obj, arg, None)) for arg in pos_args)
823 if vargs is not None and hasattr(obj, vargs):
824 output.extend([repr(val) for val in getattr(obj, vargs)])
826 for arg, defval in kw_args.items():
827 if arg in omit_kwarg:
828 continue
829 try:
830 val = getattr(obj, arg, missing)
831 if val is not missing and val != defval:
832 output.append("%s=%r" % (arg, val))
833 except Exception:
834 pass
836 if additional_kw:
837 for arg, defval in additional_kw:
838 try:
839 val = getattr(obj, arg, missing)
840 if val is not missing and val != defval:
841 output.append("%s=%r" % (arg, val))
842 except Exception:
843 pass
845 return "%s(%s)" % (obj.__class__.__name__, ", ".join(output))
848class portable_instancemethod(object):
849 """Turn an instancemethod into a (parent, name) pair
850 to produce a serializable callable.
852 """
854 __slots__ = "target", "name", "kwargs", "__weakref__"
856 def __getstate__(self):
857 return {
858 "target": self.target,
859 "name": self.name,
860 "kwargs": self.kwargs,
861 }
863 def __setstate__(self, state):
864 self.target = state["target"]
865 self.name = state["name"]
866 self.kwargs = state.get("kwargs", ())
868 def __init__(self, meth, kwargs=()):
869 self.target = meth.__self__
870 self.name = meth.__name__
871 self.kwargs = kwargs
873 def __call__(self, *arg, **kw):
874 kw.update(self.kwargs)
875 return getattr(self.target, self.name)(*arg, **kw)
878def class_hierarchy(cls):
879 """Return an unordered sequence of all classes related to cls.
881 Traverses diamond hierarchies.
883 Fibs slightly: subclasses of builtin types are not returned. Thus
884 class_hierarchy(class A(object)) returns (A, object), not A plus every
885 class systemwide that derives from object.
887 Old-style classes are discarded and hierarchies rooted on them
888 will not be descended.
890 """
891 if compat.py2k:
892 if isinstance(cls, types.ClassType):
893 return list()
895 hier = {cls}
896 process = list(cls.__mro__)
897 while process:
898 c = process.pop()
899 if compat.py2k:
900 if isinstance(c, types.ClassType):
901 continue
902 bases = (
903 _
904 for _ in c.__bases__
905 if _ not in hier and not isinstance(_, types.ClassType)
906 )
907 else:
908 bases = (_ for _ in c.__bases__ if _ not in hier)
910 for b in bases:
911 process.append(b)
912 hier.add(b)
914 if compat.py3k:
915 if c.__module__ == "builtins" or not hasattr(c, "__subclasses__"):
916 continue
917 else:
918 if c.__module__ == "__builtin__" or not hasattr(
919 c, "__subclasses__"
920 ):
921 continue
923 for s in [_ for _ in c.__subclasses__() if _ not in hier]:
924 process.append(s)
925 hier.add(s)
926 return list(hier)
929def iterate_attributes(cls):
930 """iterate all the keys and attributes associated
931 with a class, without using getattr().
933 Does not use getattr() so that class-sensitive
934 descriptors (i.e. property.__get__()) are not called.
936 """
937 keys = dir(cls)
938 for key in keys:
939 for c in cls.__mro__:
940 if key in c.__dict__:
941 yield (key, c.__dict__[key])
942 break
945def monkeypatch_proxied_specials(
946 into_cls,
947 from_cls,
948 skip=None,
949 only=None,
950 name="self.proxy",
951 from_instance=None,
952):
953 """Automates delegation of __specials__ for a proxying type."""
955 if only:
956 dunders = only
957 else:
958 if skip is None:
959 skip = (
960 "__slots__",
961 "__del__",
962 "__getattribute__",
963 "__metaclass__",
964 "__getstate__",
965 "__setstate__",
966 )
967 dunders = [
968 m
969 for m in dir(from_cls)
970 if (
971 m.startswith("__")
972 and m.endswith("__")
973 and not hasattr(into_cls, m)
974 and m not in skip
975 )
976 ]
978 for method in dunders:
979 try:
980 fn = getattr(from_cls, method)
981 if not hasattr(fn, "__call__"):
982 continue
983 fn = getattr(fn, "__func__", fn)
984 except AttributeError:
985 continue
986 try:
987 spec = compat.inspect_getfullargspec(fn)
988 fn_args = compat.inspect_formatargspec(spec[0])
989 d_args = compat.inspect_formatargspec(spec[0][1:])
990 except TypeError:
991 fn_args = "(self, *args, **kw)"
992 d_args = "(*args, **kw)"
994 py = (
995 "def %(method)s%(fn_args)s: "
996 "return %(name)s.%(method)s%(d_args)s" % locals()
997 )
999 env = from_instance is not None and {name: from_instance} or {}
1000 compat.exec_(py, env)
1001 try:
1002 env[method].__defaults__ = fn.__defaults__
1003 except AttributeError:
1004 pass
1005 setattr(into_cls, method, env[method])
1008def methods_equivalent(meth1, meth2):
1009 """Return True if the two methods are the same implementation."""
1011 return getattr(meth1, "__func__", meth1) is getattr(
1012 meth2, "__func__", meth2
1013 )
1016def as_interface(obj, cls=None, methods=None, required=None):
1017 """Ensure basic interface compliance for an instance or dict of callables.
1019 Checks that ``obj`` implements public methods of ``cls`` or has members
1020 listed in ``methods``. If ``required`` is not supplied, implementing at
1021 least one interface method is sufficient. Methods present on ``obj`` that
1022 are not in the interface are ignored.
1024 If ``obj`` is a dict and ``dict`` does not meet the interface
1025 requirements, the keys of the dictionary are inspected. Keys present in
1026 ``obj`` that are not in the interface will raise TypeErrors.
1028 Raises TypeError if ``obj`` does not meet the interface criteria.
1030 In all passing cases, an object with callable members is returned. In the
1031 simple case, ``obj`` is returned as-is; if dict processing kicks in then
1032 an anonymous class is returned.
1034 obj
1035 A type, instance, or dictionary of callables.
1036 cls
1037 Optional, a type. All public methods of cls are considered the
1038 interface. An ``obj`` instance of cls will always pass, ignoring
1039 ``required``..
1040 methods
1041 Optional, a sequence of method names to consider as the interface.
1042 required
1043 Optional, a sequence of mandatory implementations. If omitted, an
1044 ``obj`` that provides at least one interface method is considered
1045 sufficient. As a convenience, required may be a type, in which case
1046 all public methods of the type are required.
1048 """
1049 if not cls and not methods:
1050 raise TypeError("a class or collection of method names are required")
1052 if isinstance(cls, type) and isinstance(obj, cls):
1053 return obj
1055 interface = set(methods or [m for m in dir(cls) if not m.startswith("_")])
1056 implemented = set(dir(obj))
1058 complies = operator.ge
1059 if isinstance(required, type):
1060 required = interface
1061 elif not required:
1062 required = set()
1063 complies = operator.gt
1064 else:
1065 required = set(required)
1067 if complies(implemented.intersection(interface), required):
1068 return obj
1070 # No dict duck typing here.
1071 if not isinstance(obj, dict):
1072 qualifier = complies is operator.gt and "any of" or "all of"
1073 raise TypeError(
1074 "%r does not implement %s: %s"
1075 % (obj, qualifier, ", ".join(interface))
1076 )
1078 class AnonymousInterface(object):
1079 """A callable-holding shell."""
1081 if cls:
1082 AnonymousInterface.__name__ = "Anonymous" + cls.__name__
1083 found = set()
1085 for method, impl in dictlike_iteritems(obj):
1086 if method not in interface:
1087 raise TypeError("%r: unknown in this interface" % method)
1088 if not compat.callable(impl):
1089 raise TypeError("%r=%r is not callable" % (method, impl))
1090 setattr(AnonymousInterface, method, staticmethod(impl))
1091 found.add(method)
1093 if complies(found, required):
1094 return AnonymousInterface
1096 raise TypeError(
1097 "dictionary does not contain required keys %s"
1098 % ", ".join(required - found)
1099 )
1102class memoized_property(object):
1103 """A read-only @property that is only evaluated once."""
1105 def __init__(self, fget, doc=None):
1106 self.fget = fget
1107 self.__doc__ = doc or fget.__doc__
1108 self.__name__ = fget.__name__
1110 def __get__(self, obj, cls):
1111 if obj is None:
1112 return self
1113 obj.__dict__[self.__name__] = result = self.fget(obj)
1114 return result
1116 def _reset(self, obj):
1117 memoized_property.reset(obj, self.__name__)
1119 @classmethod
1120 def reset(cls, obj, name):
1121 obj.__dict__.pop(name, None)
1124def memoized_instancemethod(fn):
1125 """Decorate a method memoize its return value.
1127 Best applied to no-arg methods: memoization is not sensitive to
1128 argument values, and will always return the same value even when
1129 called with different arguments.
1131 """
1133 def oneshot(self, *args, **kw):
1134 result = fn(self, *args, **kw)
1136 def memo(*a, **kw):
1137 return result
1139 memo.__name__ = fn.__name__
1140 memo.__doc__ = fn.__doc__
1141 self.__dict__[fn.__name__] = memo
1142 return result
1144 return update_wrapper(oneshot, fn)
1147class HasMemoized(object):
1148 """A class that maintains the names of memoized elements in a
1149 collection for easy cache clearing, generative, etc.
1151 """
1153 __slots__ = ()
1155 _memoized_keys = frozenset()
1157 def _reset_memoizations(self):
1158 for elem in self._memoized_keys:
1159 self.__dict__.pop(elem, None)
1161 def _assert_no_memoizations(self):
1162 for elem in self._memoized_keys:
1163 assert elem not in self.__dict__
1165 def _set_memoized_attribute(self, key, value):
1166 self.__dict__[key] = value
1167 self._memoized_keys |= {key}
1169 class memoized_attribute(object):
1170 """A read-only @property that is only evaluated once.
1172 :meta private:
1174 """
1176 def __init__(self, fget, doc=None):
1177 self.fget = fget
1178 self.__doc__ = doc or fget.__doc__
1179 self.__name__ = fget.__name__
1181 def __get__(self, obj, cls):
1182 if obj is None:
1183 return self
1184 obj.__dict__[self.__name__] = result = self.fget(obj)
1185 obj._memoized_keys |= {self.__name__}
1186 return result
1188 @classmethod
1189 def memoized_instancemethod(cls, fn):
1190 """Decorate a method memoize its return value."""
1192 def oneshot(self, *args, **kw):
1193 result = fn(self, *args, **kw)
1195 def memo(*a, **kw):
1196 return result
1198 memo.__name__ = fn.__name__
1199 memo.__doc__ = fn.__doc__
1200 self.__dict__[fn.__name__] = memo
1201 self._memoized_keys |= {fn.__name__}
1202 return result
1204 return update_wrapper(oneshot, fn)
1207class MemoizedSlots(object):
1208 """Apply memoized items to an object using a __getattr__ scheme.
1210 This allows the functionality of memoized_property and
1211 memoized_instancemethod to be available to a class using __slots__.
1213 """
1215 __slots__ = ()
1217 def _fallback_getattr(self, key):
1218 raise AttributeError(key)
1220 def __getattr__(self, key):
1221 if key.startswith("_memoized"):
1222 raise AttributeError(key)
1223 elif hasattr(self, "_memoized_attr_%s" % key):
1224 value = getattr(self, "_memoized_attr_%s" % key)()
1225 setattr(self, key, value)
1226 return value
1227 elif hasattr(self, "_memoized_method_%s" % key):
1228 fn = getattr(self, "_memoized_method_%s" % key)
1230 def oneshot(*args, **kw):
1231 result = fn(*args, **kw)
1233 def memo(*a, **kw):
1234 return result
1236 memo.__name__ = fn.__name__
1237 memo.__doc__ = fn.__doc__
1238 setattr(self, key, memo)
1239 return result
1241 oneshot.__doc__ = fn.__doc__
1242 return oneshot
1243 else:
1244 return self._fallback_getattr(key)
1247# from paste.deploy.converters
1248def asbool(obj):
1249 if isinstance(obj, compat.string_types):
1250 obj = obj.strip().lower()
1251 if obj in ["true", "yes", "on", "y", "t", "1"]:
1252 return True
1253 elif obj in ["false", "no", "off", "n", "f", "0"]:
1254 return False
1255 else:
1256 raise ValueError("String is not true/false: %r" % obj)
1257 return bool(obj)
1260def bool_or_str(*text):
1261 """Return a callable that will evaluate a string as
1262 boolean, or one of a set of "alternate" string values.
1264 """
1266 def bool_or_value(obj):
1267 if obj in text:
1268 return obj
1269 else:
1270 return asbool(obj)
1272 return bool_or_value
1275def asint(value):
1276 """Coerce to integer."""
1278 if value is None:
1279 return value
1280 return int(value)
1283def coerce_kw_type(kw, key, type_, flexi_bool=True, dest=None):
1284 r"""If 'key' is present in dict 'kw', coerce its value to type 'type\_' if
1285 necessary. If 'flexi_bool' is True, the string '0' is considered false
1286 when coercing to boolean.
1287 """
1289 if dest is None:
1290 dest = kw
1292 if (
1293 key in kw
1294 and (not isinstance(type_, type) or not isinstance(kw[key], type_))
1295 and kw[key] is not None
1296 ):
1297 if type_ is bool and flexi_bool:
1298 dest[key] = asbool(kw[key])
1299 else:
1300 dest[key] = type_(kw[key])
1303def constructor_key(obj, cls):
1304 """Produce a tuple structure that is cacheable using the __dict__ of
1305 obj to retrieve values
1307 """
1308 names = get_cls_kwargs(cls)
1309 return (cls,) + tuple(
1310 (k, obj.__dict__[k]) for k in names if k in obj.__dict__
1311 )
1314def constructor_copy(obj, cls, *args, **kw):
1315 """Instantiate cls using the __dict__ of obj as constructor arguments.
1317 Uses inspect to match the named arguments of ``cls``.
1319 """
1321 names = get_cls_kwargs(cls)
1322 kw.update(
1323 (k, obj.__dict__[k]) for k in names.difference(kw) if k in obj.__dict__
1324 )
1325 return cls(*args, **kw)
1328def counter():
1329 """Return a threadsafe counter function."""
1331 lock = compat.threading.Lock()
1332 counter = itertools.count(1)
1334 # avoid the 2to3 "next" transformation...
1335 def _next():
1336 with lock:
1337 return next(counter)
1339 return _next
1342def duck_type_collection(specimen, default=None):
1343 """Given an instance or class, guess if it is or is acting as one of
1344 the basic collection types: list, set and dict. If the __emulates__
1345 property is present, return that preferentially.
1346 """
1348 if hasattr(specimen, "__emulates__"):
1349 # canonicalize set vs sets.Set to a standard: the builtin set
1350 if specimen.__emulates__ is not None and issubclass(
1351 specimen.__emulates__, set
1352 ):
1353 return set
1354 else:
1355 return specimen.__emulates__
1357 isa = isinstance(specimen, type) and issubclass or isinstance
1358 if isa(specimen, list):
1359 return list
1360 elif isa(specimen, set):
1361 return set
1362 elif isa(specimen, dict):
1363 return dict
1365 if hasattr(specimen, "append"):
1366 return list
1367 elif hasattr(specimen, "add"):
1368 return set
1369 elif hasattr(specimen, "set"):
1370 return dict
1371 else:
1372 return default
1375def assert_arg_type(arg, argtype, name):
1376 if isinstance(arg, argtype):
1377 return arg
1378 else:
1379 if isinstance(argtype, tuple):
1380 raise exc.ArgumentError(
1381 "Argument '%s' is expected to be one of type %s, got '%s'"
1382 % (name, " or ".join("'%s'" % a for a in argtype), type(arg))
1383 )
1384 else:
1385 raise exc.ArgumentError(
1386 "Argument '%s' is expected to be of type '%s', got '%s'"
1387 % (name, argtype, type(arg))
1388 )
1391def dictlike_iteritems(dictlike):
1392 """Return a (key, value) iterator for almost any dict-like object."""
1394 if compat.py3k:
1395 if hasattr(dictlike, "items"):
1396 return list(dictlike.items())
1397 else:
1398 if hasattr(dictlike, "iteritems"):
1399 return dictlike.iteritems()
1400 elif hasattr(dictlike, "items"):
1401 return iter(dictlike.items())
1403 getter = getattr(dictlike, "__getitem__", getattr(dictlike, "get", None))
1404 if getter is None:
1405 raise TypeError("Object '%r' is not dict-like" % dictlike)
1407 if hasattr(dictlike, "iterkeys"):
1409 def iterator():
1410 for key in dictlike.iterkeys():
1411 yield key, getter(key)
1413 return iterator()
1414 elif hasattr(dictlike, "keys"):
1415 return iter((key, getter(key)) for key in dictlike.keys())
1416 else:
1417 raise TypeError("Object '%r' is not dict-like" % dictlike)
1420class classproperty(property):
1421 """A decorator that behaves like @property except that operates
1422 on classes rather than instances.
1424 The decorator is currently special when using the declarative
1425 module, but note that the
1426 :class:`~.sqlalchemy.ext.declarative.declared_attr`
1427 decorator should be used for this purpose with declarative.
1429 """
1431 def __init__(self, fget, *arg, **kw):
1432 super(classproperty, self).__init__(fget, *arg, **kw)
1433 self.__doc__ = fget.__doc__
1435 def __get__(desc, self, cls):
1436 return desc.fget(cls)
1439class hybridproperty(object):
1440 def __init__(self, func):
1441 self.func = func
1442 self.clslevel = func
1444 def __get__(self, instance, owner):
1445 if instance is None:
1446 clsval = self.clslevel(owner)
1447 return clsval
1448 else:
1449 return self.func(instance)
1451 def classlevel(self, func):
1452 self.clslevel = func
1453 return self
1456class hybridmethod(object):
1457 """Decorate a function as cls- or instance- level."""
1459 def __init__(self, func):
1460 self.func = self.__func__ = func
1461 self.clslevel = func
1463 def __get__(self, instance, owner):
1464 if instance is None:
1465 return self.clslevel.__get__(owner, owner.__class__)
1466 else:
1467 return self.func.__get__(instance, owner)
1469 def classlevel(self, func):
1470 self.clslevel = func
1471 return self
1474class _symbol(int):
1475 def __new__(self, name, doc=None, canonical=None):
1476 """Construct a new named symbol."""
1477 assert isinstance(name, compat.string_types)
1478 if canonical is None:
1479 canonical = hash(name)
1480 v = int.__new__(_symbol, canonical)
1481 v.name = name
1482 if doc:
1483 v.__doc__ = doc
1484 return v
1486 def __reduce__(self):
1487 return symbol, (self.name, "x", int(self))
1489 def __str__(self):
1490 return repr(self)
1492 def __repr__(self):
1493 return "symbol(%r)" % self.name
1496_symbol.__name__ = "symbol"
1499class symbol(object):
1500 """A constant symbol.
1502 >>> symbol('foo') is symbol('foo')
1503 True
1504 >>> symbol('foo')
1505 <symbol 'foo>
1507 A slight refinement of the MAGICCOOKIE=object() pattern. The primary
1508 advantage of symbol() is its repr(). They are also singletons.
1510 Repeated calls of symbol('name') will all return the same instance.
1512 The optional ``doc`` argument assigns to ``__doc__``. This
1513 is strictly so that Sphinx autoattr picks up the docstring we want
1514 (it doesn't appear to pick up the in-module docstring if the datamember
1515 is in a different module - autoattribute also blows up completely).
1516 If Sphinx fixes/improves this then we would no longer need
1517 ``doc`` here.
1519 """
1521 symbols = {}
1522 _lock = compat.threading.Lock()
1524 def __new__(cls, name, doc=None, canonical=None):
1525 with cls._lock:
1526 sym = cls.symbols.get(name)
1527 if sym is None:
1528 cls.symbols[name] = sym = _symbol(name, doc, canonical)
1529 return sym
1531 @classmethod
1532 def parse_user_argument(
1533 cls, arg, choices, name, resolve_symbol_names=False
1534 ):
1535 """Given a user parameter, parse the parameter into a chosen symbol.
1537 The user argument can be a string name that matches the name of a
1538 symbol, or the symbol object itself, or any number of alternate choices
1539 such as True/False/ None etc.
1541 :param arg: the user argument.
1542 :param choices: dictionary of symbol object to list of possible
1543 entries.
1544 :param name: name of the argument. Used in an :class:`.ArgumentError`
1545 that is raised if the parameter doesn't match any available argument.
1546 :param resolve_symbol_names: include the name of each symbol as a valid
1547 entry.
1549 """
1550 # note using hash lookup is tricky here because symbol's `__hash__`
1551 # is its int value which we don't want included in the lookup
1552 # explicitly, so we iterate and compare each.
1553 for sym, choice in choices.items():
1554 if arg is sym:
1555 return sym
1556 elif resolve_symbol_names and arg == sym.name:
1557 return sym
1558 elif arg in choice:
1559 return sym
1561 if arg is None:
1562 return None
1564 raise exc.ArgumentError("Invalid value for '%s': %r" % (name, arg))
1567_creation_order = 1
1570def set_creation_order(instance):
1571 """Assign a '_creation_order' sequence to the given instance.
1573 This allows multiple instances to be sorted in order of creation
1574 (typically within a single thread; the counter is not particularly
1575 threadsafe).
1577 """
1578 global _creation_order
1579 instance._creation_order = _creation_order
1580 _creation_order += 1
1583def warn_exception(func, *args, **kwargs):
1584 """executes the given function, catches all exceptions and converts to
1585 a warning.
1587 """
1588 try:
1589 return func(*args, **kwargs)
1590 except Exception:
1591 warn("%s('%s') ignored" % sys.exc_info()[0:2])
1594def ellipses_string(value, len_=25):
1595 try:
1596 if len(value) > len_:
1597 return "%s..." % value[0:len_]
1598 else:
1599 return value
1600 except TypeError:
1601 return value
1604class _hash_limit_string(compat.text_type):
1605 """A string subclass that can only be hashed on a maximum amount
1606 of unique values.
1608 This is used for warnings so that we can send out parameterized warnings
1609 without the __warningregistry__ of the module, or the non-overridable
1610 "once" registry within warnings.py, overloading memory,
1613 """
1615 def __new__(cls, value, num, args):
1616 interpolated = (value % args) + (
1617 " (this warning may be suppressed after %d occurrences)" % num
1618 )
1619 self = super(_hash_limit_string, cls).__new__(cls, interpolated)
1620 self._hash = hash("%s_%d" % (value, hash(interpolated) % num))
1621 return self
1623 def __hash__(self):
1624 return self._hash
1626 def __eq__(self, other):
1627 return hash(self) == hash(other)
1630def warn(msg, code=None):
1631 """Issue a warning.
1633 If msg is a string, :class:`.exc.SAWarning` is used as
1634 the category.
1636 """
1637 if code:
1638 _warnings_warn(exc.SAWarning(msg, code=code))
1639 else:
1640 _warnings_warn(msg, exc.SAWarning)
1643def warn_limited(msg, args):
1644 """Issue a warning with a parameterized string, limiting the number
1645 of registrations.
1647 """
1648 if args:
1649 msg = _hash_limit_string(msg, 10, args)
1650 _warnings_warn(msg, exc.SAWarning)
1653def _warnings_warn(message, category=None, stacklevel=2):
1655 # adjust the given stacklevel to be outside of SQLAlchemy
1656 try:
1657 frame = sys._getframe(stacklevel)
1658 except ValueError:
1659 # being called from less than 3 (or given) stacklevels, weird,
1660 # but don't crash
1661 stacklevel = 0
1662 except:
1663 # _getframe() doesn't work, weird interpreter issue, weird,
1664 # ok, but don't crash
1665 stacklevel = 0
1666 else:
1667 # using __name__ here requires that we have __name__ in the
1668 # __globals__ of the decorated string functions we make also.
1669 # we generate this using {"__name__": fn.__module__}
1670 while frame is not None and re.match(
1671 r"^(?:sqlalchemy\.|alembic\.)", frame.f_globals.get("__name__", "")
1672 ):
1673 frame = frame.f_back
1674 stacklevel += 1
1676 if category is not None:
1677 warnings.warn(message, category, stacklevel=stacklevel + 1)
1678 else:
1679 warnings.warn(message, stacklevel=stacklevel + 1)
1682def only_once(fn, retry_on_exception):
1683 """Decorate the given function to be a no-op after it is called exactly
1684 once."""
1686 once = [fn]
1688 def go(*arg, **kw):
1689 # strong reference fn so that it isn't garbage collected,
1690 # which interferes with the event system's expectations
1691 strong_fn = fn # noqa
1692 if once:
1693 once_fn = once.pop()
1694 try:
1695 return once_fn(*arg, **kw)
1696 except:
1697 if retry_on_exception:
1698 once.insert(0, once_fn)
1699 raise
1701 return go
1704_SQLA_RE = re.compile(r"sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py")
1705_UNITTEST_RE = re.compile(r"unit(?:2|test2?/)")
1708def chop_traceback(tb, exclude_prefix=_UNITTEST_RE, exclude_suffix=_SQLA_RE):
1709 """Chop extraneous lines off beginning and end of a traceback.
1711 :param tb:
1712 a list of traceback lines as returned by ``traceback.format_stack()``
1714 :param exclude_prefix:
1715 a regular expression object matching lines to skip at beginning of
1716 ``tb``
1718 :param exclude_suffix:
1719 a regular expression object matching lines to skip at end of ``tb``
1720 """
1721 start = 0
1722 end = len(tb) - 1
1723 while start <= end and exclude_prefix.search(tb[start]):
1724 start += 1
1725 while start <= end and exclude_suffix.search(tb[end]):
1726 end -= 1
1727 return tb[start : end + 1]
1730NoneType = type(None)
1733def attrsetter(attrname):
1734 code = "def set(obj, value):" " obj.%s = value" % attrname
1735 env = locals().copy()
1736 exec(code, env)
1737 return env["set"]
1740class EnsureKWArgType(type):
1741 r"""Apply translation of functions to accept \**kw arguments if they
1742 don't already.
1744 """
1746 def __init__(cls, clsname, bases, clsdict):
1747 fn_reg = cls.ensure_kwarg
1748 if fn_reg:
1749 for key in clsdict:
1750 m = re.match(fn_reg, key)
1751 if m:
1752 fn = clsdict[key]
1753 spec = compat.inspect_getfullargspec(fn)
1754 if not spec.varkw:
1755 clsdict[key] = wrapped = cls._wrap_w_kw(fn)
1756 setattr(cls, key, wrapped)
1757 super(EnsureKWArgType, cls).__init__(clsname, bases, clsdict)
1759 def _wrap_w_kw(self, fn):
1760 def wrap(*arg, **kw):
1761 return fn(*arg)
1763 return update_wrapper(wrap, fn)
1766def wrap_callable(wrapper, fn):
1767 """Augment functools.update_wrapper() to work with objects with
1768 a ``__call__()`` method.
1770 :param fn:
1771 object with __call__ method
1773 """
1774 if hasattr(fn, "__name__"):
1775 return update_wrapper(wrapper, fn)
1776 else:
1777 _f = wrapper
1778 _f.__name__ = fn.__class__.__name__
1779 if hasattr(fn, "__module__"):
1780 _f.__module__ = fn.__module__
1782 if hasattr(fn.__call__, "__doc__") and fn.__call__.__doc__:
1783 _f.__doc__ = fn.__call__.__doc__
1784 elif fn.__doc__:
1785 _f.__doc__ = fn.__doc__
1787 return _f
1790def quoted_token_parser(value):
1791 """Parse a dotted identifier with accommodation for quoted names.
1793 Includes support for SQL-style double quotes as a literal character.
1795 E.g.::
1797 >>> quoted_token_parser("name")
1798 ["name"]
1799 >>> quoted_token_parser("schema.name")
1800 ["schema", "name"]
1801 >>> quoted_token_parser('"Schema"."Name"')
1802 ['Schema', 'Name']
1803 >>> quoted_token_parser('"Schema"."Name""Foo"')
1804 ['Schema', 'Name""Foo']
1806 """
1808 if '"' not in value:
1809 return value.split(".")
1811 # 0 = outside of quotes
1812 # 1 = inside of quotes
1813 state = 0
1814 result = [[]]
1815 idx = 0
1816 lv = len(value)
1817 while idx < lv:
1818 char = value[idx]
1819 if char == '"':
1820 if state == 1 and idx < lv - 1 and value[idx + 1] == '"':
1821 result[-1].append('"')
1822 idx += 1
1823 else:
1824 state ^= 1
1825 elif char == "." and state == 0:
1826 result.append([])
1827 else:
1828 result[-1].append(char)
1829 idx += 1
1831 return ["".join(token) for token in result]
1834def add_parameter_text(params, text):
1835 params = _collections.to_list(params)
1837 def decorate(fn):
1838 doc = fn.__doc__ is not None and fn.__doc__ or ""
1839 if doc:
1840 doc = inject_param_text(doc, {param: text for param in params})
1841 fn.__doc__ = doc
1842 return fn
1844 return decorate
1847def _dedent_docstring(text):
1848 split_text = text.split("\n", 1)
1849 if len(split_text) == 1:
1850 return text
1851 else:
1852 firstline, remaining = split_text
1853 if not firstline.startswith(" "):
1854 return firstline + "\n" + textwrap.dedent(remaining)
1855 else:
1856 return textwrap.dedent(text)
1859def inject_docstring_text(doctext, injecttext, pos):
1860 doctext = _dedent_docstring(doctext or "")
1861 lines = doctext.split("\n")
1862 if len(lines) == 1:
1863 lines.append("")
1864 injectlines = textwrap.dedent(injecttext).split("\n")
1865 if injectlines[0]:
1866 injectlines.insert(0, "")
1868 blanks = [num for num, line in enumerate(lines) if not line.strip()]
1869 blanks.insert(0, 0)
1871 inject_pos = blanks[min(pos, len(blanks) - 1)]
1873 lines = lines[0:inject_pos] + injectlines + lines[inject_pos:]
1874 return "\n".join(lines)
1877_param_reg = re.compile(r"(\s+):param (.+?):")
1880def inject_param_text(doctext, inject_params):
1881 doclines = collections.deque(doctext.splitlines())
1882 lines = []
1884 # TODO: this is not working for params like ":param case_sensitive=True:"
1886 to_inject = None
1887 while doclines:
1888 line = doclines.popleft()
1890 m = _param_reg.match(line)
1892 if to_inject is None:
1893 if m:
1894 param = m.group(2).lstrip("*")
1895 if param in inject_params:
1896 # default indent to that of :param: plus one
1897 indent = " " * len(m.group(1)) + " "
1899 # but if the next line has text, use that line's
1900 # indentation
1901 if doclines:
1902 m2 = re.match(r"(\s+)\S", doclines[0])
1903 if m2:
1904 indent = " " * len(m2.group(1))
1906 to_inject = indent + inject_params[param]
1907 elif m:
1908 lines.extend(["\n", to_inject, "\n"])
1909 to_inject = None
1910 elif not line.rstrip():
1911 lines.extend([line, to_inject, "\n"])
1912 to_inject = None
1913 elif line.endswith("::"):
1914 # TODO: this still wont cover if the code example itself has blank
1915 # lines in it, need to detect those via indentation.
1916 lines.extend([line, doclines.popleft()])
1917 continue
1918 lines.append(line)
1920 return "\n".join(lines)
1923def repr_tuple_names(names):
1924 """Trims a list of strings from the middle and return a string of up to
1925 four elements. Strings greater than 11 characters will be truncated"""
1926 if len(names) == 0:
1927 return None
1928 flag = len(names) <= 4
1929 names = names[0:4] if flag else names[0:3] + names[-1:]
1930 res = ["%s.." % name[:11] if len(name) > 11 else name for name in names]
1931 if flag:
1932 return ", ".join(res)
1933 else:
1934 return "%s, ..., %s" % (", ".join(res[0:3]), res[-1])
1937def has_compiled_ext():
1938 try:
1939 from sqlalchemy import cimmutabledict # noqa: F401
1940 from sqlalchemy import cprocessors # noqa: F401
1941 from sqlalchemy import cresultproxy # noqa: F401
1943 return True
1944 except ImportError:
1945 return False