Skip to content

Question: List of instances / Callables? #4358

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

Closed
izn opened this issue Dec 13, 2017 · 3 comments
Closed

Question: List of instances / Callables? #4358

izn opened this issue Dec 13, 2017 · 3 comments

Comments

@izn
Copy link

izn commented Dec 13, 2017

I've started testing mypy on an application, but I don't get success when trying to work with instances of class.

ButtonGenerator.__new__ + buttons should accept a List of BaseButton (URLButton) or simply a BaseButton instance.

buttons.py

class BaseButton(object):
    def __init__(self):
        pass    

class ButtonGenerator(BaseButton):
    def __new__(self, buttons: Union[List[BaseButton], BaseButton]) -> Union[List[Dict[str, str]], Dict[str, str]]:
        if (isinstance(buttons, list)):
            if (not all(isinstance(button, BaseButton) for button in buttons)):
                raise ValueError('Invalid Buttons instances.')

            return [button.button_dict for button in buttons]

        return buttons.button_dict


class URLButton(BaseButton):
    def __init__(self, url, title=None):
        self.button_dict = {
            'type': 'external',
            'url': url
        }

        if (title):
            self.button_dict['title'] = title

Errors

buttons.py:17: error: "BaseButton" has no attribute "button_dict"
buttons.py:19: error: "BaseButton" has no attribute "button_dict"

Example: print(ButtonGenerator(URLButton('https://github.com/python/mypy')))

I tried with List of Callable also.

Am I doing it wrong?

@emmatyping
Copy link
Member

You need to narrow the type to URLButton it appears. If you tried to pass a BaseButton instance it would cause an attribute error at runtime as BaseButton does not have an attribute button_dict. I believe you want ButtonGenerator to look like:

class ButtonGenerator(BaseButton):
    def __new__(self, buttons: Union[List[URLButton], URLButton]) -> Union[List[Dict[str, str]], Dict[str, str]]:
        if (isinstance(buttons, list)):
            if (not all(isinstance(button, BaseButton) for button in buttons)):
                raise ValueError('Invalid Buttons instances.')

            return [button.button_dict for button in buttons]

        return buttons.button_dict

And that should typecheck.

@izn
Copy link
Author

izn commented Dec 13, 2017

@ethanhs You're right, but URLButton extends from BaseButton - and using pure Python the inheritance (button_dict) works.

Is there some limitation on MyPy about parent/child classes?

(I have a lot of classes extending from BaseButton and all should be accepted on buttons argument in ButtonGenerator)

@emmatyping
Copy link
Member

@izn This isn't really a problem with mypy, more of a constraint of nominal static typing. Since you annotated __new__ to take BaseButton or a list thereof, the argument buttons has one of those two types. Since BaseButton type has no attribute button_dict, it is a type error. You can fix this by either adding button_dict to BaseButton if all of your inheriting classes have button_dict or, if there are some that do not have the button_dict attribute, then you need to either create an intermediate class or define a protocol that has that attribute. #1424 may help as well.

@izn izn closed this as completed Dec 14, 2017
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

No branches or pull requests

2 participants