| | 207 | |
| | 208 | def __setattr__(self, name, value): |
| | 209 | # XXX Horrible hack to avoid ConflictErrors. |
| | 210 | # The problem is that the adaptation IMemberInfo(principal) |
| | 211 | # eventually leads to calling: |
| | 212 | # zope.location.location.located(memberinfo, principal, |
| | 213 | # name='...MemberInfo') |
| | 214 | |
| | 215 | # ... which checks whether principal is object.__parent__, and |
| | 216 | # if not, calls zope.location.location.locate() which re-sets |
| | 217 | # the memberinfo's __parent__ and __name__ attributes. And |
| | 218 | # for some weird reason, we have two instances of the same |
| | 219 | # principal and they are not the same object. In other words, |
| | 220 | # zope.location is broken when the parent is a principal. So |
| | 221 | # we mutate the persistent MemberInfo on every request for no |
| | 222 | # reason, even when anonymous -- which leads to ZODB bloat and |
| | 223 | # frequent ConflictErrors. To avoid that, we don't save those |
| | 224 | # attrs unless the new values are different. |
| | 225 | if name == '__name__' and self.__name__ == value: |
| | 226 | return |
| | 227 | elif name == '__parent__': |
| | 228 | # principals don't define __eq__, so compare their attributes. |
| | 229 | if self.__parent__.__dict__ == value.__dict__: |
| | 230 | return |
| | 231 | # Persistent.__setattr__() is where _p_changed is set True, |
| | 232 | # so if we bail out before this, the object is not marked dirty. |
| | 233 | super(MemberInfo, self).__setattr__(name, value) |