mock.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. # mock.py
  2. # Test tools for mocking and patching.
  3. # Copyright (C) 2007-2009 Michael Foord
  4. # E-mail: fuzzyman AT voidspace DOT org DOT uk
  5. # mock 0.6.0
  6. # http://www.voidspace.org.uk/python/mock/
  7. # Released subject to the BSD License
  8. # Please see http://www.voidspace.org.uk/python/license.shtml
  9. # 2009-11-25: Licence downloaded from above URL.
  10. # BEGIN DOWNLOADED LICENSE
  11. #
  12. # Copyright (c) 2003-2009, Michael Foord
  13. # All rights reserved.
  14. # E-mail : fuzzyman AT voidspace DOT org DOT uk
  15. #
  16. # Redistribution and use in source and binary forms, with or without
  17. # modification, are permitted provided that the following conditions are
  18. # met:
  19. #
  20. #
  21. # * Redistributions of source code must retain the above copyright
  22. # notice, this list of conditions and the following disclaimer.
  23. #
  24. # * Redistributions in binary form must reproduce the above
  25. # copyright notice, this list of conditions and the following
  26. # disclaimer in the documentation and/or other materials provided
  27. # with the distribution.
  28. #
  29. # * Neither the name of Michael Foord nor the name of Voidspace
  30. # may be used to endorse or promote products derived from this
  31. # software without specific prior written permission.
  32. #
  33. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  34. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  35. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  36. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  37. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  39. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  40. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  41. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  43. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. #
  45. # END DOWNLOADED LICENSE
  46. # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
  47. # Comments, suggestions and bug reports welcome.
  48. __all__ = (
  49. 'Mock',
  50. 'patch',
  51. 'patch_object',
  52. 'sentinel',
  53. 'DEFAULT'
  54. )
  55. __version__ = '0.6.0'
  56. class SentinelObject(object):
  57. def __init__(self, name):
  58. self.name = name
  59. def __repr__(self):
  60. return '<SentinelObject "%s">' % self.name
  61. class Sentinel(object):
  62. def __init__(self):
  63. self._sentinels = {}
  64. def __getattr__(self, name):
  65. return self._sentinels.setdefault(name, SentinelObject(name))
  66. sentinel = Sentinel()
  67. DEFAULT = sentinel.DEFAULT
  68. class OldStyleClass:
  69. pass
  70. ClassType = type(OldStyleClass)
  71. def _is_magic(name):
  72. return '__%s__' % name[2:-2] == name
  73. def _copy(value):
  74. if type(value) in (dict, list, tuple, set):
  75. return type(value)(value)
  76. return value
  77. class Mock(object):
  78. def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
  79. name=None, parent=None, wraps=None):
  80. self._parent = parent
  81. self._name = name
  82. if spec is not None and not isinstance(spec, list):
  83. spec = [member for member in dir(spec) if not _is_magic(member)]
  84. self._methods = spec
  85. self._children = {}
  86. self._return_value = return_value
  87. self.side_effect = side_effect
  88. self._wraps = wraps
  89. self.reset_mock()
  90. def reset_mock(self):
  91. self.called = False
  92. self.call_args = None
  93. self.call_count = 0
  94. self.call_args_list = []
  95. self.method_calls = []
  96. for child in self._children.itervalues():
  97. child.reset_mock()
  98. if isinstance(self._return_value, Mock):
  99. self._return_value.reset_mock()
  100. def __get_return_value(self):
  101. if self._return_value is DEFAULT:
  102. self._return_value = Mock()
  103. return self._return_value
  104. def __set_return_value(self, value):
  105. self._return_value = value
  106. return_value = property(__get_return_value, __set_return_value)
  107. def __call__(self, *args, **kwargs):
  108. self.called = True
  109. self.call_count += 1
  110. self.call_args = (args, kwargs)
  111. self.call_args_list.append((args, kwargs))
  112. parent = self._parent
  113. name = self._name
  114. while parent is not None:
  115. parent.method_calls.append((name, args, kwargs))
  116. if parent._parent is None:
  117. break
  118. name = parent._name + '.' + name
  119. parent = parent._parent
  120. ret_val = DEFAULT
  121. if self.side_effect is not None:
  122. if (isinstance(self.side_effect, Exception) or
  123. isinstance(self.side_effect, (type, ClassType)) and
  124. issubclass(self.side_effect, Exception)):
  125. raise self.side_effect
  126. ret_val = self.side_effect(*args, **kwargs)
  127. if ret_val is DEFAULT:
  128. ret_val = self.return_value
  129. if self._wraps is not None and self._return_value is DEFAULT:
  130. return self._wraps(*args, **kwargs)
  131. if ret_val is DEFAULT:
  132. ret_val = self.return_value
  133. return ret_val
  134. def __getattr__(self, name):
  135. if self._methods is not None:
  136. if name not in self._methods:
  137. raise AttributeError("Mock object has no attribute '%s'" % name)
  138. elif _is_magic(name):
  139. raise AttributeError(name)
  140. if name not in self._children:
  141. wraps = None
  142. if self._wraps is not None:
  143. wraps = getattr(self._wraps, name)
  144. self._children[name] = Mock(parent=self, name=name, wraps=wraps)
  145. return self._children[name]
  146. def assert_called_with(self, *args, **kwargs):
  147. assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)
  148. def _dot_lookup(thing, comp, import_path):
  149. try:
  150. return getattr(thing, comp)
  151. except AttributeError:
  152. __import__(import_path)
  153. return getattr(thing, comp)
  154. def _importer(target):
  155. components = target.split('.')
  156. import_path = components.pop(0)
  157. thing = __import__(import_path)
  158. for comp in components:
  159. import_path += ".%s" % comp
  160. thing = _dot_lookup(thing, comp, import_path)
  161. return thing
  162. class _patch(object):
  163. def __init__(self, target, attribute, new, spec, create):
  164. self.target = target
  165. self.attribute = attribute
  166. self.new = new
  167. self.spec = spec
  168. self.create = create
  169. self.has_local = False
  170. def __call__(self, func):
  171. if hasattr(func, 'patchings'):
  172. func.patchings.append(self)
  173. return func
  174. def patched(*args, **keywargs):
  175. # don't use a with here (backwards compatability with 2.5)
  176. extra_args = []
  177. for patching in patched.patchings:
  178. arg = patching.__enter__()
  179. if patching.new is DEFAULT:
  180. extra_args.append(arg)
  181. args += tuple(extra_args)
  182. try:
  183. return func(*args, **keywargs)
  184. finally:
  185. for patching in getattr(patched, 'patchings', []):
  186. patching.__exit__()
  187. patched.patchings = [self]
  188. patched.__name__ = func.__name__
  189. patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
  190. func.func_code.co_firstlineno)
  191. return patched
  192. def get_original(self):
  193. target = self.target
  194. name = self.attribute
  195. create = self.create
  196. original = DEFAULT
  197. if _has_local_attr(target, name):
  198. try:
  199. original = target.__dict__[name]
  200. except AttributeError:
  201. # for instances of classes with slots, they have no __dict__
  202. original = getattr(target, name)
  203. elif not create and not hasattr(target, name):
  204. raise AttributeError("%s does not have the attribute %r" % (target, name))
  205. return original
  206. def __enter__(self):
  207. new, spec, = self.new, self.spec
  208. original = self.get_original()
  209. if new is DEFAULT:
  210. # XXXX what if original is DEFAULT - shouldn't use it as a spec
  211. inherit = False
  212. if spec == True:
  213. # set spec to the object we are replacing
  214. spec = original
  215. if isinstance(spec, (type, ClassType)):
  216. inherit = True
  217. new = Mock(spec=spec)
  218. if inherit:
  219. new.return_value = Mock(spec=spec)
  220. self.temp_original = original
  221. setattr(self.target, self.attribute, new)
  222. return new
  223. def __exit__(self, *_):
  224. if self.temp_original is not DEFAULT:
  225. setattr(self.target, self.attribute, self.temp_original)
  226. else:
  227. delattr(self.target, self.attribute)
  228. del self.temp_original
  229. def patch_object(target, attribute, new=DEFAULT, spec=None, create=False):
  230. return _patch(target, attribute, new, spec, create)
  231. def patch(target, new=DEFAULT, spec=None, create=False):
  232. try:
  233. target, attribute = target.rsplit('.', 1)
  234. except (TypeError, ValueError):
  235. raise TypeError("Need a valid target to patch. You supplied: %r" % (target,))
  236. target = _importer(target)
  237. return _patch(target, attribute, new, spec, create)
  238. def _has_local_attr(obj, name):
  239. try:
  240. return name in vars(obj)
  241. except TypeError:
  242. # objects without a __dict__
  243. return hasattr(obj, name)