Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# util/langhelpers.py 

2# Copyright (C) 2005-2020 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 

8"""Routines to help with the creation, loading and introspection of 

9modules, classes, hierarchies, attributes, functions, and methods. 

10 

11""" 

12from functools import update_wrapper 

13import hashlib 

14import inspect 

15import itertools 

16import operator 

17import re 

18import sys 

19import textwrap 

20import types 

21import warnings 

22 

23from . import _collections 

24from . import compat 

25from .. import exc 

26 

27 

28def md5_hex(x): 

29 if compat.py3k: 

30 x = x.encode("utf-8") 

31 m = hashlib.md5() 

32 m.update(x) 

33 return m.hexdigest() 

34 

35 

36class safe_reraise(object): 

37 """Reraise an exception after invoking some 

38 handler code. 

39 

40 Stores the existing exception info before 

41 invoking so that it is maintained across a potential 

42 coroutine context switch. 

43 

44 e.g.:: 

45 

46 try: 

47 sess.commit() 

48 except: 

49 with safe_reraise(): 

50 sess.rollback() 

51 

52 """ 

53 

54 __slots__ = ("warn_only", "_exc_info") 

55 

56 def __init__(self, warn_only=False): 

57 self.warn_only = warn_only 

58 

59 def __enter__(self): 

60 self._exc_info = sys.exc_info() 

61 

62 def __exit__(self, type_, value, traceback): 

63 # see #2703 for notes 

64 if type_ is None: 

65 exc_type, exc_value, exc_tb = self._exc_info 

66 self._exc_info = None # remove potential circular references 

67 if not self.warn_only: 

68 compat.raise_( 

69 exc_value, with_traceback=exc_tb, 

70 ) 

71 else: 

72 if not compat.py3k and self._exc_info and self._exc_info[1]: 

73 # emulate Py3K's behavior of telling us when an exception 

74 # occurs in an exception handler. 

75 warn( 

76 "An exception has occurred during handling of a " 

77 "previous exception. The previous exception " 

78 "is:\n %s %s\n" % (self._exc_info[0], self._exc_info[1]) 

79 ) 

80 self._exc_info = None # remove potential circular references 

81 compat.raise_(value, with_traceback=traceback) 

82 

83 

84def clsname_as_plain_name(cls): 

85 return " ".join( 

86 n.lower() for n in re.findall(r"([A-Z][a-z]+)", cls.__name__) 

87 ) 

88 

89 

90def decode_slice(slc): 

91 """decode a slice object as sent to __getitem__. 

92 

93 takes into account the 2.5 __index__() method, basically. 

94 

95 """ 

96 ret = [] 

97 for x in slc.start, slc.stop, slc.step: 

98 if hasattr(x, "__index__"): 

99 x = x.__index__() 

100 ret.append(x) 

101 return tuple(ret) 

102 

103 

104def _unique_symbols(used, *bases): 

105 used = set(used) 

106 for base in bases: 

107 pool = itertools.chain( 

108 (base,), 

109 compat.itertools_imap(lambda i: base + str(i), range(1000)), 

110 ) 

111 for sym in pool: 

112 if sym not in used: 

113 used.add(sym) 

114 yield sym 

115 break 

116 else: 

117 raise NameError("exhausted namespace for symbol base %s" % base) 

118 

119 

120def map_bits(fn, n): 

121 """Call the given function given each nonzero bit from n.""" 

122 

123 while n: 

124 b = n & (~n + 1) 

125 yield fn(b) 

126 n ^= b 

127 

128 

129def decorator(target): 

130 """A signature-matching decorator factory.""" 

131 

132 def decorate(fn): 

133 if not inspect.isfunction(fn) and not inspect.ismethod(fn): 

134 raise Exception("not a decoratable function") 

135 

136 spec = compat.inspect_getfullargspec(fn) 

137 names = tuple(spec[0]) + spec[1:3] + (fn.__name__,) 

138 targ_name, fn_name = _unique_symbols(names, "target", "fn") 

139 

140 metadata = dict(target=targ_name, fn=fn_name) 

141 metadata.update(format_argspec_plus(spec, grouped=False)) 

142 metadata["name"] = fn.__name__ 

143 code = ( 

144 """\ 

145def %(name)s(%(args)s): 

146 return %(target)s(%(fn)s, %(apply_kw)s) 

147""" 

148 % metadata 

149 ) 

150 decorated = _exec_code_in_env( 

151 code, {targ_name: target, fn_name: fn}, fn.__name__ 

152 ) 

153 decorated.__defaults__ = getattr(fn, "im_func", fn).__defaults__ 

154 decorated.__wrapped__ = fn 

155 return update_wrapper(decorated, fn) 

156 

157 return update_wrapper(decorate, target) 

158 

159 

160def _exec_code_in_env(code, env, fn_name): 

161 exec(code, env) 

162 return env[fn_name] 

163 

164 

165def public_factory(target, location, class_location=None): 

166 """Produce a wrapping function for the given cls or classmethod. 

167 

168 Rationale here is so that the __init__ method of the 

169 class can serve as documentation for the function. 

170 

171 """ 

172 if isinstance(target, type): 

173 fn = target.__init__ 

174 callable_ = target 

175 doc = ( 

176 "Construct a new :class:`.%s` object. \n\n" 

177 "This constructor is mirrored as a public API function; " 

178 "see :func:`sqlalchemy%s` " 

179 "for a full usage and argument description." 

180 % (target.__name__, location) 

181 ) 

182 else: 

183 fn = callable_ = target 

184 doc = ( 

185 "This function is mirrored; see :func:`sqlalchemy%s` " 

186 "for a description of arguments." % location 

187 ) 

188 

189 location_name = location.split(".")[-1] 

190 spec = compat.inspect_getfullargspec(fn) 

191 del spec[0][0] 

192 metadata = format_argspec_plus(spec, grouped=False) 

193 metadata["name"] = location_name 

194 code = ( 

195 """\ 

196def %(name)s(%(args)s): 

197 return cls(%(apply_kw)s) 

198""" 

199 % metadata 

200 ) 

201 env = {"cls": callable_, "symbol": symbol} 

202 exec(code, env) 

203 decorated = env[location_name] 

204 if hasattr(fn, "_linked_to"): 

205 linked_to, linked_to_location = fn._linked_to 

206 linked_to_doc = linked_to.__doc__ 

207 if class_location is None: 

208 class_location = "%s.%s" % (target.__module__, target.__name__) 

209 

210 linked_to_doc = inject_docstring_text( 

211 linked_to_doc, 

212 ".. container:: inherited_member\n\n " 

213 "Inherited from :func:`sqlalchemy%s`; this constructor " 

214 "creates a :class:`%s` object" 

215 % (linked_to_location, class_location), 

216 1, 

217 ) 

218 decorated.__doc__ = linked_to_doc 

219 else: 

220 decorated.__doc__ = fn.__doc__ 

221 

222 decorated.__module__ = "sqlalchemy" + location.rsplit(".", 1)[0] 

223 if decorated.__module__ not in sys.modules: 

224 raise ImportError( 

225 "public_factory location %s is not in sys.modules" 

226 % (decorated.__module__,) 

227 ) 

