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

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 

7 

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

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

10 

11""" 

12 

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 

24 

25from . import _collections 

26from . import compat 

27from .. import exc 

28 

29 

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() 

36 

37 

38class safe_reraise(object): 

39 """Reraise an exception after invoking some 

40 handler code. 

41 

42 Stores the existing exception info before 

43 invoking so that it is maintained across a potential 

44 coroutine context switch. 

45 

46 e.g.:: 

47 

48 try: 

49 sess.commit() 

50 except: 

51 with safe_reraise(): 

52 sess.rollback() 

53 

54 """ 

55 

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

57 

58 def __init__(self, warn_only=False): 

59 self.warn_only = warn_only 

60 

61 def __enter__(self): 

62 self._exc_info = sys.exc_info() 

63 

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) 

85 

86 

87def walk_subclasses(cls): 

88 seen = set() 

89 

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 

99 

100 

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 

109 

110 

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 ) 

115 

116 

117def method_is_overridden(instance_or_cls, against_method): 

118 """Return True if the two class methods don't match.""" 

119 

120 if not isinstance(instance_or_cls, type): 

121 current_cls = instance_or_cls.__class__ 

122 else: 

123 current_cls = instance_or_cls 

124 

125 method_name = against_method.__name__ 

126 

127 current_method = getattr(current_cls, method_name) 

128 

129 return current_method != against_method 

130 

131 

132def decode_slice(slc): 

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

134 

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

136 

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) 

144 

145 

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) 

160 

161 

162def map_bits(fn, n): 

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

164 

165 while n: 

166 b = n & (~n + 1) 

167 yield fn(b) 

168 n ^= b 

169 

170 

171def decorator(target): 

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

173 

174 def decorate(fn): 

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

176 raise Exception("not a decoratable function") 

177 

178 spec = compat.inspect_getfullargspec(fn) 

179 env = {} 

180 

181 spec = _update_argspec_defaults_into_env(spec, env) 

182 

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

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

185 

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__}) 

197 

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) 

202 

203 return update_wrapper(decorate, target) 

204 

205 

206def _update_argspec_defaults_into_env(spec, env): 

207 """given a FullArgSpec, convert defaults to be symbol names in an env.""" 

208 

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 

225 

226 

227def _exec_code_in_env(code, env, fn_name): 

228 exec(code, env) 

229 return env[fn_name] 

230 

231 

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

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

234 

235 Rationale here is so that the __init__ method of the 

236 class can serve as documentation for the function. 

237 

238 """ 

239 

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 ) 

259 

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] 

279 

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__) 

285 

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__ 

299 

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 ) 

306 

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) 

315 

316 return decorated 

317 

318 

319class PluginLoader(object): 

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

321 self.group = group 

322 self.impls = {} 

323 self.auto_fn = auto_fn 

324 

325 def clear(self): 

326 self.impls.clear() 

327 

328 def load(self, name): 

329 if name in self.impls: 

330 return self.impls[name]() 

331 

332 if self.auto_fn: 

333 loader = self.auto_fn(name) 

334 if loader: 

335 self.impls[name] = loader 

336 return loader() 

337 

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() 

342 

343 raise exc.NoSuchModuleError( 

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

345 ) 

346 

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) 

353 

354 self.impls[name] = load 

355 

356 

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 ) 

375 

376 

377def get_cls_kwargs(cls, _set=None): 

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

379 

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. 

384 

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. 

388 

389 No anonymous tuple arguments please ! 

390 

391 """ 

392 toplevel = _set is None 

393 if toplevel: 

394 _set = set() 

395 

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

397 

398 has_init = ( 

399 ctr 

400 and isinstance(ctr, types.FunctionType) 

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

402 ) 

403 

404 if has_init: 

405 names, has_kw = _inspect_func_args(ctr) 

406 _set.update(names) 

407 

408 if not has_kw and not toplevel: 

409 return None 

410 

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 

415 

416 _set.discard("self") 

417 return _set 

418 

419 

420def get_func_kwargs(func): 

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

