/DLR_Main/Languages/IronPython/Tests/test_attribute_customize.py

https://bitbucket.org/mdavid/dlr
Python | 272 lines | 183 code | 69 blank | 20 comment | 14 complexity | 16c07359420f4605caa418a1cda90003 MD5 | raw file
  1. #####################################################################################
  2. #
  3. # Copyright (c) Microsoft Corporation. All rights reserved.
  4. #
  5. # This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. # copy of the license can be found in the License.html file at the root of this distribution. If
  7. # you cannot locate the Apache License, Version 2.0, please send an email to
  8. # ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. # by the terms of the Apache License, Version 2.0.
  10. #
  11. # You must not remove this notice, or any other, from this software.
  12. #
  13. #
  14. #####################################################################################
  15. from iptest.assert_util import *
  16. global flag
  17. def new_classes():
  18. class C1: pass
  19. class C2(object): pass
  20. return C1, C2
  21. # used as handler for __getattr__ and __getattribute__
  22. def return_100(self, name): return 100
  23. def throw_attribute_error(self, name): raise AttributeError
  24. def throw_assertion_error(self, name): raise AssertionError
  25. def test_getattr_alone():
  26. C1, C2 = new_classes()
  27. def __init__(self): self.x = 10
  28. def plus_100(self, name):
  29. if self.__class__ == C2:
  30. pass
  31. for C in [C1, C2]:
  32. C.__init__ = __init__
  33. C.y = 20
  34. c = C()
  35. C.__getattr__ = return_100
  36. AreEqual([c.x, c.y, c.z], [10, 20, 100])
  37. def access_z(): return c.z
  38. C.__getattr__ = throw_attribute_error
  39. AreEqual([c.x, c.y], [10, 20])
  40. AssertError(AttributeError, access_z)
  41. C.__getattr__ = throw_assertion_error
  42. AssertError(AssertionError, access_z)
  43. C.__getattr__ = lambda self, name: self.x + 200 # access attribute inside
  44. AreEqual([c.x, c.y, c.z], [10, 20, 210])
  45. del C.__getattr__
  46. AssertError(AttributeError, access_z)
  47. def test_setattr_alone():
  48. global flag
  49. C1, C2 = new_classes()
  50. def f(self): self.x = 10
  51. def simply_record(self, name, value): global flag; flag = "%s %s" % (name, value)
  52. def simply_throw(self, name, value): raise AssertionError
  53. def add_10_via_dict(self, name, value):
  54. self.__dict__[name] = value + 10
  55. def add_20_via_object(self, name, value):
  56. if self.__class__ == C2:
  57. object.__setattr__(self, name, value + 20)
  58. if self.__class__ == C1:
  59. self.__dict__[name] = value + 20
  60. for C in [C1, C2]:
  61. C.set_something = f
  62. c = C()
  63. c.x = 0
  64. C.__setattr__ = simply_record
  65. flag = 0
  66. c.set_something()
  67. AreEqual(flag, "x 10")
  68. AreEqual(c.x, 0) # unchanged
  69. c.y = 20
  70. AreEqual(flag, "y 20")
  71. AssertError(AttributeError, lambda: c.y)
  72. C.__setattr__ = simply_throw
  73. AssertError(AssertionError, c.set_something) # even if c.x already exists
  74. C.z = 30 # ok: class variable
  75. AreEqual(c.z, 30)
  76. C.__setattr__ = add_10_via_dict
  77. c.set_something()
  78. AreEqual(c.x, 20)
  79. C.__setattr__ = add_20_via_object
  80. c.u = 50
  81. AreEqual(c.u, 70)
  82. del C.__setattr__
  83. c.z = 40
  84. AreEqual([c.z, C.z], [40, 30])
  85. def test_delattr_only():
  86. C1, C2 = new_classes()
  87. # low pri
  88. @disabled("bug 365168")
  89. def test_negative1():
  90. class C:
  91. def __setattr__(self, name, value):
  92. object.__setattr__(self, name, value)
  93. try: C().x = 1
  94. except TypeError: pass
  95. else: Fail("should have thrown: can't apply this __setattr__ to instance object")
  96. class C:
  97. def __getattr__(self, name):
  98. object.__getattribute__(self, name)
  99. AssertErrorWithMessage(AttributeError, "'instance' object has no attribute 'x'", lambda: C().x)
  100. def test_bad_signatures():
  101. C1, C2 = new_classes()
  102. def bad1(self): pass
  103. def bad2(self, x): pass
  104. def bad3(self, x, y): pass
  105. def bad4(self, x, y, z): pass
  106. for C in [C1, C2]:
  107. c = C()
  108. def f(): c.x = 1
  109. for bad_for_get in [bad1, bad3]:
  110. C.__getattr__ = bad_for_get
  111. AssertError(TypeError, lambda: c.x)
  112. for bad_for_set in [bad2, bad4]:
  113. C.__setattr__ = bad_for_set
  114. AssertError(TypeError, f)
  115. for bad_for_getattribute in [bad1, bad3]:
  116. C2.__getattribute__ = bad_for_getattribute
  117. AssertError(TypeError, lambda: c.x)
  118. def test_getattribute_only():
  119. class C:
  120. def __getattribute__(self, name):
  121. return 10
  122. c = C()
  123. AssertError(AttributeError, lambda: c.x) # __getattribute__ only works for new-style
  124. class C(object):
  125. def set_y(self): self.y = 30
  126. c = C()
  127. f = c.set_y
  128. c.x = 10
  129. C.__getattribute__ = return_100
  130. AreEqual(100, c.x)
  131. c.x = 20
  132. def plus_100(self, name):
  133. try:
  134. return object.__getattribute__(self, name) + 100
  135. except AttributeError:
  136. return 200
  137. C.__getattribute__ = plus_100
  138. AreEqual(120, c.x)
  139. f()
  140. AreEqual(130, c.y)
  141. AreEqual(200, c.z)
  142. C.__getattribute__ = throw_attribute_error
  143. AssertError(AttributeError, lambda: c.x)
  144. C.__getattribute__ = throw_assertion_error
  145. AssertError(AssertionError, lambda: c.x)
  146. del C.__getattribute__
  147. AreEqual(c.x, 20)
  148. AreEqual(c.y, 30)
  149. AssertError(AttributeError, lambda: c.z)
  150. def test_getattr_and_getattribute_together():
  151. class C(object): pass
  152. c = C()
  153. C.__getattr__ = lambda *args: 20
  154. C.__getattribute__ = lambda *args: 30
  155. AreEqual(c.x, 30)
  156. C.__getattribute__ = throw_attribute_error
  157. AreEqual(c.x, 20)
  158. C.__getattribute__ = throw_assertion_error
  159. AssertError(AssertionError, lambda: c.x)
  160. C.__getattribute__ = lambda *args: C.__getattr__(*args)
  161. AreEqual(c.x, 20)
  162. def test_subclassing():
  163. C1, C2 = new_classes()
  164. ## new style
  165. class D(C2): pass
  166. d = D()
  167. d.x = 10
  168. C2.__getattr__ = return_100
  169. AreEqual(d.y, 100)
  170. del C2.__getattr__
  171. def f(self, name, value): self.__dict__[name] = value + 10
  172. C2.__setattr__ = f
  173. d.x = 20
  174. AreEqual(d.x, 30)
  175. del C2.__setattr__
  176. C2.__getattribute__ = return_100
  177. #AreEqual(d.x, 100) # bug 365242
  178. ## old style
  179. class D(C1): pass
  180. d = D()
  181. C1.__getattr__ = return_100
  182. #AssertError(AttributeError, lambda: d.y) # (no?) dynamism for old style, bug 365266
  183. class D(C1): pass
  184. d = D()
  185. d.x = 10
  186. AreEqual([d.x, d.y], [10, 100])
  187. C1.__setattr__ = f
  188. class D(C1): pass
  189. d = D()
  190. d.x = 20
  191. AreEqual([d.x, d.y], [30, 100])
  192. C1.__getattribute__ = lambda *args: 200 # __getattribute__ not honored
  193. class D(C1): pass
  194. d = D()
  195. AreEqual([d.x, d.y], [100, 100])
  196. @disabled("bug 369042")
  197. def test_delete_getattribute():
  198. class B(object):
  199. def __getattribute__(self, name): pass
  200. class D(B): pass
  201. def f(): del D.__getattribute__
  202. AssertError(AttributeError, f)
  203. run_test(__name__)