228 if compat.py2k or hasattr(fn, "__func__"): 

229 fn.__func__.__doc__ = doc 

230 if not hasattr(fn.__func__, "_linked_to"): 

231 fn.__func__._linked_to = (decorated, location) 

232 else: 

233 fn.__doc__ = doc 

234 if not hasattr(fn, "_linked_to"): 

235 fn._linked_to = (decorated, location) 

236 return decorated 

237 

238 

239class PluginLoader(object): 

240 def __init__(self, group, auto_fn=None): 

241 self.group = group 

242 self.impls = {} 

243 self.auto_fn = auto_fn 

244 

245 def clear(self): 

246 self.impls.clear() 

247 

248 def load(self, name): 

249 if name in self.impls: 

250 return self.impls[name]() 

251 

252 if self.auto_fn: 

253 loader = self.auto_fn(name) 

254 if loader: 

255 self.impls[name] = loader 

256 return loader() 

257 

258 try: 

259 import pkg_resources 

260 except ImportError: 

261 pass 

262 else: 

263 for impl in pkg_resources.iter_entry_points(self.group, name): 

264 self.impls[name] = impl.load 

265 return impl.load() 

266 

267 raise exc.NoSuchModuleError( 

268 "Can't load plugin: %s:%s" % (self.group, name) 

269 ) 

270 

271 def register(self, name, modulepath, objname): 

272 def load(): 

273 mod = compat.import_(modulepath) 

274 for token in modulepath.split(".")[1:]: 

275 mod = getattr(mod, token) 

276 return getattr(mod, objname) 

277 

278 self.impls[name] = load 

279 

280 

281def _inspect_func_args(fn): 

282 try: 

283 co_varkeywords = inspect.CO_VARKEYWORDS 

284 except AttributeError: 

285 # https://docs.python.org/3/library/inspect.html 

286 # The flags are specific to CPython, and may not be defined in other 

287 # Python implementations. Furthermore, the flags are an implementation 

288 # detail, and can be removed or deprecated in future Python releases. 

289 spec = compat.inspect_getfullargspec(fn) 

290 return spec[0], bool(spec[2]) 

291 else: 

292 # use fn.__code__ plus flags to reduce method call overhead 

293 co = fn.__code__ 

294 nargs = co.co_argcount 

295 return ( 

296 list(co.co_varnames[:nargs]), 

297 bool(co.co_flags & co_varkeywords), 

298 ) 

299 

300 

301def get_cls_kwargs(cls, _set=None): 

302 r"""Return the full set of inherited kwargs for the given `cls`. 

303 

304 Probes a class's __init__ method, collecting all named arguments. If the 

305 __init__ defines a \**kwargs catch-all, then the constructor is presumed 

306 to pass along unrecognized keywords to its base classes, and the 

307 collection process is repeated recursively on each of the bases. 

308 

309 Uses a subset of inspect.getfullargspec() to cut down on method overhead, 

310 as this is used within the Core typing system to create copies of type 

311 objects which is a performance-sensitive operation. 

312 

313 No anonymous tuple arguments please ! 

314 

315 """ 

316 toplevel = _set is None 

317 if toplevel: 

318 _set = set() 

319 

320 ctr = cls.__dict__.get("__init__", False) 

321 

322 has_init = ( 

323 ctr 

324 and isinstance(ctr, types.FunctionType) 

325 and isinstance(ctr.__code__, types.CodeType) 

326 ) 

327 

328 if has_init: 

329 names, has_kw = _inspect_func_args(ctr) 

330 _set.update(names) 

331 

332 if not has_kw and not toplevel: 

333 return None 

334 

335 if not has_init or has_kw: 

336 for c in cls.__bases__: 

337 if get_cls_kwargs(c, _set) is None: 

338 break 

339 

340 _set.discard("self") 

341 return _set 

342 

343 

344def get_func_kwargs(func): 

345 """Return the set of legal kwargs for the given `func`. 

346 

347 Uses getargspec so is safe to call for methods, functions, 

348 etc. 

349 

350 """ 

351 

352 return compat.inspect_getfullargspec(func)[0] 

353 

354 

355def get_callable_argspec(fn, no_self=False, _is_init=False): 

356 """Return the argument signature for any callable. 

357 

358 All pure-Python callables are accepted, including 

359 functions, methods, classes, objects with __call__; 

360 builtins and other edge cases like functools.partial() objects 

361 raise a TypeError. 

362 

363 """ 

364 if inspect.isbuiltin(fn): 

365 raise TypeError("Can't inspect builtin: %s" % fn) 

366 elif inspect.isfunction(fn): 

367 if _is_init and no_self: 

368 spec = compat.inspect_getfullargspec(fn) 

369 return compat.FullArgSpec( 

370 spec.args[1:], 

371 spec.varargs, 

372 spec.varkw, 

373 spec.defaults, 

374 spec.kwonlyargs, 

375 spec.kwonlydefaults, 

376 spec.annotations, 

377 ) 

378 else: 

379 return compat.inspect_getfullargspec(fn) 

380 elif inspect.ismethod(fn): 

381 if no_self and (_is_init or fn.__self__): 

382 spec = compat.inspect_getfullargspec(fn.__func__) 

383 return compat.FullArgSpec( 

384 spec.args[1:], 

385 spec.varargs, 

386 spec.varkw, 

387 spec.defaults, 

388 spec.kwonlyargs, 

389 spec.kwonlydefaults, 

390 spec.annotations, 

391 ) 

392 else: 

393 return compat.inspect_getfullargspec(fn.__func__) 

394 elif inspect.isclass(fn): 

395 return get_callable_argspec( 

396 fn.__init__, no_self=no_self, _is_init=True 

397 ) 

398 elif hasattr(fn, "__func__"): 

399 return compat.inspect_getfullargspec(fn.__func__) 

400 elif hasattr(fn, "__call__"): 

401 if inspect.ismethod(fn.__call__): 

402 return get_callable_argspec(fn.__call__, no_self=no_self) 

403 else: 

404 raise TypeError("Can't inspect callable: %s" % fn) 

405 else: 

406 raise TypeError("Can't inspect callable: %s" % fn) 

407 

408 

409def format_argspec_plus(fn, grouped=True): 

410 """Returns a dictionary of formatted, introspected function arguments. 

411 

412 A enhanced variant of inspect.formatargspec to support code generation. 

413 

414 fn 

415 An inspectable callable or tuple of inspect getargspec() results. 

416 grouped 

417 Defaults to True; include (parens, around, argument) lists 

418 

419 Returns: 

420 

421 args 

422 Full inspect.formatargspec for fn 

423 self_arg 

424 The name of the first positional argument, varargs[0], or None 

425 if the function defines no positional arguments. 

426 apply_pos 

427 args, re-written in calling rather than receiving syntax. Arguments are 

428 passed positionally. 

429 apply_kw 

430 Like apply_pos, except keyword-ish args are passed as keywords. 

431 

432 Example:: 

433 

434 >>> format_argspec_plus(lambda self, a, b, c=3, **d: 123) 

435 {'args': '(self, a, b, c=3, **d)', 

436 'self_arg': 'self', 

437 'apply_kw': '(self, a, b, c=c, **d)', 

438 'apply_pos': '(self, a, b, c, **d)'} 

439 

440 """ 

