[3] | 1 | # util.py |
---|
| 2 | # Copyright (C) 2005, 2006, 2007, 2008, 2009 Michael Bayer mike_mp@zzzcomputing.com |
---|
| 3 | # |
---|
| 4 | # This module is part of SQLAlchemy and is released under |
---|
| 5 | # the MIT License: http://www.opensource.org/licenses/mit-license.php |
---|
| 6 | |
---|
| 7 | import inspect, itertools, operator, sys, warnings, weakref |
---|
| 8 | import __builtin__ |
---|
| 9 | types = __import__('types') |
---|
| 10 | |
---|
| 11 | from sqlalchemy import exc |
---|
| 12 | |
---|
| 13 | try: |
---|
| 14 | import threading |
---|
| 15 | except ImportError: |
---|
| 16 | import dummy_threading as threading |
---|
| 17 | |
---|
| 18 | py3k = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0) |
---|
| 19 | |
---|
| 20 | if py3k: |
---|
| 21 | set_types = set |
---|
| 22 | elif sys.version_info < (2, 6): |
---|
| 23 | import sets |
---|
| 24 | set_types = set, sets.Set |
---|
| 25 | else: |
---|
| 26 | # 2.6 deprecates sets.Set, but we still need to be able to detect them |
---|
| 27 | # in user code and as return values from DB-APIs |
---|
| 28 | ignore = ('ignore', None, DeprecationWarning, None, 0) |
---|
| 29 | try: |
---|
| 30 | warnings.filters.insert(0, ignore) |
---|
| 31 | except Exception: |
---|
| 32 | import sets |
---|
| 33 | else: |
---|
| 34 | import sets |
---|
| 35 | warnings.filters.remove(ignore) |
---|
| 36 | |
---|
| 37 | set_types = set, sets.Set |
---|
| 38 | |
---|
| 39 | EMPTY_SET = frozenset() |
---|
| 40 | |
---|
| 41 | if py3k: |
---|
| 42 | import pickle |
---|
| 43 | else: |
---|
| 44 | try: |
---|
| 45 | import cPickle as pickle |
---|
| 46 | except ImportError: |
---|
| 47 | import pickle |
---|
| 48 | |
---|
| 49 | # a controversial feature, required by MySQLdb currently |
---|
| 50 | def buffer(x): |
---|
| 51 | return x |
---|
| 52 | |
---|
| 53 | buffer = getattr(__builtin__, 'buffer', buffer) |
---|
| 54 | |
---|
| 55 | if sys.version_info >= (2, 5): |
---|
| 56 | class PopulateDict(dict): |
---|
| 57 | """A dict which populates missing values via a creation function. |
---|
| 58 | |
---|
| 59 | Note the creation function takes a key, unlike |
---|
| 60 | collections.defaultdict. |
---|
| 61 | |
---|
| 62 | """ |
---|
| 63 | |
---|
| 64 | def __init__(self, creator): |
---|
| 65 | self.creator = creator |
---|
| 66 | |
---|
| 67 | def __missing__(self, key): |
---|
| 68 | self[key] = val = self.creator(key) |
---|
| 69 | return val |
---|
| 70 | else: |
---|
| 71 | class PopulateDict(dict): |
---|
| 72 | """A dict which populates missing values via a creation function.""" |
---|
| 73 | |
---|
| 74 | def __init__(self, creator): |
---|
| 75 | self.creator = creator |
---|
| 76 | |
---|
| 77 | def __getitem__(self, key): |
---|
| 78 | try: |
---|
| 79 | return dict.__getitem__(self, key) |
---|
| 80 | except KeyError: |
---|
| 81 | self[key] = value = self.creator(key) |
---|
| 82 | return value |
---|
| 83 | |
---|
| 84 | if py3k: |
---|
| 85 | def callable(fn): |
---|
| 86 | return hasattr(fn, '__call__') |
---|
| 87 | else: |
---|
| 88 | callable = __builtin__.callable |
---|
| 89 | |
---|
| 90 | if py3k: |
---|
| 91 | from functools import reduce |
---|
| 92 | else: |
---|
| 93 | reduce = __builtin__.reduce |
---|
| 94 | |
---|
| 95 | try: |
---|
| 96 | from collections import defaultdict |
---|
| 97 | except ImportError: |
---|
| 98 | class defaultdict(dict): |
---|
| 99 | def __init__(self, default_factory=None, *a, **kw): |
---|
| 100 | if (default_factory is not None and |
---|
| 101 | not hasattr(default_factory, '__call__')): |
---|
| 102 | raise TypeError('first argument must be callable') |
---|
| 103 | dict.__init__(self, *a, **kw) |
---|
| 104 | self.default_factory = default_factory |
---|
| 105 | def __getitem__(self, key): |
---|
| 106 | try: |
---|
| 107 | return dict.__getitem__(self, key) |
---|
| 108 | except KeyError: |
---|
| 109 | return self.__missing__(key) |
---|
| 110 | def __missing__(self, key): |
---|
| 111 | if self.default_factory is None: |
---|
| 112 | raise KeyError(key) |
---|
| 113 | self[key] = value = self.default_factory() |
---|
| 114 | return value |
---|
| 115 | def __reduce__(self): |
---|
| 116 | if self.default_factory is None: |
---|
| 117 | args = tuple() |
---|
| 118 | else: |
---|
| 119 | args = self.default_factory, |
---|
| 120 | return type(self), args, None, None, self.iteritems() |
---|
| 121 | def copy(self): |
---|
| 122 | return self.__copy__() |
---|
| 123 | def __copy__(self): |
---|
| 124 | return type(self)(self.default_factory, self) |
---|
| 125 | def __deepcopy__(self, memo): |
---|
| 126 | import copy |
---|
| 127 | return type(self)(self.default_factory, |
---|
| 128 | copy.deepcopy(self.items())) |
---|
| 129 | def __repr__(self): |
---|
| 130 | return 'defaultdict(%s, %s)' % (self.default_factory, |
---|
| 131 | dict.__repr__(self)) |
---|
| 132 | |
---|
| 133 | |
---|
| 134 | def to_list(x, default=None): |
---|
| 135 | if x is None: |
---|
| 136 | return default |
---|
| 137 | if not isinstance(x, (list, tuple)): |
---|
| 138 | return [x] |
---|
| 139 | else: |
---|
| 140 | return x |
---|
| 141 | |
---|
| 142 | def to_set(x): |
---|
| 143 | if x is None: |
---|
| 144 | return set() |
---|
| 145 | if not isinstance(x, set): |
---|
| 146 | return set(to_list(x)) |
---|
| 147 | else: |
---|
| 148 | return x |
---|
| 149 | |
---|
| 150 | def to_column_set(x): |
---|
| 151 | if x is None: |
---|
| 152 | return column_set() |
---|
| 153 | if not isinstance(x, column_set): |
---|
| 154 | return column_set(to_list(x)) |
---|
| 155 | else: |
---|
| 156 | return x |
---|
| 157 | |
---|
| 158 | |
---|
| 159 | try: |
---|
| 160 | from functools import update_wrapper |
---|
| 161 | except ImportError: |
---|
| 162 | def update_wrapper(wrapper, wrapped, |
---|
| 163 | assigned=('__doc__', '__module__', '__name__'), |
---|
| 164 | updated=('__dict__',)): |
---|
| 165 | for attr in assigned: |
---|
| 166 | setattr(wrapper, attr, getattr(wrapped, attr)) |
---|
| 167 | for attr in updated: |
---|
| 168 | getattr(wrapper, attr).update(getattr(wrapped, attr, ())) |
---|
| 169 | return wrapper |
---|
| 170 | |
---|
| 171 | try: |
---|
| 172 | from functools import partial |
---|
| 173 | except: |
---|
| 174 | def partial(func, *args, **keywords): |
---|
| 175 | def newfunc(*fargs, **fkeywords): |
---|
| 176 | newkeywords = keywords.copy() |
---|
| 177 | newkeywords.update(fkeywords) |
---|
| 178 | return func(*(args + fargs), **newkeywords) |
---|
| 179 | return newfunc |
---|
| 180 | |
---|
| 181 | |
---|
| 182 | def accepts_a_list_as_starargs(list_deprecation=None): |
---|
| 183 | def decorate(fn): |
---|
| 184 | |
---|
| 185 | spec = inspect.getargspec(fn) |
---|
| 186 | assert spec[1], 'Decorated function does not accept *args' |
---|
| 187 | |
---|
| 188 | def _deprecate(): |
---|
| 189 | if list_deprecation: |
---|
| 190 | if list_deprecation == 'pending': |
---|
| 191 | warning_type = exc.SAPendingDeprecationWarning |
---|
| 192 | else: |
---|
| 193 | warning_type = exc.SADeprecationWarning |
---|
| 194 | msg = ( |
---|
| 195 | "%s%s now accepts multiple %s arguments as a " |
---|
| 196 | "variable argument list. Supplying %s as a single " |
---|
| 197 | "list is deprecated and support will be removed " |
---|
| 198 | "in a future release." % ( |
---|
| 199 | fn.func_name, |
---|
| 200 | inspect.formatargspec(*spec), |
---|
| 201 | spec[1], spec[1])) |
---|
| 202 | warnings.warn(msg, warning_type, stacklevel=3) |
---|
| 203 | |
---|
| 204 | def go(fn, *args, **kw): |
---|
| 205 | if isinstance(args[-1], list): |
---|
| 206 | _deprecate() |
---|
| 207 | return fn(*(list(args[0:-1]) + args[-1]), **kw) |
---|
| 208 | else: |
---|
| 209 | return fn(*args, **kw) |
---|
| 210 | |
---|
| 211 | return decorator(go)(fn) |
---|
| 212 | |
---|
| 213 | return decorate |
---|
| 214 | |
---|
| 215 | def unique_symbols(used, *bases): |
---|
| 216 | used = set(used) |
---|
| 217 | for base in bases: |
---|
| 218 | pool = itertools.chain((base,), |
---|
| 219 | itertools.imap(lambda i: base + str(i), |
---|
| 220 | xrange(1000))) |
---|
| 221 | for sym in pool: |
---|
| 222 | if sym not in used: |
---|
| 223 | used.add(sym) |
---|
| 224 | yield sym |
---|
| 225 | break |
---|
| 226 | else: |
---|
| 227 | raise NameError("exhausted namespace for symbol base %s" % base) |
---|
| 228 | |
---|
| 229 | def decorator(target): |
---|
| 230 | """A signature-matching decorator factory.""" |
---|
| 231 | |
---|
| 232 | def decorate(fn): |
---|
| 233 | spec = inspect.getargspec(fn) |
---|
| 234 | names = tuple(spec[0]) + spec[1:3] + (fn.func_name,) |
---|
| 235 | targ_name, fn_name = unique_symbols(names, 'target', 'fn') |
---|
| 236 | |
---|
| 237 | metadata = dict(target=targ_name, fn=fn_name) |
---|
| 238 | metadata.update(format_argspec_plus(spec, grouped=False)) |
---|
| 239 | |
---|
| 240 | code = 'lambda %(args)s: %(target)s(%(fn)s, %(apply_kw)s)' % ( |
---|
| 241 | metadata) |
---|
| 242 | decorated = eval(code, {targ_name:target, fn_name:fn}) |
---|
| 243 | decorated.func_defaults = getattr(fn, 'im_func', fn).func_defaults |
---|
| 244 | return update_wrapper(decorated, fn) |
---|
| 245 | return update_wrapper(decorate, target) |
---|
| 246 | |
---|
| 247 | |
---|
| 248 | if sys.version_info >= (2, 5): |
---|
| 249 | def decode_slice(slc): |
---|
| 250 | """decode a slice object as sent to __getitem__. |
---|
| 251 | |
---|
| 252 | takes into account the 2.5 __index__() method, basically. |
---|
| 253 | |
---|
| 254 | """ |
---|
| 255 | ret = [] |
---|
| 256 | for x in slc.start, slc.stop, slc.step: |
---|
| 257 | if hasattr(x, '__index__'): |
---|
| 258 | x = x.__index__() |
---|
| 259 | ret.append(x) |
---|
| 260 | return tuple(ret) |
---|
| 261 | else: |
---|
| 262 | def decode_slice(slc): |
---|
| 263 | return (slc.start, slc.stop, slc.step) |
---|
| 264 | |
---|
| 265 | def flatten_iterator(x): |
---|
| 266 | """Given an iterator of which further sub-elements may also be |
---|
| 267 | iterators, flatten the sub-elements into a single iterator. |
---|
| 268 | |
---|
| 269 | """ |
---|
| 270 | for elem in x: |
---|
| 271 | if not isinstance(elem, basestring) and hasattr(elem, '__iter__'): |
---|
| 272 | for y in flatten_iterator(elem): |
---|
| 273 | yield y |
---|
| 274 | else: |
---|
| 275 | yield elem |
---|
| 276 | |
---|
| 277 | def get_cls_kwargs(cls): |
---|
| 278 | """Return the full set of inherited kwargs for the given `cls`. |
---|
| 279 | |
---|
| 280 | Probes a class's __init__ method, collecting all named arguments. If the |
---|
| 281 | __init__ defines a \**kwargs catch-all, then the constructor is presumed to |
---|
| 282 | pass along unrecognized keywords to it's base classes, and the collection |
---|
| 283 | process is repeated recursively on each of the bases. |
---|
| 284 | |
---|
| 285 | """ |
---|
| 286 | |
---|
| 287 | for c in cls.__mro__: |
---|
| 288 | if '__init__' in c.__dict__: |
---|
| 289 | stack = set([c]) |
---|
| 290 | break |
---|
| 291 | else: |
---|
| 292 | return [] |
---|
| 293 | |
---|
| 294 | args = set() |
---|
| 295 | while stack: |
---|
| 296 | class_ = stack.pop() |
---|
| 297 | ctr = class_.__dict__.get('__init__', False) |
---|
| 298 | if not ctr or not isinstance(ctr, types.FunctionType): |
---|
| 299 | continue |
---|
| 300 | names, _, has_kw, _ = inspect.getargspec(ctr) |
---|
| 301 | args.update(names) |
---|
| 302 | if has_kw: |
---|
| 303 | stack.update(class_.__bases__) |
---|
| 304 | args.discard('self') |
---|
| 305 | return list(args) |
---|
| 306 | |
---|
| 307 | def get_func_kwargs(func): |
---|
| 308 | """Return the full set of legal kwargs for the given `func`.""" |
---|
| 309 | return inspect.getargspec(func)[0] |
---|
| 310 | |
---|
| 311 | def format_argspec_plus(fn, grouped=True): |
---|
| 312 | """Returns a dictionary of formatted, introspected function arguments. |
---|
| 313 | |
---|
| 314 | A enhanced variant of inspect.formatargspec to support code generation. |
---|
| 315 | |
---|
| 316 | fn |
---|
| 317 | An inspectable callable or tuple of inspect getargspec() results. |
---|
| 318 | grouped |
---|
| 319 | Defaults to True; include (parens, around, argument) lists |
---|
| 320 | |
---|
| 321 | Returns: |
---|
| 322 | |
---|
| 323 | args |
---|
| 324 | Full inspect.formatargspec for fn |
---|
| 325 | self_arg |
---|
| 326 | The name of the first positional argument, varargs[0], or None |
---|
| 327 | if the function defines no positional arguments. |
---|
| 328 | apply_pos |
---|
| 329 | args, re-written in calling rather than receiving syntax. Arguments are |
---|
| 330 | passed positionally. |
---|
| 331 | apply_kw |
---|
| 332 | Like apply_pos, except keyword-ish args are passed as keywords. |
---|
| 333 | |
---|
| 334 | Example:: |
---|
| 335 | |
---|
| 336 | >>> format_argspec_plus(lambda self, a, b, c=3, **d: 123) |
---|
| 337 | {'args': '(self, a, b, c=3, **d)', |
---|
| 338 | 'self_arg': 'self', |
---|
| 339 | 'apply_kw': '(self, a, b, c=c, **d)', |
---|
| 340 | 'apply_pos': '(self, a, b, c, **d)'} |
---|
| 341 | |
---|
| 342 | """ |
---|
| 343 | spec = callable(fn) and inspect.getargspec(fn) or fn |
---|
| 344 | args = inspect.formatargspec(*spec) |
---|
| 345 | if spec[0]: |
---|
| 346 | self_arg = spec[0][0] |
---|
| 347 | elif spec[1]: |
---|
| 348 | self_arg = '%s[0]' % spec[1] |
---|
| 349 | else: |
---|
| 350 | self_arg = None |
---|
| 351 | apply_pos = inspect.formatargspec(spec[0], spec[1], spec[2]) |
---|
| 352 | defaulted_vals = spec[3] is not None and spec[0][0-len(spec[3]):] or () |
---|
| 353 | apply_kw = inspect.formatargspec(spec[0], spec[1], spec[2], defaulted_vals, |
---|
| 354 | formatvalue=lambda x: '=' + x) |
---|
| 355 | if grouped: |
---|
| 356 | return dict(args=args, self_arg=self_arg, |
---|
| 357 | apply_pos=apply_pos, apply_kw=apply_kw) |
---|
| 358 | else: |
---|
| 359 | return dict(args=args[1:-1], self_arg=self_arg, |
---|
| 360 | apply_pos=apply_pos[1:-1], apply_kw=apply_kw[1:-1]) |
---|
| 361 | |
---|
| 362 | def format_argspec_init(method, grouped=True): |
---|
| 363 | """format_argspec_plus with considerations for typical __init__ methods |
---|
| 364 | |
---|
| 365 | Wraps format_argspec_plus with error handling strategies for typical |
---|
| 366 | __init__ cases:: |
---|
| 367 | |
---|
| 368 | object.__init__ -> (self) |
---|
| 369 | other unreflectable (usually C) -> (self, *args, **kwargs) |
---|
| 370 | |
---|
| 371 | """ |
---|
| 372 | try: |
---|
| 373 | return format_argspec_plus(method, grouped=grouped) |
---|
| 374 | except TypeError: |
---|
| 375 | self_arg = 'self' |
---|
| 376 | if method is object.__init__: |
---|
| 377 | args = grouped and '(self)' or 'self' |
---|
| 378 | else: |
---|
| 379 | args = (grouped and '(self, *args, **kwargs)' |
---|
| 380 | or 'self, *args, **kwargs') |
---|
| 381 | return dict(self_arg='self', args=args, apply_pos=args, apply_kw=args) |
---|
| 382 | |
---|
| 383 | def getargspec_init(method): |
---|
| 384 | """inspect.getargspec with considerations for typical __init__ methods |
---|
| 385 | |
---|
| 386 | Wraps inspect.getargspec with error handling for typical __init__ cases:: |
---|
| 387 | |
---|
| 388 | object.__init__ -> (self) |
---|
| 389 | other unreflectable (usually C) -> (self, *args, **kwargs) |
---|
| 390 | |
---|
| 391 | """ |
---|
| 392 | try: |
---|
| 393 | return inspect.getargspec(method) |
---|
| 394 | except TypeError: |
---|
| 395 | if method is object.__init__: |
---|
| 396 | return (['self'], None, None, None) |
---|
| 397 | else: |
---|
| 398 | return (['self'], 'args', 'kwargs', None) |
---|
| 399 | |
---|
| 400 | |
---|
| 401 | def unbound_method_to_callable(func_or_cls): |
---|
| 402 | """Adjust the incoming callable such that a 'self' argument is not required.""" |
---|
| 403 | |
---|
| 404 | if isinstance(func_or_cls, types.MethodType) and not func_or_cls.im_self: |
---|
| 405 | return func_or_cls.im_func |
---|
| 406 | else: |
---|
| 407 | return func_or_cls |
---|
| 408 | |
---|
| 409 | def class_hierarchy(cls): |
---|
| 410 | """Return an unordered sequence of all classes related to cls. |
---|
| 411 | |
---|
| 412 | Traverses diamond hierarchies. |
---|
| 413 | |
---|
| 414 | Fibs slightly: subclasses of builtin types are not returned. Thus |
---|
| 415 | class_hierarchy(class A(object)) returns (A, object), not A plus every |
---|
| 416 | class systemwide that derives from object. |
---|
| 417 | |
---|
| 418 | Old-style classes are discarded and hierarchies rooted on them |
---|
| 419 | will not be descended. |
---|
| 420 | |
---|
| 421 | """ |
---|
| 422 | if isinstance(cls, types.ClassType): |
---|
| 423 | return list() |
---|
| 424 | hier = set([cls]) |
---|
| 425 | process = list(cls.__mro__) |
---|
| 426 | while process: |
---|
| 427 | c = process.pop() |
---|
| 428 | if isinstance(c, types.ClassType): |
---|
| 429 | continue |
---|
| 430 | for b in (_ for _ in c.__bases__ |
---|
| 431 | if _ not in hier and not isinstance(_, types.ClassType)): |
---|
| 432 | process.append(b) |
---|
| 433 | hier.add(b) |
---|
| 434 | if c.__module__ == '__builtin__' or not hasattr(c, '__subclasses__'): |
---|
| 435 | continue |
---|
| 436 | for s in [_ for _ in c.__subclasses__() if _ not in hier]: |
---|
| 437 | process.append(s) |
---|
| 438 | hier.add(s) |
---|
| 439 | return list(hier) |
---|
| 440 | |
---|
| 441 | def iterate_attributes(cls): |
---|
| 442 | """iterate all the keys and attributes associated with a class, without using getattr(). |
---|
| 443 | |
---|
| 444 | Does not use getattr() so that class-sensitive descriptors (i.e. property.__get__()) |
---|
| 445 | are not called. |
---|
| 446 | |
---|
| 447 | """ |
---|
| 448 | keys = dir(cls) |
---|
| 449 | for key in keys: |
---|
| 450 | for c in cls.__mro__: |
---|
| 451 | if key in c.__dict__: |
---|
| 452 | yield (key, c.__dict__[key]) |
---|
| 453 | break |
---|
| 454 | |
---|
| 455 | # from paste.deploy.converters |
---|
| 456 | def asbool(obj): |
---|
| 457 | if isinstance(obj, (str, unicode)): |
---|
| 458 | obj = obj.strip().lower() |
---|
| 459 | if obj in ['true', 'yes', 'on', 'y', 't', '1']: |
---|
| 460 | return True |
---|
| 461 | elif obj in ['false', 'no', 'off', 'n', 'f', '0']: |
---|
| 462 | return False |
---|
| 463 | else: |
---|
| 464 | raise ValueError("String is not true/false: %r" % obj) |
---|
| 465 | return bool(obj) |
---|
| 466 | |
---|
| 467 | def coerce_kw_type(kw, key, type_, flexi_bool=True): |
---|
| 468 | """If 'key' is present in dict 'kw', coerce its value to type 'type\_' if |
---|
| 469 | necessary. If 'flexi_bool' is True, the string '0' is considered false |
---|
| 470 | when coercing to boolean. |
---|
| 471 | """ |
---|
| 472 | |
---|
| 473 | if key in kw and type(kw[key]) is not type_ and kw[key] is not None: |
---|
| 474 | if type_ is bool and flexi_bool: |
---|
| 475 | kw[key] = asbool(kw[key]) |
---|
| 476 | else: |
---|
| 477 | kw[key] = type_(kw[key]) |
---|
| 478 | |
---|
| 479 | def duck_type_collection(specimen, default=None): |
---|
| 480 | """Given an instance or class, guess if it is or is acting as one of |
---|
| 481 | the basic collection types: list, set and dict. If the __emulates__ |
---|
| 482 | property is present, return that preferentially. |
---|
| 483 | """ |
---|
| 484 | |
---|
| 485 | if hasattr(specimen, '__emulates__'): |
---|
| 486 | # canonicalize set vs sets.Set to a standard: the builtin set |
---|
| 487 | if (specimen.__emulates__ is not None and |
---|
| 488 | issubclass(specimen.__emulates__, set_types)): |
---|
| 489 | return set |
---|
| 490 | else: |
---|
| 491 | return specimen.__emulates__ |
---|
| 492 | |
---|
| 493 | isa = isinstance(specimen, type) and issubclass or isinstance |
---|
| 494 | if isa(specimen, list): |
---|
| 495 | return list |
---|
| 496 | elif isa(specimen, set_types): |
---|
| 497 | return set |
---|
| 498 | elif isa(specimen, dict): |
---|
| 499 | return dict |
---|
| 500 | |
---|
| 501 | if hasattr(specimen, 'append'): |
---|
| 502 | return list |
---|
| 503 | elif hasattr(specimen, 'add'): |
---|
| 504 | return set |
---|
| 505 | elif hasattr(specimen, 'set'): |
---|
| 506 | return dict |
---|
| 507 | else: |
---|
| 508 | return default |
---|
| 509 | |
---|
| 510 | def dictlike_iteritems(dictlike): |
---|
| 511 | """Return a (key, value) iterator for almost any dict-like object.""" |
---|
| 512 | |
---|
| 513 | if hasattr(dictlike, 'iteritems'): |
---|
| 514 | return dictlike.iteritems() |
---|
| 515 | elif hasattr(dictlike, 'items'): |
---|
| 516 | return iter(dictlike.items()) |
---|
| 517 | |
---|
| 518 | getter = getattr(dictlike, '__getitem__', getattr(dictlike, 'get', None)) |
---|
| 519 | if getter is None: |
---|
| 520 | raise TypeError( |
---|
| 521 | "Object '%r' is not dict-like" % dictlike) |
---|
| 522 | |
---|
| 523 | if hasattr(dictlike, 'iterkeys'): |
---|
| 524 | def iterator(): |
---|
| 525 | for key in dictlike.iterkeys(): |
---|
| 526 | yield key, getter(key) |
---|
| 527 | return iterator() |
---|
| 528 | elif hasattr(dictlike, 'keys'): |
---|
| 529 | return iter((key, getter(key)) for key in dictlike.keys()) |
---|
| 530 | else: |
---|
| 531 | raise TypeError( |
---|
| 532 | "Object '%r' is not dict-like" % dictlike) |
---|
| 533 | |
---|
| 534 | def assert_arg_type(arg, argtype, name): |
---|
| 535 | if isinstance(arg, argtype): |
---|
| 536 | return arg |
---|
| 537 | else: |
---|
| 538 | if isinstance(argtype, tuple): |
---|
| 539 | raise exc.ArgumentError("Argument '%s' is expected to be one of type %s, got '%s'" % (name, ' or '.join("'%s'" % str(a) for a in argtype), str(type(arg)))) |
---|
| 540 | else: |
---|
| 541 | raise exc.ArgumentError("Argument '%s' is expected to be of type '%s', got '%s'" % (name, str(argtype), str(type(arg)))) |
---|
| 542 | |
---|
| 543 | _creation_order = 1 |
---|
| 544 | def set_creation_order(instance): |
---|
| 545 | """Assign a '_creation_order' sequence to the given instance. |
---|
| 546 | |
---|
| 547 | This allows multiple instances to be sorted in order of creation |
---|
| 548 | (typically within a single thread; the counter is not particularly |
---|
| 549 | threadsafe). |
---|
| 550 | |
---|
| 551 | """ |
---|
| 552 | global _creation_order |
---|
| 553 | instance._creation_order = _creation_order |
---|
| 554 | _creation_order +=1 |
---|
| 555 | |
---|
| 556 | def warn_exception(func, *args, **kwargs): |
---|
| 557 | """executes the given function, catches all exceptions and converts to a warning.""" |
---|
| 558 | try: |
---|
| 559 | return func(*args, **kwargs) |
---|
| 560 | except: |
---|
| 561 | warn("%s('%s') ignored" % sys.exc_info()[0:2]) |
---|
| 562 | |
---|
| 563 | def monkeypatch_proxied_specials(into_cls, from_cls, skip=None, only=None, |
---|
| 564 | name='self.proxy', from_instance=None): |
---|
| 565 | """Automates delegation of __specials__ for a proxying type.""" |
---|
| 566 | |
---|
| 567 | if only: |
---|
| 568 | dunders = only |
---|
| 569 | else: |
---|
| 570 | if skip is None: |
---|
| 571 | skip = ('__slots__', '__del__', '__getattribute__', |
---|
| 572 | '__metaclass__', '__getstate__', '__setstate__') |
---|
| 573 | dunders = [m for m in dir(from_cls) |
---|
| 574 | if (m.startswith('__') and m.endswith('__') and |
---|
| 575 | not hasattr(into_cls, m) and m not in skip)] |
---|
| 576 | for method in dunders: |
---|
| 577 | try: |
---|
| 578 | fn = getattr(from_cls, method) |
---|
| 579 | if not hasattr(fn, '__call__'): |
---|
| 580 | continue |
---|
| 581 | fn = getattr(fn, 'im_func', fn) |
---|
| 582 | except AttributeError: |
---|
| 583 | continue |
---|
| 584 | try: |
---|
| 585 | spec = inspect.getargspec(fn) |
---|
| 586 | fn_args = inspect.formatargspec(spec[0]) |
---|
| 587 | d_args = inspect.formatargspec(spec[0][1:]) |
---|
| 588 | except TypeError: |
---|
| 589 | fn_args = '(self, *args, **kw)' |
---|
| 590 | d_args = '(*args, **kw)' |
---|
| 591 | |
---|
| 592 | py = ("def %(method)s%(fn_args)s: " |
---|
| 593 | "return %(name)s.%(method)s%(d_args)s" % locals()) |
---|
| 594 | |
---|
| 595 | env = from_instance is not None and {name: from_instance} or {} |
---|
| 596 | exec py in env |
---|
| 597 | try: |
---|
| 598 | env[method].func_defaults = fn.func_defaults |
---|
| 599 | except AttributeError: |
---|
| 600 | pass |
---|
| 601 | setattr(into_cls, method, env[method]) |
---|
| 602 | |
---|
| 603 | |
---|
| 604 | class OrderedProperties(object): |
---|
| 605 | """An object that maintains the order in which attributes are set upon it. |
---|
| 606 | |
---|
| 607 | Also provides an iterator and a very basic getitem/setitem |
---|
| 608 | interface to those attributes. |
---|
| 609 | |
---|
| 610 | (Not really a dict, since it iterates over values, not keys. Not really |
---|
| 611 | a list, either, since each value must have a key associated; hence there is |
---|
| 612 | no append or extend.) |
---|
| 613 | """ |
---|
| 614 | |
---|
| 615 | def __init__(self): |
---|
| 616 | self.__dict__['_data'] = OrderedDict() |
---|
| 617 | |
---|
| 618 | def __len__(self): |
---|
| 619 | return len(self._data) |
---|
| 620 | |
---|
| 621 | def __iter__(self): |
---|
| 622 | return self._data.itervalues() |
---|
| 623 | |
---|
| 624 | def __add__(self, other): |
---|
| 625 | return list(self) + list(other) |
---|
| 626 | |
---|
| 627 | def __setitem__(self, key, object): |
---|
| 628 | self._data[key] = object |
---|
| 629 | |
---|
| 630 | def __getitem__(self, key): |
---|
| 631 | return self._data[key] |
---|
| 632 | |
---|
| 633 | def __delitem__(self, key): |
---|
| 634 | del self._data[key] |
---|
| 635 | |
---|
| 636 | def __setattr__(self, key, object): |
---|
| 637 | self._data[key] = object |
---|
| 638 | |
---|
| 639 | def __getstate__(self): |
---|
| 640 | return {'_data': self.__dict__['_data']} |
---|
| 641 | |
---|
| 642 | def __setstate__(self, state): |
---|
| 643 | self.__dict__['_data'] = state['_data'] |
---|
| 644 | |
---|
| 645 | def __getattr__(self, key): |
---|
| 646 | try: |
---|
| 647 | return self._data[key] |
---|
| 648 | except KeyError: |
---|
| 649 | raise AttributeError(key) |
---|
| 650 | |
---|
| 651 | def __contains__(self, key): |
---|
| 652 | return key in self._data |
---|
| 653 | |
---|
| 654 | def update(self, value): |
---|
| 655 | self._data.update(value) |
---|
| 656 | |
---|
| 657 | def get(self, key, default=None): |
---|
| 658 | if key in self: |
---|
| 659 | return self[key] |
---|
| 660 | else: |
---|
| 661 | return default |
---|
| 662 | |
---|
| 663 | def keys(self): |
---|
| 664 | return self._data.keys() |
---|
| 665 | |
---|
| 666 | def has_key(self, key): |
---|
| 667 | return self._data.has_key(key) |
---|
| 668 | |
---|
| 669 | def clear(self): |
---|
| 670 | self._data.clear() |
---|
| 671 | |
---|
| 672 | |
---|
| 673 | class OrderedDict(dict): |
---|
| 674 | """A dict that returns keys/values/items in the order they were added.""" |
---|
| 675 | |
---|
| 676 | def __init__(self, ____sequence=None, **kwargs): |
---|
| 677 | self._list = [] |
---|
| 678 | if ____sequence is None: |
---|
| 679 | if kwargs: |
---|
| 680 | self.update(**kwargs) |
---|
| 681 | else: |
---|
| 682 | self.update(____sequence, **kwargs) |
---|
| 683 | |
---|
| 684 | def clear(self): |
---|
| 685 | self._list = [] |
---|
| 686 | dict.clear(self) |
---|
| 687 | |
---|
| 688 | def copy(self): |
---|
| 689 | return self.__copy__() |
---|
| 690 | |
---|
| 691 | def __copy__(self): |
---|
| 692 | return OrderedDict(self) |
---|
| 693 | |
---|
| 694 | def sort(self, *arg, **kw): |
---|
| 695 | self._list.sort(*arg, **kw) |
---|
| 696 | |
---|
| 697 | def update(self, ____sequence=None, **kwargs): |
---|
| 698 | if ____sequence is not None: |
---|
| 699 | if hasattr(____sequence, 'keys'): |
---|
| 700 | for key in ____sequence.keys(): |
---|
| 701 | self.__setitem__(key, ____sequence[key]) |
---|
| 702 | else: |
---|
| 703 | for key, value in ____sequence: |
---|
| 704 | self[key] = value |
---|
| 705 | if kwargs: |
---|
| 706 | self.update(kwargs) |
---|
| 707 | |
---|
| 708 | def setdefault(self, key, value): |
---|
| 709 | if key not in self: |
---|
| 710 | self.__setitem__(key, value) |
---|
| 711 | return value |
---|
| 712 | else: |
---|
| 713 | return self.__getitem__(key) |
---|
| 714 | |
---|
| 715 | def __iter__(self): |
---|
| 716 | return iter(self._list) |
---|
| 717 | |
---|
| 718 | def values(self): |
---|
| 719 | return [self[key] for key in self._list] |
---|
| 720 | |
---|
| 721 | def itervalues(self): |
---|
| 722 | return iter(self.values()) |
---|
| 723 | |
---|
| 724 | def keys(self): |
---|
| 725 | return list(self._list) |
---|
| 726 | |
---|
| 727 | def iterkeys(self): |
---|
| 728 | return iter(self.keys()) |
---|
| 729 | |
---|
| 730 | def items(self): |
---|
| 731 | return [(key, self[key]) for key in self.keys()] |
---|
| 732 | |
---|
| 733 | def iteritems(self): |
---|
| 734 | return iter(self.items()) |
---|
| 735 | |
---|
| 736 | def __setitem__(self, key, object): |
---|
| 737 | if key not in self: |
---|
| 738 | self._list.append(key) |
---|
| 739 | dict.__setitem__(self, key, object) |
---|
| 740 | |
---|
| 741 | def __delitem__(self, key): |
---|
| 742 | dict.__delitem__(self, key) |
---|
| 743 | self._list.remove(key) |
---|
| 744 | |
---|
| 745 | def pop(self, key, *default): |
---|
| 746 | present = key in self |
---|
| 747 | value = dict.pop(self, key, *default) |
---|
| 748 | if present: |
---|
| 749 | self._list.remove(key) |
---|
| 750 | return value |
---|
| 751 | |
---|
| 752 | def popitem(self): |
---|
| 753 | item = dict.popitem(self) |
---|
| 754 | self._list.remove(item[0]) |
---|
| 755 | return item |
---|
| 756 | |
---|
| 757 | class OrderedSet(set): |
---|
| 758 | def __init__(self, d=None): |
---|
| 759 | set.__init__(self) |
---|
| 760 | self._list = [] |
---|
| 761 | if d is not None: |
---|
| 762 | self.update(d) |
---|
| 763 | |
---|
| 764 | def add(self, element): |
---|
| 765 | if element not in self: |
---|
| 766 | self._list.append(element) |
---|
| 767 | set.add(self, element) |
---|
| 768 | |
---|
| 769 | def remove(self, element): |
---|
| 770 | set.remove(self, element) |
---|
| 771 | self._list.remove(element) |
---|
| 772 | |
---|
| 773 | def insert(self, pos, element): |
---|
| 774 | if element not in self: |
---|
| 775 | self._list.insert(pos, element) |
---|
| 776 | set.add(self, element) |
---|
| 777 | |
---|
| 778 | def discard(self, element): |
---|
| 779 | if element in self: |
---|
| 780 | self._list.remove(element) |
---|
| 781 | set.remove(self, element) |
---|
| 782 | |
---|
| 783 | def clear(self): |
---|
| 784 | set.clear(self) |
---|
| 785 | self._list = [] |
---|
| 786 | |
---|
| 787 | def __getitem__(self, key): |
---|
| 788 | return self._list[key] |
---|
| 789 | |
---|
| 790 | def __iter__(self): |
---|
| 791 | return iter(self._list) |
---|
| 792 | |
---|
| 793 | def __repr__(self): |
---|
| 794 | return '%s(%r)' % (self.__class__.__name__, self._list) |
---|
| 795 | |
---|
| 796 | __str__ = __repr__ |
---|
| 797 | |
---|
| 798 | def update(self, iterable): |
---|
| 799 | add = self.add |
---|
| 800 | for i in iterable: |
---|
| 801 | add(i) |
---|
| 802 | return self |
---|
| 803 | |
---|
| 804 | __ior__ = update |
---|
| 805 | |
---|
| 806 | def union(self, other): |
---|
| 807 | result = self.__class__(self) |
---|
| 808 | result.update(other) |
---|
| 809 | return result |
---|
| 810 | |
---|
| 811 | __or__ = union |
---|
| 812 | |
---|
| 813 | def intersection(self, other): |
---|
| 814 | other = set(other) |
---|
| 815 | return self.__class__(a for a in self if a in other) |
---|
| 816 | |
---|
| 817 | __and__ = intersection |
---|
| 818 | |
---|
| 819 | def symmetric_difference(self, other): |
---|
| 820 | other = set(other) |
---|
| 821 | result = self.__class__(a for a in self if a not in other) |
---|
| 822 | result.update(a for a in other if a not in self) |
---|
| 823 | return result |
---|
| 824 | |
---|
| 825 | __xor__ = symmetric_difference |
---|
| 826 | |
---|
| 827 | def difference(self, other): |
---|
| 828 | other = set(other) |
---|
| 829 | return self.__class__(a for a in self if a not in other) |
---|
| 830 | |
---|
| 831 | __sub__ = difference |
---|
| 832 | |
---|
| 833 | def intersection_update(self, other): |
---|
| 834 | other = set(other) |
---|
| 835 | set.intersection_update(self, other) |
---|
| 836 | self._list = [ a for a in self._list if a in other] |
---|
| 837 | return self |
---|
| 838 | |
---|
| 839 | __iand__ = intersection_update |
---|
| 840 | |
---|
| 841 | def symmetric_difference_update(self, other): |
---|
| 842 | set.symmetric_difference_update(self, other) |
---|
| 843 | self._list = [ a for a in self._list if a in self] |
---|
| 844 | self._list += [ a for a in other._list if a in self] |
---|
| 845 | return self |
---|
| 846 | |
---|
| 847 | __ixor__ = symmetric_difference_update |
---|
| 848 | |
---|
| 849 | def difference_update(self, other): |
---|
| 850 | set.difference_update(self, other) |
---|
| 851 | self._list = [ a for a in self._list if a in self] |
---|
| 852 | return self |
---|
| 853 | |
---|
| 854 | __isub__ = difference_update |
---|
| 855 | |
---|
| 856 | |
---|
| 857 | class IdentitySet(object): |
---|
| 858 | """A set that considers only object id() for uniqueness. |
---|
| 859 | |
---|
| 860 | This strategy has edge cases for builtin types- it's possible to have |
---|
| 861 | two 'foo' strings in one of these sets, for example. Use sparingly. |
---|
| 862 | |
---|
| 863 | """ |
---|
| 864 | |
---|
| 865 | _working_set = set |
---|
| 866 | |
---|
| 867 | def __init__(self, iterable=None): |
---|
| 868 | self._members = dict() |
---|
| 869 | if iterable: |
---|
| 870 | for o in iterable: |
---|
| 871 | self.add(o) |
---|
| 872 | |
---|
| 873 | def add(self, value): |
---|
| 874 | self._members[id(value)] = value |
---|
| 875 | |
---|
| 876 | def __contains__(self, value): |
---|
| 877 | return id(value) in self._members |
---|
| 878 | |
---|
| 879 | def remove(self, value): |
---|
| 880 | del self._members[id(value)] |
---|
| 881 | |
---|
| 882 | def discard(self, value): |
---|
| 883 | try: |
---|
| 884 | self.remove(value) |
---|
| 885 | except KeyError: |
---|
| 886 | pass |
---|
| 887 | |
---|
| 888 | def pop(self): |
---|
| 889 | try: |
---|
| 890 | pair = self._members.popitem() |
---|
| 891 | return pair[1] |
---|
| 892 | except KeyError: |
---|
| 893 | raise KeyError('pop from an empty set') |
---|
| 894 | |
---|
| 895 | def clear(self): |
---|
| 896 | self._members.clear() |
---|
| 897 | |
---|
| 898 | def __cmp__(self, other): |
---|
| 899 | raise TypeError('cannot compare sets using cmp()') |
---|
| 900 | |
---|
| 901 | def __eq__(self, other): |
---|
| 902 | if isinstance(other, IdentitySet): |
---|
| 903 | return self._members == other._members |
---|
| 904 | else: |
---|
| 905 | return False |
---|
| 906 | |
---|
| 907 | def __ne__(self, other): |
---|
| 908 | if isinstance(other, IdentitySet): |
---|
| 909 | return self._members != other._members |
---|
| 910 | else: |
---|
| 911 | return True |
---|
| 912 | |
---|
| 913 | def issubset(self, iterable): |
---|
| 914 | other = type(self)(iterable) |
---|
| 915 | |
---|
| 916 | if len(self) > len(other): |
---|
| 917 | return False |
---|
| 918 | for m in itertools.ifilterfalse(other._members.has_key, |
---|
| 919 | self._members.iterkeys()): |
---|
| 920 | return False |
---|
| 921 | return True |
---|
| 922 | |
---|
| 923 | def __le__(self, other): |
---|
| 924 | if not isinstance(other, IdentitySet): |
---|
| 925 | return NotImplemented |
---|
| 926 | return self.issubset(other) |
---|
| 927 | |
---|
| 928 | def __lt__(self, other): |
---|
| 929 | if not isinstance(other, IdentitySet): |
---|
| 930 | return NotImplemented |
---|
| 931 | return len(self) < len(other) and self.issubset(other) |
---|
| 932 | |
---|
| 933 | def issuperset(self, iterable): |
---|
| 934 | other = type(self)(iterable) |
---|
| 935 | |
---|
| 936 | if len(self) < len(other): |
---|
| 937 | return False |
---|
| 938 | |
---|
| 939 | for m in itertools.ifilterfalse(self._members.has_key, |
---|
| 940 | other._members.iterkeys()): |
---|
| 941 | return False |
---|
| 942 | return True |
---|
| 943 | |
---|
| 944 | def __ge__(self, other): |
---|
| 945 | if not isinstance(other, IdentitySet): |
---|
| 946 | return NotImplemented |
---|
| 947 | return self.issuperset(other) |
---|
| 948 | |
---|
| 949 | def __gt__(self, other): |
---|
| 950 | if not isinstance(other, IdentitySet): |
---|
| 951 | return NotImplemented |
---|
| 952 | return len(self) > len(other) and self.issuperset(other) |
---|
| 953 | |
---|
| 954 | def union(self, iterable): |
---|
| 955 | result = type(self)() |
---|
| 956 | # testlib.pragma exempt:__hash__ |
---|
| 957 | result._members.update( |
---|
| 958 | self._working_set(self._member_id_tuples()).union(_iter_id(iterable))) |
---|
| 959 | return result |
---|
| 960 | |
---|
| 961 | def __or__(self, other): |
---|
| 962 | if not isinstance(other, IdentitySet): |
---|
| 963 | return NotImplemented |
---|
| 964 | return self.union(other) |
---|
| 965 | |
---|
| 966 | def update(self, iterable): |
---|
| 967 | self._members = self.union(iterable)._members |
---|
| 968 | |
---|
| 969 | def __ior__(self, other): |
---|
| 970 | if not isinstance(other, IdentitySet): |
---|
| 971 | return NotImplemented |
---|
| 972 | self.update(other) |
---|
| 973 | return self |
---|
| 974 | |
---|
| 975 | def difference(self, iterable): |
---|
| 976 | result = type(self)() |
---|
| 977 | # testlib.pragma exempt:__hash__ |
---|
| 978 | result._members.update( |
---|
| 979 | self._working_set(self._member_id_tuples()).difference(_iter_id(iterable))) |
---|
| 980 | return result |
---|
| 981 | |
---|
| 982 | def __sub__(self, other): |
---|
| 983 | if not isinstance(other, IdentitySet): |
---|
| 984 | return NotImplemented |
---|
| 985 | return self.difference(other) |
---|
| 986 | |
---|
| 987 | def difference_update(self, iterable): |
---|
| 988 | self._members = self.difference(iterable)._members |
---|
| 989 | |
---|
| 990 | def __isub__(self, other): |
---|
| 991 | if not isinstance(other, IdentitySet): |
---|
| 992 | return NotImplemented |
---|
| 993 | self.difference_update(other) |
---|
| 994 | return self |
---|
| 995 | |
---|
| 996 | def intersection(self, iterable): |
---|
| 997 | result = type(self)() |
---|
| 998 | # testlib.pragma exempt:__hash__ |
---|
| 999 | result._members.update( |
---|
| 1000 | self._working_set(self._member_id_tuples()).intersection(_iter_id(iterable))) |
---|
| 1001 | return result |
---|
| 1002 | |
---|
| 1003 | def __and__(self, other): |
---|
| 1004 | if not isinstance(other, IdentitySet): |
---|
| 1005 | return NotImplemented |
---|
| 1006 | return self.intersection(other) |
---|
| 1007 | |
---|
| 1008 | def intersection_update(self, iterable): |
---|
| 1009 | self._members = self.intersection(iterable)._members |
---|
| 1010 | |
---|
| 1011 | def __iand__(self, other): |
---|
| 1012 | if not isinstance(other, IdentitySet): |
---|
| 1013 | return NotImplemented |
---|
| 1014 | self.intersection_update(other) |
---|
| 1015 | return self |
---|
| 1016 | |
---|
| 1017 | def symmetric_difference(self, iterable): |
---|
| 1018 | result = type(self)() |
---|
| 1019 | # testlib.pragma exempt:__hash__ |
---|
| 1020 | result._members.update( |
---|
| 1021 | self._working_set(self._member_id_tuples()).symmetric_difference(_iter_id(iterable))) |
---|
| 1022 | return result |
---|
| 1023 | |
---|
| 1024 | def _member_id_tuples(self): |
---|
| 1025 | return ((id(v), v) for v in self._members.itervalues()) |
---|
| 1026 | |
---|
| 1027 | def __xor__(self, other): |
---|
| 1028 | if not isinstance(other, IdentitySet): |
---|
| 1029 | return NotImplemented |
---|
| 1030 | return self.symmetric_difference(other) |
---|
| 1031 | |
---|
| 1032 | def symmetric_difference_update(self, iterable): |
---|
| 1033 | self._members = self.symmetric_difference(iterable)._members |
---|
| 1034 | |
---|
| 1035 | def __ixor__(self, other): |
---|
| 1036 | if not isinstance(other, IdentitySet): |
---|
| 1037 | return NotImplemented |
---|
| 1038 | self.symmetric_difference(other) |
---|
| 1039 | return self |
---|
| 1040 | |
---|
| 1041 | def copy(self): |
---|
| 1042 | return type(self)(self._members.itervalues()) |
---|
| 1043 | |
---|
| 1044 | __copy__ = copy |
---|
| 1045 | |
---|
| 1046 | def __len__(self): |
---|
| 1047 | return len(self._members) |
---|
| 1048 | |
---|
| 1049 | def __iter__(self): |
---|
| 1050 | return self._members.itervalues() |
---|
| 1051 | |
---|
| 1052 | def __hash__(self): |
---|
| 1053 | raise TypeError('set objects are unhashable') |
---|
| 1054 | |
---|
| 1055 | def __repr__(self): |
---|
| 1056 | return '%s(%r)' % (type(self).__name__, self._members.values()) |
---|
| 1057 | |
---|
| 1058 | |
---|
| 1059 | class OrderedIdentitySet(IdentitySet): |
---|
| 1060 | class _working_set(OrderedSet): |
---|
| 1061 | # a testing pragma: exempt the OIDS working set from the test suite's |
---|
| 1062 | # "never call the user's __hash__" assertions. this is a big hammer, |
---|
| 1063 | # but it's safe here: IDS operates on (id, instance) tuples in the |
---|
| 1064 | # working set. |
---|
| 1065 | __sa_hash_exempt__ = True |
---|
| 1066 | |
---|
| 1067 | def __init__(self, iterable=None): |
---|
| 1068 | IdentitySet.__init__(self) |
---|
| 1069 | self._members = OrderedDict() |
---|
| 1070 | if iterable: |
---|
| 1071 | for o in iterable: |
---|
| 1072 | self.add(o) |
---|
| 1073 | |
---|
| 1074 | def _iter_id(iterable): |
---|
| 1075 | """Generator: ((id(o), o) for o in iterable).""" |
---|
| 1076 | |
---|
| 1077 | for item in iterable: |
---|
| 1078 | yield id(item), item |
---|
| 1079 | |
---|
| 1080 | # define collections that are capable of storing |
---|
| 1081 | # ColumnElement objects as hashable keys/elements. |
---|
| 1082 | column_set = set |
---|
| 1083 | column_dict = dict |
---|
| 1084 | ordered_column_set = OrderedSet |
---|
| 1085 | populate_column_dict = PopulateDict |
---|
| 1086 | |
---|
| 1087 | def unique_list(seq, compare_with=set): |
---|
| 1088 | seen = compare_with() |
---|
| 1089 | return [x for x in seq if x not in seen and not seen.add(x)] |
---|
| 1090 | |
---|
| 1091 | class UniqueAppender(object): |
---|
| 1092 | """Appends items to a collection ensuring uniqueness. |
---|
| 1093 | |
---|
| 1094 | Additional appends() of the same object are ignored. Membership is |
---|
| 1095 | determined by identity (``is a``) not equality (``==``). |
---|
| 1096 | """ |
---|
| 1097 | |
---|
| 1098 | def __init__(self, data, via=None): |
---|
| 1099 | self.data = data |
---|
| 1100 | self._unique = IdentitySet() |
---|
| 1101 | if via: |
---|
| 1102 | self._data_appender = getattr(data, via) |
---|
| 1103 | elif hasattr(data, 'append'): |
---|
| 1104 | self._data_appender = data.append |
---|
| 1105 | elif hasattr(data, 'add'): |
---|
| 1106 | # TODO: we think its a set here. bypass unneeded uniquing logic ? |
---|
| 1107 | self._data_appender = data.add |
---|
| 1108 | |
---|
| 1109 | def append(self, item): |
---|
| 1110 | if item not in self._unique: |
---|
| 1111 | self._data_appender(item) |
---|
| 1112 | self._unique.add(item) |
---|
| 1113 | |
---|
| 1114 | def __iter__(self): |
---|
| 1115 | return iter(self.data) |
---|
| 1116 | |
---|
| 1117 | |
---|
| 1118 | class ScopedRegistry(object): |
---|
| 1119 | """A Registry that can store one or multiple instances of a single |
---|
| 1120 | class on a per-thread scoped basis, or on a customized scope. |
---|
| 1121 | |
---|
| 1122 | createfunc |
---|
| 1123 | a callable that returns a new object to be placed in the registry |
---|
| 1124 | |
---|
| 1125 | scopefunc |
---|
| 1126 | a callable that will return a key to store/retrieve an object. |
---|
| 1127 | """ |
---|
| 1128 | |
---|
| 1129 | def __init__(self, createfunc, scopefunc): |
---|
| 1130 | self.createfunc = createfunc |
---|
| 1131 | self.scopefunc = scopefunc |
---|
| 1132 | self.registry = {} |
---|
| 1133 | |
---|
| 1134 | def __call__(self): |
---|
| 1135 | key = self.scopefunc() |
---|
| 1136 | try: |
---|
| 1137 | return self.registry[key] |
---|
| 1138 | except KeyError: |
---|
| 1139 | return self.registry.setdefault(key, self.createfunc()) |
---|
| 1140 | |
---|
| 1141 | def has(self): |
---|
| 1142 | return self.scopefunc() in self.registry |
---|
| 1143 | |
---|
| 1144 | def set(self, obj): |
---|
| 1145 | self.registry[self.scopefunc()] = obj |
---|
| 1146 | |
---|
| 1147 | def clear(self): |
---|
| 1148 | try: |
---|
| 1149 | del self.registry[self.scopefunc()] |
---|
| 1150 | except KeyError: |
---|
| 1151 | pass |
---|
| 1152 | |
---|
| 1153 | class ThreadLocalRegistry(ScopedRegistry): |
---|
| 1154 | def __init__(self, createfunc): |
---|
| 1155 | self.createfunc = createfunc |
---|
| 1156 | self.registry = threading.local() |
---|
| 1157 | |
---|
| 1158 | def __call__(self): |
---|
| 1159 | try: |
---|
| 1160 | return self.registry.value |
---|
| 1161 | except AttributeError: |
---|
| 1162 | val = self.registry.value = self.createfunc() |
---|
| 1163 | return val |
---|
| 1164 | |
---|
| 1165 | def has(self): |
---|
| 1166 | return hasattr(self.registry, "value") |
---|
| 1167 | |
---|
| 1168 | def set(self, obj): |
---|
| 1169 | self.registry.value = obj |
---|
| 1170 | |
---|
| 1171 | def clear(self): |
---|
| 1172 | try: |
---|
| 1173 | del self.registry.value |
---|
| 1174 | except AttributeError: |
---|
| 1175 | pass |
---|
| 1176 | |
---|
| 1177 | class _symbol(object): |
---|
| 1178 | def __init__(self, name): |
---|
| 1179 | """Construct a new named symbol.""" |
---|
| 1180 | assert isinstance(name, str) |
---|
| 1181 | self.name = name |
---|
| 1182 | def __reduce__(self): |
---|
| 1183 | return symbol, (self.name,) |
---|
| 1184 | def __repr__(self): |
---|
| 1185 | return "<symbol '%s>" % self.name |
---|
| 1186 | _symbol.__name__ = 'symbol' |
---|
| 1187 | |
---|
| 1188 | |
---|
| 1189 | class symbol(object): |
---|
| 1190 | """A constant symbol. |
---|
| 1191 | |
---|
| 1192 | >>> symbol('foo') is symbol('foo') |
---|
| 1193 | True |
---|
| 1194 | >>> symbol('foo') |
---|
| 1195 | <symbol 'foo> |
---|
| 1196 | |
---|
| 1197 | A slight refinement of the MAGICCOOKIE=object() pattern. The primary |
---|
| 1198 | advantage of symbol() is its repr(). They are also singletons. |
---|
| 1199 | |
---|
| 1200 | Repeated calls of symbol('name') will all return the same instance. |
---|
| 1201 | |
---|
| 1202 | """ |
---|
| 1203 | symbols = {} |
---|
| 1204 | _lock = threading.Lock() |
---|
| 1205 | |
---|
| 1206 | def __new__(cls, name): |
---|
| 1207 | cls._lock.acquire() |
---|
| 1208 | try: |
---|
| 1209 | sym = cls.symbols.get(name) |
---|
| 1210 | if sym is None: |
---|
| 1211 | cls.symbols[name] = sym = _symbol(name) |
---|
| 1212 | return sym |
---|
| 1213 | finally: |
---|
| 1214 | symbol._lock.release() |
---|
| 1215 | |
---|
| 1216 | |
---|
| 1217 | def as_interface(obj, cls=None, methods=None, required=None): |
---|
| 1218 | """Ensure basic interface compliance for an instance or dict of callables. |
---|
| 1219 | |
---|
| 1220 | Checks that ``obj`` implements public methods of ``cls`` or has members |
---|
| 1221 | listed in ``methods``. If ``required`` is not supplied, implementing at |
---|
| 1222 | least one interface method is sufficient. Methods present on ``obj`` that |
---|
| 1223 | are not in the interface are ignored. |
---|
| 1224 | |
---|
| 1225 | If ``obj`` is a dict and ``dict`` does not meet the interface |
---|
| 1226 | requirements, the keys of the dictionary are inspected. Keys present in |
---|
| 1227 | ``obj`` that are not in the interface will raise TypeErrors. |
---|
| 1228 | |
---|
| 1229 | Raises TypeError if ``obj`` does not meet the interface criteria. |
---|
| 1230 | |
---|
| 1231 | In all passing cases, an object with callable members is returned. In the |
---|
| 1232 | simple case, ``obj`` is returned as-is; if dict processing kicks in then |
---|
| 1233 | an anonymous class is returned. |
---|
| 1234 | |
---|
| 1235 | obj |
---|
| 1236 | A type, instance, or dictionary of callables. |
---|
| 1237 | cls |
---|
| 1238 | Optional, a type. All public methods of cls are considered the |
---|
| 1239 | interface. An ``obj`` instance of cls will always pass, ignoring |
---|
| 1240 | ``required``.. |
---|
| 1241 | methods |
---|
| 1242 | Optional, a sequence of method names to consider as the interface. |
---|
| 1243 | required |
---|
| 1244 | Optional, a sequence of mandatory implementations. If omitted, an |
---|
| 1245 | ``obj`` that provides at least one interface method is considered |
---|
| 1246 | sufficient. As a convenience, required may be a type, in which case |
---|
| 1247 | all public methods of the type are required. |
---|
| 1248 | |
---|
| 1249 | """ |
---|
| 1250 | if not cls and not methods: |
---|
| 1251 | raise TypeError('a class or collection of method names are required') |
---|
| 1252 | |
---|
| 1253 | if isinstance(cls, type) and isinstance(obj, cls): |
---|
| 1254 | return obj |
---|
| 1255 | |
---|
| 1256 | interface = set(methods or [m for m in dir(cls) if not m.startswith('_')]) |
---|
| 1257 | implemented = set(dir(obj)) |
---|
| 1258 | |
---|
| 1259 | complies = operator.ge |
---|
| 1260 | if isinstance(required, type): |
---|
| 1261 | required = interface |
---|
| 1262 | elif not required: |
---|
| 1263 | required = set() |
---|
| 1264 | complies = operator.gt |
---|
| 1265 | else: |
---|
| 1266 | required = set(required) |
---|
| 1267 | |
---|
| 1268 | if complies(implemented.intersection(interface), required): |
---|
| 1269 | return obj |
---|
| 1270 | |
---|
| 1271 | # No dict duck typing here. |
---|
| 1272 | if not type(obj) is dict: |
---|
| 1273 | qualifier = complies is operator.gt and 'any of' or 'all of' |
---|
| 1274 | raise TypeError("%r does not implement %s: %s" % ( |
---|
| 1275 | obj, qualifier, ', '.join(interface))) |
---|
| 1276 | |
---|
| 1277 | class AnonymousInterface(object): |
---|
| 1278 | """A callable-holding shell.""" |
---|
| 1279 | |
---|
| 1280 | if cls: |
---|
| 1281 | AnonymousInterface.__name__ = 'Anonymous' + cls.__name__ |
---|
| 1282 | found = set() |
---|
| 1283 | |
---|
| 1284 | for method, impl in dictlike_iteritems(obj): |
---|
| 1285 | if method not in interface: |
---|
| 1286 | raise TypeError("%r: unknown in this interface" % method) |
---|
| 1287 | if not callable(impl): |
---|
| 1288 | raise TypeError("%r=%r is not callable" % (method, impl)) |
---|
| 1289 | setattr(AnonymousInterface, method, staticmethod(impl)) |
---|
| 1290 | found.add(method) |
---|
| 1291 | |
---|
| 1292 | if complies(found, required): |
---|
| 1293 | return AnonymousInterface |
---|
| 1294 | |
---|
| 1295 | raise TypeError("dictionary does not contain required keys %s" % |
---|
| 1296 | ', '.join(required - found)) |
---|
| 1297 | |
---|
| 1298 | def function_named(fn, name): |
---|
| 1299 | """Return a function with a given __name__. |
---|
| 1300 | |
---|
| 1301 | Will assign to __name__ and return the original function if possible on |
---|
| 1302 | the Python implementation, otherwise a new function will be constructed. |
---|
| 1303 | |
---|
| 1304 | """ |
---|
| 1305 | try: |
---|
| 1306 | fn.__name__ = name |
---|
| 1307 | except TypeError: |
---|
| 1308 | fn = types.FunctionType(fn.func_code, fn.func_globals, name, |
---|
| 1309 | fn.func_defaults, fn.func_closure) |
---|
| 1310 | return fn |
---|
| 1311 | |
---|
| 1312 | class memoized_property(object): |
---|
| 1313 | """A read-only @property that is only evaluated once.""" |
---|
| 1314 | def __init__(self, fget, doc=None): |
---|
| 1315 | self.fget = fget |
---|
| 1316 | self.__doc__ = doc or fget.__doc__ |
---|
| 1317 | self.__name__ = fget.__name__ |
---|
| 1318 | |
---|
| 1319 | def __get__(self, obj, cls): |
---|
| 1320 | if obj is None: |
---|
| 1321 | return None |
---|
| 1322 | obj.__dict__[self.__name__] = result = self.fget(obj) |
---|
| 1323 | return result |
---|
| 1324 | |
---|
| 1325 | |
---|
| 1326 | class memoized_instancemethod(object): |
---|
| 1327 | """Decorate a method memoize its return value. |
---|
| 1328 | |
---|
| 1329 | Best applied to no-arg methods: memoization is not sensitive to |
---|
| 1330 | argument values, and will always return the same value even when |
---|
| 1331 | called with different arguments. |
---|
| 1332 | |
---|
| 1333 | """ |
---|
| 1334 | def __init__(self, fget, doc=None): |
---|
| 1335 | self.fget = fget |
---|
| 1336 | self.__doc__ = doc or fget.__doc__ |
---|
| 1337 | self.__name__ = fget.__name__ |
---|
| 1338 | |
---|
| 1339 | def __get__(self, obj, cls): |
---|
| 1340 | if obj is None: |
---|
| 1341 | return None |
---|
| 1342 | def oneshot(*args, **kw): |
---|
| 1343 | result = self.fget(obj, *args, **kw) |
---|
| 1344 | memo = lambda *a, **kw: result |
---|
| 1345 | memo.__name__ = self.__name__ |
---|
| 1346 | memo.__doc__ = self.__doc__ |
---|
| 1347 | obj.__dict__[self.__name__] = memo |
---|
| 1348 | return result |
---|
| 1349 | oneshot.__name__ = self.__name__ |
---|
| 1350 | oneshot.__doc__ = self.__doc__ |
---|
| 1351 | return oneshot |
---|
| 1352 | |
---|
| 1353 | def reset_memoized(instance, name): |
---|
| 1354 | instance.__dict__.pop(name, None) |
---|
| 1355 | |
---|
| 1356 | class WeakIdentityMapping(weakref.WeakKeyDictionary): |
---|
| 1357 | """A WeakKeyDictionary with an object identity index. |
---|
| 1358 | |
---|
| 1359 | Adds a .by_id dictionary to a regular WeakKeyDictionary. Trades |
---|
| 1360 | performance during mutation operations for accelerated lookups by id(). |
---|
| 1361 | |
---|
| 1362 | The usual cautions about weak dictionaries and iteration also apply to |
---|
| 1363 | this subclass. |
---|
| 1364 | |
---|
| 1365 | """ |
---|
| 1366 | _none = symbol('none') |
---|
| 1367 | |
---|
| 1368 | def __init__(self): |
---|
| 1369 | weakref.WeakKeyDictionary.__init__(self) |
---|
| 1370 | self.by_id = {} |
---|
| 1371 | self._weakrefs = {} |
---|
| 1372 | |
---|
| 1373 | def __setitem__(self, object, value): |
---|
| 1374 | oid = id(object) |
---|
| 1375 | self.by_id[oid] = value |
---|
| 1376 | if oid not in self._weakrefs: |
---|
| 1377 | self._weakrefs[oid] = self._ref(object) |
---|
| 1378 | weakref.WeakKeyDictionary.__setitem__(self, object, value) |
---|
| 1379 | |
---|
| 1380 | def __delitem__(self, object): |
---|
| 1381 | del self._weakrefs[id(object)] |
---|
| 1382 | del self.by_id[id(object)] |
---|
| 1383 | weakref.WeakKeyDictionary.__delitem__(self, object) |
---|
| 1384 | |
---|
| 1385 | def setdefault(self, object, default=None): |
---|
| 1386 | value = weakref.WeakKeyDictionary.setdefault(self, object, default) |
---|
| 1387 | oid = id(object) |
---|
| 1388 | if value is default: |
---|
| 1389 | self.by_id[oid] = default |
---|
| 1390 | if oid not in self._weakrefs: |
---|
| 1391 | self._weakrefs[oid] = self._ref(object) |
---|
| 1392 | return value |
---|
| 1393 | |
---|
| 1394 | def pop(self, object, default=_none): |
---|
| 1395 | if default is self._none: |
---|
| 1396 | value = weakref.WeakKeyDictionary.pop(self, object) |
---|
| 1397 | else: |
---|
| 1398 | value = weakref.WeakKeyDictionary.pop(self, object, default) |
---|
| 1399 | if id(object) in self.by_id: |
---|
| 1400 | del self._weakrefs[id(object)] |
---|
| 1401 | del self.by_id[id(object)] |
---|
| 1402 | return value |
---|
| 1403 | |
---|
| 1404 | def popitem(self): |
---|
| 1405 | item = weakref.WeakKeyDictionary.popitem(self) |
---|
| 1406 | oid = id(item[0]) |
---|
| 1407 | del self._weakrefs[oid] |
---|
| 1408 | del self.by_id[oid] |
---|
| 1409 | return item |
---|
| 1410 | |
---|
| 1411 | def clear(self): |
---|
| 1412 | self._weakrefs.clear() |
---|
| 1413 | self.by_id.clear() |
---|
| 1414 | weakref.WeakKeyDictionary.clear(self) |
---|
| 1415 | |
---|
| 1416 | def update(self, *a, **kw): |
---|
| 1417 | raise NotImplementedError |
---|
| 1418 | |
---|
| 1419 | def _cleanup(self, wr, key=None): |
---|
| 1420 | if key is None: |
---|
| 1421 | key = wr.key |
---|
| 1422 | try: |
---|
| 1423 | del self._weakrefs[key] |
---|
| 1424 | except (KeyError, AttributeError): # pragma: no cover |
---|
| 1425 | pass # pragma: no cover |
---|
| 1426 | try: |
---|
| 1427 | del self.by_id[key] |
---|
| 1428 | except (KeyError, AttributeError): # pragma: no cover |
---|
| 1429 | pass # pragma: no cover |
---|
| 1430 | |
---|
| 1431 | class _keyed_weakref(weakref.ref): |
---|
| 1432 | def __init__(self, object, callback): |
---|
| 1433 | weakref.ref.__init__(self, object, callback) |
---|
| 1434 | self.key = id(object) |
---|
| 1435 | |
---|
| 1436 | def _ref(self, object): |
---|
| 1437 | return self._keyed_weakref(object, self._cleanup) |
---|
| 1438 | |
---|
| 1439 | |
---|
| 1440 | def warn(msg): |
---|
| 1441 | if isinstance(msg, basestring): |
---|
| 1442 | warnings.warn(msg, exc.SAWarning, stacklevel=3) |
---|
| 1443 | else: |
---|
| 1444 | warnings.warn(msg, stacklevel=3) |
---|
| 1445 | |
---|
| 1446 | def warn_deprecated(msg): |
---|
| 1447 | warnings.warn(msg, exc.SADeprecationWarning, stacklevel=3) |
---|
| 1448 | |
---|
| 1449 | def warn_pending_deprecation(msg): |
---|
| 1450 | warnings.warn(msg, exc.SAPendingDeprecationWarning, stacklevel=3) |
---|
| 1451 | |
---|
| 1452 | def deprecated(message=None, add_deprecation_to_docstring=True): |
---|
| 1453 | """Decorates a function and issues a deprecation warning on use. |
---|
| 1454 | |
---|
| 1455 | message |
---|
| 1456 | If provided, issue message in the warning. A sensible default |
---|
| 1457 | is used if not provided. |
---|
| 1458 | |
---|
| 1459 | add_deprecation_to_docstring |
---|
| 1460 | Default True. If False, the wrapped function's __doc__ is left |
---|
| 1461 | as-is. If True, the 'message' is prepended to the docs if |
---|
| 1462 | provided, or sensible default if message is omitted. |
---|
| 1463 | """ |
---|
| 1464 | |
---|
| 1465 | if add_deprecation_to_docstring: |
---|
| 1466 | header = message is not None and message or 'Deprecated.' |
---|
| 1467 | else: |
---|
| 1468 | header = None |
---|
| 1469 | |
---|
| 1470 | if message is None: |
---|
| 1471 | message = "Call to deprecated function %(func)s" |
---|
| 1472 | |
---|
| 1473 | def decorate(fn): |
---|
| 1474 | return _decorate_with_warning( |
---|
| 1475 | fn, exc.SADeprecationWarning, |
---|
| 1476 | message % dict(func=fn.__name__), header) |
---|
| 1477 | return decorate |
---|
| 1478 | |
---|
| 1479 | def pending_deprecation(version, message=None, |
---|
| 1480 | add_deprecation_to_docstring=True): |
---|
| 1481 | """Decorates a function and issues a pending deprecation warning on use. |
---|
| 1482 | |
---|
| 1483 | version |
---|
| 1484 | An approximate future version at which point the pending deprecation |
---|
| 1485 | will become deprecated. Not used in messaging. |
---|
| 1486 | |
---|
| 1487 | message |
---|
| 1488 | If provided, issue message in the warning. A sensible default |
---|
| 1489 | is used if not provided. |
---|
| 1490 | |
---|
| 1491 | add_deprecation_to_docstring |
---|
| 1492 | Default True. If False, the wrapped function's __doc__ is left |
---|
| 1493 | as-is. If True, the 'message' is prepended to the docs if |
---|
| 1494 | provided, or sensible default if message is omitted. |
---|
| 1495 | """ |
---|
| 1496 | |
---|
| 1497 | if add_deprecation_to_docstring: |
---|
| 1498 | header = message is not None and message or 'Deprecated.' |
---|
| 1499 | else: |
---|
| 1500 | header = None |
---|
| 1501 | |
---|
| 1502 | if message is None: |
---|
| 1503 | message = "Call to deprecated function %(func)s" |
---|
| 1504 | |
---|
| 1505 | def decorate(fn): |
---|
| 1506 | return _decorate_with_warning( |
---|
| 1507 | fn, exc.SAPendingDeprecationWarning, |
---|
| 1508 | message % dict(func=fn.__name__), header) |
---|
| 1509 | return decorate |
---|
| 1510 | |
---|
| 1511 | def _decorate_with_warning(func, wtype, message, docstring_header=None): |
---|
| 1512 | """Wrap a function with a warnings.warn and augmented docstring.""" |
---|
| 1513 | |
---|
| 1514 | @decorator |
---|
| 1515 | def warned(fn, *args, **kwargs): |
---|
| 1516 | warnings.warn(wtype(message), stacklevel=3) |
---|
| 1517 | return fn(*args, **kwargs) |
---|
| 1518 | |
---|
| 1519 | doc = func.__doc__ is not None and func.__doc__ or '' |
---|
| 1520 | if docstring_header is not None: |
---|
| 1521 | docstring_header %= dict(func=func.__name__) |
---|
| 1522 | docs = doc and doc.expandtabs().split('\n') or [] |
---|
| 1523 | indent = '' |
---|
| 1524 | for line in docs[1:]: |
---|
| 1525 | text = line.lstrip() |
---|
| 1526 | if text: |
---|
| 1527 | indent = line[0:len(line) - len(text)] |
---|
| 1528 | break |
---|
| 1529 | point = min(len(docs), 1) |
---|
| 1530 | docs.insert(point, '\n' + indent + docstring_header.rstrip()) |
---|
| 1531 | doc = '\n'.join(docs) |
---|
| 1532 | |
---|
| 1533 | decorated = warned(func) |
---|
| 1534 | decorated.__doc__ = doc |
---|
| 1535 | return decorated |
---|