diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f5f96dc87b19..42ea6cf97995 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -220,6 +220,9 @@ def analyse_class_attribute_access(itype: Instance, if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) + if itype.type.is_enum and not (is_lvalue or is_decorated or is_method): + return itype + t = node.type if t: is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class diff --git a/mypy/nodes.py b/mypy/nodes.py index ea9e3cced71f..9f3e2ff59be6 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1398,6 +1398,7 @@ class TypeInfo(SymbolNode): names = None # type: SymbolTable # Names defined directly in this type is_abstract = False # Does the class have any abstract attributes? abstract_attributes = None # type: List[str] + is_enum = False # Information related to type annotations. diff --git a/mypy/semanal.py b/mypy/semanal.py index 68d6585e5a89..9db94a290db4 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -622,6 +622,8 @@ def analyze_base_classes(self, defn: ClassDef) -> None: defn.base_types.append(base) elif not isinstance(base, UnboundType): self.fail('Invalid base class', base_expr) + if isinstance(base, Instance): + defn.info.is_enum = base.type.fullname() == 'enum.Enum' # Add 'object' as implicit base if there is no other base class. if (not defn.base_types and defn.fullname != 'builtins.object'): obj = self.object_type() diff --git a/mypy/test/data/pythoneval-enum.test b/mypy/test/data/pythoneval-enum.test new file mode 100644 index 000000000000..4c900e22217a --- /dev/null +++ b/mypy/test/data/pythoneval-enum.test @@ -0,0 +1,18 @@ +-- Test cases for type checking mypy programs using full stubs and running +-- using CPython. +-- +-- These are mostly regression tests -- no attempt is made to make these +-- complete. +-- +-- This test file checks Enum + +[case testEnum] +from enum import Enum +class Medal(Enum): + gold = 1 + silver = 2 + bronze = 3 +m = Medal.gold +m = 1 +[out] +_program.py, line 7: Incompatible types in assignment (expression has type "int", variable has type "Medal") diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index ce5c497ca3bd..fcd4dfef0054 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -27,7 +27,8 @@ python_eval_files = ['pythoneval.test', 'python2eval.test'] -python_34_eval_files = ['pythoneval-asyncio.test'] +python_34_eval_files = ['pythoneval-asyncio.test', + 'pythoneval-enum.test'] # Path to Python 3 interpreter python3_path = sys.executable diff --git a/stubs/3.2/enum.pyi b/stubs/3.2/enum.pyi deleted file mode 100644 index 406ee77082c4..000000000000 --- a/stubs/3.2/enum.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# Stubs for enum (Python 3.4) -# -# TODO: This isn't working yet! - -class Enum: - name = '' - value = '' - - def __init__(self, value): pass - -class IntEnum(int, Enum): pass - -def unique(enumeration): pass diff --git a/stubs/3.4/enum.pyi b/stubs/3.4/enum.pyi new file mode 100644 index 000000000000..d5145d14188b --- /dev/null +++ b/stubs/3.4/enum.pyi @@ -0,0 +1,24 @@ +from typing import List, Any, TypeVar + + +class Enum: + def __new__(cls, value: Any) -> None: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __dir__(self) -> List[str]: ... + def __format__(self, format_spec: str) -> str: ... + def __hash__(self) -> Any: ... + def __reduce_ex__(self, proto: Any) -> Any: ... + + def name(self) -> str: ... + def value(self) -> Any: ... + + +class IntEnum(int, Enum): + pass + + +_T = TypeVar('_T') + +def unique(enumeration: _T) -> _T: + pass