441 if compat.callable(fn): 

442 spec = compat.inspect_getfullargspec(fn) 

443 else: 

444 spec = fn 

445 

446 args = compat.inspect_formatargspec(*spec) 

447 if spec[0]: 

448 self_arg = spec[0][0] 

449 elif spec[1]: 

450 self_arg = "%s[0]" % spec[1] 

451 else: 

452 self_arg = None 

453 

454 apply_pos = compat.inspect_formatargspec( 

455 spec[0], spec[1], spec[2], None, spec[4] 

456 ) 

457 num_defaults = 0 

458 if spec[3]: 

459 num_defaults += len(spec[3]) 

460 if spec[4]: 

461 num_defaults += len(spec[4]) 

462 name_args = spec[0] + spec[4] 

463 

464 if num_defaults: 

465 defaulted_vals = name_args[0 - num_defaults :] 

466 else: 

467 defaulted_vals = () 

468 

469 apply_kw = compat.inspect_formatargspec( 

470 name_args, 

471 spec[1], 

472 spec[2], 

473 defaulted_vals, 

474 formatvalue=lambda x: "=" + x, 

475 ) 

476 if grouped: 

477 return dict( 

478 args=args, 

479 self_arg=self_arg, 

480 apply_pos=apply_pos, 

481 apply_kw=apply_kw, 

482 ) 

483 else: 

484 return dict( 

485 args=args[1:-1], 

486 self_arg=self_arg, 

487 apply_pos=apply_pos[1:-1], 

488 apply_kw=apply_kw[1:-1], 

489 ) 

490 

491 

492def format_argspec_init(method, grouped=True): 

493 """format_argspec_plus with considerations for typical __init__ methods 

494 

495 Wraps format_argspec_plus with error handling strategies for typical 

496 __init__ cases:: 

497 

498 object.__init__ -> (self) 

499 other unreflectable (usually C) -> (self, *args, **kwargs) 

500 

501 """ 

502 if method is object.__init__: 

503 args = grouped and "(self)" or "self" 

504 else: 

505 try: 

506 return format_argspec_plus(method, grouped=grouped) 

507 except TypeError: 

508 args = ( 

509 grouped 

510 and "(self, *args, **kwargs)" 

511 or "self, *args, **kwargs" 

512 ) 

513 return dict(self_arg="self", args=args, apply_pos=args, apply_kw=args) 

514 

515 

516def getargspec_init(method): 

517 """inspect.getargspec with considerations for typical __init__ methods 

518 

519 Wraps inspect.getargspec with error handling for typical __init__ cases:: 

520 

521 object.__init__ -> (self) 

522 other unreflectable (usually C) -> (self, *args, **kwargs) 

523 

524 """ 

525 try: 

526 return compat.inspect_getfullargspec(method) 

527 except TypeError: 

528 if method is object.__init__: 

529 return (["self"], None, None, None) 

530 else: 

531 return (["self"], "args", "kwargs", None) 

532 

533 

534def unbound_method_to_callable(func_or_cls): 

535 """Adjust the incoming callable such that a 'self' argument is not 

536 required. 

537 

538 """ 

539 

540 if isinstance(func_or_cls, types.MethodType) and not func_or_cls.__self__: 

541 return func_or_cls.__func__ 

542 else: 

543 return func_or_cls 

544 

545 

546def generic_repr(obj, additional_kw=(), to_inspect=None, omit_kwarg=()): 

547 """Produce a __repr__() based on direct association of the __init__() 

548 specification vs. same-named attributes present. 

549 

550 """ 

551 if to_inspect is None: 

552 to_inspect = [obj] 

553 else: 

554 to_inspect = _collections.to_list(to_inspect) 

555 

556 missing = object() 

557 

558 pos_args = [] 

559 kw_args = _collections.OrderedDict() 

560 vargs = None 

561 for i, insp in enumerate(to_inspect): 

562 try: 

563 spec = compat.inspect_getfullargspec(insp.__init__) 

564 except TypeError: 

565 continue 

566 else: 

567 default_len = spec.defaults and len(spec.defaults) or 0 

568 if i == 0: 

569 if spec.varargs: 

570 vargs = spec.varargs 

571 if default_len: 

572 pos_args.extend(spec.args[1:-default_len]) 

573 else: 

574 pos_args.extend(spec.args[1:]) 

575 else: 

576 kw_args.update( 

577 [(arg, missing) for arg in spec.args[1:-default_len]] 

578 ) 

579 

580 if default_len: 

581 kw_args.update( 

582 [ 

583 (arg, default) 

584 for arg, default in zip( 

585 spec.args[-default_len:], spec.defaults 

586 ) 

587 ] 

588 ) 

589 output = [] 

590 

591 output.extend(repr(getattr(obj, arg, None)) for arg in pos_args) 

592 

593 if vargs is not None and hasattr(obj, vargs): 

594 output.extend([repr(val) for val in getattr(obj, vargs)]) 

595 

596 for arg, defval in kw_args.items(): 

597 if arg in omit_kwarg: 

598 continue 

599 try: 

600 val = getattr(obj, arg, missing) 

601 if val is not missing and val != defval: 

602 output.append("%s=%r" % (arg, val)) 

603 except Exception: 

604 pass 

605 

606 if additional_kw: 

607 for arg, defval in additional_kw: 

608 try: 

609 val = getattr(obj, arg, missing) 

610 if val is not missing and val != defval: 

611 output.append("%s=%r" % (arg, val)) 

612 except Exception: 

613 pass 

614 

615 return "%s(%s)" % (obj.__class__.__name__, ", ".join(output)) 

616 

617 

618class portable_instancemethod(object): 

619 """Turn an instancemethod into a (parent, name) pair 

620 to produce a serializable callable. 

621 

622 """ 

623 

624 __slots__ = "target", "name", "kwargs", "__weakref__" 

625 

626 def __getstate__(self): 

627 return { 

628 "target": self.target, 

629 "name": self.name, 

630 "kwargs": self.kwargs, 

631 } 

632 

633 def __setstate__(self, state): 

634 self.target = state["target"] 

635 self.name = state["name"] 

636 self.kwargs = state.get("kwargs", ()) 

637 

638 def __init__(self, meth, kwargs=()): 

639 self.target = meth.__self__ 

640 self.name = meth.__name__ 

641 self.kwargs = kwargs 

642 

643 def __call__(self, *arg, **kw): 

644 kw.update(self.kwargs) 

645 return getattr(self.target, self.name)(*arg, **kw) 

646 

647 

648def class_hierarchy(cls): 

649 """Return an unordered sequence of all classes related to cls. 

650 

651 Traverses diamond hierarchies. 

652 

653 Fibs slightly: subclasses of builtin types are not returned. Thus 

654 class_hierarchy(class A(object)) returns (A, object), not A plus every 

655 class systemwide that derives from object. 

656 

657 Old-style classes are discarded and hierarchies rooted on them 

658 will not be descended. 

659 

660 """ 

661 if compat.py2k: 

662 if isinstance(cls, types.ClassType): 

663 return list() 

664 

665 hier = {cls} 

666 process = list(cls.__mro__) 