422 

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

424 etc. 

425 

426 """ 

427 

428 return compat.inspect_getfullargspec(func)[0] 

429 

430 

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

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

433 

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. 

438 

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) 

483 

484 

485def format_argspec_plus(fn, grouped=True): 

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

487 

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

489 

490 fn 

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

492 grouped 

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

494 

495 Returns: 

496 

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 

509 

510 Example:: 

511 

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)'} 

517 

518 """ 

519 if compat.callable(fn): 

520 spec = compat.inspect_getfullargspec(fn) 

521 else: 

522 spec = fn 

523 

524 args = compat.inspect_formatargspec(*spec) 

525 

526 apply_pos = compat.inspect_formatargspec( 

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

528 ) 

529 

530 if spec[0]: 

531 self_arg = spec[0][0] 

532 

533 apply_pos_proxied = compat.inspect_formatargspec( 

534 spec[0][1:], spec[1], spec[2], None, spec[4] 

535 ) 

536 

537 elif spec[1]: 

538 # I'm not sure what this is 

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

540 

541 apply_pos_proxied = apply_pos 

542 else: 

543 self_arg = None 

544 apply_pos_proxied = apply_pos 

545 

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] 

552 

553 if num_defaults: 

554 defaulted_vals = name_args[0 - num_defaults :] 

555 else: 

556 defaulted_vals = () 

557 

558 apply_kw = compat.inspect_formatargspec( 

559 name_args, 

560 spec[1], 

561 spec[2], 

562 defaulted_vals, 

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

564 ) 

565 

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 

576 

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 ) 

595 

596 

597def format_argspec_init(method, grouped=True): 

598 """format_argspec_plus with considerations for typical __init__ methods 

599 

600 Wraps format_argspec_plus with error handling strategies for typical 

601 __init__ cases:: 

602 

603 object.__init__ -> (self) 

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

605 

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 ) 

628 

629 

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. 

639 

640 The class to be instrumented must define a single accessor "_proxied". 

641 

642 """ 

643 

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__} 

649 

650 spec = _update_argspec_defaults_into_env(spec, env) 

651 caller_argspec = format_argspec_plus(spec, grouped=False) 

652 

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 } 

660 

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 ) 

674 

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 ) 

684 

685 if clslevel: 

686 proxy_fn = classmethod(proxy_fn) 

687 

688 return proxy_fn 

689 

690 def makeprop(name): 

691 attr = target_cls.__dict__.get(name, None) 

692 

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 

706 

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} 

715 

716 getset = _exec_code_in_env(code, {"doc": doc}, "getset") 

717 

718 return getset 

719 

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)) 

726 

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)) 

733 

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)) 

740 

741 return cls 

742 

743 return decorate 

744 

745 

746def getargspec_init(method): 

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

748 

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

750 

751 object.__init__ -> (self) 

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

753 

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) 

762 

763 

764def unbound_method_to_callable(func_or_cls): 

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

766 required. 

767 

768 """ 

769 

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 

774 

775 

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. 

779 

780 """ 

781 if to_inspect is None: 

782 to_inspect = [obj] 

783 else: 

784 to_inspect = _collections.to_list(to_inspect) 

785 

786 missing = object() 

787 

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 ) 

809 

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 = [] 

820 

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

822 

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

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

825 

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 

835 

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 

844 

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

846 

847 

848class portable_instancemethod(object): 

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

850 to produce a serializable callable. 

851 

852 """ 

853 

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

855 

856 def __getstate__(self): 

857 return { 

858 "target": self.target, 

859 "name": self.name, 

860 "kwargs": self.kwargs, 

861 } 

862 

863 def __setstate__(self, state): 

864 self.target = state["target"] 

865 self.name = state["name"] 

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

867 

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

869 self.target = meth.__self__ 

870 self.name = meth.__name__ 

871 self.kwargs = kwargs 

872 

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

874 kw.update(self.kwargs) 

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

876 

877 

878def class_hierarchy(cls): 

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

880 

881 Traverses diamond hierarchies. 

882 

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. 

886 

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

888 will not be descended. 

889 

890 """ 

