You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1090 lines
						
					
					
						
							28 KiB
						
					
					
				
			
		
		
	
	
							1090 lines
						
					
					
						
							28 KiB
						
					
					
				# util/_collections.py
 | 
						|
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
 | 
						|
# <see AUTHORS file>
 | 
						|
#
 | 
						|
# This module is part of SQLAlchemy and is released under
 | 
						|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
 | 
						|
 | 
						|
"""Collection classes and helpers."""
 | 
						|
 | 
						|
from __future__ import absolute_import
 | 
						|
 | 
						|
import operator
 | 
						|
import types
 | 
						|
import weakref
 | 
						|
 | 
						|
from .compat import binary_types
 | 
						|
from .compat import collections_abc
 | 
						|
from .compat import itertools_filterfalse
 | 
						|
from .compat import py2k
 | 
						|
from .compat import py37
 | 
						|
from .compat import string_types
 | 
						|
from .compat import threading
 | 
						|
 | 
						|
 | 
						|
EMPTY_SET = frozenset()
 | 
						|
 | 
						|
 | 
						|
class ImmutableContainer(object):
 | 
						|
    def _immutable(self, *arg, **kw):
 | 
						|
        raise TypeError("%s object is immutable" % self.__class__.__name__)
 | 
						|
 | 
						|
    __delitem__ = __setitem__ = __setattr__ = _immutable
 | 
						|
 | 
						|
 | 
						|
def _immutabledict_py_fallback():
 | 
						|
    class immutabledict(ImmutableContainer, dict):
 | 
						|
 | 
						|
        clear = (
 | 
						|
            pop
 | 
						|
        ) = popitem = setdefault = update = ImmutableContainer._immutable
 | 
						|
 | 
						|
        def __new__(cls, *args):
 | 
						|
            new = dict.__new__(cls)
 | 
						|
            dict.__init__(new, *args)
 | 
						|
            return new
 | 
						|
 | 
						|
        def __init__(self, *args):
 | 
						|
            pass
 | 
						|
 | 
						|
        def __reduce__(self):
 | 
						|
            return _immutabledict_reconstructor, (dict(self),)
 | 
						|
 | 
						|
        def union(self, __d=None):
 | 
						|
            if not __d:
 | 
						|
                return self
 | 
						|
 | 
						|
            new = dict.__new__(self.__class__)
 | 
						|
            dict.__init__(new, self)
 | 
						|
            dict.update(new, __d)
 | 
						|
            return new
 | 
						|
 | 
						|
        def _union_w_kw(self, __d=None, **kw):
 | 
						|
            # not sure if C version works correctly w/ this yet
 | 
						|
            if not __d and not kw:
 | 
						|
                return self
 | 
						|
 | 
						|
            new = dict.__new__(self.__class__)
 | 
						|
            dict.__init__(new, self)
 | 
						|
            if __d:
 | 
						|
                dict.update(new, __d)
 | 
						|
            dict.update(new, kw)
 | 
						|
            return new
 | 
						|
 | 
						|
        def merge_with(self, *dicts):
 | 
						|
            new = None
 | 
						|
            for d in dicts:
 | 
						|
                if d:
 | 
						|
                    if new is None:
 | 
						|
                        new = dict.__new__(self.__class__)
 | 
						|
                        dict.__init__(new, self)
 | 
						|
                    dict.update(new, d)
 | 
						|
            if new is None:
 | 
						|
                return self
 | 
						|
 | 
						|
            return new
 | 
						|
 | 
						|
        def __repr__(self):
 | 
						|
            return "immutabledict(%s)" % dict.__repr__(self)
 | 
						|
 | 
						|
    return immutabledict
 | 
						|
 | 
						|
 | 
						|
try:
 | 
						|
    from sqlalchemy.cimmutabledict import immutabledict
 | 
						|
 | 
						|
    collections_abc.Mapping.register(immutabledict)
 | 
						|
 | 
						|
except ImportError:
 | 
						|
    immutabledict = _immutabledict_py_fallback()
 | 
						|
 | 
						|
    def _immutabledict_reconstructor(*arg):
 | 
						|
        """do the pickle dance"""
 | 
						|
        return immutabledict(*arg)
 | 
						|
 | 
						|
 | 
						|
def coerce_to_immutabledict(d):
 | 
						|
    if not d:
 | 
						|
        return EMPTY_DICT
 | 
						|
    elif isinstance(d, immutabledict):
 | 
						|
        return d
 | 
						|
    else:
 | 
						|
        return immutabledict(d)
 | 
						|
 | 
						|
 | 
						|
EMPTY_DICT = immutabledict()
 | 
						|
 | 
						|
 | 
						|