667 while process: 

668 c = process.pop() 

669 if compat.py2k: 

670 if isinstance(c, types.ClassType): 

671 continue 

672 bases = ( 

673 _ 

674 for _ in c.__bases__ 

675 if _ not in hier and not isinstance(_, types.ClassType) 

676 ) 

677 else: 

678 bases = (_ for _ in c.__bases__ if _ not in hier) 

679 

680 for b in bases: 

681 process.append(b) 

682 hier.add(b) 

683 

684 if compat.py3k: 

685 if c.__module__ == "builtins" or not hasattr(c, "__subclasses__"): 

686 continue 

687 else: 

688 if c.__module__ == "__builtin__" or not hasattr( 

689 c, "__subclasses__" 

690 ): 

691 continue 

692 

693 for s in [_ for _ in c.__subclasses__() if _ not in hier]: 

694 process.append(s) 

695 hier.add(s) 

696 return list(hier) 

697 

698 

699def iterate_attributes(cls): 

700 """iterate all the keys and attributes associated 

701 with a class, without using getattr(). 

702 

703 Does not use getattr() so that class-sensitive 

704 descriptors (i.e. property.__get__()) are not called. 

705 

706 """ 

707 keys = dir(cls) 

708 for key in keys: 

709 for c in cls.__mro__: 

710 if key in c.__dict__: 

711 yield (key, c.__dict__[key]) 

712 break 

713 

714 

715def monkeypatch_proxied_specials( 

716 into_cls, 

717 from_cls, 

718 skip=None, 

719 only=None, 

720 name="self.proxy", 

721 from_instance=None, 

722): 

723 """Automates delegation of __specials__ for a proxying type.""" 

724 

725 if only: 

726 dunders = only 

727 else: 

728 if skip is None: 

729 skip = ( 

730 "__slots__", 

731 "__del__", 

732 "__getattribute__", 

733 "__metaclass__", 

734 "__getstate__", 

735 "__setstate__", 

736 ) 

737 dunders = [ 

738 m 

739 for m in dir(from_cls) 

740 if ( 

741 m.startswith("__") 

742 and m.endswith("__") 

743 and not hasattr(into_cls, m) 

744 and m not in skip 

745 ) 

746 ] 

747 

748 for method in dunders: 

749 try: 

750 fn = getattr(from_cls, method) 

751 if not hasattr(fn, "__call__"): 

752 continue 

753 fn = getattr(fn, "im_func", fn) 

754 except AttributeError: 

755 continue 

756 try: 

757 spec = compat.inspect_getfullargspec(fn) 

758 fn_args = compat.inspect_formatargspec(spec[0]) 

759 d_args = compat.inspect_formatargspec(spec[0][1:]) 

760 except TypeError: 

761 fn_args = "(self, *args, **kw)" 

762 d_args = "(*args, **kw)" 

763 

764 py = ( 

765 "def %(method)s%(fn_args)s: " 

766 "return %(name)s.%(method)s%(d_args)s" % locals() 

767 ) 

768 

769 env = from_instance is not None and {name: from_instance} or {} 

770 compat.exec_(py, env) 

771 try: 

772 env[method].__defaults__ = fn.__defaults__ 

773 except AttributeError: 

774 pass 

775 setattr(into_cls, method, env[method]) 

776 

777 

778def methods_equivalent(meth1, meth2): 

779 """Return True if the two methods are the same implementation.""" 

780 

781 return getattr(meth1, "__func__", meth1) is getattr( 

782 meth2, "__func__", meth2 

783 ) 

784 

785 

786def as_interface(obj, cls=None, methods=None, required=None): 

787 """Ensure basic interface compliance for an instance or dict of callables. 

788 

789 Checks that ``obj`` implements public methods of ``cls`` or has members 

790 listed in ``methods``. If ``required`` is not supplied, implementing at 

791 least one interface method is sufficient. Methods present on ``obj`` that 

792 are not in the interface are ignored. 

793 

794 If ``obj`` is a dict and ``dict`` does not meet the interface 

795 requirements, the keys of the dictionary are inspected. Keys present in 

796 ``obj`` that are not in the interface will raise TypeErrors. 

797 

798 Raises TypeError if ``obj`` does not meet the interface criteria. 

799 

800 In all passing cases, an object with callable members is returned. In the 

801 simple case, ``obj`` is returned as-is; if dict processing kicks in then 

802 an anonymous class is returned. 

803 

804 obj 

805 A type, instance, or dictionary of callables. 

806 cls 

807 Optional, a type. All public methods of cls are considered the 

808 interface. An ``obj`` instance of cls will always pass, ignoring 

809 ``required``.. 

810 methods 

811 Optional, a sequence of method names to consider as the interface. 

812 required 

813 Optional, a sequence of mandatory implementations. If omitted, an 

814 ``obj`` that provides at least one interface method is considered 

815 sufficient. As a convenience, required may be a type, in which case 

816 all public methods of the type are required. 

817 

818 """ 

819 if not cls and not methods: 

820 raise TypeError("a class or collection of method names are required") 

821 

822 if isinstance(cls, type) and isinstance(obj, cls): 

823 return obj 

824 

825 interface = set(methods or [m for m in dir(cls) if not m.startswith("_")]) 

826 implemented = set(dir(obj)) 

827 

828 complies = operator.ge 

829 if isinstance(required, type): 

830 required = interface 

831 elif not required: 

832 required = set() 

833 complies = operator.gt 

834 else: 

835 required = set(required) 

836 

837 if complies(implemented.intersection(interface), required): 

838 return obj 

839 

840 # No dict duck typing here. 

841 if not isinstance(obj, dict): 

842 qualifier = complies is operator.gt and "any of" or "all of" 

843 raise TypeError( 

844 "%r does not implement %s: %s" 

845 % (obj, qualifier, ", ".join(interface)) 

846 ) 

847 

848 class AnonymousInterface(object): 

849 """A callable-holding shell.""" 

850 

851 if cls: 

852 AnonymousInterface.__name__ = "Anonymous" + cls.__name__ 

853 found = set() 

854 

855 for method, impl in dictlike_iteritems(obj): 

856 if method not in interface: 

857 raise TypeError("%r: unknown in this interface" % method) 

858 if not compat.callable(impl): 

859 raise TypeError("%r=%r is not callable" % (method, impl)) 

860 setattr(AnonymousInterface, method, staticmethod(impl)) 

861 found.add(method) 

862 

863 if complies(found, required): 

864 return AnonymousInterface 

865 

866 raise TypeError( 

867 "dictionary does not contain required keys %s" 

868 % ", ".join(required - found) 

869 ) 

870 

871 

872class memoized_property(object): 

873 """A read-only @property that is only evaluated once.""" 

874 

875 def __init__(self, fget, doc=None): 

876 self.fget = fget 

877 self.__doc__ = doc or fget.__doc__ 

878 self.__name__ = fget.__name__ 

879 

880 def __get__(self, obj, cls): 

881 if obj is None: 

882 return self 

883 obj.__dict__[self.__name__] = result = self.fget(obj) 

884 return result 

885 

886 def _reset(self, obj): 

887 memoized_property.reset(obj, self.__name__) 

888 

889 @classmethod 

