Mypy: "multiple values for keyword argument" with star expansion

Created on 8 May 2019  路  8Comments  路  Source: python/mypy

MyPy seems to be unaware of the length of a Tuple when wrapped in a Union:

from typing import Union, Tuple


def get_tuple() -> Union[Tuple[int, int], Tuple[None, None]]:
    return (1, 2)


def process_tuple(one, two, flag=False):
    print(one, two, flag)


process_tuple(*get_tuple(), flag=True)
1.py:12: error: "process_tuple" gets multiple values for keyword argument "flag"

Same code without a unionized type (def get_tuple() -> Tuple[int, int]:) works.

bug false-positive priority-1-normal topic-union-types

Most helpful comment

To add to the severity, consider this ubiquitous use case of initializing a constructor with custom keyword arguments:

from typing import Any


class Foo:
    def __init__(self, a: str, b: str = 'Foo kwarg', c: str = 'Foo kwarg') -> None:
        print(f"{a=} {b=} {c=}")


class Bar(Foo):
    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, b='Bar kwarg', **kwargs)


Bar('my arg')
# prints:
#    a='my arg' b='Bar kwarg' c='Foo kwarg'

The above code is syntactically correct and runs fine, but mypy produces this on the super() line:

c:\Users\tim\code\blog-py\generator\foo.py:11: error: "__init__" of "Foo" gets multiple values for keyword argument "b"

For now, I will unfortunately # type: ignore this line in my code.

All 8 comments

Oh, it looks like one more place where we need to special-case union types.

I think this is a pretty rare situation, so I set priority to low.

Updated priority to normal since this is a clear bug and we'd be happy to accept a PR that fixes this (at least if the fix is clean enough).

To add to the severity, consider this ubiquitous use case of initializing a constructor with custom keyword arguments:

from typing import Any


class Foo:
    def __init__(self, a: str, b: str = 'Foo kwarg', c: str = 'Foo kwarg') -> None:
        print(f"{a=} {b=} {c=}")


class Bar(Foo):
    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, b='Bar kwarg', **kwargs)


Bar('my arg')
# prints:
#    a='my arg' b='Bar kwarg' c='Foo kwarg'

The above code is syntactically correct and runs fine, but mypy produces this on the super() line:

c:\Users\tim\code\blog-py\generator\foo.py:11: error: "__init__" of "Foo" gets multiple values for keyword argument "b"

For now, I will unfortunately # type: ignore this line in my code.

@t-mart technically I think it would be more correct to have Bar's init be:

def __init__(self, b='Bar kwarg', *args: Any, **kwargs: Any) -> None:
    super().__init__(*args, b=b, **kwargs)

Since in your version above it would be possible to have multiple arguments for b passed to Foo. Unfortunately though, this re-writing still produces the same error.

As an additional example, quite common when some of the parameters are loaded from some external source and then you need to supply the rest of the parameters explicitly:

class MyClass:
    def __init__(self, param1: str, param2: str, param3: str):
        self.param1 = param1
        self.param2 = param2

data = {"param1": "value1", "param2": "value2"}  # coming from some external source
result = MyClass(**data, param3="value3")

would give

error: "MyClass" gets multiple values for keyword argument "param3"

@alexey-tereshenkov-oxb your example passes on mypy master :-)

@alexey-tereshenkov-oxb your example passes on mypy master :-)

Thanks for pinging :) I am on mypy 0.790

Was this page helpful?
0 / 5 - 0 ratings

Related issues

PeterJCLaw picture PeterJCLaw  路  3Comments

gregbedwell picture gregbedwell  路  3Comments

kissge picture kissge  路  3Comments

co-dh picture co-dh  路  3Comments

Stiivi picture Stiivi  路  3Comments