class FacadeDict(ImmutableContainer, dict):
 | 
						|
    """A dictionary that is not publicly mutable."""
 | 
						|
 | 
						|
    clear = pop = popitem = setdefault = update = ImmutableContainer._immutable
 | 
						|
 | 
						|
    def __new__(cls, *args):
 | 
						|
        new = dict.__new__(cls)
 | 
						|
        return new
 | 
						|
 | 
						|
    def copy(self):
 | 
						|
        raise NotImplementedError(
 | 
						|
            "an immutabledict shouldn't need to be copied.  use dict(d) "
 | 
						|
            "if you need a mutable dictionary."
 | 
						|
        )
 | 
						|
 | 
						|
    def __reduce__(self):
 | 
						|
        return FacadeDict, (dict(self),)
 | 
						|
 | 
						|
    def _insert_item(self, key, value):
 | 
						|
        """insert an item into the dictionary directly."""
 | 
						|
        dict.__setitem__(self, key, value)
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return "FacadeDict(%s)" % dict.__repr__(self)
 | 
						|
 | 
						|
 | 
						|
class Properties(object):
 | 
						|
    """Provide a __getattr__/__setattr__ interface over a dict."""
 | 
						|
 | 
						|
    __slots__ = ("_data",)
 | 
						|
 | 
						|
    def __init__(self, data):
 | 
						|
        object.__setattr__(self, "_data", data)
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self._data)
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return iter(list(self._data.values()))
 | 
						|
 | 
						|
    def __dir__(self):
 | 
						|
        return dir(super(Properties, self)) + [
 | 
						|
            str(k) for k in self._data.keys()
 | 
						|
        ]
 | 
						|
 | 
						|
    def __add__(self, other):
 | 
						|
        return list(self) + list(other)
 | 
						|
 | 
						|
    def __setitem__(self, key, obj):
 | 
						|
        self._data[key] = obj
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
        return self._data[key]
 | 
						|
 | 
						|
    def __delitem__(self, key):
 | 
						|
        del self._data[key]
 | 
						|
 | 
						|
    def __setattr__(self, key, obj):
 | 
						|
        self._data[key] = obj
 | 
						|
 | 
						|
    def __getstate__(self):
 | 
						|
        return {"_data": self._data}
 | 
						|
 | 
						|
    def __setstate__(self, state):
 | 
						|
        object.__setattr__(self, "_data", state["_data"])
 | 
						|
 | 
						|
    def __getattr__(self, key):
 | 
						|
        try:
 | 
						|
            return self._data[key]
 | 
						|
        except KeyError:
 | 
						|
            raise AttributeError(key)
 | 
						|
 | 
						|
    def __contains__(self, key):
 | 
						|
        return key in self._data
 | 
						|
 | 
						|
    def as_immutable(self):
 | 
						|
        """Return an immutable proxy for this :class:`.Properties`."""
 | 
						|
 | 
						|
        return ImmutableProperties(self._data)
 | 
						|
 | 
						|
    def update(self, value):
 | 
						|
        self._data.update(value)
 | 
						|
 | 
						|
    def get(self, key, default=None):
 | 
						|
        if key in self:
 | 
						|
            return self[key]
 | 
						|
        else:
 | 
						|
            return default
 | 
						|
 | 
						|
    def keys(self):
 | 
						|
        return list(self._data)
 | 
						|
 | 
						|
    def values(self):
 | 
						|
        return list(self._data.values())
 | 
						|
 | 
						|
    def items(self):
 | 
						|
        return list(self._data.items())
 | 
						|
 | 
						|
    def has_key(self, key):
 | 
						|
        return key in self._data
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        self._data.clear()
 | 
						|
 | 
						|
 | 
						|
class OrderedProperties(Properties):
 | 
						|
    """Provide a __getattr__/__setattr__ interface with an OrderedDict
 | 
						|
    as backing store."""
 | 
						|
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        Properties.__init__(self, OrderedDict())
 | 
						|
 | 
						|
 | 
						|
class ImmutableProperties(ImmutableContainer, Properties):
 | 
						|
    """Provide immutable dict/object attribute to an underlying dictionary."""
 | 
						|
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
def _ordered_dictionary_sort(d, key=None):
 | 
						|
    """Sort an OrderedDict in-place."""
 | 
						|
 | 
						|
    items = [(k, d[k]) for k in sorted(d, key=key)]
 | 
						|
 | 
						|
    d.clear()
 | 
						|
 | 
						|
    d.update(items)
 | 
						|
 | 
						|
 | 
						|
if py37:
 | 
						|
    OrderedDict = dict
 | 
						|
    sort_dictionary = _ordered_dictionary_sort
 | 
						|
 | 
						|
