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

**kwargs & relations between int, float & bool #9182

Closed
dsuch opened this issue Jul 21, 2020 · 5 comments
Closed

**kwargs & relations between int, float & bool #9182

dsuch opened this issue Jul 21, 2020 · 5 comments

Comments

@dsuch
Copy link

dsuch commented Jul 21, 2020

Hello,

this is a user question.

Python is 3.6.9 and mypy is mypy 0.790+dev.28829fbb684d7535680934b401c05698db5b4d85.

I have a class with methods as below:

from typing import Union

class MyClass(object):

    def func(self, *args:str, **kwargs:Union[int, float]) -> None:
        self.foo = kwargs.pop('foo', 1.0) # type: float

    def func2(self, *args:str, **kwargs:Union[float, int]) -> None:
        self.bar = kwargs.pop('bar', 1) # type: int

    def func3(self, *args:str, **kwargs:Union[bool, int]) -> None:
        self.baz = kwargs.pop('baz', True) # type: bool

In all the functions, **kwargs is a dictionary of str keys with values that may be of several types.

The errors I am getting are:

$ mypy --strict mypy4.py
mypy4.py:9: error: Incompatible types in assignment (expression has type "float", variable has type "int")
mypy4.py:12: error: Incompatible types in assignment (expression has type "int", variable has type "bool")
Found 2 errors in 1 file (checked 1 source file)
$

I would like to clarify if this occurs in relation to this comment in #1850 and the underlying implementation's assumptions of which type is a sub-type of which one?

I can deal with it by using cast, as in the code below, so this is more of a question if the above is expected.

from typing import cast, Union

class MyClass(object):

    def func(self, *args:str, **kwargs:Union[int, float]) -> None:
        self.foo = kwargs.pop('foo', 1.0) # type: float

    def func2(self, *args:str, **kwargs:Union[float, int]) -> None:
        self.bar = cast(int, kwargs.pop('bar', 1)) # type: int

    def func3(self, *args:str, **kwargs:Union[bool, int]) -> None:
        self.baz = cast(bool, kwargs.pop('baz', True)) # type: bool

Please note that the type comments are still needed because otherwise my IDE will not understand the results of casting.

Thanks.

@Akuli
Copy link
Contributor

Akuli commented Jul 21, 2020

AFAIK casting is the correct way to extract one of many Unioned types, and cast(int, union_of_int_and_stuff) should return an int even without a type comment (IDE issue).

If #4441 was implemented, then you could do something like this:

class OptionsDict(TypedDict):
    foo: float
    bar: int
    baz: bool

class MyClass:
    def func2(self, *args, str, **kwargs: Expand[OptionsDict]) -> None:
        self.bar = kwargs['bar']
        # self.bar is now bool, without a type comment or cast

@Akuli
Copy link
Contributor

Akuli commented Jul 21, 2020

Hmm, this might work too:

def func2(self, *args: str, **kwargs: Union[int, float]) -> None:
    self.bar = kwargs['bar']
    assert isinstance(self.bar, int)
    # what do mypy and your ide say now?

@dsuch
Copy link
Author

dsuch commented Jul 21, 2020

Hi @Akuli,

from typing import Union

class MyClass:
    def func2(self, *args: str, **kwargs: Union[int, float]) -> None:
        self.bar = kwargs['bar']
        assert isinstance(self.bar, int)

There are no errors from mypy and the IDE considers self.bar to be an int now.

However, with this code ..

from typing import Union

class MyClass:
    def func2(self, *args: str, **kwargs: Union[int, float]) -> None:
        self.bar = kwargs['bar']
        assert isinstance(self.bar, str)

.. there are still no errors from mypy --strict even if the IDE treats self.bar as str.

@Akuli
Copy link
Contributor

Akuli commented Jul 21, 2020

I don't know why mypy doesn't complain if you do isinstance(something_that_cant_be_string, str).

@JukkaL
Copy link
Collaborator

JukkaL commented Sep 11, 2020

The original behavior seems reasonable, since int is considered by mypy as a subtype of float, and bool is a subclass of int and so also compatible with float. Union[int, float] is basically the same type as float.

@JukkaL JukkaL closed this as completed Sep 11, 2020
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

3 participants