891 if compat.py2k: 

892 if isinstance(cls, types.ClassType): 

893 return list() 

894 

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) 

909 

910 for b in bases: 

911 process.append(b) 

912 hier.add(b) 

913 

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 

922 

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

924 process.append(s) 

925 hier.add(s) 

926 return list(hier) 

927 

928 

929def iterate_attributes(cls): 

930 """iterate all the keys and attributes associated 

931 with a class, without using getattr(). 

932 

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

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

935 

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 

943 

944 

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.""" 

954 

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 ] 

977 

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)" 

993 

994 py = ( 

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

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

997 ) 

998 

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]) 

1006 

1007 

1008def methods_equivalent(meth1, meth2): 

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

1010 

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

1012 meth2, "__func__", meth2 

1013 ) 

1014 

1015 

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

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

1018 

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. 

1023 

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. 

1027 

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

1029 

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. 

1033 

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. 

1047 

1048 """ 

1049 if not cls and not methods: 

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

1051 

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

1053 return obj 

1054 

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

1056 implemented = set(dir(obj)) 

1057 

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) 

1066 

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

1068 return obj 

1069 

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 ) 

1077 

1078 class AnonymousInterface(object): 

1079 """A callable-holding shell.""" 

1080 

1081 if cls: 

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

1083 found = set() 

1084 

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) 

1092 

1093 if complies(found, required): 

1094 return AnonymousInterface 

1095 

1096 raise TypeError( 

1097 "dictionary does not contain required keys %s" 

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

1099 ) 

1100 

1101 

1102class memoized_property(object): 

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

1104 

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

1106 self.fget = fget 

1107 self.__doc__ = doc or fget.__doc__ 

1108 self.__name__ = fget.__name__ 

1109 

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 

1115 

1116 def _reset(self, obj): 

1117 memoized_property.reset(obj, self.__name__) 

1118 

1119 @classmethod 

1120 def reset(cls, obj, name): 

1121 obj.__dict__.pop(name, None) 

1122 

1123 

1124def memoized_instancemethod(fn): 

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

1126 

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. 

1130 

1131 """ 

1132 

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

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

1135 

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

1137 return result 

1138 

1139 memo.__name__ = fn.__name__ 

1140 memo.__doc__ = fn.__doc__ 

1141 self.__dict__[fn.__name__] = memo 

1142 return result 

1143 

1144 return update_wrapper(oneshot, fn) 

1145 

1146 

1147class HasMemoized(object): 

1148 """A class that maintains the names of memoized elements in a 

1149 collection for easy cache clearing, generative, etc. 

1150 

1151 """ 

1152 

1153 __slots__ = () 

1154 

1155 _memoized_keys = frozenset() 

1156 

1157 def _reset_memoizations(self): 

1158 for elem in self._memoized_keys: 

1159 self.__dict__.pop(elem, None) 

1160 

1161 def _assert_no_memoizations(self): 

1162 for elem in self._memoized_keys: 

1163 assert elem not in self.__dict__ 

1164 

1165 def _set_memoized_attribute(self, key, value): 

1166 self.__dict__[key] = value 

1167 self._memoized_keys |= {key} 

1168 

1169 class memoized_attribute(object): 

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

1171 

1172 :meta private: 

1173 

1174 """ 

1175 

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

1177 self.fget = fget 

1178 self.__doc__ = doc or fget.__doc__ 

1179 self.__name__ = fget.__name__ 

1180 

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 

1187 

1188 @classmethod 

1189 def memoized_instancemethod(cls, fn): 

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

1191 

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

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

1194 

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

1196 return result 

1197 

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 

1203 

1204 return update_wrapper(oneshot, fn) 

1205 

1206 

1207class MemoizedSlots(object): 

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

1209 

1210 This allows the functionality of memoized_property and 

1211 memoized_instancemethod to be available to a class using __slots__. 

1212 

1213 """ 

