entities.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # testing/entities.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. import sqlalchemy as sa
  8. from sqlalchemy import exc as sa_exc
  9. _repr_stack = set()
  10. class BasicEntity(object):
  11. def __init__(self, **kw):
  12. for key, value in kw.items():
  13. setattr(self, key, value)
  14. def __repr__(self):
  15. if id(self) in _repr_stack:
  16. return object.__repr__(self)
  17. _repr_stack.add(id(self))
  18. try:
  19. return "%s(%s)" % (
  20. (self.__class__.__name__),
  21. ', '.join(["%s=%r" % (key, getattr(self, key))
  22. for key in sorted(self.__dict__.keys())
  23. if not key.startswith('_')]))
  24. finally:
  25. _repr_stack.remove(id(self))
  26. _recursion_stack = set()
  27. class ComparableEntity(BasicEntity):
  28. def __hash__(self):
  29. return hash(self.__class__)
  30. def __ne__(self, other):
  31. return not self.__eq__(other)
  32. def __eq__(self, other):
  33. """'Deep, sparse compare.
  34. Deeply compare two entities, following the non-None attributes of the
  35. non-persisted object, if possible.
  36. """
  37. if other is self:
  38. return True
  39. elif not self.__class__ == other.__class__:
  40. return False
  41. if id(self) in _recursion_stack:
  42. return True
  43. _recursion_stack.add(id(self))
  44. try:
  45. # pick the entity that's not SA persisted as the source
  46. try:
  47. self_key = sa.orm.attributes.instance_state(self).key
  48. except sa.orm.exc.NO_STATE:
  49. self_key = None
  50. if other is None:
  51. a = self
  52. b = other
  53. elif self_key is not None:
  54. a = other
  55. b = self
  56. else:
  57. a = self
  58. b = other
  59. for attr in list(a.__dict__):
  60. if attr.startswith('_'):
  61. continue
  62. value = getattr(a, attr)
  63. try:
  64. # handle lazy loader errors
  65. battr = getattr(b, attr)
  66. except (AttributeError, sa_exc.UnboundExecutionError):
  67. return False
  68. if hasattr(value, '__iter__'):
  69. if hasattr(value, '__getitem__') and not hasattr(
  70. value, 'keys'):
  71. if list(value) != list(battr):
  72. return False
  73. else:
  74. if set(value) != set(battr):
  75. return False
  76. else:
  77. if value is not None and value != battr:
  78. return False
  79. return True
  80. finally:
  81. _recursion_stack.remove(id(self))