Repository: python/mypy

Issue:
Bug: Functional Enum API Unexpected arguments to Enum()
I get error 
```
enum-repro.py:3: error: Unexpected arguments to Enum()
Found 1 error in 1 file (checked 1 source file)
```

When mypy checking for example 
```python
from enum import Enum

enum_name = Enum(
    value="enum_name",
    names={
        "a": 1,
        "b": 2
    }
)
print(enum_name)
```

I'm using mypy 0.782 and Python 3.8.3. Also tried latest master (0.790+dev.eae1860bef0a6fe06753459bf97633a41e789ed7)

Since this is valid python3 syntax, I would expect mypy to at least let it through, maybe with a warning about dynamic types being impossible to infer at static typecheck time.

For my actual use case, I could try using the class EnumName(Enum)): form but It doesn't fully support my use case, because I would like to use names containing spaces and special symbols.


Discussion:
This should work if you use positional instead of keyword arguments. For example, the mypy test suite contains a `E = Enum('E', {'foo': 1, 'bar': 2})`. It wouldn't be hard to support keyword arguments (feel free to submit a PR!), but we just haven't taken the time to implement it.
Okay great, I understand that specifically not being implemented. The unnamed arguments will do as a workaround for now, solves my use case.
hi, @JukkaL @JelleZijlstra I want to fix this issue, could you please give some suggestions about where to start?
@LiuYuHui you can add new test cases to the `check-enum.test` file. The implementation would probably go in the `parse_enum_call_args` method in `semanal_enum.py`.
I am having the same issue.

Resolving patch:
diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py
--- a/mypy/semanal_enum.py
+++ b/mypy/semanal_enum.py
@@ -8,7 +8,7 @@
 from mypy.nodes import (
     Expression, Context, TypeInfo, AssignmentStmt, NameExpr, CallExpr, RefExpr, StrExpr,
     UnicodeExpr, TupleExpr, ListExpr, DictExpr, Var, SymbolTableNode, MDEF, ARG_POS,
-    EnumCallExpr, MemberExpr
+    ARG_NAMED, EnumCallExpr, MemberExpr
 )
 from mypy.semanal_shared import SemanticAnalyzerInterface
 from mypy.options import Options
@@ -104,23 +104,37 @@ def parse_enum_call_args(self, call: CallExpr,
         Return a tuple of fields, values, was there an error.
         """
         args = call.args
+        if not all([arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds]):
+            return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call)
         if len(args) < 2:
             return self.fail_enum_call_arg("Too few arguments for %s()" % class_name, call)
-        if len(args) > 2:
+        if len(args) > 6:
             return self.fail_enum_call_arg("Too many arguments for %s()" % class_name, call)
-        if call.arg_kinds != [ARG_POS, ARG_POS]:
-            return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call)
-        if not isinstance(args[0], (StrExpr, UnicodeExpr)):
+        valid_name = [None, 'value', 'names', 'module', 'qualname', 'type', 'start']
+        for arg_name in call.arg_names:
+            if arg_name not in valid_name:
+                self.fail_enum_call_arg("Unexpected keyword argument '{}'".format(arg_name), call)
+        value, names = None, None
+        for arg_name, arg in zip(call.arg_names, args):
+            if arg_name == 'value':
+                value = arg
+            if arg_name == 'names':
+                names = arg
+        if value is None:
+            value = args[0]
+        if names is None:
+            names = args[1]
+        if not isinstance(value, (StrExpr, UnicodeExpr)):
             return self.fail_enum_call_arg(
                 "%s() expects a string literal as the first argument" % class_name, call)
         items = []
         values = []  # type: List[Optional[Expression]]
-        if isinstance(args[1], (StrExpr, UnicodeExpr)):
-            fields = args[1].value
+        if isinstance(names, (StrExpr, UnicodeExpr)):
+            fields = names.value
             for field in fields.replace(',', ' ').split():
                 items.append(field)
-        elif isinstance(args[1], (TupleExpr, ListExpr)):
-            seq_items = args[1].items
+        elif isinstance(names, (TupleExpr, ListExpr)):
+            seq_items = names.items
             if all(isinstance(seq_item, (StrExpr, UnicodeExpr)) for seq_item in seq_items):
                 items = [cast(Union[StrExpr, UnicodeExpr], seq_item).value
                          for seq_item in seq_items]
@@ -139,8 +153,8 @@ def parse_enum_call_args(self, call: CallExpr,
                     "%s() with tuple or list expects strings or (name, value) pairs" %
                     class_name,
                     call)
-        elif isinstance(args[1], DictExpr):
-            for key, value in args[1].items:
+        elif isinstance(names, DictExpr):
+            for key, value in names.items:
                 if not isinstance(key, (StrExpr, UnicodeExpr)):
                     return self.fail_enum_call_arg(
                         "%s() with dict literal requires string literals" % class_name, call)


Test patch:
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -316,17 +316,27 @@ main:5: note: Revealed type is 'Literal[__main__.F.baz]?'
 main:6: note: Revealed type is 'Literal[1]?'
 main:7: note: Revealed type is 'Literal['bar']?'
 
+
+[case testEnumKeywordsArgs]
+from enum import Enum, IntEnum
+
+PictureSize = Enum('PictureSize', 'P0 P1 P2 P3 P4 P5 P6 P7 P8', type=str, module=__name__)
+fake_enum1 = Enum('fake_enum1', ['a', 'b'])
+fake_enum2 = Enum('fake_enum1', names=['a', 'b'])
+fake_enum3 = Enum(value='fake_enum1', names=['a', 'b'])
+fake_enum4 = Enum(value='fake_enum1', names=['a', 'b'] , module=__name__)
+
 [case testFunctionalEnumErrors]
 from enum import Enum, IntEnum
 A = Enum('A')
 B = Enum('B', 42)
-C = Enum('C', 'a b', 'x')
+C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q')
 D = Enum('D', foo)
 bar = 'x y z'
 E = Enum('E', bar)
 I = IntEnum('I')
 J = IntEnum('I', 42)
-K = IntEnum('I', 'p q', 'z')
+K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q')
 L = Enum('L', ' ')
 M = Enum('M', ())
 N = IntEnum('M', [])
@@ -357,7 +367,7 @@ main:14: error: Enum() with tuple or list expects strings or (name, value) pairs
 main:15: error: Enum() with tuple or list expects strings or (name, value) pairs
 main:16: error: IntEnum() with tuple or list expects strings or (name, value) pairs
 main:17: error: Enum() with dict literal requires string literals
-main:18: error: Unexpected arguments to Enum()
+main:18: error: Unexpected keyword argument 'keyword'
 main:19: error: Unexpected arguments to Enum()
 main:20: error: Unexpected arguments to Enum()
 main:22: error: "Type[W]" has no attribute "c"