else:
 | 
						|
    # prevent sort_dictionary from being used against a plain dictionary
 | 
						|
    # for Python < 3.7
 | 
						|
 | 
						|
    def sort_dictionary(d, key=None):
 | 
						|
        """Sort an OrderedDict in place."""
 | 
						|
 | 
						|
        d._ordered_dictionary_sort(key=key)
 | 
						|
 | 
						|
    class OrderedDict(dict):
 | 
						|
        """Dictionary that maintains insertion order.
 | 
						|
 | 
						|
        Superseded by Python dict as of Python 3.7
 | 
						|
 | 
						|
        """
 | 
						|
 | 
						|
        __slots__ = ("_list",)
 | 
						|
 | 
						|
        def _ordered_dictionary_sort(self, key=None):
 | 
						|
            _ordered_dictionary_sort(self, key=key)
 | 
						|
 | 
						|
        def __reduce__(self):
 | 
						|
            return OrderedDict, (self.items(),)
 | 
						|
 | 
						|
        def __init__(self, ____sequence=None, **kwargs):
 | 
						|
            self._list = []
 | 
						|
            if ____sequence is None:
 | 
						|
                if kwargs:
 | 
						|
                    self.update(**kwargs)
 | 
						|
            else:
 | 
						|
                self.update(____sequence, **kwargs)
 | 
						|
 | 
						|
        def clear(self):
 | 
						|
            self._list = []
 | 
						|
            dict.clear(self)
 | 
						|
 | 
						|
        def copy(self):
 | 
						|
            return self.__copy__()
 | 
						|
 | 
						|
        def __copy__(self):
 | 
						|
            return OrderedDict(self)
 | 
						|
 | 
						|
        def update(self, ____sequence=None, **kwargs):
 | 
						|
            if ____sequence is not None:
 | 
						|
                if hasattr(____sequence, "keys"):
 | 
						|
                    for key in ____sequence.keys():
 | 
						|
                        self.__setitem__(key, ____sequence[key])
 | 
						|
                else:
 | 
						|
                    for key, value in ____sequence:
 | 
						|
                        self[key] = value
 | 
						|
            if kwargs:
 | 
						|
                self.update(kwargs)
 | 
						|
 | 
						|
        def setdefault(self, key, value):
 | 
						|
            if key not in self:
 | 
						|
                self.__setitem__(key, value)
 | 
						|
                return value
 | 
						|
            else:
 | 
						|
                return self.__getitem__(key)
 | 
						|
 | 
						|
        def __iter__(self):
 | 
						|
            return iter(self._list)
 | 
						|
 | 
						|
        def keys(self):
 | 
						|
            return list(self)
 | 
						|
 | 
						|
        def values(self):
 | 
						|
            return [self[key] for key in self._list]
 | 
						|
 | 
						|
        def items(self):
 | 
						|
            return [(key, self[key]) for key in self._list]
 | 
						|
 | 
						|
        if py2k:
 | 
						|
 | 
						|
            def itervalues(self):
 | 
						|
                return iter(self.values())
 | 
						|
 | 
						|
            def iterkeys(self):
 | 
						|
                return iter(self)
 | 
						|
 | 
						|
            def iteritems(self):
 | 
						|
                return iter(self.items())
 | 
						|
 | 
						|
        def __setitem__(self, key, obj):
 | 
						|
            if key not in self:
 | 
						|
                try:
 | 
						|
                    self._list.append(key)
 | 
						|
                except AttributeError:
 | 
						|
                    # work around Python pickle loads() with
 | 
						|
                    # dict subclass (seems to ignore __setstate__?)
 | 
						|
                    self._list = [key]
 | 
						|
            dict.__setitem__(self, key, obj)
 | 
						|
 | 
						|
        def __delitem__(self, key):
 | 
						|
            dict.__delitem__(self, key)
 | 
						|
            self._list.remove(key)
 | 
						|
 | 
						|
        def pop(self, key, *default):
 | 
						|
            present = key in self
 | 
						|
            value = dict.pop(self, key, *default)
 | 
						|
            if present:
 | 
						|
                self._list.remove(key)
 | 
						|
            return value
 | 
						|
 | 
						|
        def popitem(self):
 | 
						|
            item = dict.popitem(self)
 | 
						|
            self._list.remove(item[0])
 | 
						|
            return item
 | 
						|
 | 
						|
 | 
						|