890 def reset(cls, obj, name): 

891 obj.__dict__.pop(name, None) 

892 

893 

894def memoized_instancemethod(fn): 

895 """Decorate a method memoize its return value. 

896 

897 Best applied to no-arg methods: memoization is not sensitive to 

898 argument values, and will always return the same value even when 

899 called with different arguments. 

900 

901 """ 

902 

903 def oneshot(self, *args, **kw): 

904 result = fn(self, *args, **kw) 

905 

906 def memo(*a, **kw): 

907 return result 

908 

909 memo.__name__ = fn.__name__ 

910 memo.__doc__ = fn.__doc__ 

911 self.__dict__[fn.__name__] = memo 

912 return result 

913 

914 return update_wrapper(oneshot, fn) 

915 

916 

917class group_expirable_memoized_property(object): 

918 """A family of @memoized_properties that can be expired in tandem.""" 

919 

920 def __init__(self, attributes=()): 

921 self.attributes = [] 

922 if attributes: 

923 self.attributes.extend(attributes) 

924 

925 def expire_instance(self, instance): 

926 """Expire all memoized properties for *instance*.""" 

927 stash = instance.__dict__ 

928 for attribute in self.attributes: 

929 stash.pop(attribute, None) 

930 

931 def __call__(self, fn): 

932 self.attributes.append(fn.__name__) 

933 return memoized_property(fn) 

934 

935 def method(self, fn): 

936 self.attributes.append(fn.__name__) 

937 return memoized_instancemethod(fn) 

938 

939 

940class MemoizedSlots(object): 

941 """Apply memoized items to an object using a __getattr__ scheme. 

942 

943 This allows the functionality of memoized_property and 

944 memoized_instancemethod to be available to a class using __slots__. 

945 

946 """ 

947 

948 __slots__ = () 

949 

950 def _fallback_getattr(self, key): 

951 raise AttributeError(key) 

952 

953 def __getattr__(self, key): 

954 if key.startswith("_memoized"): 

955 raise AttributeError(key) 

956 elif hasattr(self, "_memoized_attr_%s" % key): 

957 value = getattr(self, "_memoized_attr_%s" % key)() 

958 setattr(self, key, value) 

959 return value 

960 elif hasattr(self, "_memoized_method_%s" % key): 

961 fn = getattr(self, "_memoized_method_%s" % key) 

962 

963 def oneshot(*args, **kw): 

964 result = fn(*args, **kw) 

965 

966 def memo(*a, **kw): 

967 return result 

968 

969 memo.__name__ = fn.__name__ 

970 memo.__doc__ = fn.__doc__ 

971 setattr(self, key, memo) 

972 return result 

973 

974 oneshot.__doc__ = fn.__doc__ 

975 return oneshot 

976 else: 

977 return self._fallback_getattr(key) 

978 

979 

980def dependency_for(modulename, add_to_all=False): 

981 def decorate(obj): 

982 tokens = modulename.split(".") 

983 mod = compat.import_( 

984 ".".join(tokens[0:-1]), globals(), locals(), [tokens[-1]] 

985 ) 

986 mod = getattr(mod, tokens[-1]) 

987 setattr(mod, obj.__name__, obj) 

988 if add_to_all and hasattr(mod, "__all__"): 

989 mod.__all__.append(obj.__name__) 

990 return obj 

991 

992 return decorate 

993 

994 

995class dependencies(object): 

996 """Apply imported dependencies as arguments to a function. 

997 

998 E.g.:: 

999 

1000 @util.dependencies( 

1001 "sqlalchemy.sql.widget", 

1002 "sqlalchemy.engine.default" 

1003 ); 

1004 def some_func(self, widget, default, arg1, arg2, **kw): 

1005 # ... 

1006 

1007 Rationale is so that the impact of a dependency cycle can be 

1008 associated directly with the few functions that cause the cycle, 

1009 and not pollute the module-level namespace. 

1010 

1011 """ 

1012 

1013 def __init__(self, *deps): 

1014 self.import_deps = [] 

1015 for dep in deps: 

1016 tokens = dep.split(".") 

1017 self.import_deps.append( 

1018 dependencies._importlater(".".join(tokens[0:-1]), tokens[-1]) 

1019 ) 

1020 

1021 def __call__(self, fn): 

1022 import_deps = self.import_deps 

1023 spec = compat.inspect_getfullargspec(fn) 

1024 

1025 spec_zero = list(spec[0]) 

1026 hasself = spec_zero[0] in ("self", "cls") 

1027 

1028 for i in range(len(import_deps)): 

1029 spec[0][i + (1 if hasself else 0)] = "import_deps[%r]" % i 

1030 

1031 inner_spec = format_argspec_plus(spec, grouped=False) 

1032 

1033 for impname in import_deps: 

1034 del spec_zero[1 if hasself else 0] 

1035 spec[0][:] = spec_zero 

1036 

1037 outer_spec = format_argspec_plus(spec, grouped=False) 

1038 

1039 code = "lambda %(args)s: fn(%(apply_kw)s)" % { 

1040 "args": outer_spec["args"], 

1041 "apply_kw": inner_spec["apply_kw"], 

1042 } 

1043 

1044 decorated = eval(code, locals()) 

1045 decorated.__defaults__ = getattr(fn, "im_func", fn).__defaults__ 

1046 return update_wrapper(decorated, fn) 

1047 

1048 @classmethod 

1049 def resolve_all(cls, path): 

1050 for m in list(dependencies._unresolved): 

1051 if m._full_path.startswith(path): 

1052 m._resolve() 

1053 

1054 _unresolved = set() 

1055 _by_key = {} 

1056 

1057 class _importlater(object): 

1058 _unresolved = set() 

1059 

1060 _by_key = {} 

1061 

1062 def __new__(cls, path, addtl): 

1063 key = path + "." + addtl 

1064 if key in dependencies._by_key: 

1065 return dependencies._by_key[key] 

1066 else: 

1067 dependencies._by_key[key] = imp = object.__new__(cls) 

1068 return imp 

1069 

1070 def __init__(self, path, addtl): 

1071 self._il_path = path 

1072 self._il_addtl = addtl 

1073 dependencies._unresolved.add(self) 

1074 

1075 @property 

1076 def _full_path(self): 

1077 return self._il_path + "." + self._il_addtl 

1078 

1079 @memoized_property 

1080 def module(self): 

1081 if self in dependencies._unresolved: 

1082 raise ImportError( 

1083 "importlater.resolve_all() hasn't " 

1084 "been called (this is %s %s)" 

1085 % (self._il_path, self._il_addtl) 

1086 ) 

1087 

1088 return getattr(self._initial_import, self._il_addtl) 

1089 

1090 def _resolve(self): 

1091 dependencies._unresolved.discard(self) 

1092 self._initial_import = compat.import_( 

1093 self._il_path, globals(), locals(), [self._il_addtl] 

1094 ) 

1095 

1096 def __getattr__(self, key): 

1097 if key == "module": 

1098 raise ImportError( 

1099 "Could not resolve module %s" % self._full_path 

1100 ) 

1101 try: 

1102 attr = getattr(self.module, key) 

1103 except AttributeError: 

