1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
import types
import functools
from sentinels import NOTHING
from types import FunctionType
from types import MethodType
from types import BuiltinMethodType
from .dtypes import MethodDescriptorType
from .exceptions import InvalidEntryPoint
from .exceptions import CannotMockFunctions
from .signature import FunctionSignature
from .mock_handle import MockHandle
class ClassMockHandle(MockHandle):
def __init__(self, forge, mock, mocked_class, behave_as_instance, hybrid):
super(ClassMockHandle, self).__init__(forge, mock, behave_as_instance)
self._assert_is_not_function(mocked_class)
self.mocked_class = mocked_class
self._hybrid = hybrid
def _describe(self):
desc = self._get_class_description()
type_str = 'Mock' if self.behaves_as_instance else 'Class mock'
return "<%s of %r>" % (type_str, desc)
def _get_class_description(self):
return getattr(self.mocked_class, '__name__', '?')
def _assert_is_not_function(self, mocked_class):
if type(mocked_class) in (types.FunctionType, types.MethodType, types.BuiltinFunctionType):
raise CannotMockFunctions("Cannot mock functions as classes. Use create_function_stub instead.")
def _has_method(self, name):
return hasattr(self.mocked_class, name)
def _get_real_function(self, name):
returned = self._get_real_method(name)
if hasattr(returned, '__func__'):
return returned.__func__
return returned
def _get_real_method(self, name):
if name == '__call__' and not self.behaves_as_instance:
return self._get_constructor_method()
return getattr(self.mocked_class, name, NOTHING)
def _get_constructor_method(self):
returned = getattr(self.mocked_class, "__init__", object.__init__)
if type(returned) is type(object.__init__) and returned.__objclass__ is object:
# in some cases where the class doesn't have a constructor,
# simulate an empty ctor...
fake_constructor = lambda self: None
fake_constructor.__name__ = "__init__"
returned = types.MethodType(fake_constructor, self.mocked_class)
return returned
def _check_unrecorded_method_getting(self, name):
pass # unrecorded methods can be obtained, but not called...
def _check_getting_method_stub_without_recorded_calls(self, name, stub):
pass # also ok
def has_nonmethod_class_member(self, name):
value = getattr(self.mocked_class, name, NOTHING)
if value is NOTHING:
return False
return type(value) not in (FunctionType, MethodType, BuiltinMethodType, MethodDescriptorType)
def get_nonmethod_class_member(self, name):
return getattr(self.mocked_class, name)
def get_method(self, name):
if self._hybrid:
if self.forge.is_replaying() and not self.forge.stubs.has_initialized_method_stub(self.mock, name):
return self._build_hybrid_method(name)
return super(ClassMockHandle, self).get_method(name)
def _build_hybrid_method(self, name):
real_method = self._get_real_method(name)
if not self._can_use_as_entry_point(name, real_method):
raise InvalidEntryPoint("%s is not a method that can be used as a hybrid entry pont" % (name,))
return functools.partial(self._get_real_function(name), self._build_hybrid_self_arg(real_method))
def _build_hybrid_self_arg(self, method):
sig = FunctionSignature(method)
if sig.is_class_method() and self.behaves_as_instance:
return self.mocked_class
return self.mock
def _can_use_as_entry_point(self, name, method):
if self._is_static_method(name):
return False
sig = FunctionSignature(method)
if (sig.is_bound_method() and not sig.is_class_method()) and not self.behaves_as_instance:
return False
return True
def _check_special_method_call(self, name, args, kwargs):
if name == '__call__':
if self.behaves_as_instance and not self.is_callable():
raise TypeError("%s instance is not callable!" % (self.mocked_class,))
else:
if not self.has_method(name):
raise TypeError("%s instance has no attribute %r" % (self.mocked_class, name))
def is_callable(self):
if not hasattr(self.mocked_class, "__call__"):
return False
call_method = self.mocked_class.__call__
if getattr(call_method, "__objclass__", None) is type(self.mocked_class):
return False
if getattr(call_method, "__self__", None) is not None:
#__call__ is already bound, for some reason
return False
return True
def _is_binding_needed(self, name, method_stub):
if not method_stub.__forge__.is_bound():
if name == '__call__' and not self.behaves_as_instance:
# constructors are normally bound...
return True, NOTHING
if method_stub.__forge__.signature.is_class_method():
return True, self.mocked_class
if self._is_static_method(name):
return False, None
if self.behaves_as_instance and not method_stub.__forge__.signature.is_bound_method():
return True, self.mock
return False, None
def _is_static_method(self, method_name):
return isinstance(self.mocked_class.__dict__.get(method_name, None), staticmethod)
|