class OrderedSet(set):
 | 
						|
    def __init__(self, d=None):
 | 
						|
        set.__init__(self)
 | 
						|
        if d is not None:
 | 
						|
            self._list = unique_list(d)
 | 
						|
            set.update(self, self._list)
 | 
						|
        else:
 | 
						|
            self._list = []
 | 
						|
 | 
						|
    def add(self, element):
 | 
						|
        if element not in self:
 | 
						|
            self._list.append(element)
 | 
						|
        set.add(self, element)
 | 
						|
 | 
						|
    def remove(self, element):
 | 
						|
        set.remove(self, element)
 | 
						|
        self._list.remove(element)
 | 
						|
 | 
						|
    def insert(self, pos, element):
 | 
						|
        if element not in self:
 | 
						|
            self._list.insert(pos, element)
 | 
						|
        set.add(self, element)
 | 
						|
 | 
						|
    def discard(self, element):
 | 
						|
        if element in self:
 | 
						|
            self._list.remove(element)
 | 
						|
            set.remove(self, element)
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        set.clear(self)
 | 
						|
        self._list = []
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
        return self._list[key]
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return iter(self._list)
 | 
						|
 | 
						|
    def __add__(self, other):
 | 
						|
        return self.union(other)
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return "%s(%r)" % (self.__class__.__name__, self._list)
 | 
						|
 | 
						|
    __str__ = __repr__
 | 
						|
 | 
						|
    def update(self, iterable):
 | 
						|
        for e in iterable:
 | 
						|
            if e not in self:
 | 
						|
                self._list.append(e)
 | 
						|
                set.add(self, e)
 | 
						|
        return self
 | 
						|
 | 
						|
    __ior__ = update
 | 
						|
 | 
						|
    def union(self, other):
 | 
						|
        result = self.__class__(self)
 | 
						|
        result.update(other)
 | 
						|
        return result
 | 
						|
 | 
						|
    __or__ = union
 | 
						|
 | 
						|
    def intersection(self, other):
 | 
						|
        other = set(other)
 | 
						|
        return self.__class__(a for a in self if a in other)
 | 
						|
 | 
						|
    __and__ = intersection
 | 
						|
 | 
						|
    def symmetric_difference(self, other):
 | 
						|
        other = set(other)
 | 
						|
        result = self.__class__(a for a in self if a not in other)
 | 
						|
        result.update(a for a in other if a not in self)
 | 
						|
        return result
 | 
						|
 | 
						|
    __xor__ = symmetric_difference
 | 
						|
 | 
						|
    def difference(self, other):
 | 
						|
        other = set(other)
 | 
						|
        return self.__class__(a for a in self if a not in other)
 | 
						|
 | 
						|
    __sub__ = difference
 | 
						|
 | 
						|
    def intersection_update(self, other):
 | 
						|
        other = set(other)
 | 
						|
        set.intersection_update(self, other)
 | 
						|
        self._list = [a for a in self._list if a in other]
 | 
						|
        return self
 | 
						|
 | 
						|
    __iand__ = intersection_update
 | 
						|
 | 
						|
    def symmetric_difference_update(self, other):
 | 
						|
        set.symmetric_difference_update(self, other)
 | 
						|
        self._list = [a for a in self._list if a in self]
 | 
						|
        self._list += [a for a in other._list if a in self]
 | 
						|
        return self
 | 
						|
 | 
						|
    __ixor__ = symmetric_difference_update
 | 
						|
 | 
						|
    def difference_update(self, other):
 | 
						|
        set.difference_update(self, other)
 | 
						|
        self._list = [a for a in self._list if a in self]
 | 
						|
        return self
 | 
						|
 | 
						|
    __isub__ = difference_update
 | 
						|
 | 
						|
 | 
						|