1214 

1215 __slots__ = () 

1216 

1217 def _fallback_getattr(self, key): 

1218 raise AttributeError(key) 

1219 

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) 

1229 

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

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

1232 

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

1234 return result 

1235 

1236 memo.__name__ = fn.__name__ 

1237 memo.__doc__ = fn.__doc__ 

1238 setattr(self, key, memo) 

1239 return result 

1240 

1241 oneshot.__doc__ = fn.__doc__ 

1242 return oneshot 

1243 else: 

1244 return self._fallback_getattr(key) 

1245 

1246 

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) 

1258 

1259 

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. 

1263 

1264 """ 

1265 

1266 def bool_or_value(obj): 

1267 if obj in text: 

1268 return obj 

1269 else: 

1270 return asbool(obj) 

1271 

1272 return bool_or_value 

1273 

1274 

1275def asint(value): 

1276 """Coerce to integer.""" 

1277 

1278 if value is None: 

1279 return value 

1280 return int(value) 

1281 

1282 

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 """ 

1288 

1289 if dest is None: 

1290 dest = kw 

1291 

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]) 

1301 

1302 

1303def constructor_key(obj, cls): 

1304 """Produce a tuple structure that is cacheable using the __dict__ of 

1305 obj to retrieve values 

1306 

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 ) 

1312 

1313 

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

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

1316 

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

1318 

1319 """ 

1320 

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) 

1326 

1327 

1328def counter(): 

1329 """Return a threadsafe counter function.""" 

1330 

1331 lock = compat.threading.Lock() 

1332 counter = itertools.count(1) 

1333 

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

1335 def _next(): 

1336 with lock: 

1337 return next(counter) 

1338 

1339 return _next 

1340 

1341 

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 """ 

1347 

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__ 

1356 

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 

1364 

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 

1373 

1374 

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 ) 

1389 

1390 

1391def dictlike_iteritems(dictlike): 

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

1393 

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()) 

1402 

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

1404 if getter is None: 

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

1406 

1407 if hasattr(dictlike, "iterkeys"): 

1408 

1409 def iterator(): 

1410 for key in dictlike.iterkeys(): 

1411 yield key, getter(key) 

1412 

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) 

1418 

1419 

1420class classproperty(property): 

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

1422 on classes rather than instances. 

1423 

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. 

1428 

1429 """ 

1430 

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

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

1433 self.__doc__ = fget.__doc__ 

1434 

1435 def __get__(desc, self, cls): 

1436 return desc.fget(cls) 

1437 

1438 

1439class hybridproperty(object): 

1440 def __init__(self, func): 

1441 self.func = func 

1442 self.clslevel = func 

1443 

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) 

1450 

1451 def classlevel(self, func): 

1452 self.clslevel = func 

1453 return self 

1454 

1455 

1456class hybridmethod(object): 

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

1458 

1459 def __init__(self, func): 

1460 self.func = self.__func__ = func 

1461 self.clslevel = func 

1462 

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) 

1468 

1469 def classlevel(self, func): 

1470 self.clslevel = func 

1471 return self 

1472 

1473 

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 

1485 

1486 def __reduce__(self): 

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

1488 

1489 def __str__(self): 

1490 return repr(self) 

1491 

1492 def __repr__(self): 

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

1494 

1495 

1496_symbol.__name__ = "symbol" 

1497 

1498 

1499class symbol(object): 

1500 """A constant symbol. 

1501 

1502 >>> symbol('foo') is symbol('foo') 

1503 True 

1504 >>> symbol('foo') 

1505 <symbol 'foo> 

1506 

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

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

1509 

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

1511 

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. 

1518 

1519 """ 

1520 

1521 symbols = {} 

1522 _lock = compat.threading.Lock() 

1523 

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 

1530 

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. 

1536 

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. 

1540 

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. 

1548 

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 

1560 

1561 if arg is None: 

1562 return None 

1563 

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

1565 

1566 

1567_creation_order = 1 

1568 

1569 

1570def set_creation_order(instance): 

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

