Remove Elixir library
Update SQLAlchemy
This commit is contained in:
@@ -1,34 +1,45 @@
|
||||
# util/__init__.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from compat import callable, cmp, reduce, defaultdict, py25_dict, \
|
||||
threading, py3k_warning, jython, pypy, cpython, win32, set_types, buffer, \
|
||||
pickle, update_wrapper, partial, md5_hex, decode_slice, dottedgetter,\
|
||||
parse_qsl, any, contextmanager, next
|
||||
from .compat import callable, cmp, reduce, \
|
||||
threading, py3k, py33, py2k, jython, pypy, cpython, win32, \
|
||||
pickle, dottedgetter, parse_qsl, namedtuple, next, reraise, \
|
||||
raise_from_cause, text_type, string_types, int_types, binary_type, \
|
||||
quote_plus, with_metaclass, print_, itertools_filterfalse, u, ue, b,\
|
||||
unquote_plus, unquote, b64decode, b64encode, byte_buffer, itertools_filter,\
|
||||
iterbytes, StringIO, inspect_getargspec
|
||||
|
||||
from _collections import NamedTuple, ImmutableContainer, immutabledict, \
|
||||
from ._collections import KeyedTuple, ImmutableContainer, immutabledict, \
|
||||
Properties, OrderedProperties, ImmutableProperties, OrderedDict, \
|
||||
OrderedSet, IdentitySet, OrderedIdentitySet, column_set, \
|
||||
column_dict, ordered_column_set, populate_column_dict, unique_list, \
|
||||
UniqueAppender, PopulateDict, EMPTY_SET, to_list, to_set, \
|
||||
to_column_set, update_copy, flatten_iterator, WeakIdentityMapping, \
|
||||
LRUCache, ScopedRegistry, ThreadLocalRegistry
|
||||
to_column_set, update_copy, flatten_iterator, \
|
||||
LRUCache, ScopedRegistry, ThreadLocalRegistry, WeakSequence, \
|
||||
coerce_generator_arg
|
||||
|
||||
from langhelpers import iterate_attributes, class_hierarchy, \
|
||||
from .langhelpers import iterate_attributes, class_hierarchy, \
|
||||
portable_instancemethod, unbound_method_to_callable, \
|
||||
getargspec_init, format_argspec_init, format_argspec_plus, \
|
||||
get_func_kwargs, get_cls_kwargs, decorator, as_interface, \
|
||||
memoized_property, memoized_instancemethod, \
|
||||
reset_memoized, group_expirable_memoized_property, importlater, \
|
||||
memoized_property, memoized_instancemethod, md5_hex, \
|
||||
group_expirable_memoized_property, dependencies, decode_slice, \
|
||||
monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\
|
||||
duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\
|
||||
classproperty, set_creation_order, warn_exception, warn, NoneType,\
|
||||
constructor_copy, methods_equivalent, chop_traceback, asint,\
|
||||
generic_repr, counter
|
||||
generic_repr, counter, PluginLoader, hybridmethod, safe_reraise,\
|
||||
get_callable_argspec, only_once
|
||||
|
||||
from deprecations import warn_deprecated, warn_pending_deprecation, \
|
||||
deprecated, pending_deprecation
|
||||
from .deprecations import warn_deprecated, warn_pending_deprecation, \
|
||||
deprecated, pending_deprecation, inject_docstring_text
|
||||
|
||||
# things that used to be not always available,
|
||||
# but are now as of current support Python versions
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
from functools import update_wrapper
|
||||
from contextlib import contextmanager
|
||||
|
||||
@@ -1,44 +1,111 @@
|
||||
# util/_collections.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Collection classes and helpers."""
|
||||
|
||||
import sys
|
||||
import itertools
|
||||
from __future__ import absolute_import
|
||||
import weakref
|
||||
import operator
|
||||
from langhelpers import symbol
|
||||
from compat import time_func, threading
|
||||
from .compat import threading, itertools_filterfalse
|
||||
from . import py2k
|
||||
import types
|
||||
|
||||
EMPTY_SET = frozenset()
|
||||
|
||||
|
||||
class NamedTuple(tuple):
|
||||
"""tuple() subclass that adds labeled names.
|
||||
class KeyedTuple(tuple):
|
||||
"""``tuple`` subclass that adds labeled names.
|
||||
|
||||
Is also pickleable.
|
||||
E.g.::
|
||||
|
||||
>>> k = KeyedTuple([1, 2, 3], labels=["one", "two", "three"])
|
||||
>>> k.one
|
||||
1
|
||||
>>> k.two
|
||||
2
|
||||
|
||||
Result rows returned by :class:`.Query` that contain multiple
|
||||
ORM entities and/or column expressions make use of this
|
||||
class to return rows.
|
||||
|
||||
The :class:`.KeyedTuple` exhibits similar behavior to the
|
||||
``collections.namedtuple()`` construct provided in the Python
|
||||
standard library, however is architected very differently.
|
||||
Unlike ``collections.namedtuple()``, :class:`.KeyedTuple` is
|
||||
does not rely on creation of custom subtypes in order to represent
|
||||
a new series of keys, instead each :class:`.KeyedTuple` instance
|
||||
receives its list of keys in place. The subtype approach
|
||||
of ``collections.namedtuple()`` introduces significant complexity
|
||||
and performance overhead, which is not necessary for the
|
||||
:class:`.Query` object's use case.
|
||||
|
||||
.. versionchanged:: 0.8
|
||||
Compatibility methods with ``collections.namedtuple()`` have been
|
||||
added including :attr:`.KeyedTuple._fields` and
|
||||
:meth:`.KeyedTuple._asdict`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`ormtutorial_querying`
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, vals, labels=None):
|
||||
t = tuple.__new__(cls, vals)
|
||||
t._labels = []
|
||||
if labels:
|
||||
t.__dict__.update(zip(labels, vals))
|
||||
t._labels = labels
|
||||
return t
|
||||
|
||||
def keys(self):
|
||||
"""Return a list of string key names for this :class:`.KeyedTuple`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`.KeyedTuple._fields`
|
||||
|
||||
"""
|
||||
|
||||
return [l for l in self._labels if l is not None]
|
||||
|
||||
@property
|
||||
def _fields(self):
|
||||
"""Return a tuple of string key names for this :class:`.KeyedTuple`.
|
||||
|
||||
This method provides compatibility with ``collections.namedtuple()``.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.KeyedTuple.keys`
|
||||
|
||||
"""
|
||||
return tuple(self.keys())
|
||||
|
||||
def _asdict(self):
|
||||
"""Return the contents of this :class:`.KeyedTuple` as a dictionary.
|
||||
|
||||
This method provides compatibility with ``collections.namedtuple()``,
|
||||
with the exception that the dictionary returned is **not** ordered.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
"""
|
||||
return dict((key, self.__dict__[key]) for key in self.keys())
|
||||
|
||||
|
||||
class ImmutableContainer(object):
|
||||
def _immutable(self, *arg, **kw):
|
||||
raise TypeError("%s object is immutable" % self.__class__.__name__)
|
||||
|
||||
__delitem__ = __setitem__ = __setattr__ = _immutable
|
||||
|
||||
|
||||
class immutabledict(ImmutableContainer, dict):
|
||||
|
||||
clear = pop = popitem = setdefault = \
|
||||
@@ -66,6 +133,7 @@ class immutabledict(ImmutableContainer, dict):
|
||||
def __repr__(self):
|
||||
return "immutabledict(%s)" % dict.__repr__(self)
|
||||
|
||||
|
||||
class Properties(object):
|
||||
"""Provide a __getattr__/__setattr__ interface over a dict."""
|
||||
|
||||
@@ -76,7 +144,7 @@ class Properties(object):
|
||||
return len(self._data)
|
||||
|
||||
def __iter__(self):
|
||||
return self._data.itervalues()
|
||||
return iter(list(self._data.values()))
|
||||
|
||||
def __add__(self, other):
|
||||
return list(self) + list(other)
|
||||
@@ -123,7 +191,13 @@ class Properties(object):
|
||||
return default
|
||||
|
||||
def keys(self):
|
||||
return self._data.keys()
|
||||
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
|
||||
@@ -131,6 +205,7 @@ class Properties(object):
|
||||
def clear(self):
|
||||
self._data.clear()
|
||||
|
||||
|
||||
class OrderedProperties(Properties):
|
||||
"""Provide a __getattr__/__setattr__ interface with an OrderedDict
|
||||
as backing store."""
|
||||
@@ -187,23 +262,55 @@ class OrderedDict(dict):
|
||||
def __iter__(self):
|
||||
return iter(self._list)
|
||||
|
||||
def values(self):
|
||||
return [self[key] for key in self._list]
|
||||
|
||||
def itervalues(self):
|
||||
return iter([self[key] for key in self._list])
|
||||
if py2k:
|
||||
def values(self):
|
||||
return [self[key] for key in self._list]
|
||||
|
||||
def keys(self):
|
||||
return list(self._list)
|
||||
def keys(self):
|
||||
return self._list
|
||||
|
||||
def iterkeys(self):
|
||||
return iter(self.keys())
|
||||
def itervalues(self):
|
||||
return iter([self[key] for key in self._list])
|
||||
|
||||
def items(self):
|
||||
return [(key, self[key]) for key in self.keys()]
|
||||
def iterkeys(self):
|
||||
return iter(self)
|
||||
|
||||
def iteritems(self):
|
||||
return iter(self.items())
|
||||
|
||||
def items(self):
|
||||
return [(key, self[key]) for key in self._list]
|
||||
else:
|
||||
def values(self):
|
||||
#return (self[key] for key in self)
|
||||
return (self[key] for key in self._list)
|
||||
|
||||
def keys(self):
|
||||
#return iter(self)
|
||||
return iter(self._list)
|
||||
|
||||
def items(self):
|
||||
#return ((key, self[key]) for key in self)
|
||||
return ((key, self[key]) for key in self._list)
|
||||
|
||||
_debug_iter = False
|
||||
if _debug_iter:
|
||||
# normally disabled to reduce function call
|
||||
# overhead
|
||||
def __iter__(self):
|
||||
len_ = len(self._list)
|
||||
for item in self._list:
|
||||
yield item
|
||||
assert len_ == len(self._list), \
|
||||
"Dictionary changed size during iteration"
|
||||
def values(self):
|
||||
return (self[key] for key in self)
|
||||
def keys(self):
|
||||
return iter(self)
|
||||
def items(self):
|
||||
return ((key, self[key]) for key in self)
|
||||
|
||||
def iteritems(self):
|
||||
return iter(self.items())
|
||||
|
||||
def __setitem__(self, key, object):
|
||||
if key not in self:
|
||||
@@ -231,6 +338,7 @@ class OrderedDict(dict):
|
||||
self._list.remove(item[0])
|
||||
return item
|
||||
|
||||
|
||||
class OrderedSet(set):
|
||||
def __init__(self, d=None):
|
||||
set.__init__(self)
|
||||
@@ -314,22 +422,22 @@ class OrderedSet(set):
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
self._list = [a for a in self._list if a in self]
|
||||
return self
|
||||
|
||||
__isub__ = difference_update
|
||||
@@ -376,9 +484,6 @@ class IdentitySet(object):
|
||||
def clear(self):
|
||||
self._members.clear()
|
||||
|
||||
def __sub__(self, other):
|
||||
return self.difference(other)
|
||||
|
||||
def __cmp__(self, other):
|
||||
raise TypeError('cannot compare sets using cmp()')
|
||||
|
||||
@@ -399,8 +504,8 @@ class IdentitySet(object):
|
||||
|
||||
if len(self) > len(other):
|
||||
return False
|
||||
for m in itertools.ifilterfalse(other._members.__contains__,
|
||||
self._members.iterkeys()):
|
||||
for m in itertools_filterfalse(other._members.__contains__,
|
||||
iter(self._members.keys())):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -420,8 +525,8 @@ class IdentitySet(object):
|
||||
if len(self) < len(other):
|
||||
return False
|
||||
|
||||
for m in itertools.ifilterfalse(self._members.__contains__,
|
||||
other._members.iterkeys()):
|
||||
for m in itertools_filterfalse(self._members.__contains__,
|
||||
iter(other._members.keys())):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -438,8 +543,9 @@ class IdentitySet(object):
|
||||
def union(self, iterable):
|
||||
result = type(self)()
|
||||
# testlib.pragma exempt:__hash__
|
||||
result._members.update(
|
||||
self._working_set(self._member_id_tuples()).union(_iter_id(iterable)))
|
||||
members = self._member_id_tuples()
|
||||
other = _iter_id(iterable)
|
||||
result._members.update(self._working_set(members).union(other))
|
||||
return result
|
||||
|
||||
def __or__(self, other):
|
||||
@@ -459,8 +565,9 @@ class IdentitySet(object):
|
||||
def difference(self, iterable):
|
||||
result = type(self)()
|
||||
# testlib.pragma exempt:__hash__
|
||||
result._members.update(
|
||||
self._working_set(self._member_id_tuples()).difference(_iter_id(iterable)))
|
||||
members = self._member_id_tuples()
|
||||
other = _iter_id(iterable)
|
||||
result._members.update(self._working_set(members).difference(other))
|
||||
return result
|
||||
|
||||
def __sub__(self, other):
|
||||
@@ -480,8 +587,9 @@ class IdentitySet(object):
|
||||
def intersection(self, iterable):
|
||||
result = type(self)()
|
||||
# testlib.pragma exempt:__hash__
|
||||
result._members.update(
|
||||
self._working_set(self._member_id_tuples()).intersection(_iter_id(iterable)))
|
||||
members = self._member_id_tuples()
|
||||
other = _iter_id(iterable)
|
||||
result._members.update(self._working_set(members).intersection(other))
|
||||
return result
|
||||
|
||||
def __and__(self, other):
|
||||
@@ -501,12 +609,14 @@ class IdentitySet(object):
|
||||
def symmetric_difference(self, iterable):
|
||||
result = type(self)()
|
||||
# testlib.pragma exempt:__hash__
|
||||
members = self._member_id_tuples()
|
||||
other = _iter_id(iterable)
|
||||
result._members.update(
|
||||
self._working_set(self._member_id_tuples()).symmetric_difference(_iter_id(iterable)))
|
||||
self._working_set(members).symmetric_difference(other))
|
||||
return result
|
||||
|
||||
def _member_id_tuples(self):
|
||||
return ((id(v), v) for v in self._members.itervalues())
|
||||
return ((id(v), v) for v in self._members.values())
|
||||
|
||||
def __xor__(self, other):
|
||||
if not isinstance(other, IdentitySet):
|
||||
@@ -523,7 +633,7 @@ class IdentitySet(object):
|
||||
return self
|
||||
|
||||
def copy(self):
|
||||
return type(self)(self._members.itervalues())
|
||||
return type(self)(iter(self._members.values()))
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
@@ -531,13 +641,41 @@ class IdentitySet(object):
|
||||
return len(self._members)
|
||||
|
||||
def __iter__(self):
|
||||
return self._members.itervalues()
|
||||
return iter(self._members.values())
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('set objects are unhashable')
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (type(self).__name__, self._members.values())
|
||||
return '%s(%r)' % (type(self).__name__, list(self._members.values()))
|
||||
|
||||
|
||||
class WeakSequence(object):
|
||||
def __init__(self, __elements=()):
|
||||
self._storage = [
|
||||
weakref.ref(element, self._remove) for element in __elements
|
||||
]
|
||||
|
||||
def append(self, item):
|
||||
self._storage.append(weakref.ref(item, self._remove))
|
||||
|
||||
def _remove(self, ref):
|
||||
self._storage.remove(ref)
|
||||
|
||||
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):
|
||||
@@ -556,37 +694,25 @@ class OrderedIdentitySet(IdentitySet):
|
||||
self.add(o)
|
||||
|
||||
|
||||
if sys.version_info >= (2, 5):
|
||||
class PopulateDict(dict):
|
||||
"""A dict which populates missing values via a creation function.
|
||||
class PopulateDict(dict):
|
||||
"""A dict which populates missing values via a creation function.
|
||||
|
||||
Note the creation function takes a key, unlike
|
||||
collections.defaultdict.
|
||||
Note the creation function takes a key, unlike
|
||||
collections.defaultdict.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, creator):
|
||||
self.creator = creator
|
||||
def __init__(self, creator):
|
||||
self.creator = creator
|
||||
|
||||
def __missing__(self, key):
|
||||
self[key] = val = self.creator(key)
|
||||
return val
|
||||
else:
|
||||
class PopulateDict(dict):
|
||||
"""A dict which populates missing values via a creation function."""
|
||||
def __missing__(self, key):
|
||||
self[key] = val = self.creator(key)
|
||||
return val
|
||||
|
||||
def __init__(self, creator):
|
||||
self.creator = creator
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
self[key] = value = self.creator(key)
|
||||
return value
|
||||
|
||||
# define collections that are capable of storing
|
||||
# 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
|
||||
@@ -603,6 +729,7 @@ def unique_list(seq, hashfunc=None):
|
||||
if hashfunc(x) not in seen
|
||||
and not seen.__setitem__(hashfunc(x), True)]
|
||||
|
||||
|
||||
class UniqueAppender(object):
|
||||
"""Appends items to a collection ensuring uniqueness.
|
||||
|
||||
@@ -629,6 +756,12 @@ class UniqueAppender(object):
|
||||
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
|
||||
@@ -637,6 +770,7 @@ def to_list(x, default=None):
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
def to_set(x):
|
||||
if x is None:
|
||||
return set()
|
||||
@@ -645,6 +779,7 @@ def to_set(x):
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
def to_column_set(x):
|
||||
if x is None:
|
||||
return column_set()
|
||||
@@ -653,6 +788,7 @@ def to_column_set(x):
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
def update_copy(d, _new=None, **kw):
|
||||
"""Copy the given dict and update with the given values."""
|
||||
|
||||
@@ -662,104 +798,19 @@ def update_copy(d, _new=None, **kw):
|
||||
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, basestring) and hasattr(elem, '__iter__'):
|
||||
if not isinstance(elem, str) and hasattr(elem, '__iter__'):
|
||||
for y in flatten_iterator(elem):
|
||||
yield y
|
||||
else:
|
||||
yield elem
|
||||
|
||||
class WeakIdentityMapping(weakref.WeakKeyDictionary):
|
||||
"""A WeakKeyDictionary with an object identity index.
|
||||
|
||||
Adds a .by_id dictionary to a regular WeakKeyDictionary. Trades
|
||||
performance during mutation operations for accelerated lookups by id().
|
||||
|
||||
The usual cautions about weak dictionaries and iteration also apply to
|
||||
this subclass.
|
||||
|
||||
"""
|
||||
_none = symbol('none')
|
||||
|
||||
def __init__(self):
|
||||
weakref.WeakKeyDictionary.__init__(self)
|
||||
self.by_id = {}
|
||||
self._weakrefs = {}
|
||||
|
||||
def __setitem__(self, object, value):
|
||||
oid = id(object)
|
||||
self.by_id[oid] = value
|
||||
if oid not in self._weakrefs:
|
||||
self._weakrefs[oid] = self._ref(object)
|
||||
weakref.WeakKeyDictionary.__setitem__(self, object, value)
|
||||
|
||||
def __delitem__(self, object):
|
||||
del self._weakrefs[id(object)]
|
||||
del self.by_id[id(object)]
|
||||
weakref.WeakKeyDictionary.__delitem__(self, object)
|
||||
|
||||
def setdefault(self, object, default=None):
|
||||
value = weakref.WeakKeyDictionary.setdefault(self, object, default)
|
||||
oid = id(object)
|
||||
if value is default:
|
||||
self.by_id[oid] = default
|
||||
if oid not in self._weakrefs:
|
||||
self._weakrefs[oid] = self._ref(object)
|
||||
return value
|
||||
|
||||
def pop(self, object, default=_none):
|
||||
if default is self._none:
|
||||
value = weakref.WeakKeyDictionary.pop(self, object)
|
||||
else:
|
||||
value = weakref.WeakKeyDictionary.pop(self, object, default)
|
||||
if id(object) in self.by_id:
|
||||
del self._weakrefs[id(object)]
|
||||
del self.by_id[id(object)]
|
||||
return value
|
||||
|
||||
def popitem(self):
|
||||
item = weakref.WeakKeyDictionary.popitem(self)
|
||||
oid = id(item[0])
|
||||
del self._weakrefs[oid]
|
||||
del self.by_id[oid]
|
||||
return item
|
||||
|
||||
def clear(self):
|
||||
# Py2K
|
||||
# in 3k, MutableMapping calls popitem()
|
||||
self._weakrefs.clear()
|
||||
self.by_id.clear()
|
||||
# end Py2K
|
||||
weakref.WeakKeyDictionary.clear(self)
|
||||
|
||||
def update(self, *a, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def _cleanup(self, wr, key=None):
|
||||
if key is None:
|
||||
key = wr.key
|
||||
try:
|
||||
del self._weakrefs[key]
|
||||
except (KeyError, AttributeError): # pragma: no cover
|
||||
pass # pragma: no cover
|
||||
try:
|
||||
del self.by_id[key]
|
||||
except (KeyError, AttributeError): # pragma: no cover
|
||||
pass # pragma: no cover
|
||||
|
||||
class _keyed_weakref(weakref.ref):
|
||||
def __init__(self, object, callback):
|
||||
weakref.ref.__init__(self, object, callback)
|
||||
self.key = id(object)
|
||||
|
||||
def _ref(self, object):
|
||||
return self._keyed_weakref(object, self._cleanup)
|
||||
|
||||
|
||||
class LRUCache(dict):
|
||||
"""Dictionary with 'squishy' removal of least
|
||||
@@ -869,6 +920,7 @@ class ScopedRegistry(object):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
class ThreadLocalRegistry(ScopedRegistry):
|
||||
"""A :class:`.ScopedRegistry` that uses a ``threading.local()``
|
||||
variable for storage.
|
||||
@@ -897,9 +949,9 @@ class ThreadLocalRegistry(ScopedRegistry):
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def _iter_id(iterable):
|
||||
"""Generator: ((id(o), o) for o in iterable)."""
|
||||
|
||||
for item in iterable:
|
||||
yield id(item), item
|
||||
|
||||
|
||||
+155
-183
@@ -1,5 +1,5 @@
|
||||
# util/compat.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -8,45 +8,24 @@
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
import dummy_threading as threading
|
||||
|
||||
py33 = sys.version_info >= (3, 3)
|
||||
py32 = sys.version_info >= (3, 2)
|
||||
py3k_warning = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0)
|
||||
py3k = sys.version_info >= (3, 0)
|
||||
py2k = sys.version_info < (3, 0)
|
||||
jython = sys.platform.startswith('java')
|
||||
pypy = hasattr(sys, 'pypy_version_info')
|
||||
win32 = sys.platform.startswith('win')
|
||||
cpython = not pypy and not jython # TODO: something better for this ?
|
||||
|
||||
if py3k_warning:
|
||||
set_types = set
|
||||
elif sys.version_info < (2, 6):
|
||||
import sets
|
||||
set_types = set, sets.Set
|
||||
else:
|
||||
# 2.6 deprecates sets.Set, but we still need to be able to detect them
|
||||
# in user code and as return values from DB-APIs
|
||||
ignore = ('ignore', None, DeprecationWarning, None, 0)
|
||||
import warnings
|
||||
try:
|
||||
warnings.filters.insert(0, ignore)
|
||||
except Exception:
|
||||
import sets
|
||||
else:
|
||||
import sets
|
||||
warnings.filters.remove(ignore)
|
||||
import collections
|
||||
next = next
|
||||
|
||||
set_types = set, sets.Set
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
def next(iter):
|
||||
return iter.next()
|
||||
else:
|
||||
next = next
|
||||
if py3k_warning:
|
||||
if py3k:
|
||||
import pickle
|
||||
else:
|
||||
try:
|
||||
@@ -54,146 +33,121 @@ else:
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
# a controversial feature, required by MySQLdb currently
|
||||
def buffer(x):
|
||||
return x
|
||||
ArgSpec = collections.namedtuple("ArgSpec",
|
||||
["args", "varargs", "keywords", "defaults"])
|
||||
|
||||
# Py2K
|
||||
buffer = buffer
|
||||
# end Py2K
|
||||
if py3k:
|
||||
import builtins
|
||||
|
||||
try:
|
||||
from contextlib import contextmanager
|
||||
except ImportError:
|
||||
def contextmanager(fn):
|
||||
return fn
|
||||
from inspect import getfullargspec as inspect_getfullargspec
|
||||
from urllib.parse import quote_plus, unquote_plus, parse_qsl, quote, unquote
|
||||
import configparser
|
||||
from io import StringIO
|
||||
|
||||
try:
|
||||
from functools import update_wrapper
|
||||
except ImportError:
|
||||
def update_wrapper(wrapper, wrapped,
|
||||
assigned=('__doc__', '__module__', '__name__'),
|
||||
updated=('__dict__',)):
|
||||
for attr in assigned:
|
||||
setattr(wrapper, attr, getattr(wrapped, attr))
|
||||
for attr in updated:
|
||||
getattr(wrapper, attr).update(getattr(wrapped, attr, ()))
|
||||
return wrapper
|
||||
from io import BytesIO as byte_buffer
|
||||
|
||||
try:
|
||||
from functools import partial
|
||||
except ImportError:
|
||||
def partial(func, *args, **keywords):
|
||||
def newfunc(*fargs, **fkeywords):
|
||||
newkeywords = keywords.copy()
|
||||
newkeywords.update(fkeywords)
|
||||
return func(*(args + fargs), **newkeywords)
|
||||
return newfunc
|
||||
def inspect_getargspec(func):
|
||||
return ArgSpec(
|
||||
*inspect_getfullargspec(func)[0:4]
|
||||
)
|
||||
|
||||
string_types = str,
|
||||
binary_type = bytes
|
||||
text_type = str
|
||||
int_types = int,
|
||||
iterbytes = iter
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
# emits a nasty deprecation warning
|
||||
# in newer pythons
|
||||
from cgi import parse_qsl
|
||||
else:
|
||||
from urlparse import parse_qsl
|
||||
def u(s):
|
||||
return s
|
||||
|
||||
# Py3K
|
||||
#from inspect import getfullargspec as inspect_getfullargspec
|
||||
# Py2K
|
||||
from inspect import getargspec as inspect_getfullargspec
|
||||
# end Py2K
|
||||
def ue(s):
|
||||
return s
|
||||
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
if py32:
|
||||
callable = callable
|
||||
else:
|
||||
def callable(fn):
|
||||
return hasattr(fn, '__call__')
|
||||
|
||||
if py3k_warning:
|
||||
# they're bringing it back in 3.2. brilliant !
|
||||
def callable(fn):
|
||||
return hasattr(fn, '__call__')
|
||||
def cmp(a, b):
|
||||
return (a > b) - (a < b)
|
||||
|
||||
from functools import reduce
|
||||
|
||||
print_ = getattr(builtins, "print")
|
||||
|
||||
import_ = getattr(builtins, '__import__')
|
||||
|
||||
import itertools
|
||||
itertools_filterfalse = itertools.filterfalse
|
||||
itertools_filter = filter
|
||||
itertools_imap = map
|
||||
|
||||
import base64
|
||||
def b64encode(x):
|
||||
return base64.b64encode(x).decode('ascii')
|
||||
def b64decode(x):
|
||||
return base64.b64decode(x.encode('ascii'))
|
||||
|
||||
else:
|
||||
from inspect import getargspec as inspect_getfullargspec
|
||||
inspect_getargspec = inspect_getfullargspec
|
||||
from urllib import quote_plus, unquote_plus, quote, unquote
|
||||
from urlparse import parse_qsl
|
||||
import ConfigParser as configparser
|
||||
from StringIO import StringIO
|
||||
from cStringIO import StringIO as byte_buffer
|
||||
|
||||
string_types = basestring,
|
||||
binary_type = str
|
||||
text_type = unicode
|
||||
int_types = int, long
|
||||
def iterbytes(buf):
|
||||
return (ord(byte) for byte in buf)
|
||||
|
||||
def u(s):
|
||||
# this differs from what six does, which doesn't support non-ASCII
|
||||
# strings - we only use u() with
|
||||
# literal source strings, and all our source files with non-ascii
|
||||
# in them (all are tests) are utf-8 encoded.
|
||||
return unicode(s, "utf-8")
|
||||
|
||||
def ue(s):
|
||||
return unicode(s, "unicode_escape")
|
||||
|
||||
def b(s):
|
||||
return s
|
||||
|
||||
def import_(*args):
|
||||
if len(args) == 4:
|
||||
args = args[0:3] + ([str(arg) for arg in args[3]],)
|
||||
return __import__(*args)
|
||||
|
||||
callable = callable
|
||||
cmp = cmp
|
||||
reduce = reduce
|
||||
|
||||
try:
|
||||
from collections import defaultdict
|
||||
except ImportError:
|
||||
class defaultdict(dict):
|
||||
def __init__(self, default_factory=None, *a, **kw):
|
||||
if (default_factory is not None and
|
||||
not hasattr(default_factory, '__call__')):
|
||||
raise TypeError('first argument must be callable')
|
||||
dict.__init__(self, *a, **kw)
|
||||
self.default_factory = default_factory
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
return self.__missing__(key)
|
||||
def __missing__(self, key):
|
||||
if self.default_factory is None:
|
||||
raise KeyError(key)
|
||||
self[key] = value = self.default_factory()
|
||||
return value
|
||||
def __reduce__(self):
|
||||
if self.default_factory is None:
|
||||
args = tuple()
|
||||
else:
|
||||
args = self.default_factory,
|
||||
return type(self), args, None, None, self.iteritems()
|
||||
def copy(self):
|
||||
return self.__copy__()
|
||||
def __copy__(self):
|
||||
return type(self)(self.default_factory, self)
|
||||
def __deepcopy__(self, memo):
|
||||
import copy
|
||||
return type(self)(self.default_factory,
|
||||
copy.deepcopy(self.items()))
|
||||
def __repr__(self):
|
||||
return 'defaultdict(%s, %s)' % (self.default_factory,
|
||||
dict.__repr__(self))
|
||||
import base64
|
||||
b64encode = base64.b64encode
|
||||
b64decode = base64.b64decode
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
for arg in enumerate(args):
|
||||
if not isinstance(arg, basestring):
|
||||
arg = str(arg)
|
||||
fp.write(arg)
|
||||
|
||||
# find or create a dict implementation that supports __missing__
|
||||
class _probe(dict):
|
||||
def __missing__(self, key):
|
||||
return 1
|
||||
import itertools
|
||||
itertools_filterfalse = itertools.ifilterfalse
|
||||
itertools_filter = itertools.ifilter
|
||||
itertools_imap = itertools.imap
|
||||
|
||||
try:
|
||||
try:
|
||||
_probe()['missing']
|
||||
py25_dict = dict
|
||||
except KeyError:
|
||||
class py25_dict(dict):
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
try:
|
||||
missing = self.__missing__
|
||||
except AttributeError:
|
||||
raise KeyError(key)
|
||||
else:
|
||||
return missing(key)
|
||||
finally:
|
||||
del _probe
|
||||
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
_md5 = hashlib.md5
|
||||
except ImportError:
|
||||
import md5
|
||||
_md5 = md5.new
|
||||
|
||||
def md5_hex(x):
|
||||
# Py3K
|
||||
#x = x.encode('utf-8')
|
||||
m = _md5()
|
||||
m.update(x)
|
||||
return m.hexdigest()
|
||||
|
||||
import time
|
||||
if win32 or jython:
|
||||
@@ -201,43 +155,61 @@ if win32 or jython:
|
||||
else:
|
||||
time_func = time.time
|
||||
|
||||
if sys.version_info >= (2, 5):
|
||||
any = any
|
||||
from collections import namedtuple
|
||||
from operator import attrgetter as dottedgetter
|
||||
|
||||
|
||||
if py3k:
|
||||
def reraise(tp, value, tb=None, cause=None):
|
||||
if cause is not None:
|
||||
value.__cause__ = cause
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
def raise_from_cause(exception, exc_info=None):
|
||||
if exc_info is None:
|
||||
exc_info = sys.exc_info()
|
||||
exc_type, exc_value, exc_tb = exc_info
|
||||
reraise(type(exception), exception, tb=exc_tb, cause=exc_value)
|
||||
else:
|
||||
def any(iterator):
|
||||
for item in iterator:
|
||||
if bool(item):
|
||||
return True
|
||||
exec("def reraise(tp, value, tb=None, cause=None):\n"
|
||||
" raise tp, value, tb\n")
|
||||
|
||||
def raise_from_cause(exception, exc_info=None):
|
||||
# not as nice as that of Py3K, but at least preserves
|
||||
# the code line where the issue occurred
|
||||
if exc_info is None:
|
||||
exc_info = sys.exc_info()
|
||||
exc_type, exc_value, exc_tb = exc_info
|
||||
reraise(type(exception), exception, tb=exc_tb)
|
||||
|
||||
if py3k:
|
||||
exec_ = getattr(builtins, 'exec')
|
||||
else:
|
||||
def exec_(func_text, globals_, lcl=None):
|
||||
if lcl is None:
|
||||
exec('exec func_text in globals_')
|
||||
else:
|
||||
return False
|
||||
|
||||
if sys.version_info >= (2, 5):
|
||||
def decode_slice(slc):
|
||||
"""decode a slice object as sent to __getitem__.
|
||||
|
||||
takes into account the 2.5 __index__() method, basically.
|
||||
|
||||
"""
|
||||
ret = []
|
||||
for x in slc.start, slc.stop, slc.step:
|
||||
if hasattr(x, '__index__'):
|
||||
x = x.__index__()
|
||||
ret.append(x)
|
||||
return tuple(ret)
|
||||
else:
|
||||
def decode_slice(slc):
|
||||
return (slc.start, slc.stop, slc.step)
|
||||
|
||||
if sys.version_info >= (2, 6):
|
||||
from operator import attrgetter as dottedgetter
|
||||
else:
|
||||
def dottedgetter(attr):
|
||||
def g(obj):
|
||||
for name in attr.split("."):
|
||||
obj = getattr(obj, name)
|
||||
return obj
|
||||
return g
|
||||
exec('exec func_text in globals_, lcl')
|
||||
|
||||
|
||||
import decimal
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass.
|
||||
|
||||
Drops the middle class upon creation.
|
||||
|
||||
Source: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/
|
||||
|
||||
"""
|
||||
|
||||
class metaclass(meta):
|
||||
__call__ = type.__call__
|
||||
__init__ = type.__init__
|
||||
def __new__(cls, name, this_bases, d):
|
||||
if this_bases is None:
|
||||
return type.__new__(cls, name, (), d)
|
||||
return meta(name, bases, d)
|
||||
return metaclass('temporary_class', None, {})
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# util/deprecations.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -7,17 +7,20 @@
|
||||
"""Helpers related to deprecation of functions, methods, classes, other
|
||||
functionality."""
|
||||
|
||||
from sqlalchemy import exc
|
||||
from .. import exc
|
||||
import warnings
|
||||
import re
|
||||
from langhelpers import decorator
|
||||
from .langhelpers import decorator
|
||||
|
||||
|
||||
def warn_deprecated(msg, stacklevel=3):
|
||||
warnings.warn(msg, exc.SADeprecationWarning, stacklevel=stacklevel)
|
||||
|
||||
|
||||
def warn_pending_deprecation(msg, stacklevel=3):
|
||||
warnings.warn(msg, exc.SAPendingDeprecationWarning, stacklevel=stacklevel)
|
||||
|
||||
|
||||
def deprecated(version, message=None, add_deprecation_to_docstring=True):
|
||||
"""Decorates a function and issues a deprecation warning on use.
|
||||
|
||||
@@ -47,6 +50,7 @@ def deprecated(version, message=None, add_deprecation_to_docstring=True):
|
||||
message % dict(func=fn.__name__), header)
|
||||
return decorate
|
||||
|
||||
|
||||
def pending_deprecation(version, message=None,
|
||||
add_deprecation_to_docstring=True):
|
||||
"""Decorates a function and issues a pending deprecation warning on use.
|
||||
@@ -80,6 +84,7 @@ def pending_deprecation(version, message=None,
|
||||
message % dict(func=fn.__name__), header)
|
||||
return decorate
|
||||
|
||||
|
||||
def _sanitize_restructured_text(text):
|
||||
def repl(m):
|
||||
type_, name = m.group(1, 2)
|
||||
@@ -102,17 +107,37 @@ def _decorate_with_warning(func, wtype, message, docstring_header=None):
|
||||
doc = func.__doc__ is not None and func.__doc__ or ''
|
||||
if docstring_header is not None:
|
||||
docstring_header %= dict(func=func.__name__)
|
||||
docs = doc and doc.expandtabs().split('\n') or []
|
||||
indent = ''
|
||||
for line in docs[1:]:
|
||||
text = line.lstrip()
|
||||
if text:
|
||||
indent = line[0:len(line) - len(text)]
|
||||
break
|
||||
point = min(len(docs), 1)
|
||||
docs.insert(point, '\n' + indent + docstring_header.rstrip())
|
||||
doc = '\n'.join(docs)
|
||||
|
||||
doc = inject_docstring_text(doc, docstring_header, 1)
|
||||
|
||||
decorated = warned(func)
|
||||
decorated.__doc__ = doc
|
||||
return decorated
|
||||
|
||||
import textwrap
|
||||
|
||||
def _dedent_docstring(text):
|
||||
split_text = text.split("\n", 1)
|
||||
if len(split_text) == 1:
|
||||
return text
|
||||
else:
|
||||
firstline, remaining = split_text
|
||||
if not firstline.startswith(" "):
|
||||
return firstline + "\n" + textwrap.dedent(remaining)
|
||||
else:
|
||||
return textwrap.dedent(text)
|
||||
|
||||
def inject_docstring_text(doctext, injecttext, pos):
|
||||
doctext = _dedent_docstring(doctext or "")
|
||||
lines = doctext.split('\n')
|
||||
injectlines = textwrap.dedent(injecttext).split("\n")
|
||||
if injectlines[0]:
|
||||
injectlines.insert(0, "")
|
||||
|
||||
blanks = [num for num, line in enumerate(lines) if not line.strip()]
|
||||
blanks.insert(0, 0)
|
||||
|
||||
inject_pos = blanks[min(pos, len(blanks) - 1)]
|
||||
|
||||
lines = lines[0:inject_pos] + injectlines + lines[inject_pos:]
|
||||
return "\n".join(lines)
|
||||
|
||||
+510
-213
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,53 @@
|
||||
# util/queue.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""An adaptation of Py2.3/2.4's Queue module which supports reentrant
|
||||
behavior, using RLock instead of Lock for its mutex object.
|
||||
behavior, using RLock instead of Lock for its mutex object. The
|
||||
Queue object is used exclusively by the sqlalchemy.pool.QueuePool
|
||||
class.
|
||||
|
||||
This is to support the connection pool's usage of weakref callbacks to return
|
||||
connections to the underlying Queue, which can in extremely
|
||||
rare cases be invoked within the ``get()`` method of the Queue itself,
|
||||
producing a ``put()`` inside the ``get()`` and therefore a reentrant
|
||||
condition."""
|
||||
condition.
|
||||
|
||||
An additional change includes a special "abort" method which can be used
|
||||
to immediately raise a special exception for threads that are blocking
|
||||
on get(). This is to accommodate a rare race condition that can occur
|
||||
within QueuePool.
|
||||
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
from time import time as _time
|
||||
from sqlalchemy.util import threading
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
def notify_all(condition):
|
||||
condition.notify()
|
||||
else:
|
||||
def notify_all(condition):
|
||||
condition.notify_all()
|
||||
from .compat import threading
|
||||
|
||||
|
||||
__all__ = ['Empty', 'Full', 'Queue']
|
||||
__all__ = ['Empty', 'Full', 'Queue', 'SAAbort']
|
||||
|
||||
|
||||
class Empty(Exception):
|
||||
"Exception raised by Queue.get(block=0)/get_nowait()."
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Full(Exception):
|
||||
"Exception raised by Queue.put(block=0)/put_nowait()."
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SAAbort(Exception):
|
||||
"Special SQLA exception to abort waiting"
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
|
||||
class Queue:
|
||||
def __init__(self, maxsize=0):
|
||||
"""Initialize a queue object with a given maximum size.
|
||||
@@ -146,7 +151,6 @@ class Queue:
|
||||
return an item if one is immediately available, else raise the
|
||||
``Empty`` exception (`timeout` is ignored in that case).
|
||||
"""
|
||||
|
||||
self.not_empty.acquire()
|
||||
try:
|
||||
if not block:
|
||||
@@ -154,7 +158,11 @@ class Queue:
|
||||
raise Empty
|
||||
elif timeout is None:
|
||||
while self._empty():
|
||||
self.not_empty.wait()
|
||||
# wait for only half a second, then
|
||||
# loop around, so that we can see a change in
|
||||
# _sqla_abort_context in case we missed the notify_all()
|
||||
# called by abort()
|
||||
self.not_empty.wait(.5)
|
||||
if self._sqla_abort_context:
|
||||
raise SAAbort(self._sqla_abort_context)
|
||||
else:
|
||||
@@ -183,7 +191,10 @@ class Queue:
|
||||
if not self.not_full.acquire(False):
|
||||
return
|
||||
try:
|
||||
notify_all(self.not_empty)
|
||||
# note that this is now optional
|
||||
# as the waiters in get() both loop around
|
||||
# to check the _sqla_abort_context flag periodically
|
||||
self.not_empty.notify_all()
|
||||
finally:
|
||||
self.not_full.release()
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# util/topological.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Topological sorting algorithms."""
|
||||
|
||||
from sqlalchemy.exc import CircularDependencyError
|
||||
from sqlalchemy import util
|
||||
|
||||
from ..exc import CircularDependencyError
|
||||
from .. import util
|
||||
|
||||
__all__ = ['sort', 'sort_as_subsets', 'find_cycles']
|
||||
|
||||
|
||||
def sort_as_subsets(tuples, allitems):
|
||||
|
||||
edges = util.defaultdict(set)
|
||||
@@ -36,6 +36,7 @@ def sort_as_subsets(tuples, allitems):
|
||||
todo.difference_update(output)
|
||||
yield output
|
||||
|
||||
|
||||
def sort(tuples, allitems):
|
||||
"""sort the given list of items by dependency.
|
||||
|
||||
@@ -46,8 +47,10 @@ def sort(tuples, allitems):
|
||||
for s in set_:
|
||||
yield s
|
||||
|
||||
|
||||
def find_cycles(tuples, allitems):
|
||||
# straight from gvr with some mods
|
||||
# adapted from:
|
||||
# http://neopythonic.blogspot.com/2009/01/detecting-cycles-in-directed-graph.html
|
||||
|
||||
edges = util.defaultdict(set)
|
||||
for parent, child in tuples:
|
||||
@@ -84,6 +87,7 @@ def find_cycles(tuples, allitems):
|
||||
node = stack.pop()
|
||||
return output
|
||||
|
||||
|
||||
def _gen_edges(edges):
|
||||
return set([
|
||||
(right, left)
|
||||
|
||||
Reference in New Issue
Block a user