class IdentitySet(object):
 | 
						|
    """A set that considers only object id() for uniqueness.
 | 
						|
 | 
						|
    This strategy has edge cases for builtin types- it's possible to have
 | 
						|
    two 'foo' strings in one of these sets, for example.  Use sparingly.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, iterable=None):
 | 
						|
        self._members = dict()
 | 
						|
        if iterable:
 | 
						|
            self.update(iterable)
 | 
						|
 | 
						|
    def add(self, value):
 | 
						|
        self._members[id(value)] = value
 | 
						|
 | 
						|
    def __contains__(self, value):
 | 
						|
        return id(value) in self._members
 | 
						|
 | 
						|
    def remove(self, value):
 | 
						|
        del self._members[id(value)]
 | 
						|
 | 
						|
    def discard(self, value):
 | 
						|
        try:
 | 
						|
            self.remove(value)
 | 
						|
        except KeyError:
 | 
						|
            pass
 | 
						|
 | 
						|
    def pop(self):
 | 
						|
        try:
 | 
						|
            pair = self._members.popitem()
 | 
						|
            return pair[1]
 | 
						|
        except KeyError:
 | 
						|
            raise KeyError("pop from an empty set")
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        self._members.clear()
 | 
						|
 | 
						|
    def __cmp__(self, other):
 | 
						|
        raise TypeError("cannot compare sets using cmp()")
 | 
						|
 | 
						|
    def __eq__(self, other):
 | 
						|
        if isinstance(other, IdentitySet):
 | 
						|
            return self._members == other._members
 | 
						|
        else:
 | 
						|
            return False
 | 
						|
 | 
						|
    def __ne__(self, other):
 | 
						|
        if isinstance(other, IdentitySet):
 | 
						|
            return self._members != other._members
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
 | 
						|
    def issubset(self, iterable):
 | 
						|
        if isinstance(iterable, self.__class__):
 | 
						|
            other = iterable
 | 
						|
        else:
 | 
						|
            other = self.__class__(iterable)
 | 
						|
 | 
						|
        if len(self) > len(other):
 | 
						|
            return False
 | 
						|
        for m in itertools_filterfalse(
 | 
						|
            other._members.__contains__, iter(self._members.keys())
 | 
						|
        ):
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def __le__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.issubset(other)
 | 
						|
 | 
						|
    def __lt__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return len(self) < len(other) and self.issubset(other)
 | 
						|
 | 
						|
    def issuperset(self, iterable):
 | 
						|
        if isinstance(iterable, self.__class__):
 | 
						|
            other = iterable
 | 
						|
        else:
 | 
						|
            other = self.__class__(iterable)
 | 
						|
 | 
						|
        if len(self) < len(other):
 | 
						|
            return False
 | 
						|
 | 
						|
        for m in itertools_filterfalse(
 | 
						|
            self._members.__contains__, iter(other._members.keys())
 | 
						|
        ):
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def __ge__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.issuperset(other)
 | 
						|
 | 
						|
    def __gt__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return len(self) > len(other) and self.issuperset(other)
 | 
						|
 | 
						|
    def union(self, iterable):
 | 
						|
        result = self.__class__()
 | 
						|
        members = self._members
 | 
						|
        result._members.update(members)
 | 
						|
        result._members.update((id(obj), obj) for obj in iterable)
 | 
						|
        return result
 | 
						|
 | 
						|
    def __or__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.union(other)
 | 
						|
 | 
						|
    def update(self, iterable):
 | 
						|
        self._members.update((id(obj), obj) for obj in iterable)
 | 
						|
 | 
						|
    def __ior__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        self.update(other)
 | 
						|
        return self
 | 
						|
 | 
						|
    def difference(self, iterable):
 | 
						|
        result = self.__class__()
 | 
						|
        members = self._members
 | 
						|
        if isinstance(iterable, self.__class__):
 | 
						|
            other = set(iterable._members.keys())
 | 
						|
        else:
 | 
						|
            other = {id(obj) for obj in iterable}
 | 
						|
        result._members.update(
 | 
						|
            ((k, v) for k, v in members.items() if k not in other)
 | 
						|
        )
 | 
						|
        return result
 | 
						|
 | 
						|
    def __sub__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.difference(other)
 | 
						|
 | 
						|
    def difference_update(self, iterable):
 | 
						|
        self._members = self.difference(iterable)._members
 | 
						|
 | 
						|
    def __isub__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        self.difference_update(other)
 | 
						|
        return self
 | 
						|
 | 
						|
    def intersection(self, iterable):
 | 
						|
        result = self.__class__()
 | 
						|
        members = self._members
 | 
						|
        if isinstance(iterable, self.__class__):
 | 
						|
            other = set(iterable._members.keys())
 | 
						|
        else:
 | 
						|
            other = {id(obj) for obj in iterable}
 | 
						|
        result._members.update(
 | 
						|
            (k, v) for k, v in members.items() if k in other
 | 
						|
        )
 | 
						|
        return result
 | 
						|
 | 
						|
    def __and__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.intersection(other)
 | 
						|
 | 
						|
    def intersection_update(self, iterable):
 | 
						|
        self._members = self.intersection(iterable)._members
 | 
						|
 | 
						|
    def __iand__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        self.intersection_update(other)
 | 
						|
        return self
 | 
						|
 | 
						|
    def symmetric_difference(self, iterable):
 | 
						|
        result = self.__class__()
 | 
						|
        members = self._members
 | 
						|
        if isinstance(iterable, self.__class__):
 | 
						|
            other = iterable._members
 | 
						|
        else:
 | 
						|
            other = {id(obj): obj for obj in iterable}
 | 
						|
        result._members.update(
 | 
						|
            ((k, v) for k, v in members.items() if k not in other)
 | 
						|
        )
 | 
						|
        result._members.update(
 | 
						|
            ((k, v) for k, v in other.items() if k not in members)
 | 
						|
        )
 | 
						|
        return result
 | 
						|
 | 
						|
    def __xor__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        return self.symmetric_difference(other)
 | 
						|
 | 
						|
    def symmetric_difference_update(self, iterable):
 | 
						|
        self._members = self.symmetric_difference(iterable)._members
 | 
						|
 | 
						|
    def __ixor__(self, other):
 | 
						|
        if not isinstance(other, IdentitySet):
 | 
						|
            return NotImplemented
 | 
						|
        self.symmetric_difference(other)
 | 
						|
        return self
 | 
						|
 | 
						|
    def copy(self):
 | 
						|
        return type(self)(iter(self._members.values()))
 | 
						|
 | 
						|
    __copy__ = copy
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self._members)
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return iter(self._members.values())
 | 
						|
 | 
						|
    def __hash__(self):
 | 
						|
        raise TypeError("set objects are unhashable")
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return "%s(%r)" % (type(self).__name__, list(self._members.values()))
 | 
						|
 | 
						|
 | 
						|
class WeakSequence(object):
 | 
						|
    def __init__(self, __elements=()):
 | 
						|
        # adapted from weakref.WeakKeyDictionary, prevent reference
 | 
						|
        # cycles in the collection itself
 | 
						|
        def _remove(item, selfref=weakref.ref(self)):
 | 
						|
            self = selfref()
 | 
						|
            if self is not None:
 | 
						|
                self._storage.remove(item)
 | 
						|
 | 
						|
        self._remove = _remove
 | 
						|
        self._storage = [
 | 
						|
            weakref.ref(element, _remove) for element in __elements
 | 
						|
        ]
 | 
						|
 | 
						|
    def append(self, item):
 | 
						|
        self._storage.append(weakref.ref(item, self._remove))
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self._storage)
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return (
 | 
						|
            obj for obj in (ref() for ref in self._storage) if obj is not None
 | 
						|
        )
 | 
						|
 | 
						|
    def __getitem__(self, index):
 | 
						|
        try:
 | 
						|
            obj = self._storage[index]
 | 
						|
        except KeyError:
 | 
						|
            raise IndexError("Index %s out of range" % index)
 | 
						|
        else:
 | 
						|
            return obj()
 | 
						|
 | 
						|
 | 
						|
class OrderedIdentitySet(IdentitySet):
 | 
						|
    def __init__(self, iterable=None):
 | 
						|
        IdentitySet.__init__(self)
 | 
						|
        self._members = OrderedDict()
 | 
						|
        if iterable:
 | 
						|
            for o in iterable:
 | 
						|
                self.add(o)
 | 
						|
 | 
						|
 | 
						|
class PopulateDict(dict):
 | 
						|
    """A dict which populates missing values via a creation function.
 | 
						|
 | 
						|
    Note the creation function takes a key, unlike
 | 
						|
    collections.defaultdict.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, creator):
 | 
						|
        self.creator = creator
 | 
						|
 | 
						|
    def __missing__(self, key):
 | 
						|
        self[key] = val = self.creator(key)
 | 
						|
        return val
 | 
						|
 | 
						|
 | 
						|
