Updated SqlAlchemy
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
# ext/associationproxy.py
|
||||
# Copyright (C) 2005-2017 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
|
||||
|
||||
"""Contain the ``AssociationProxy`` class.
|
||||
|
||||
The ``AssociationProxy`` is a Python property object which provides
|
||||
@@ -9,43 +16,37 @@ See the example ``examples/association/proxied_association.py``.
|
||||
import itertools
|
||||
import operator
|
||||
import weakref
|
||||
from sqlalchemy import exceptions
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy import util
|
||||
from sqlalchemy.orm import collections
|
||||
from sqlalchemy.sql import not_
|
||||
from .. import exc, orm, util
|
||||
from ..orm import collections, interfaces
|
||||
from ..sql import not_, or_
|
||||
|
||||
|
||||
def association_proxy(target_collection, attr, **kw):
|
||||
"""Return a Python property implementing a view of *attr* over a collection.
|
||||
r"""Return a Python property implementing a view of a target
|
||||
attribute which references an attribute on members of the
|
||||
target.
|
||||
|
||||
Implements a read/write view over an instance's *target_collection*,
|
||||
extracting *attr* from each member of the collection. The property acts
|
||||
somewhat like this list comprehension::
|
||||
The returned value is an instance of :class:`.AssociationProxy`.
|
||||
|
||||
[getattr(member, *attr*)
|
||||
for member in getattr(instance, *target_collection*)]
|
||||
Implements a Python property representing a relationship as a collection
|
||||
of simpler values, or a scalar value. The proxied property will mimic
|
||||
the collection type of the target (list, dict or set), or, in the case of
|
||||
a one to one relationship, a simple scalar value.
|
||||
|
||||
Unlike the list comprehension, the collection returned by the property is
|
||||
always in sync with *target_collection*, and mutations made to either
|
||||
collection will be reflected in both.
|
||||
:param target_collection: Name of the attribute we'll proxy to.
|
||||
This attribute is typically mapped by
|
||||
:func:`~sqlalchemy.orm.relationship` to link to a target collection, but
|
||||
can also be a many-to-one or non-scalar relationship.
|
||||
|
||||
Implements a Python property representing a relationship as a collection of
|
||||
simpler values. The proxied property will mimic the collection type of
|
||||
the target (list, dict or set), or, in the case of a one to one relationship,
|
||||
a simple scalar value.
|
||||
|
||||
:param target_collection: Name of the relationship attribute we'll proxy to,
|
||||
usually created with :func:`~sqlalchemy.orm.relationship`.
|
||||
|
||||
:param attr: Attribute on the associated instances we'll proxy for.
|
||||
:param attr: Attribute on the associated instance or instances we'll
|
||||
proxy for.
|
||||
|
||||
For example, given a target collection of [obj1, obj2], a list created
|
||||
by this proxy property would look like [getattr(obj1, *attr*),
|
||||
getattr(obj2, *attr*)]
|
||||
|
||||
If the relationship is one-to-one or otherwise uselist=False, then simply:
|
||||
getattr(obj, *attr*)
|
||||
If the relationship is one-to-one or otherwise uselist=False, then
|
||||
simply: getattr(obj, *attr*)
|
||||
|
||||
:param creator: optional.
|
||||
|
||||
@@ -69,59 +70,78 @@ def association_proxy(target_collection, attr, **kw):
|
||||
situation.
|
||||
|
||||
:param \*\*kw: Passes along any other keyword arguments to
|
||||
:class:`AssociationProxy`.
|
||||
:class:`.AssociationProxy`.
|
||||
|
||||
"""
|
||||
return AssociationProxy(target_collection, attr, **kw)
|
||||
|
||||
|
||||
class AssociationProxy(object):
|
||||
ASSOCIATION_PROXY = util.symbol('ASSOCIATION_PROXY')
|
||||
"""Symbol indicating an :class:`InspectionAttr` that's
|
||||
of type :class:`.AssociationProxy`.
|
||||
|
||||
Is assigned to the :attr:`.InspectionAttr.extension_type`
|
||||
attibute.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class AssociationProxy(interfaces.InspectionAttrInfo):
|
||||
"""A descriptor that presents a read/write view of an object attribute."""
|
||||
|
||||
is_attribute = False
|
||||
extension_type = ASSOCIATION_PROXY
|
||||
|
||||
def __init__(self, target_collection, attr, creator=None,
|
||||
getset_factory=None, proxy_factory=None, proxy_bulk_set=None):
|
||||
"""Arguments are:
|
||||
getset_factory=None, proxy_factory=None,
|
||||
proxy_bulk_set=None, info=None):
|
||||
"""Construct a new :class:`.AssociationProxy`.
|
||||
|
||||
target_collection
|
||||
Name of the collection we'll proxy to, usually created with
|
||||
'relationship()' in a mapper setup.
|
||||
The :func:`.association_proxy` function is provided as the usual
|
||||
entrypoint here, though :class:`.AssociationProxy` can be instantiated
|
||||
and/or subclassed directly.
|
||||
|
||||
attr
|
||||
Attribute on the collected instances we'll proxy for. For example,
|
||||
given a target collection of [obj1, obj2], a list created by this
|
||||
proxy property would look like [getattr(obj1, attr), getattr(obj2,
|
||||
attr)]
|
||||
:param target_collection: Name of the collection we'll proxy to,
|
||||
usually created with :func:`.relationship`.
|
||||
|
||||
creator
|
||||
Optional. When new items are added to this proxied collection, new
|
||||
instances of the class collected by the target collection will be
|
||||
created. For list and set collections, the target class constructor
|
||||
will be called with the 'value' for the new instance. For dict
|
||||
types, two arguments are passed: key and value.
|
||||
:param attr: Attribute on the collected instances we'll proxy
|
||||
for. For example, given a target collection of [obj1, obj2], a
|
||||
list created by this proxy property would look like
|
||||
[getattr(obj1, attr), getattr(obj2, attr)]
|
||||
|
||||
:param creator: Optional. When new items are added to this proxied
|
||||
collection, new instances of the class collected by the target
|
||||
collection will be created. For list and set collections, the
|
||||
target class constructor will be called with the 'value' for the
|
||||
new instance. For dict types, two arguments are passed:
|
||||
key and value.
|
||||
|
||||
If you want to construct instances differently, supply a 'creator'
|
||||
function that takes arguments as above and returns instances.
|
||||
|
||||
getset_factory
|
||||
Optional. Proxied attribute access is automatically handled by
|
||||
routines that get and set values based on the `attr` argument for
|
||||
this proxy.
|
||||
:param getset_factory: Optional. Proxied attribute access is
|
||||
automatically handled by routines that get and set values based on
|
||||
the `attr` argument for this proxy.
|
||||
|
||||
If you would like to customize this behavior, you may supply a
|
||||
`getset_factory` callable that produces a tuple of `getter` and
|
||||
`setter` functions. The factory is called with two arguments, the
|
||||
abstract type of the underlying collection and this proxy instance.
|
||||
|
||||
proxy_factory
|
||||
Optional. The type of collection to emulate is determined by
|
||||
sniffing the target collection. If your collection type can't be
|
||||
determined by duck typing or you'd like to use a different
|
||||
collection implementation, you may supply a factory function to
|
||||
produce those collections. Only applicable to non-scalar relationships.
|
||||
:param proxy_factory: Optional. The type of collection to emulate is
|
||||
determined by sniffing the target collection. If your collection
|
||||
type can't be determined by duck typing or you'd like to use a
|
||||
different collection implementation, you may supply a factory
|
||||
function to produce those collections. Only applicable to
|
||||
non-scalar relationships.
|
||||
|
||||
proxy_bulk_set
|
||||
Optional, use with proxy_factory. See the _set() method for
|
||||
details.
|
||||
:param proxy_bulk_set: Optional, use with proxy_factory. See
|
||||
the _set() method for details.
|
||||
|
||||
:param info: optional, will be assigned to
|
||||
:attr:`.AssociationProxy.info` if present.
|
||||
|
||||
.. versionadded:: 1.0.9
|
||||
|
||||
"""
|
||||
self.target_collection = target_collection
|
||||
@@ -131,36 +151,107 @@ class AssociationProxy(object):
|
||||
self.proxy_factory = proxy_factory
|
||||
self.proxy_bulk_set = proxy_bulk_set
|
||||
|
||||
self.scalar = None
|
||||
self.owning_class = None
|
||||
self.key = '_%s_%s_%s' % (
|
||||
type(self).__name__, target_collection, id(self))
|
||||
self.collection_class = None
|
||||
if info:
|
||||
self.info = info
|
||||
|
||||
@property
|
||||
def remote_attr(self):
|
||||
"""The 'remote' :class:`.MapperProperty` referenced by this
|
||||
:class:`.AssociationProxy`.
|
||||
|
||||
.. versionadded:: 0.7.3
|
||||
|
||||
See also:
|
||||
|
||||
:attr:`.AssociationProxy.attr`
|
||||
|
||||
:attr:`.AssociationProxy.local_attr`
|
||||
|
||||
"""
|
||||
return getattr(self.target_class, self.value_attr)
|
||||
|
||||
@property
|
||||
def local_attr(self):
|
||||
"""The 'local' :class:`.MapperProperty` referenced by this
|
||||
:class:`.AssociationProxy`.
|
||||
|
||||
.. versionadded:: 0.7.3
|
||||
|
||||
See also:
|
||||
|
||||
:attr:`.AssociationProxy.attr`
|
||||
|
||||
:attr:`.AssociationProxy.remote_attr`
|
||||
|
||||
"""
|
||||
return getattr(self.owning_class, self.target_collection)
|
||||
|
||||
@property
|
||||
def attr(self):
|
||||
"""Return a tuple of ``(local_attr, remote_attr)``.
|
||||
|
||||
This attribute is convenient when specifying a join
|
||||
using :meth:`.Query.join` across two relationships::
|
||||
|
||||
sess.query(Parent).join(*Parent.proxied.attr)
|
||||
|
||||
.. versionadded:: 0.7.3
|
||||
|
||||
See also:
|
||||
|
||||
:attr:`.AssociationProxy.local_attr`
|
||||
|
||||
:attr:`.AssociationProxy.remote_attr`
|
||||
|
||||
"""
|
||||
return (self.local_attr, self.remote_attr)
|
||||
|
||||
def _get_property(self):
|
||||
return (orm.class_mapper(self.owning_class).
|
||||
get_property(self.target_collection))
|
||||
|
||||
@property
|
||||
@util.memoized_property
|
||||
def target_class(self):
|
||||
"""The class the proxy is attached to."""
|
||||
"""The intermediary class handled by this :class:`.AssociationProxy`.
|
||||
|
||||
Intercepted append/set/assignment events will result
|
||||
in the generation of new instances of this class.
|
||||
|
||||
"""
|
||||
return self._get_property().mapper.class_
|
||||
|
||||
def _target_is_scalar(self):
|
||||
return not self._get_property().uselist
|
||||
@util.memoized_property
|
||||
def scalar(self):
|
||||
"""Return ``True`` if this :class:`.AssociationProxy` proxies a scalar
|
||||
relationship on the local side."""
|
||||
|
||||
scalar = not self._get_property().uselist
|
||||
if scalar:
|
||||
self._initialize_scalar_accessors()
|
||||
return scalar
|
||||
|
||||
@util.memoized_property
|
||||
def _value_is_scalar(self):
|
||||
return not self._get_property().\
|
||||
mapper.get_property(self.value_attr).uselist
|
||||
|
||||
@util.memoized_property
|
||||
def _target_is_object(self):
|
||||
return getattr(self.target_class, self.value_attr).impl.uses_objects
|
||||
|
||||
def __get__(self, obj, class_):
|
||||
if self.owning_class is None:
|
||||
self.owning_class = class_ and class_ or type(obj)
|
||||
if obj is None:
|
||||
return self
|
||||
elif self.scalar is None:
|
||||
self.scalar = self._target_is_scalar()
|
||||
if self.scalar:
|
||||
self._initialize_scalar_accessors()
|
||||
|
||||
if self.scalar:
|
||||
return self._scalar_get(getattr(obj, self.target_collection))
|
||||
target = getattr(obj, self.target_collection)
|
||||
return self._scalar_get(target)
|
||||
else:
|
||||
try:
|
||||
# If the owning instance is reborn (orm session resurrect,
|
||||
@@ -173,14 +264,10 @@ class AssociationProxy(object):
|
||||
proxy = self._new(_lazy_collection(obj, self.target_collection))
|
||||
setattr(obj, self.key, (id(obj), proxy))
|
||||
return proxy
|
||||
|
||||
|
||||
def __set__(self, obj, values):
|
||||
if self.owning_class is None:
|
||||
self.owning_class = type(obj)
|
||||
if self.scalar is None:
|
||||
self.scalar = self._target_is_scalar()
|
||||
if self.scalar:
|
||||
self._initialize_scalar_accessors()
|
||||
|
||||
if self.scalar:
|
||||
creator = self.creator and self.creator or self.target_class
|
||||
@@ -209,7 +296,8 @@ class AssociationProxy(object):
|
||||
|
||||
def _default_getset(self, collection_class):
|
||||
attr = self.value_attr
|
||||
getter = operator.attrgetter(attr)
|
||||
_getter = operator.attrgetter(attr)
|
||||
getter = lambda target: _getter(target) if target is not None else None
|
||||
if collection_class is dict:
|
||||
setter = lambda o, k, v: setattr(o, attr, v)
|
||||
else:
|
||||
@@ -221,21 +309,25 @@ class AssociationProxy(object):
|
||||
self.collection_class = util.duck_type_collection(lazy_collection())
|
||||
|
||||
if self.proxy_factory:
|
||||
return self.proxy_factory(lazy_collection, creator, self.value_attr, self)
|
||||
return self.proxy_factory(
|
||||
lazy_collection, creator, self.value_attr, self)
|
||||
|
||||
if self.getset_factory:
|
||||
getter, setter = self.getset_factory(self.collection_class, self)
|
||||
else:
|
||||
getter, setter = self._default_getset(self.collection_class)
|
||||
|
||||
|
||||
if self.collection_class is list:
|
||||
return _AssociationList(lazy_collection, creator, getter, setter, self)
|
||||
return _AssociationList(
|
||||
lazy_collection, creator, getter, setter, self)
|
||||
elif self.collection_class is dict:
|
||||
return _AssociationDict(lazy_collection, creator, getter, setter, self)
|
||||
return _AssociationDict(
|
||||
lazy_collection, creator, getter, setter, self)
|
||||
elif self.collection_class is set:
|
||||
return _AssociationSet(lazy_collection, creator, getter, setter, self)
|
||||
return _AssociationSet(
|
||||
lazy_collection, creator, getter, setter, self)
|
||||
else:
|
||||
raise exceptions.ArgumentError(
|
||||
raise exc.ArgumentError(
|
||||
'could not guess which interface to use for '
|
||||
'collection_class "%s" backing "%s"; specify a '
|
||||
'proxy_factory and proxy_bulk_set manually' %
|
||||
@@ -248,7 +340,7 @@ class AssociationProxy(object):
|
||||
getter, setter = self.getset_factory(self.collection_class, self)
|
||||
else:
|
||||
getter, setter = self._default_getset(self.collection_class)
|
||||
|
||||
|
||||
proxy.creator = creator
|
||||
proxy.getter = getter
|
||||
proxy.setter = setter
|
||||
@@ -263,28 +355,102 @@ class AssociationProxy(object):
|
||||
elif self.collection_class is set:
|
||||
proxy.update(values)
|
||||
else:
|
||||
raise exceptions.ArgumentError(
|
||||
'no proxy_bulk_set supplied for custom '
|
||||
'collection_class implementation')
|
||||
raise exc.ArgumentError(
|
||||
'no proxy_bulk_set supplied for custom '
|
||||
'collection_class implementation')
|
||||
|
||||
@property
|
||||
def _comparator(self):
|
||||
return self._get_property().comparator
|
||||
|
||||
def any(self, criterion=None, **kwargs):
|
||||
return self._comparator.any(getattr(self.target_class, self.value_attr).has(criterion, **kwargs))
|
||||
|
||||
"""Produce a proxied 'any' expression using EXISTS.
|
||||
|
||||
This expression will be a composed product
|
||||
using the :meth:`.RelationshipProperty.Comparator.any`
|
||||
and/or :meth:`.RelationshipProperty.Comparator.has`
|
||||
operators of the underlying proxied attributes.
|
||||
|
||||
"""
|
||||
if self._target_is_object:
|
||||
if self._value_is_scalar:
|
||||
value_expr = getattr(
|
||||
self.target_class, self.value_attr).has(
|
||||
criterion, **kwargs)
|
||||
else:
|
||||
value_expr = getattr(
|
||||
self.target_class, self.value_attr).any(
|
||||
criterion, **kwargs)
|
||||
else:
|
||||
value_expr = criterion
|
||||
|
||||
# check _value_is_scalar here, otherwise
|
||||
# we're scalar->scalar - call .any() so that
|
||||
# the "can't call any() on a scalar" msg is raised.
|
||||
if self.scalar and not self._value_is_scalar:
|
||||
return self._comparator.has(
|
||||
value_expr
|
||||
)
|
||||
else:
|
||||
return self._comparator.any(
|
||||
value_expr
|
||||
)
|
||||
|
||||
def has(self, criterion=None, **kwargs):
|
||||
return self._comparator.has(getattr(self.target_class, self.value_attr).has(criterion, **kwargs))
|
||||
"""Produce a proxied 'has' expression using EXISTS.
|
||||
|
||||
This expression will be a composed product
|
||||
using the :meth:`.RelationshipProperty.Comparator.any`
|
||||
and/or :meth:`.RelationshipProperty.Comparator.has`
|
||||
operators of the underlying proxied attributes.
|
||||
|
||||
"""
|
||||
|
||||
if self._target_is_object:
|
||||
return self._comparator.has(
|
||||
getattr(self.target_class, self.value_attr).
|
||||
has(criterion, **kwargs)
|
||||
)
|
||||
else:
|
||||
if criterion is not None or kwargs:
|
||||
raise exc.ArgumentError(
|
||||
"Non-empty has() not allowed for "
|
||||
"column-targeted association proxy; use ==")
|
||||
return self._comparator.has()
|
||||
|
||||
def contains(self, obj):
|
||||
return self._comparator.any(**{self.value_attr: obj})
|
||||
"""Produce a proxied 'contains' expression using EXISTS.
|
||||
|
||||
This expression will be a composed product
|
||||
using the :meth:`.RelationshipProperty.Comparator.any`
|
||||
, :meth:`.RelationshipProperty.Comparator.has`,
|
||||
and/or :meth:`.RelationshipProperty.Comparator.contains`
|
||||
operators of the underlying proxied attributes.
|
||||
"""
|
||||
|
||||
if self.scalar and not self._value_is_scalar:
|
||||
return self._comparator.has(
|
||||
getattr(self.target_class, self.value_attr).contains(obj)
|
||||
)
|
||||
else:
|
||||
return self._comparator.any(**{self.value_attr: obj})
|
||||
|
||||
def __eq__(self, obj):
|
||||
return self._comparator.has(**{self.value_attr: obj})
|
||||
# note the has() here will fail for collections; eq_()
|
||||
# is only allowed with a scalar.
|
||||
if obj is None:
|
||||
return or_(
|
||||
self._comparator.has(**{self.value_attr: obj}),
|
||||
self._comparator == None
|
||||
)
|
||||
else:
|
||||
return self._comparator.has(**{self.value_attr: obj})
|
||||
|
||||
def __ne__(self, obj):
|
||||
return not_(self.__eq__(obj))
|
||||
# note the has() here will fail for collections; eq_()
|
||||
# is only allowed with a scalar.
|
||||
return self._comparator.has(
|
||||
getattr(self.target_class, self.value_attr) != obj)
|
||||
|
||||
|
||||
class _lazy_collection(object):
|
||||
@@ -295,22 +461,23 @@ class _lazy_collection(object):
|
||||
def __call__(self):
|
||||
obj = self.ref()
|
||||
if obj is None:
|
||||
raise exceptions.InvalidRequestError(
|
||||
"stale association proxy, parent object has gone out of "
|
||||
"scope")
|
||||
raise exc.InvalidRequestError(
|
||||
"stale association proxy, parent object has gone out of "
|
||||
"scope")
|
||||
return getattr(obj, self.target)
|
||||
|
||||
def __getstate__(self):
|
||||
return {'obj':self.ref(), 'target':self.target}
|
||||
|
||||
return {'obj': self.ref(), 'target': self.target}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.ref = weakref.ref(state['obj'])
|
||||
self.target = state['target']
|
||||
|
||||
|
||||
class _AssociationCollection(object):
|
||||
def __init__(self, lazy_collection, creator, getter, setter, parent):
|
||||
"""Constructs an _AssociationCollection.
|
||||
|
||||
"""Constructs an _AssociationCollection.
|
||||
|
||||
This will always be a subclass of either _AssociationList,
|
||||
_AssociationSet, or _AssociationDict.
|
||||
|
||||
@@ -344,17 +511,20 @@ class _AssociationCollection(object):
|
||||
def __len__(self):
|
||||
return len(self.col)
|
||||
|
||||
def __nonzero__(self):
|
||||
def __bool__(self):
|
||||
return bool(self.col)
|
||||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def __getstate__(self):
|
||||
return {'parent':self.parent, 'lazy_collection':self.lazy_collection}
|
||||
return {'parent': self.parent, 'lazy_collection': self.lazy_collection}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.parent = state['parent']
|
||||
self.lazy_collection = state['lazy_collection']
|
||||
self.parent._inflate(self)
|
||||
|
||||
|
||||
|
||||
class _AssociationList(_AssociationCollection):
|
||||
"""Generic, converting, list-to-list proxy."""
|
||||
|
||||
@@ -368,7 +538,10 @@ class _AssociationList(_AssociationCollection):
|
||||
return self.setter(object, value)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self._get(self.col[index])
|
||||
if not isinstance(index, slice):
|
||||
return self._get(self.col[index])
|
||||
else:
|
||||
return [self._get(member) for member in self.col[index]]
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
if not isinstance(index, slice):
|
||||
@@ -382,11 +555,12 @@ class _AssociationList(_AssociationCollection):
|
||||
stop = index.stop
|
||||
step = index.step or 1
|
||||
|
||||
rng = range(index.start or 0, stop, step)
|
||||
start = index.start or 0
|
||||
rng = list(range(index.start or 0, stop, step))
|
||||
if step == 1:
|
||||
for i in rng:
|
||||
del self[index.start]
|
||||
i = index.start
|
||||
del self[start]
|
||||
i = start
|
||||
for item in value:
|
||||
self.insert(i, item)
|
||||
i += 1
|
||||
@@ -429,7 +603,7 @@ class _AssociationList(_AssociationCollection):
|
||||
|
||||
for member in self.col:
|
||||
yield self._get(member)
|
||||
raise StopIteration
|
||||
return
|
||||
|
||||
def append(self, value):
|
||||
item = self._create(value)
|
||||
@@ -437,7 +611,7 @@ class _AssociationList(_AssociationCollection):
|
||||
|
||||
def count(self, value):
|
||||
return sum([1 for _ in
|
||||
itertools.ifilter(lambda v: v == value, iter(self))])
|
||||
util.itertools_filter(lambda v: v == value, iter(self))])
|
||||
|
||||
def extend(self, values):
|
||||
for v in values:
|
||||
@@ -536,14 +710,16 @@ class _AssociationList(_AssociationCollection):
|
||||
def __hash__(self):
|
||||
raise TypeError("%s objects are unhashable" % type(self).__name__)
|
||||
|
||||
for func_name, func in locals().items():
|
||||
if (util.callable(func) and func.func_name == func_name and
|
||||
not func.__doc__ and hasattr(list, func_name)):
|
||||
for func_name, func in list(locals().items()):
|
||||
if (util.callable(func) and func.__name__ == func_name and
|
||||
not func.__doc__ and hasattr(list, func_name)):
|
||||
func.__doc__ = getattr(list, func_name).__doc__
|
||||
del func_name, func
|
||||
|
||||
|
||||
_NotProvided = util.symbol('_NotProvided')
|
||||
|
||||
|
||||
class _AssociationDict(_AssociationCollection):
|
||||
"""Generic, converting, dict-to-dict proxy."""
|
||||
|
||||
@@ -577,7 +753,7 @@ class _AssociationDict(_AssociationCollection):
|
||||
return key in self.col
|
||||
|
||||
def __iter__(self):
|
||||
return self.col.iterkeys()
|
||||
return iter(self.col.keys())
|
||||
|
||||
def clear(self):
|
||||
self.col.clear()
|
||||
@@ -622,24 +798,27 @@ class _AssociationDict(_AssociationCollection):
|
||||
def keys(self):
|
||||
return self.col.keys()
|
||||
|
||||
def iterkeys(self):
|
||||
return self.col.iterkeys()
|
||||
if util.py2k:
|
||||
def iteritems(self):
|
||||
return ((key, self._get(self.col[key])) for key in self.col)
|
||||
|
||||
def values(self):
|
||||
return [ self._get(member) for member in self.col.values() ]
|
||||
def itervalues(self):
|
||||
return (self._get(self.col[key]) for key in self.col)
|
||||
|
||||
def itervalues(self):
|
||||
for key in self.col:
|
||||
yield self._get(self.col[key])
|
||||
raise StopIteration
|
||||
def iterkeys(self):
|
||||
return self.col.iterkeys()
|
||||
|
||||
def items(self):
|
||||
return [(k, self._get(self.col[k])) for k in self]
|
||||
def values(self):
|
||||
return [self._get(member) for member in self.col.values()]
|
||||
|
||||
def iteritems(self):
|
||||
for key in self.col:
|
||||
yield (key, self._get(self.col[key]))
|
||||
raise StopIteration
|
||||
def items(self):
|
||||
return [(k, self._get(self.col[k])) for k in self]
|
||||
else:
|
||||
def items(self):
|
||||
return ((key, self._get(self.col[key])) for key in self.col)
|
||||
|
||||
def values(self):
|
||||
return (self._get(self.col[key]) for key in self.col)
|
||||
|
||||
def pop(self, key, default=_NotProvided):
|
||||
if default is _NotProvided:
|
||||
@@ -658,11 +837,20 @@ class _AssociationDict(_AssociationCollection):
|
||||
len(a))
|
||||
elif len(a) == 1:
|
||||
seq_or_map = a[0]
|
||||
for item in seq_or_map:
|
||||
if isinstance(item, tuple):
|
||||
self[item[0]] = item[1]
|
||||
else:
|
||||
# discern dict from sequence - took the advice from
|
||||
# http://www.voidspace.org.uk/python/articles/duck_typing.shtml
|
||||
# still not perfect :(
|
||||
if hasattr(seq_or_map, 'keys'):
|
||||
for item in seq_or_map:
|
||||
self[item] = seq_or_map[item]
|
||||
else:
|
||||
try:
|
||||
for k, v in seq_or_map:
|
||||
self[k] = v
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"dictionary update sequence "
|
||||
"requires 2-element tuples")
|
||||
|
||||
for key, value in kw:
|
||||
self[key] = value
|
||||
@@ -673,9 +861,9 @@ class _AssociationDict(_AssociationCollection):
|
||||
def __hash__(self):
|
||||
raise TypeError("%s objects are unhashable" % type(self).__name__)
|
||||
|
||||
for func_name, func in locals().items():
|
||||
if (util.callable(func) and func.func_name == func_name and
|
||||
not func.__doc__ and hasattr(dict, func_name)):
|
||||
for func_name, func in list(locals().items()):
|
||||
if (util.callable(func) and func.__name__ == func_name and
|
||||
not func.__doc__ and hasattr(dict, func_name)):
|
||||
func.__doc__ = getattr(dict, func_name).__doc__
|
||||
del func_name, func
|
||||
|
||||
@@ -695,12 +883,14 @@ class _AssociationSet(_AssociationCollection):
|
||||
def __len__(self):
|
||||
return len(self.col)
|
||||
|
||||
def __nonzero__(self):
|
||||
def __bool__(self):
|
||||
if self.col:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def __contains__(self, value):
|
||||
for member in self.col:
|
||||
# testlib.pragma exempt:__eq__
|
||||
@@ -717,7 +907,7 @@ class _AssociationSet(_AssociationCollection):
|
||||
"""
|
||||
for member in self.col:
|
||||
yield self._get(member)
|
||||
raise StopIteration
|
||||
return
|
||||
|
||||
def add(self, value):
|
||||
if value not in self:
|
||||
@@ -871,8 +1061,8 @@ class _AssociationSet(_AssociationCollection):
|
||||
def __hash__(self):
|
||||
raise TypeError("%s objects are unhashable" % type(self).__name__)
|
||||
|
||||
for func_name, func in locals().items():
|
||||
if (util.callable(func) and func.func_name == func_name and
|
||||
not func.__doc__ and hasattr(set, func_name)):
|
||||
for func_name, func in list(locals().items()):
|
||||
if (util.callable(func) and func.__name__ == func_name and
|
||||
not func.__doc__ and hasattr(set, func_name)):
|
||||
func.__doc__ = getattr(set, func_name).__doc__
|
||||
del func_name, func
|
||||
|
||||
Reference in New Issue
Block a user