Repository: python/mypy
Pull request: Accept empty list unpacking for function without parameter
Fixes #4997.

Problem statement:
Erroneous 'Too many arguments' error
It appears as though mypy treats argument unpacking as containing at least one element. Here's an example:

```python
from typing import List

class Foo:
    pass

args: List[str] = []
packet = Foo(*args)
```

```
tbug.py:7: error: Too many arguments for "Foo"
```

I guess Mypy is assuming that you would never unpack zero arguments.

In my particular case I have a registry of 'packet classes' and a dynamic number of arguments parsed from the network. Some of those packet classes legitimately have 0 parameters, and Mypy gives me an error about each of them.

Running `mypy 0.600` with no other flags.

Hints from issue discussion:

Commit message: Accept empty list unpacking for function without parameter

Patch to review:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 135f796d479e..4c0f3c1a4c6f 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1233,7 +1233,7 @@ def check_for_extra_actual_arguments(self,
         for i, kind in enumerate(actual_kinds):
             if i not in all_actuals and (
                     kind != nodes.ARG_STAR or
-                    not is_empty_tuple(actual_types[i])):
+                    is_not_empty_tuple(actual_types[i])):
                 # Extra actual: not matched by a formal argument.
                 ok = False
                 if kind != nodes.ARG_NAMED:
@@ -3833,9 +3833,9 @@ def is_async_def(t: Type) -> bool:
     return isinstance(t, Instance) and t.type.fullname() == 'typing.Coroutine'
 
 
-def is_empty_tuple(t: Type) -> bool:
+def is_not_empty_tuple(t: Type) -> bool:
     t = get_proper_type(t)
-    return isinstance(t, TupleType) and not t.items
+    return isinstance(t, TupleType) and bool(t.items)
 
 
 def is_duplicate_mapping(mapping: List[int], actual_kinds: List[int]) -> bool:
diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test
index ceca1fe57c94..7c22980ec582 100644
--- a/test-data/unit/check-varargs.test
+++ b/test-data/unit/check-varargs.test
@@ -353,7 +353,7 @@ class CC(C): pass
 from typing import Any
 d, a = None, None # type: (Any, A)
 f(a, a, *d) # Fail
-f(a, *d)    # Fail
+f(a, *d)    # Ok
 f(*d)       # Ok
 
 g(*d)
@@ -366,7 +366,6 @@ class A: pass
 [builtins fixtures/list.pyi]
 [out]
 main:3: error: Too many arguments for "f"
-main:4: error: Too many arguments for "f"
 
 [case testListVarArgsAndSubtyping]
 from typing import List
@@ -448,6 +447,15 @@ def f(x: int, *, y: str) -> None: pass
 f(y='x', *(1,))
 [builtins fixtures/list.pyi]
 
+[case testVarArgsEmptyList]
+from typing import List
+
+def foo() -> None:
+    pass
+
+lst: List[int] = []
+foo(*lst)
+[builtins fixtures/list.pyi]
 
 -- Overloads + varargs
 -- -------------------


Code review comments:
diff_hunk: @@ -3833,9 +3833,9 @@ def is_async_def(t: Type) -> bool:
     return isinstance(t, Instance) and t.type.fullname() == 'typing.Coroutine'
 
 
-def is_empty_tuple(t: Type) -> bool:
+def is_not_empty_tuple(t: Type) -> bool:
path: mypy/checkexpr.py
text: @user1:
I was a little confused by the name at first. What about renaming this to `is_non_empty_tuple`?

@author:
Fixed.