- changed title to cant redefine `__hash__()` or `__nonzero__()` on mapped classes
cant redefine `__hash__()` or `__nonzero__()` on mapped classes
Because the Session/UOW stores instances in Sets all over the place, it relies upon the hash behavior of mapped instances to determine instance identity. All of the set-based storage of instances should be modified to use some special set which guarantees to use the id(obj)
of the instance regardless of __hash__()
.
example stack trace:
File '<string>', line 1 in <lambda>
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/Pylons-0.9.6rc1-py2.5.egg/pylons/decorators/__init__.py', line 160 in wrapper
return func(self, *args, **kwargs)
File '/Users/dialtone/dev/mipworld/mip/mip/controllers/links/main.py', line 36 in add_POST
new.tags.append(newt)
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/SQLAlchemy-0.3.9-py2.5.egg/sqlalchemy/orm/attributes.py', line 528 in append
self.__setrecord(item)
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/SQLAlchemy-0.3.9-py2.5.egg/sqlalchemy/orm/attributes.py', line 500 in __setrecord
self.attr.append_event(event, self.obj, item)
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/SQLAlchemy-0.3.9-py2.5.egg/sqlalchemy/orm/attributes.py', line 343 in append_event
ext.append(event or self, obj, value)
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/SQLAlchemy-0.3.9-py2.5.egg/sqlalchemy/orm/unitofwork.py', line 44 in append
if self.cascade is not None and self.cascade.save_update and item not in sess:
File '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/SQLAlchemy-0.3.9-py2.5.egg/sqlalchemy/orm/session.py', line 642 in __contains__
return self._is_attached(obj) and (obj in self.uow.new or self.identity_map.has_key(obj._instance_key))
AttributeError: 'Tag' object has no attribute '_instance_key'
with a class such as:
class Tag(Base):
def __init__(self, name):
30 self.name = name
31
32 def __repr__(self):
33 return "%s(%r)" % (self.__class__.__name__, self.name)
34
35 def __eq__(self, other):
36 if isinstance(other, (str, unicode)):
37 return self.name == other
38 return self.name == other.name
39
40 def __hash__(self):
41 return hash(self.name)
Comments (4)
-
reporter -
2d3f907ac0a23d410ecc3c74afc6d63bd2abc186 has an
id()
-basedset
implementation that could do the trick. Also this little ditty is useful for rooting out current usage:Index: test/testlib/__init__.py =================================================================== --- test/testlib/__init__.py (revision 3684) +++ test/testlib/__init__.py (working copy) @@ -11,7 +11,20 @@ import testlib.engines as engines +import sqlalchemy.orm +def mapper(type_, *args, **kw): + if '__hash__' not in type_.__dict__: + def __hash__(*a): + raise AssertionError("%s.__hash__ called." % type_.__name__) + type_.__hash__ = __hash__ + if '__nonzero__' not in type_.__dict__: + def __nonzero__(*a): + raise AssertionError("%s.__nonzero__ called." % type_.__name__) + type_.__nonzero__ = __nonzero__ + return sqlalchemy.orm.mapper(type_, *args, **kw) + __all__ = ('testing', + 'mapper', 'Table', 'Column', 'PersistTest', 'AssertMixin', 'ORMTest', 'SQLCompileTest', 'profiling', 'engines')
-
- changed status to resolved
This (and
__eq__
support) is in 429e69db67baa8fc93ff2b55361ba2831cc26144. Also added--noncomparable
,--unhashable
and--truthless
options to the test suite- these set up instrumented__methods__
on every class that goes throughmapper()
. There's also a compact mini-test for everyday test runs to catch obvious regressions. -
reporter - removed milestone
Removing milestone: 0.4.xx (automated comment)
- Log in to comment