class WeakPopulateDict(dict):
 | 
						|
    """Like PopulateDict, but assumes a self + a method and does not create
 | 
						|
    a reference cycle.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, creator_method):
 | 
						|
        self.creator = creator_method.__func__
 | 
						|
        weakself = creator_method.__self__
 | 
						|
        self.weakself = weakref.ref(weakself)
 | 
						|
 | 
						|
    def __missing__(self, key):
 | 
						|
        self[key] = val = self.creator(self.weakself(), key)
 | 
						|
        return val
 | 
						|
 | 
						|
 | 
						|
# Define collections that are capable of storing
 | 
						|
# ColumnElement objects as hashable keys/elements.
 | 
						|
# At this point, these are mostly historical, things
 | 
						|
# used to be more complicated.
 | 
						|
column_set = set
 | 
						|
column_dict = dict
 | 
						|
ordered_column_set = OrderedSet
 | 
						|
 | 
						|
 | 
						|
_getters = PopulateDict(operator.itemgetter)
 | 
						|
 | 
						|
_property_getters = PopulateDict(
 | 
						|
    lambda idx: property(operator.itemgetter(idx))
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def unique_list(seq, hashfunc=None):
 | 
						|
    seen = set()
 | 
						|
    seen_add = seen.add
 | 
						|
    if not hashfunc:
 | 
						|
        return [x for x in seq if x not in seen and not seen_add(x)]
 | 
						|
    else:
 | 
						|
        return [
 | 
						|
            x
 | 
						|
            for x in seq
 | 
						|
            if hashfunc(x) not in seen and not seen_add(hashfunc(x))
 | 
						|
        ]
 | 
						|
 | 
						|
 | 
						|
class UniqueAppender(object):
 | 
						|
    """Appends items to a collection ensuring uniqueness.
 | 
						|
 | 
						|
    Additional appends() of the same object are ignored.  Membership is
 | 
						|
    determined by identity (``is a``) not equality (``==``).
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, data, via=None):
 | 
						|
        self.data = data
 | 
						|
        self._unique = {}
 | 
						|
        if via:
 | 
						|
            self._data_appender = getattr(data, via)
 | 
						|
        elif hasattr(data, "append"):
 | 
						|
            self._data_appender = data.append
 | 
						|
        elif hasattr(data, "add"):
 | 
						|
            self._data_appender = data.add
 | 
						|
 | 
						|
    def append(self, item):
 | 
						|
        id_ = id(item)
 | 
						|
        if id_ not in self._unique:
 | 
						|
            self._data_appender(item)
 | 
						|
            self._unique[id_] = True
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return iter(self.data)
 | 
						|
 | 
						|
 | 
						|
def coerce_generator_arg(arg):
 | 
						|
    if len(arg) == 1 and isinstance(arg[0], types.GeneratorType):
 | 
						|
        return list(arg[0])
 | 
						|
    else:
 | 
						|
        return arg
 | 
						|
 | 
						|
 | 
						|
