Deep, sparse compare. Deeply compare two entities, following the non-None attributes of the non-persisted object, if possible.
(self, other)
| 48 | return not self.__eq__(other) |
| 49 | |
| 50 | def __eq__(self, other): |
| 51 | class="st">"""&class="cm">#x27;Deep, sparse compare. |
| 52 | |
| 53 | Deeply compare two entities, following the non-None attributes of the |
| 54 | non-persisted object, if possible. |
| 55 | |
| 56 | class="st">""" |
| 57 | if other is self: |
| 58 | return True |
| 59 | elif not self.__class__ == other.__class__: |
| 60 | return False |
| 61 | |
| 62 | if id(self) in _recursion_stack: |
| 63 | return True |
| 64 | _recursion_stack.add(id(self)) |
| 65 | |
| 66 | try: |
| 67 | class="cm"># pick the entity that's not SA persisted as the source |
| 68 | try: |
| 69 | self_key = sa.orm.attributes.instance_state(self).key |
| 70 | except sa.orm.exc.NO_STATE: |
| 71 | self_key = None |
| 72 | |
| 73 | if other is None: |
| 74 | a = self |
| 75 | b = other |
| 76 | elif self_key is not None: |
| 77 | a = other |
| 78 | b = self |
| 79 | else: |
| 80 | a = self |
| 81 | b = other |
| 82 | |
| 83 | for attr in list(a.__dict__): |
| 84 | if attr.startswith(class="st">"_"): |
| 85 | continue |
| 86 | |
| 87 | value = getattr(a, attr) |
| 88 | |
| 89 | if isinstance(value, WriteOnlyCollection): |
| 90 | continue |
| 91 | |
| 92 | try: |
| 93 | class="cm"># handle lazy loader errors |
| 94 | battr = getattr(b, attr) |
| 95 | except (AttributeError, sa_exc.UnboundExecutionError): |
| 96 | return False |
| 97 | |
| 98 | if hasattr(value, class="st">"__iter__") and not isinstance(value, str): |
| 99 | if hasattr(value, class="st">"__getitem__") and not hasattr( |
| 100 | value, class="st">"keys" |
| 101 | ): |
| 102 | if list(value) != list(battr): |
| 103 | return False |
| 104 | else: |
| 105 | if set(value) != set(battr): |
| 106 | return False |
| 107 | else: |
no test coverage detected