Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename CoroutineMock to CoroutineFunctionMock #103

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Martiusweb
Copy link
Owner

The old name is kept for backward-compatibility but will be removed for 1.0.

Fixes #102

@Kentzo
Copy link
Contributor

Kentzo commented Aug 16, 2018

There can be existing code like this:

isinstance(m, CoroutineMock)

and this change should not break it.

@Martiusweb
Copy link
Owner Author

I've yet to find a way to generate the warning without breaking anything:

The function breaks isinstance(m, CoroutineMock) but a subclass would break
isinstance(ASubClassOfCoroutineFunctionMock, CoroutineMock), while it shouldn't.

@Martiusweb
Copy link
Owner Author

I tried different things and I gave up triggering the warning when the symbol is accessed, this will be too complex for a very small benefit. CoroutineMock = CoroutineFunctionMock is the safest solution.

I added the warning when the module is installed, I hope this will be sufficient...

@Kentzo
Copy link
Contributor

Kentzo commented Aug 17, 2018

One option is to provide a custom metaclass with overridden __instancecheck__ and __subclasscheck__ (PEP 3119).

It might cause the trouble to users who subclassed CoroutineMock with a custom metaclasse, but I expect there are few if any,

setup.py Outdated
@@ -14,3 +14,7 @@
if __name__ == "__main__":
from setuptools import setup
setup(**args)

from warnings import warn
warn("CoroutineMock has been renamed to CoroutineFunctionMock, the alias "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't seem like a good idea: warns can be turned into exceptions.

The old name is kept for backward-compatibility but will be removed for 1.0.
@Kentzo
Copy link
Contributor

Kentzo commented Aug 29, 2018

@Martiusweb What do you thing about an alternative approach:

from warnings import warn


class _DeprecatedClassMeta(type):
    def __new__(cls, name, bases, classdict, *args, **kwargs):
        alias = kwargs.pop('alias', None)

        if alias is not None:
            def new(cls, *args, **kwargs):
                alias = getattr(cls, '_DeprecatedClassMeta__alias')
                warn("{} has been renamed to {}, the alias will be removed in the future".format(cls.__name__, alias.__name__), DeprecationWarning, stacklevel=2)
                return alias(*args, **kwargs)

            classdict['__new__'] = new
            classdict['_DeprecatedClassMeta__alias'] = alias

        # Replace deprecated base classes before instantiating User's subclasses.
        fixed_bases = []

        for b in bases:
            alias = getattr(b, '_DeprecatedClassMeta__alias', None)

            if alias is not None:
                warn("{} has been renamed to {}, the alias will be removed in the future".format(b.__name__, alias.__name__), DeprecationWarning, stacklevel=2)

            b = alias or b
            if b not in fixed_bases:
                fixed_bases.append(b)

        fixed_bases = tuple(fixed_bases)

        return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs)

    def __instancecheck__(cls, instance):
        return any(cls.__subclasscheck__(c) for c in {type(instance), instance.__class__})

    def __subclasscheck__(cls, subclass):
        if subclass is cls:
            return True
        else:
            return issubclass(subclass, getattr(cls, '_DeprecatedClassMeta__alias'))


class CoroutineFunctionMock:
    foo = 42


class CoroutineMock(metaclass=_DeprecatedClassMeta):
    _DeprecatedClassMeta__alias = CoroutineFunctionMock


class CoroutineMockSubclass(CoroutineMock):
    foo = 9000


assert not issubclass(CoroutineMock, CoroutineFunctionMock)
assert issubclass(CoroutineFunctionMock, CoroutineMock)
assert issubclass(CoroutineMockSubclass, CoroutineMock)
assert issubclass(CoroutineMockSubclass, CoroutineFunctionMock)

assert isinstance(CoroutineFunctionMock(), CoroutineMock)
assert isinstance(CoroutineMock(), CoroutineMock)
assert isinstance(CoroutineMock(), CoroutineFunctionMock)
assert isinstance(CoroutineMockSubclass(), CoroutineMock)
assert isinstance(CoroutineMockSubclass(), CoroutineFunctionMock)
assert CoroutineFunctionMock.foo == 42
assert CoroutineMockSubclass.foo == 9000

@Martiusweb Martiusweb force-pushed the master branch 3 times, most recently from c2e9ce1 to 967481c Compare April 3, 2019 15:31
@kloczek
Copy link

kloczek commented Apr 5, 2024

It would be good to rediff this PR against master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rename CoroutineMock to CoroutineFunctionMock
3 participants