1104 raise AttributeError( 

1105 "Module %s has no attribute '%s'" % (self._full_path, key) 

1106 ) 

1107 self.__dict__[key] = attr 

1108 return attr 

1109 

1110 

1111# from paste.deploy.converters 

1112def asbool(obj): 

1113 if isinstance(obj, compat.string_types): 

1114 obj = obj.strip().lower() 

1115 if obj in ["true", "yes", "on", "y", "t", "1"]: 

1116 return True 

1117 elif obj in ["false", "no", "off", "n", "f", "0"]: 

1118 return False 

1119 else: 

1120 raise ValueError("String is not true/false: %r" % obj) 

1121 return bool(obj) 

1122 

1123 

1124def bool_or_str(*text): 

1125 """Return a callable that will evaluate a string as 

1126 boolean, or one of a set of "alternate" string values. 

1127 

1128 """ 

1129 

1130 def bool_or_value(obj): 

1131 if obj in text: 

1132 return obj 

1133 else: 

1134 return asbool(obj) 

1135 

1136 return bool_or_value 

1137 

1138 

1139def asint(value): 

1140 """Coerce to integer.""" 

1141 

1142 if value is None: 

1143 return value 

1144 return int(value) 

1145 

1146 

1147def coerce_kw_type(kw, key, type_, flexi_bool=True, dest=None): 

1148 r"""If 'key' is present in dict 'kw', coerce its value to type 'type\_' if 

1149 necessary. If 'flexi_bool' is True, the string '0' is considered false 

1150 when coercing to boolean. 

1151 """ 

1152 

1153 if dest is None: 

1154 dest = kw 

1155 

1156 if ( 

1157 key in kw 

1158 and (not isinstance(type_, type) or not isinstance(kw[key], type_)) 

1159 and kw[key] is not None 

1160 ): 

1161 if type_ is bool and flexi_bool: 

1162 dest[key] = asbool(kw[key]) 

1163 else: 

1164 dest[key] = type_(kw[key]) 

1165 

1166 

1167def constructor_copy(obj, cls, *args, **kw): 

1168 """Instantiate cls using the __dict__ of obj as constructor arguments. 

1169 

1170 Uses inspect to match the named arguments of ``cls``. 

1171 

1172 """ 

1173 

1174 names = get_cls_kwargs(cls) 

1175 kw.update( 

1176 (k, obj.__dict__[k]) for k in names.difference(kw) if k in obj.__dict__ 

1177 ) 

1178 return cls(*args, **kw) 

1179 

1180 

1181def counter(): 

1182 """Return a threadsafe counter function.""" 

1183 

1184 lock = compat.threading.Lock() 

1185 counter = itertools.count(1) 

1186 

1187 # avoid the 2to3 "next" transformation... 

1188 def _next(): 

1189 lock.acquire() 

1190 try: 

1191 return next(counter) 

1192 finally: 

1193 lock.release() 

1194 

1195 return _next 

1196 

1197 

1198def duck_type_collection(specimen, default=None): 

1199 """Given an instance or class, guess if it is or is acting as one of 

1200 the basic collection types: list, set and dict. If the __emulates__ 

1201 property is present, return that preferentially. 

1202 """ 

1203 

1204 if hasattr(specimen, "__emulates__"): 

1205 # canonicalize set vs sets.Set to a standard: the builtin set 

1206 if specimen.__emulates__ is not None and issubclass( 

1207 specimen.__emulates__, set 

1208 ): 

1209 return set 

1210 else: 

1211 return specimen.__emulates__ 

1212 

1213 isa = isinstance(specimen, type) and issubclass or isinstance 

1214 if isa(specimen, list): 

1215 return list 

1216 elif isa(specimen, set): 

1217 return set 

1218 elif isa(specimen, dict): 

1219 return dict 

1220 

1221 if hasattr(specimen, "append"): 

1222 return list 

1223 elif hasattr(specimen, "add"): 

1224 return set 

1225 elif hasattr(specimen, "set"): 

1226 return dict 

1227 else: 

1228 return default 

1229 

1230 

1231def assert_arg_type(arg, argtype, name): 

1232 if isinstance(arg, argtype): 

1233 return arg 

1234 else: 

1235 if isinstance(argtype, tuple): 

1236 raise exc.ArgumentError( 

1237 "Argument '%s' is expected to be one of type %s, got '%s'" 

1238 % (name, " or ".join("'%s'" % a for a in argtype), type(arg)) 

1239 ) 

1240 else: 

1241 raise exc.ArgumentError( 

1242 "Argument '%s' is expected to be of type '%s', got '%s'" 

1243 % (name, argtype, type(arg)) 

1244 ) 

1245 

1246 

1247def dictlike_iteritems(dictlike): 

1248 """Return a (key, value) iterator for almost any dict-like object.""" 

1249 

1250 if compat.py3k: 

1251 if hasattr(dictlike, "items"): 

1252 return list(dictlike.items()) 

1253 else: 

1254 if hasattr(dictlike, "iteritems"): 

1255 return dictlike.iteritems() 

1256 elif hasattr(dictlike, "items"): 

1257 return iter(dictlike.items()) 

1258 

1259 getter = getattr(dictlike, "__getitem__", getattr(dictlike, "get", None)) 

1260 if getter is None: 

1261 raise TypeError("Object '%r' is not dict-like" % dictlike) 

1262 

1263 if hasattr(dictlike, "iterkeys"): 

1264 

1265 def iterator(): 

1266 for key in dictlike.iterkeys(): 

1267 yield key, getter(key) 

1268 

1269 return iterator() 

1270 elif hasattr(dictlike, "keys"): 

1271 return iter((key, getter(key)) for key in dictlike.keys()) 

1272 else: 

1273 raise TypeError("Object '%r' is not dict-like" % dictlike) 

1274 

1275 

1276class classproperty(property): 

1277 """A decorator that behaves like @property except that operates 

1278 on classes rather than instances. 

1279 

1280 The decorator is currently special when using the declarative 

1281 module, but note that the 

1282 :class:`~.sqlalchemy.ext.declarative.declared_attr` 

1283 decorator should be used for this purpose with declarative. 

1284 

1285 """ 

1286 

1287 def __init__(self, fget, *arg, **kw): 

1288 super(classproperty, self).__init__(fget, *arg, **kw) 

1289 self.__doc__ = fget.__doc__ 

1290 

1291 def __get__(desc, self, cls): 

1292 return desc.fget(cls) 

1293 

1294 

1295class hybridproperty(object): 

1296 def __init__(self, func): 

1297 self.func = func 

1298 

1299 def __get__(self, instance, owner): 

1300 if instance is None: 

1301 clsval = self.func(owner) 

1302 clsval.__doc__ = self.func.__doc__ 

1303 return clsval 

1304 else: 

1305 return self.func(instance) 

1306 

1307 

1308class hybridmethod(object): 

1309 """Decorate a function as cls- or instance- level.""" 

1310 

1311 def __init__(self, func): 

1312 self.func = func 

1313 

1314 def __get__(self, instance, owner): 

1315 if instance is None: 

1316 return self.func.__get__(owner, owner.__class__) 

1317 else: 

1318 return self.func.__get__(instance, owner) 

1319 

1320 

1321class _symbol(int): 

