summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES13
-rw-r--r--lib/sqlalchemy/ext/associationproxy.py30
-rw-r--r--lib/sqlalchemy/util.py8
-rw-r--r--test/ext/associationproxy.py52
4 files changed, 55 insertions, 48 deletions
diff --git a/CHANGES b/CHANGES
index e44dd18aa..16bb5aeef 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,14 +17,14 @@ CHANGES
- fixed expression translation of text() clauses; this repairs various
ORM scenarios where literal text is used for SQL expressions
-
+
- sqlite will reflect "DECIMAL" as a numeric column.
- fixed INSERT statements w.r.t. primary key columns that have SQL-expression
based default generators on them; SQL expression executes inline as normal
but will not trigger a "postfetch" condition for the column, for those DB's
who provide it via cursor.lastrowid
-
+
- Renamed the Dialect attribute 'preexecute_sequences' to
'preexecute_pk_sequences'. An attribute proxy is in place for out-of-tree
dialects using the old name.
@@ -43,13 +43,18 @@ CHANGES
- it's an error to session.save() an object which is already persistent
[ticket:840]
-
+
- behavior of query.options() is now fully based on paths, i.e. an option
such as eagerload_all('x.y.z.y.x') will apply eagerloading to only
those paths, i.e. and not 'x.y.x'; eagerload('children.children') applies
only to exactly two-levels deep, etc. [ticket:777]
-- Made access dao dection more reliable [ticket:828]
+- Made access dao detection more reliable [ticket:828]
+
+- Removed unused util.hash().
+
+- Fixed __hash__ for association proxy- these collections are unhashable,
+ just like their mutable Python counterparts.
0.4.0
-----
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py
index 0130721e1..0ee59e369 100644
--- a/lib/sqlalchemy/ext/associationproxy.py
+++ b/lib/sqlalchemy/ext/associationproxy.py
@@ -106,7 +106,7 @@ class AssociationProxy(object):
Optional, use with proxy_factory. See the _set() method for
details.
"""
-
+
self.target_collection = targetcollection # backwards compat name...
self.value_attr = attr
self.creator = creator
@@ -141,7 +141,7 @@ class AssociationProxy(object):
"scope")
return getattr(obj, target)
return lazy_collection
-
+
def __get__(self, obj, class_):
if obj is None:
self.owning_class = class_
@@ -204,12 +204,12 @@ class AssociationProxy(object):
if self.proxy_factory:
return self.proxy_factory(lazy_collection, creator, self.value_attr)
-
+
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)
elif self.collection_class is dict:
@@ -247,7 +247,7 @@ class _AssociationList(object):
lazy_collection
A callable returning a list-based collection of entities (usually
an object attribute managed by a SQLAlchemy relation())
-
+
creator
A function that creates new target entities. Given one parameter:
value. The assertion is assumed:
@@ -291,7 +291,7 @@ class _AssociationList(object):
def __getitem__(self, index):
return self._get(self.col[index])
-
+
def __setitem__(self, index, value):
if not isinstance(index, slice):
self._set(self.col[index], value)
@@ -406,7 +406,7 @@ class _AssociationList(object):
def __repr__(self):
return repr(list(self))
- def hash(self):
+ def __hash__(self):
raise TypeError("%s objects are unhashable" % type(self).__name__)
_NotProvided = object()
@@ -420,7 +420,7 @@ class _AssociationDict(object):
lazy_collection
A callable returning a dict-based collection of entities (usually
an object attribute managed by a SQLAlchemy relation())
-
+
creator
A function that creates new target entities. Given two parameters:
key and value. The assertion is assumed:
@@ -462,7 +462,7 @@ class _AssociationDict(object):
def __getitem__(self, key):
return self._get(self.col[key])
-
+
def __setitem__(self, key, value):
if key in self.col:
self._set(self.col[key], key, value)
@@ -535,7 +535,7 @@ class _AssociationDict(object):
def popitem(self):
item = self.col.popitem()
return (item[0], self._get(item[1]))
-
+
def update(self, *a, **kw):
if len(a) > 1:
raise TypeError('update expected at most 1 arguments, got %i' %
@@ -554,7 +554,7 @@ class _AssociationDict(object):
def copy(self):
return dict(self.items())
- def hash(self):
+ def __hash__(self):
raise TypeError("%s objects are unhashable" % type(self).__name__)
class _AssociationSet(object):
@@ -567,7 +567,7 @@ class _AssociationSet(object):
collection
A callable returning a set-based collection of entities (usually an
object attribute managed by a SQLAlchemy relation())
-
+
creator
A function that creates new target entities. Given one parameter:
value. The assertion is assumed:
@@ -707,10 +707,10 @@ class _AssociationSet(object):
def issubset(self, other):
return util.Set(self).issubset(other)
-
+
def issuperset(self, other):
return util.Set(self).issuperset(other)
-
+
def clear(self):
self.col.clear()
@@ -727,5 +727,5 @@ class _AssociationSet(object):
def __repr__(self):
return repr(util.Set(self))
- def hash(self):
+ def __hash__(self):
raise TypeError("%s objects are unhashable" % type(self).__name__)
diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py
index ad3b30e8d..ab697db0e 100644
--- a/lib/sqlalchemy/util.py
+++ b/lib/sqlalchemy/util.py
@@ -112,13 +112,6 @@ def flatten_iterator(x):
else:
yield elem
-def hash(string):
- """return an md5 hash of the given string."""
- h = md5.new()
- h.update(string)
- return h.hexdigest()
-
-
class ArgSingleton(type):
instances = {}
@@ -530,6 +523,7 @@ class OrderedSet(Set):
__isub__ = difference_update
+
class UniqueAppender(object):
"""appends items to a collection such that only unique items
are added."""
diff --git a/test/ext/associationproxy.py b/test/ext/associationproxy.py
index 71c029e07..2accd2fc8 100644
--- a/test/ext/associationproxy.py
+++ b/test/ext/associationproxy.py
@@ -38,7 +38,7 @@ class _CollectionOperations(PersistTest):
collection_class = self.collection_class
metadata = MetaData(testbase.db)
-
+
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
@@ -51,7 +51,7 @@ class _CollectionOperations(PersistTest):
class Parent(object):
children = association_proxy('_children', 'name')
-
+
def __init__(self, name):
self.name = name
@@ -88,7 +88,7 @@ class _CollectionOperations(PersistTest):
def _test_sequence_ops(self):
Parent, Child = self.Parent, self.Child
-
+
p1 = Parent('P1')
self.assert_(not p1._children)
@@ -114,7 +114,7 @@ class _CollectionOperations(PersistTest):
self.assert_(p1._children[0].name == 'regular')
self.assert_(p1._children[1].name == 'proxied')
-
+
del p1._children[1]
self.assert_(len(p1._children) == 1)
@@ -125,7 +125,7 @@ class _CollectionOperations(PersistTest):
self.assert_(len(p1._children) == 0)
self.assert_(len(p1.children) == 0)
-
+
p1.children = ['a','b','c']
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
@@ -152,7 +152,7 @@ class _CollectionOperations(PersistTest):
p1.children.append('changed-in-place')
self.assert_(p1.children.count('changed-in-place') == 2)
-
+
p1.children.remove('changed-in-place')
self.assert_(p1.children.count('changed-in-place') == 1)
@@ -187,7 +187,9 @@ class _CollectionOperations(PersistTest):
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
-
+ self.assertRaises(TypeError, set, [p1.children])
+
+
class DefaultTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(DefaultTest, self).__init__(*args, **kw)
@@ -196,6 +198,7 @@ class DefaultTest(_CollectionOperations):
def test_sequence_ops(self):
self._test_sequence_ops()
+
class ListTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(ListTest, self).__init__(*args, **kw)
@@ -249,7 +252,7 @@ class CustomDictTest(DictTest):
self.assert_(p1._children['a'].name == 'regular')
self.assert_(p1._children['b'].name == 'proxied')
-
+
del p1._children['b']
self.assert_(len(p1._children) == 1)
@@ -281,7 +284,7 @@ class CustomDictTest(DictTest):
p1._children = {}
self.assert_(len(p1.children) == 0)
-
+
try:
p1._children = []
self.assert_(False)
@@ -294,6 +297,9 @@ class CustomDictTest(DictTest):
except exceptions.ArgumentError:
self.assert_(True)
+ self.assertRaises(TypeError, set, [p1.children])
+
+
class SetTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(SetTest, self).__init__(*args, **kw)
@@ -344,7 +350,7 @@ class SetTest(_CollectionOperations):
self.assert_(len(p1._children) == 0)
self.assert_(len(p1.children) == 0)
-
+
p1.children = ['a','b','c']
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
@@ -379,11 +385,11 @@ class SetTest(_CollectionOperations):
p1 = self.roundtrip(p1)
self.assert_(len(p1.children) == 2)
self.assert_(popped not in p1.children)
-
+
p1.children = ['a','b','c']
p1 = self.roundtrip(p1)
self.assert_(p1.children == set(['a','b','c']))
-
+
p1.children.discard('b')
p1 = self.roundtrip(p1)
self.assert_(p1.children == set(['a', 'c']))
@@ -407,6 +413,8 @@ class SetTest(_CollectionOperations):
except exceptions.ArgumentError:
self.assert_(True)
+ self.assertRaises(TypeError, set, [p1.children])
+
def test_set_comparisons(self):
Parent, Child = self.Parent, self.Child
@@ -434,7 +442,7 @@ class SetTest(_CollectionOperations):
control.issubset(other))
self.assertEqual(p1.children.issuperset(other),
control.issuperset(other))
-
+
self.assert_((p1.children == other) == (control == other))
self.assert_((p1.children != other) == (control != other))
self.assert_((p1.children < other) == (control < other))
@@ -511,7 +519,7 @@ class CustomObjectTest(_CollectionOperations):
class ScalarTest(PersistTest):
def test_scalar_proxy(self):
metadata = MetaData(testbase.db)
-
+
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
@@ -529,7 +537,7 @@ class ScalarTest(PersistTest):
creator=lambda v: Child(bar=v))
baz = association_proxy('child', 'baz',
creator=lambda v: Child(baz=v))
-
+
def __init__(self, name):
self.name = name
@@ -553,7 +561,7 @@ class ScalarTest(PersistTest):
id, type_ = obj.id, type(obj)
session.clear()
return session.query(type_).get(id)
-
+
p = Parent('p')
# No child
@@ -573,7 +581,7 @@ class ScalarTest(PersistTest):
self.assert_(p.foo == 'a')
self.assert_(p.bar == 'x')
self.assert_(p.baz == 'c')
-
+
p = roundtrip(p)
self.assert_(p.foo == 'a')
@@ -623,12 +631,12 @@ class ScalarTest(PersistTest):
# Ensure an immediate __set__ works.
p2 = Parent('p2')
p2.bar = 'quux'
-
+
class LazyLoadTest(PersistTest):
def setUp(self):
metadata = MetaData(testbase.db)
-
+
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
@@ -641,7 +649,7 @@ class LazyLoadTest(PersistTest):
class Parent(object):
children = association_proxy('_children', 'name')
-
+
def __init__(self, name):
self.name = name
@@ -730,7 +738,7 @@ class LazyLoadTest(PersistTest):
self.assert_('_children' in p.__dict__)
self.assert_(p._children is not None)
-
+
if __name__ == "__main__":
- testbase.main()
+ testbase.main()