Ejemplo n.º 1
0
def mock_constructor(target, class_name):
    if not _is_string(class_name):
        raise ValueError(
            "Second argument must be a string with the name of the class.")
    if _is_string(target):
        target = testslide._importer(target)

    mocked_class_id = (id(target), class_name)

    if mocked_class_id in _mocked_classes:
        original_class, mocked_class = _mocked_classes[mocked_class_id]
        if not getattr(target, class_name) is mocked_class:
            raise AssertionError(
                "The class {} at {} was changed after mock_constructor() mocked "
                "it!".format(class_name, target))
        callable_mock = getattr(mocked_class, "__new__")
    else:

        original_class = getattr(target, class_name)
        if not inspect.isclass(original_class):
            raise ValueError("Target must be a class.")

        def unpatcher():
            setattr(target, class_name, original_class)
            del _mocked_classes[mocked_class_id]

        _unpatchers.append(unpatcher)

        callable_mock = _CallableMock(original_class, "__new__")

        mocked_class = type(
            str(original_class.__name__ + "Mock"),
            (original_class, ),
            {"__new__": callable_mock},
        )
        setattr(target, class_name, mocked_class)
        _mocked_classes[mocked_class_id] = (original_class, mocked_class)

    def original_callable(_, *args, **kwargs):
        return original_class(*args, **kwargs)

    return _MockCallableDSL(
        mocked_class,
        "__new__",
        callable_mock=callable_mock,
        original_callable=original_callable,
        prepend_first_arg=ANY,
    )
Ejemplo n.º 2
0
def mock_constructor(target, class_name, allow_private=False):
    if not isinstance(class_name, str):
        raise ValueError(
            "Second argument must be a string with the name of the class.")
    _bail_if_private(class_name, allow_private)
    if isinstance(target, str):
        target = testslide._importer(target)
    target_class_id = (id(target), class_name)

    if target_class_id in _mocked_target_classes:
        original_class, mocked_class = _mocked_target_classes[target_class_id]
        if not getattr(target, class_name) is mocked_class:
            raise AssertionError(
                "The class {} at {} was changed after mock_constructor() mocked "
                "it!".format(class_name, target))
        callable_mock = mocked_class.__new__
    else:
        original_class = getattr(target, class_name)

        if "__new__" in original_class.__dict__:
            raise NotImplementedError(
                "Usage with classes that define __new__() is currently not supported."
            )

        gc.collect()
        instances = [
            obj for obj in gc.get_referrers(original_class)
            if type(obj) is original_class
        ]
        if instances:
            raise RuntimeError(
                "mock_constructor() can not be used after instances of {} were created: {}"
                .format(class_name, instances))

        if not inspect.isclass(original_class):
            raise ValueError("Target must be a class.")
        elif not issubclass(original_class, object):
            raise ValueError("Old style classes are not supported.")
        callable_mock = _CallableMock(original_class, "__new__")
        mocked_class = _patch_and_return_mocked_class(target, class_name,
                                                      target_class_id,
                                                      original_class,
                                                      callable_mock)

    def original_callable(cls, *args, **kwargs):
        global _init_args_from_original_callable, _init_kwargs_from_original_callable
        assert cls is mocked_class
        # Python unconditionally calls __init__ with the same arguments as
        # __new__ once it is invoked. We save the correct arguments here,
        # so that __init__ can use them when invoked for the first time.
        _init_args_from_original_callable = args
        _init_kwargs_from_original_callable = kwargs
        return object.__new__(cls)

    return _MockConstructorDSL(
        target=mocked_class,
        method="__new__",
        cls=mocked_class,
        callable_mock=callable_mock,
        original_callable=original_callable,
    )
Ejemplo n.º 3
0
def mock_constructor(
    target: str,
    class_name: str,
    allow_private: bool = False,
    type_validation: bool = True,
) -> _MockConstructorDSL:
    if not isinstance(class_name, str):
        raise ValueError("Second argument must be a string with the name of the class.")
    _bail_if_private(class_name, allow_private)
    if isinstance(target, str):
        target = testslide._importer(target)
    target_class_id = (id(target), class_name)

    if target_class_id in _mocked_target_classes:
        original_class, mocked_class = _mocked_target_classes[target_class_id]
        if not getattr(target, class_name) is mocked_class:
            raise AssertionError(
                "The class {} at {} was changed after mock_constructor() mocked "
                "it!".format(class_name, target)
            )
        callable_mock = mocked_class.__new__
    else:
        original_class = getattr(target, class_name)

        if "__new__" in original_class.__dict__:
            raise NotImplementedError(
                "Usage with classes that define __new__() is currently not supported."
            )

        gc.collect()
        instances = [
            obj
            for obj in gc.get_referrers(original_class)
            if type(obj) is original_class
        ]
        if instances:
            raise RuntimeError(
                "mock_constructor() can not be used after instances of {} were created: {}".format(
                    class_name, instances
                )
            )

        if not inspect.isclass(original_class):
            raise ValueError("Target must be a class.")
        elif not issubclass(original_class, object):
            raise ValueError("Old style classes are not supported.")

        caller_frame = inspect.currentframe().f_back  # type: ignore
        # loading the context ends up reading files from disk and that might block
        # the event loop, so we don't do it.
        caller_frame_info = inspect.getframeinfo(caller_frame, context=0)  # type: ignore
        callable_mock = _CallableMock(original_class, "__new__", caller_frame_info)
        mocked_class = _patch_and_return_mocked_class(
            target,
            class_name,
            target_class_id,
            original_class,
            callable_mock,
            type_validation,
        )

    def original_callable(cls: type, *args: Any, **kwargs: Any) -> Any:
        global _init_args_from_original_callable, _init_kwargs_from_original_callable
        assert cls is mocked_class
        # Python unconditionally calls __init__ with the same arguments as
        # __new__ once it is invoked. We save the correct arguments here,
        # so that __init__ can use them when invoked for the first time.
        _init_args_from_original_callable = args
        _init_kwargs_from_original_callable = kwargs
        return object.__new__(cls)

    return _MockConstructorDSL(
        target=mocked_class,
        method="__new__",
        cls=mocked_class,
        callable_mock=callable_mock,
        original_callable=original_callable,
    )