1322 def __new__(self, name, doc=None, canonical=None): 

1323 """Construct a new named symbol.""" 

1324 assert isinstance(name, compat.string_types) 

1325 if canonical is None: 

1326 canonical = hash(name) 

1327 v = int.__new__(_symbol, canonical) 

1328 v.name = name 

1329 if doc: 

1330 v.__doc__ = doc 

1331 return v 

1332 

1333 def __reduce__(self): 

1334 return symbol, (self.name, "x", int(self)) 

1335 

1336 def __str__(self): 

1337 return repr(self) 

1338 

1339 def __repr__(self): 

1340 return "symbol(%r)" % self.name 

1341 

1342 

1343_symbol.__name__ = "symbol" 

1344 

1345 

1346class symbol(object): 

1347 """A constant symbol. 

1348 

1349 >>> symbol('foo') is symbol('foo') 

1350 True 

1351 >>> symbol('foo') 

1352 <symbol 'foo> 

1353 

1354 A slight refinement of the MAGICCOOKIE=object() pattern. The primary 

1355 advantage of symbol() is its repr(). They are also singletons. 

1356 

1357 Repeated calls of symbol('name') will all return the same instance. 

1358 

1359 The optional ``doc`` argument assigns to ``__doc__``. This 

1360 is strictly so that Sphinx autoattr picks up the docstring we want 

1361 (it doesn't appear to pick up the in-module docstring if the datamember 

1362 is in a different module - autoattribute also blows up completely). 

1363 If Sphinx fixes/improves this then we would no longer need 

1364 ``doc`` here. 

1365 

1366 """ 

1367 

1368 symbols = {} 

1369 _lock = compat.threading.Lock() 

1370 

1371 def __new__(cls, name, doc=None, canonical=None): 

1372 cls._lock.acquire() 

1373 try: 

1374 sym = cls.symbols.get(name) 

1375 if sym is None: 

1376 cls.symbols[name] = sym = _symbol(name, doc, canonical) 

1377 return sym 

1378 finally: 

1379 symbol._lock.release() 

1380 

1381 @classmethod 

1382 def parse_user_argument( 

1383 cls, arg, choices, name, resolve_symbol_names=False 

1384 ): 

1385 """Given a user parameter, parse the parameter into a chosen symbol. 

1386 

1387 The user argument can be a string name that matches the name of a 

1388 symbol, or the symbol object itself, or any number of alternate choices 

1389 such as True/False/ None etc. 

1390 

1391 :param arg: the user argument. 

1392 :param choices: dictionary of symbol object to list of possible 

1393 entries. 

1394 :param name: name of the argument. Used in an :class:`.ArgumentError` 

1395 that is raised if the parameter doesn't match any available argument. 

1396 :param resolve_symbol_names: include the name of each symbol as a valid 

1397 entry. 

1398 

1399 """ 

1400 # note using hash lookup is tricky here because symbol's `__hash__` 

1401 # is its int value which we don't want included in the lookup 

1402 # explicitly, so we iterate and compare each. 

1403 for sym, choice in choices.items(): 

1404 if arg is sym: 

1405 return sym 

1406 elif resolve_symbol_names and arg == sym.name: 

1407 return sym 

1408 elif arg in choice: 

1409 return sym 

1410 

1411 if arg is None: 

1412 return None 

1413 

1414 raise exc.ArgumentError("Invalid value for '%s': %r" % (name, arg)) 

1415 

1416 

1417_creation_order = 1 

1418 

1419 

1420def set_creation_order(instance): 

1421 """Assign a '_creation_order' sequence to the given instance. 

1422 

1423 This allows multiple instances to be sorted in order of creation 

1424 (typically within a single thread; the counter is not particularly 

1425 threadsafe). 

1426 

1427 """ 

1428 global _creation_order 

1429 instance._creation_order = _creation_order 

1430 _creation_order += 1 

1431 

1432 

1433def warn_exception(func, *args, **kwargs): 

1434 """executes the given function, catches all exceptions and converts to 

1435 a warning. 

1436 

1437 """ 

1438 try: 

1439 return func(*args, **kwargs) 

1440 except Exception: 

1441 warn("%s('%s') ignored" % sys.exc_info()[0:2]) 

1442 

1443 

1444def ellipses_string(value, len_=25): 

1445 try: 

1446 if len(value) > len_: 

1447 return "%s..." % value[0:len_] 

1448 else: 

1449 return value 

1450 except TypeError: 

1451 return value 

1452 

1453 

1454class _hash_limit_string(compat.text_type): 

1455 """A string subclass that can only be hashed on a maximum amount 

1456 of unique values. 

1457 

1458 This is used for warnings so that we can send out parameterized warnings 

1459 without the __warningregistry__ of the module, or the non-overridable 

1460 "once" registry within warnings.py, overloading memory, 

1461 

1462 

1463 """ 

1464 

1465 def __new__(cls, value, num, args): 

1466 interpolated = (value % args) + ( 

1467 " (this warning may be suppressed after %d occurrences)" % num 

1468 ) 

1469 self = super(_hash_limit_string, cls).__new__(cls, interpolated) 

1470 self._hash = hash("%s_%d" % (value, hash(interpolated) % num)) 

1471 return self 

1472 

1473 def __hash__(self): 

1474 return self._hash 

1475 

1476 def __eq__(self, other): 

1477 return hash(self) == hash(other) 

1478 

1479 

1480def warn(msg): 

1481 """Issue a warning. 

1482 

1483 If msg is a string, :class:`.exc.SAWarning` is used as 

1484 the category. 

1485 

1486 """ 

1487 warnings.warn(msg, exc.SAWarning, stacklevel=2) 

1488 

1489 

1490def warn_limited(msg, args): 

1491 """Issue a warning with a parameterized string, limiting the number 

1492 of registrations. 

1493 

1494 """ 

1495 if args: 

1496 msg = _hash_limit_string(msg, 10, args) 

1497 warnings.warn(msg, exc.SAWarning, stacklevel=2) 

1498 

1499 

1500def only_once(fn, retry_on_exception): 

1501 """Decorate the given function to be a no-op after it is called exactly 

1502 once.""" 

1503 

1504 once = [fn] 

1505 

1506 def go(*arg, **kw): 

1507 # strong reference fn so that it isn't garbage collected, 

1508 # which interferes with the event system's expectations 

1509 strong_fn = fn # noqa 

1510 if once: 

1511 once_fn = once.pop() 

1512 try: 

1513 return once_fn(*arg, **kw) 

1514 except: 

1515 if retry_on_exception: 

1516 once.insert(0, once_fn) 

1517 raise 

1518 

1519 return go 

1520 

1521 

1522_SQLA_RE = re.compile(r"sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py") 

1523_UNITTEST_RE = re.compile(r"unit(?:2|test2?/)") 

1524 

1525 

1526def chop_traceback(tb, exclude_prefix=_UNITTEST_RE, exclude_suffix=_SQLA_RE): 

1527 """Chop extraneous lines off beginning and end of a traceback. 

1528 

1529 :param tb: 

1530 a list of traceback lines as returned by ``traceback.format_stack()`` 

1531 

1532 :param exclude_prefix: 

1533 a regular expression object matching lines to skip at beginning of 

1534 ``tb`` 

1535 

1536 :param exclude_suffix: 

1537 a regular expression object matching lines to skip at end of ``tb`` 

1538 """ 