def to_list(x, default=None):
 | 
						|
    if x is None:
 | 
						|
        return default
 | 
						|
    if not isinstance(x, collections_abc.Iterable) or isinstance(
 | 
						|
        x, string_types + binary_types
 | 
						|
    ):
 | 
						|
        return [x]
 | 
						|
    elif isinstance(x, list):
 | 
						|
        return x
 | 
						|
    else:
 | 
						|
        return list(x)
 | 
						|
 | 
						|
 | 
						|
def has_intersection(set_, iterable):
 | 
						|
    r"""return True if any items of set\_ are present in iterable.
 | 
						|
 | 
						|
    Goes through special effort to ensure __hash__ is not called
 | 
						|
    on items in iterable that don't support it.
 | 
						|
 | 
						|
    """
 | 
						|
    # TODO: optimize, write in C, etc.
 | 
						|
    return bool(set_.intersection([i for i in iterable if i.__hash__]))
 | 
						|
 | 
						|
 | 
						|
def to_set(x):
 | 
						|
    if x is None:
 | 
						|
        return set()
 | 
						|
    if not isinstance(x, set):
 | 
						|
        return set(to_list(x))
 | 
						|
    else:
 | 
						|
        return x
 | 
						|
 | 
						|
 | 
						|
def to_column_set(x):
 | 
						|
    if x is None:
 | 
						|
        return column_set()
 | 
						|
    if not isinstance(x, column_set):
 | 
						|
        return column_set(to_list(x))
 | 
						|
    else:
 | 
						|
        return x
 | 
						|
 | 
						|
 | 
						|
def update_copy(d, _new=None, **kw):
 | 
						|
    """Copy the given dict and update with the given values."""
 | 
						|
 | 
						|
    d = d.copy()
 | 
						|
    if _new:
 | 
						|
        d.update(_new)
 | 
						|
    d.update(**kw)
 | 
						|
    return d
 | 
						|
 | 
						|
 | 
						|
def flatten_iterator(x):
 | 
						|
    """Given an iterator of which further sub-elements may also be
 | 
						|
    iterators, flatten the sub-elements into a single iterator.
 | 
						|
 | 
						|
    """
 | 
						|
    for elem in x:
 | 
						|
        if not isinstance(elem, str) and hasattr(elem, "__iter__"):
 | 
						|
            for y in flatten_iterator(elem):
 | 
						|
                yield y
 | 
						|
        else:
 | 
						|
            yield elem
 | 
						|
 | 
						|
 | 
						|
