bazarr/libs/sqlalchemy/testing/entities.py

118 lines
3.3 KiB
Python

# testing/entities.py
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: ignore-errors
from __future__ import annotations
import sqlalchemy as sa
from .. import exc as sa_exc
from ..orm.writeonly import WriteOnlyCollection
_repr_stack = set()
class BasicEntity:
def __init__(self, **kw):
for key, value in kw.items():
setattr(self, key, value)
def __repr__(self):
if id(self) in _repr_stack:
return object.__repr__(self)
_repr_stack.add(id(self))
try:
return "%s(%s)" % (
(self.__class__.__name__),
", ".join(
[
"%s=%r" % (key, getattr(self, key))
for key in sorted(self.__dict__.keys())
if not key.startswith("_")
]
),
)
finally:
_repr_stack.remove(id(self))
_recursion_stack = set()
class ComparableMixin:
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
"""'Deep, sparse compare.
Deeply compare two entities, following the non-None attributes of the
non-persisted object, if possible.
"""
if other is self:
return True
elif not self.__class__ == other.__class__:
return False
if id(self) in _recursion_stack:
return True
_recursion_stack.add(id(self))
try:
# pick the entity that's not SA persisted as the source
try:
self_key = sa.orm.attributes.instance_state(self).key
except sa.orm.exc.NO_STATE:
self_key = None
if other is None:
a = self
b = other
elif self_key is not None:
a = other
b = self
else:
a = self
b = other
for attr in list(a.__dict__):
if attr.startswith("_"):
continue
value = getattr(a, attr)
if isinstance(value, WriteOnlyCollection):
continue
try:
# handle lazy loader errors
battr = getattr(b, attr)
except (AttributeError, sa_exc.UnboundExecutionError):
return False
if hasattr(value, "__iter__") and not isinstance(value, str):
if hasattr(value, "__getitem__") and not hasattr(
value, "keys"
):
if list(value) != list(battr):
return False
else:
if set(value) != set(battr):
return False
else:
if value is not None and value != battr:
return False
return True
finally:
_recursion_stack.remove(id(self))
class ComparableEntity(ComparableMixin, BasicEntity):
def __hash__(self):
return hash(self.__class__)