1539 start = 0 

1540 end = len(tb) - 1 

1541 while start <= end and exclude_prefix.search(tb[start]): 

1542 start += 1 

1543 while start <= end and exclude_suffix.search(tb[end]): 

1544 end -= 1 

1545 return tb[start : end + 1] 

1546 

1547 

1548NoneType = type(None) 

1549 

1550 

1551def attrsetter(attrname): 

1552 code = "def set(obj, value):" " obj.%s = value" % attrname 

1553 env = locals().copy() 

1554 exec(code, env) 

1555 return env["set"] 

1556 

1557 

1558class EnsureKWArgType(type): 

1559 r"""Apply translation of functions to accept \**kw arguments if they 

1560 don't already. 

1561 

1562 """ 

1563 

1564 def __init__(cls, clsname, bases, clsdict): 

1565 fn_reg = cls.ensure_kwarg 

1566 if fn_reg: 

1567 for key in clsdict: 

1568 m = re.match(fn_reg, key) 

1569 if m: 

1570 fn = clsdict[key] 

1571 spec = compat.inspect_getfullargspec(fn) 

1572 if not spec.varkw: 

1573 clsdict[key] = wrapped = cls._wrap_w_kw(fn) 

1574 setattr(cls, key, wrapped) 

1575 super(EnsureKWArgType, cls).__init__(clsname, bases, clsdict) 

1576 

1577 def _wrap_w_kw(self, fn): 

1578 def wrap(*arg, **kw): 

1579 return fn(*arg) 

1580 

1581 return update_wrapper(wrap, fn) 

1582 

1583 

1584def wrap_callable(wrapper, fn): 

1585 """Augment functools.update_wrapper() to work with objects with 

1586 a ``__call__()`` method. 

1587 

1588 :param fn: 

1589 object with __call__ method 

1590 

1591 """ 

1592 if hasattr(fn, "__name__"): 

1593 return update_wrapper(wrapper, fn) 

1594 else: 

1595 _f = wrapper 

1596 _f.__name__ = fn.__class__.__name__ 

1597 if hasattr(fn, "__module__"): 

1598 _f.__module__ = fn.__module__ 

1599 

1600 if hasattr(fn.__call__, "__doc__") and fn.__call__.__doc__: 

1601 _f.__doc__ = fn.__call__.__doc__ 

1602 elif fn.__doc__: 

1603 _f.__doc__ = fn.__doc__ 

1604 

1605 return _f 

1606 

1607 

1608def quoted_token_parser(value): 

1609 """Parse a dotted identifier with accommodation for quoted names. 

1610 

1611 Includes support for SQL-style double quotes as a literal character. 

1612 

1613 E.g.:: 

1614 

1615 >>> quoted_token_parser("name") 

1616 ["name"] 

1617 >>> quoted_token_parser("schema.name") 

1618 ["schema", "name"] 

1619 >>> quoted_token_parser('"Schema"."Name"') 

1620 ['Schema', 'Name'] 

1621 >>> quoted_token_parser('"Schema"."Name""Foo"') 

1622 ['Schema', 'Name""Foo'] 

1623 

1624 """ 

1625 

1626 if '"' not in value: 

1627 return value.split(".") 

1628 

1629 # 0 = outside of quotes 

1630 # 1 = inside of quotes 

1631 state = 0 

1632 result = [[]] 

1633 idx = 0 

1634 lv = len(value) 

1635 while idx < lv: 

1636 char = value[idx] 

1637 if char == '"': 

1638 if state == 1 and idx < lv - 1 and value[idx + 1] == '"': 

1639 result[-1].append('"') 

1640 idx += 1 

1641 else: 

1642 state ^= 1 

1643 elif char == "." and state == 0: 

1644 result.append([]) 

1645 else: 

1646 result[-1].append(char) 

1647 idx += 1 

1648 

1649 return ["".join(token) for token in result] 

1650 

1651 

1652def add_parameter_text(params, text): 

1653 params = _collections.to_list(params) 

1654 

1655 def decorate(fn): 

1656 doc = fn.__doc__ is not None and fn.__doc__ or "" 

1657 if doc: 

1658 doc = inject_param_text(doc, {param: text for param in params}) 

1659 fn.__doc__ = doc 

1660 return fn 

1661 

1662 return decorate 

1663 

1664 

1665def _dedent_docstring(text): 

1666 split_text = text.split("\n", 1) 

1667 if len(split_text) == 1: 

1668 return text 

1669 else: 

1670 firstline, remaining = split_text 

1671 if not firstline.startswith(" "): 

1672 return firstline + "\n" + textwrap.dedent(remaining) 

1673 else: 

1674 return textwrap.dedent(text) 

1675 

1676 

1677def inject_docstring_text(doctext, injecttext, pos): 

1678 doctext = _dedent_docstring(doctext or "") 

1679 lines = doctext.split("\n") 

1680 if len(lines) == 1: 

1681 lines.append("") 

1682 injectlines = textwrap.dedent(injecttext).split("\n") 

1683 if injectlines[0]: 

1684 injectlines.insert(0, "") 

1685 

1686 blanks = [num for num, line in enumerate(lines) if not line.strip()] 

1687 blanks.insert(0, 0) 

1688 

1689 inject_pos = blanks[min(pos, len(blanks) - 1)] 

1690 

1691 lines = lines[0:inject_pos] + injectlines + lines[inject_pos:] 

1692 return "\n".join(lines) 

1693 

1694 

1695def inject_param_text(doctext, inject_params): 

1696 doclines = doctext.splitlines() 

1697 lines = [] 

1698 

1699 to_inject = None 

1700 while doclines: 

1701 line = doclines.pop(0) 

1702 if to_inject is None: 

1703 m = re.match(r"(\s+):param (?:\\\*\*?)?(.+?):", line) 

1704 if m: 

1705 param = m.group(2) 

1706 if param in inject_params: 

1707 # default indent to that of :param: plus one 

1708 indent = " " * len(m.group(1)) + " " 

1709 

1710 # but if the next line has text, use that line's 

1711 # indentntation 

1712 if doclines: 

1713 m2 = re.match(r"(\s+)\S", doclines[0]) 

1714 if m2: 

1715 indent = " " * len(m2.group(1)) 

1716 

1717 to_inject = indent + inject_params[param] 

1718 elif line.lstrip().startswith(":param "): 

1719 lines.append("\n") 

1720 lines.append(to_inject) 

1721 lines.append("\n") 

1722 to_inject = None 

1723 elif not line.rstrip(): 

1724 lines.append(line) 

1725 lines.append(to_inject) 

1726 lines.append("\n") 

1727 to_inject = None 

1728 elif line.endswith("::"): 

1729 # TODO: this still wont cover if the code example itself has blank 

1730 # lines in it, need to detect those via indentation. 

1731 lines.append(line) 

1732 lines.append( 

1733 doclines.pop(0) 

1734 ) # the blank line following a code example 

1735 continue 

1736 lines.append(line) 

1737 

1738 return "\n".join(lines)