class LRUCache(dict):
 | 
						|
    """Dictionary with 'squishy' removal of least
 | 
						|
    recently used items.
 | 
						|
 | 
						|
    Note that either get() or [] should be used here, but
 | 
						|
    generally its not safe to do an "in" check first as the dictionary
 | 
						|
    can change subsequent to that call.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    __slots__ = "capacity", "threshold", "size_alert", "_counter", "_mutex"
 | 
						|
 | 
						|
    def __init__(self, capacity=100, threshold=0.5, size_alert=None):
 | 
						|
        self.capacity = capacity
 | 
						|
        self.threshold = threshold
 | 
						|
        self.size_alert = size_alert
 | 
						|
        self._counter = 0
 | 
						|
        self._mutex = threading.Lock()
 | 
						|
 | 
						|
    def _inc_counter(self):
 | 
						|
        self._counter += 1
 | 
						|
        return self._counter
 | 
						|
 | 
						|
    def get(self, key, default=None):
 | 
						|
        item = dict.get(self, key, default)
 | 
						|
        if item is not default:
 | 
						|
            item[2] = self._inc_counter()
 | 
						|
            return item[1]
 | 
						|
        else:
 | 
						|
            return default
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
        item = dict.__getitem__(self, key)
 | 
						|
        item[2] = self._inc_counter()
 | 
						|
        return item[1]
 | 
						|
 | 
						|
    def values(self):
 | 
						|
        return [i[1] for i in dict.values(self)]
 | 
						|
 | 
						|
    def setdefault(self, key, value):
 | 
						|
        if key in self:
 | 
						|
            return self[key]
 | 
						|
        else:
 | 
						|
            self[key] = value
 | 
						|
            return value
 | 
						|
 | 
						|
    def __setitem__(self, key, value):
 | 
						|
        item = dict.get(self, key)
 | 
						|
        if item is None:
 | 
						|
            item = [key, value, self._inc_counter()]
 | 
						|
            dict.__setitem__(self, key, item)
 | 
						|
        else:
 | 
						|
            item[1] = value
 | 
						|
        self._manage_size()
 | 
						|
 | 
						|
    @property
 | 
						|
    def size_threshold(self):
 | 
						|
        return self.capacity + self.capacity * self.threshold
 | 
						|
 | 
						|
    def _manage_size(self):
 | 
						|
        if not self._mutex.acquire(False):
 | 
						|
            return
 | 
						|
        try:
 | 
						|
            size_alert = bool(self.size_alert)
 | 
						|
            while len(self) > self.capacity + self.capacity * self.threshold:
 | 
						|
                if size_alert:
 | 
						|
                    size_alert = False
 | 
						|
                    self.size_alert(self)
 | 
						|
                by_counter = sorted(
 | 
						|
                    dict.values(self), key=operator.itemgetter(2), reverse=True
 | 
						|
                )
 | 
						|
                for item in by_counter[self.capacity :]:
 | 
						|
                    try:
 | 
						|
                        del self[item[0]]
 | 
						|
                    except KeyError:
 | 
						|
                        # deleted elsewhere; skip
 | 
						|
                        continue
 | 
						|
        finally:
 | 
						|
            self._mutex.release()
 | 
						|
 | 
						|
 | 
						|
class ScopedRegistry(object):
 | 
						|
    """A Registry that can store one or multiple instances of a single
 | 
						|
    class on the basis of a "scope" function.
 | 
						|
 | 
						|
    The object implements ``__call__`` as the "getter", so by
 | 
						|
    calling ``myregistry()`` the contained object is returned
 | 
						|
    for the current scope.
 | 
						|
 | 
						|
    :param createfunc:
 | 
						|
      a callable that returns a new object to be placed in the registry
 | 
						|
 | 
						|
    :param scopefunc:
 | 
						|
      a callable that will return a key to store/retrieve an object.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, createfunc, scopefunc):
 | 
						|
        """Construct a new :class:`.ScopedRegistry`.
 | 
						|
 | 
						|
        :param createfunc:  A creation function that will generate
 | 
						|
          a new value for the current scope, if none is present.
 | 
						|
 | 
						|
        :param scopefunc:  A function that returns a hashable
 | 
						|
          token representing the current scope (such as, current
 | 
						|
          thread identifier).
 | 
						|
 | 
						|
        """
 | 
						|
        self.createfunc = createfunc
 | 
						|
        self.scopefunc = scopefunc
 | 
						|
        self.registry = {}
 | 
						|
 | 
						|
    def __call__(self):
 | 
						|
        key = self.scopefunc()
 | 
						|
        try:
 | 
						|
            return self.registry[key]
 | 
						|
        except KeyError:
 | 
						|
            return self.registry.setdefault(key, self.createfunc())
 | 
						|
 | 
						|
    def has(self):
 | 
						|
        """Return True if an object is present in the current scope."""
 | 
						|
 | 
						|
        return self.scopefunc() in self.registry
 | 
						|
 | 
						|
    def set(self, obj):
 | 
						|
        """Set the value for the current scope."""
 | 
						|
 | 
						|
        self.registry[self.scopefunc()] = obj
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        """Clear the current scope, if any."""
 | 
						|
 | 
						|
        try:
 | 
						|
            del self.registry[self.scopefunc()]
 | 
						|
        except KeyError:
 | 
						|
            pass
 | 
						|
 | 
						|
 | 
						|
class ThreadLocalRegistry(ScopedRegistry):
 | 
						|
    """A :class:`.ScopedRegistry` that uses a ``threading.local()``
 | 
						|
    variable for storage.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, createfunc):
 | 
						|
        self.createfunc = createfunc
 | 
						|
        self.registry = threading.local()
 | 
						|
 | 
						|
    def __call__(self):
 | 
						|
        try:
 | 
						|
            return self.registry.value
 | 
						|
        except AttributeError:
 | 
						|
            val = self.registry.value = self.createfunc()
 | 
						|
            return val
 | 
						|
 | 
						|
    def has(self):
 | 
						|
        return hasattr(self.registry, "value")
 | 
						|
 | 
						|
    def set(self, obj):
 | 
						|
        self.registry.value = obj
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        try:
 | 
						|
            del self.registry.value
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
 | 
						|
 | 
						|
def has_dupes(sequence, target):
 | 
						|
    """Given a sequence and search object, return True if there's more
 | 
						|
    than one, False if zero or one of them.
 | 
						|
 | 
						|
 | 
						|
    """
 | 
						|
    # compare to .index version below, this version introduces less function
 | 
						|
    # overhead and is usually the same speed.  At 15000 items (way bigger than
 | 
						|
    # a relationship-bound collection in memory usually is) it begins to
 | 
						|
    # fall behind the other version only by microseconds.
 | 
						|
    c = 0
 | 
						|
    for item in sequence:
 | 
						|
        if item is target:
 | 
						|
            c += 1
 | 
						|
            if c > 1:
 | 
						|
                return True
 | 
						|
    return False
 | 
						|
 | 
						|
 | 
						|
# .index version.  the two __contains__ calls as well
 | 
						|
# as .index() and isinstance() slow this down.
 | 
						|
# def has_dupes(sequence, target):
 | 
						|
#    if target not in sequence:
 | 
						|
#        return False
 | 
						|
#    elif not isinstance(sequence, collections_abc.Sequence):
 | 
						|
#        return False
 | 
						|
#
 | 
						|
#    idx = sequence.index(target)
 | 
						|
#    return target in sequence[idx + 1:]
 |