1572 

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). 

1576 

1577 """ 

1578 global _creation_order 

1579 instance._creation_order = _creation_order 

1580 _creation_order += 1 

1581 

1582 

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

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

1585 a warning. 

1586 

1587 """ 

1588 try: 

1589 return func(*args, **kwargs) 

1590 except Exception: 

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

1592 

1593 

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 

1602 

1603 

1604class _hash_limit_string(compat.text_type): 

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

1606 of unique values. 

1607 

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, 

1611 

1612 

1613 """ 

1614 

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 

1622 

1623 def __hash__(self): 

1624 return self._hash 

1625 

1626 def __eq__(self, other): 

1627 return hash(self) == hash(other) 

1628 

1629 

1630def warn(msg, code=None): 

1631 """Issue a warning. 

1632 

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

1634 the category. 

1635 

1636 """ 

1637 if code: 

1638 _warnings_warn(exc.SAWarning(msg, code=code)) 

1639 else: 

1640 _warnings_warn(msg, exc.SAWarning) 

1641 

1642 

1643def warn_limited(msg, args): 

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

1645 of registrations. 

1646 

1647 """ 

1648 if args: 

1649 msg = _hash_limit_string(msg, 10, args) 

1650 _warnings_warn(msg, exc.SAWarning) 

1651 

1652 

1653def _warnings_warn(message, category=None, stacklevel=2): 

1654 

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 

1675 

1676 if category is not None: 

1677 warnings.warn(message, category, stacklevel=stacklevel + 1) 

1678 else: 

1679 warnings.warn(message, stacklevel=stacklevel + 1) 

1680 

1681 

1682def only_once(fn, retry_on_exception): 

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

1684 once.""" 

1685 

1686 once = [fn] 

1687 

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 

1700 

1701 return go 

1702 

1703 

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

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

1706 

1707 

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

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

1710 

1711 :param tb: 

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

1713 

1714 :param exclude_prefix: 

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

1716 ``tb`` 

1717 

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] 

1728 

1729 

1730NoneType = type(None) 

1731 

1732 

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"] 

1738 

1739 

1740class EnsureKWArgType(type): 

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

1742 don't already. 

1743 

1744 """ 

1745 

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) 

1758 

1759 def _wrap_w_kw(self, fn): 

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

1761 return fn(*arg) 

1762 

1763 return update_wrapper(wrap, fn) 

1764 

1765 

1766def wrap_callable(wrapper, fn): 

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

1768 a ``__call__()`` method. 

1769 

1770 :param fn: 

1771 object with __call__ method 

1772 

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__ 

1781 

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__ 

1786 

1787 return _f 

1788 

1789 

1790def quoted_token_parser(value): 

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

1792 

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

1794 

1795 E.g.:: 

1796 

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'] 

1805 

1806 """ 

1807 

1808 if '"' not in value: 

1809 return value.split(".") 

1810 

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 

1830 

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

1832 

1833 

1834def add_parameter_text(params, text): 

1835 params = _collections.to_list(params) 

1836 

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 

1843 

1844 return decorate 

1845 

1846 

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) 

1857 

1858 

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, "") 

1867 

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

1869 blanks.insert(0, 0) 

1870 

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

1872 

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

1874 return "\n".join(lines) 

1875 

1876 

1877_param_reg = re.compile(r"(\s+):param (.+?):") 

1878 

1879 

1880def inject_param_text(doctext, inject_params): 

1881 doclines = collections.deque(doctext.splitlines()) 

1882 lines = [] 

1883 

1884 # TODO: this is not working for params like ":param case_sensitive=True:" 

1885 

1886 to_inject = None 

1887 while doclines: 

1888 line = doclines.popleft() 

1889 

1890 m = _param_reg.match(line) 

1891 

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)) + " " 

1898 

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)) 

1905 

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) 

1919 

1920 return "\n".join(lines) 

1921 

1922 

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]) 

1935 

1936 

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 

1942 

1943 return True 

1944 except ImportError: 

1945 return False