Skip to content

"Enum" has no attribute "__iter__" on instance attributes defined with functional API #8009

Closed

Description

@lihu-zhong

Steps to reproduce
Run mypy against the following code:

from enum import Enum


class OuterEnum(Enum):
    foo = "bar"


class Foo:
    def __init__(self,) -> None:
        self.OuterEnum = OuterEnum
        list(self.OuterEnum)
        self.InnerEnum = Enum("InnerEnum", [("foo", "bar")])
        list(self.InnerEnum)  # Error!
        [i for i in self.InnerEnum]  # Error!
        self.DictEnum = Enum("DictEnum", {"foo": "bar"})
        list(self.DictEnum)  # Error!


OtherEnum = Enum("OtherEnum", [("foo", "bar")])
list(OtherEnum)

Observed behavior
When trying to iterate over an enumeration defined as an instance attribute using the functional API, mypy raises an error when attempting to iterate over said attribute.

The above snippet raises three mypy errors where marked:

$ poetry run mypy foo.py 
foo.py:13: error: No overload variant of "list" matches argument type "Enum"
foo.py:13: note: Possible overload variant:
foo.py:13: note:     def [_T] __init__(self, iterable: Iterable[_T]) -> List[_T]
foo.py:13: note:     <1 more non-matching overload not shown>
foo.py:14: error: "Enum" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)
foo.py:16: error: No overload variant of "list" matches argument type "Enum"
foo.py:16: note: Possible overload variant:
foo.py:16: note:     def [_T] __init__(self, iterable: Iterable[_T]) -> List[_T]
foo.py:16: note:     <1 more non-matching overload not shown>
Found 3 errors in 1 file (checked 1 source file)

Expected behavior
When testing the above code, iteration over the instance attribute Enum works fine, and mypy does not raise errors when iterating over similar Enums if they're defined using the class syntax or aren't instance attributes. I would not expect mypy to raise an error here.

Environment

$ poetry run mypy --version
mypy 0.740
$ poetry run python --version
Python 3.7.4

Also tested against master with the same behavior

$ poetry run mypy --version
mypy 0.750+dev.2af0ac021335ec4a3e84a0ba739b4aea7e659c43

Thank you for your work on this tool!

Activity

  1. lihu-zhong commented on Nov 25, 2019

    @lihu-zhong
    Author

    It looks like this might be more general than just iteration -- mypy raises an error on almost any normal Enum operation:

            self.InnerEnum("bar")
            self.InnerEnum["foo"]
    

    gives

    "Enum" not callable
    Value of type "Enum" is not indexable
    
  2. ilevkivskyi commented on Nov 25, 2019

    @ilevkivskyi
    Member

    The actual bug here is that creating enum as an attribute on self should be plain prohibited (with a clear error message), see similar issue #7531 and fix for it #7662 for named tuples. Probably the same should be also for typed dicts.

  3. lihu-zhong commented on Nov 25, 2019

    @lihu-zhong
    Author

    Thanks for the prompt response; that makes sense. Out of curiosity, why should generating enums and namedtuples as attributes be prohibited? It would be quite convenient for me in this case, since I'd like to build up an enumeration from data I'm reading in from the file system.

  4. ilevkivskyi commented on Nov 25, 2019

    @ilevkivskyi
    Member

    Out of curiosity, why should generating enums and namedtuples as attributes be prohibited?

    Creating a new type for every instance doesn't really make sense in a statically typed world (assuming we will never do full scale dependent types): Foo().InnerEnum and Foo().InnerEnum are two different classes if you call it twice.

    Also this is honestly an anti-pattern. I could easily imagine this causes subtle bugs when you compare two enum values from different instances.

    Currently mypy allows assigning normal class objects (i.e. non-special like enums) as attributes, because this pattern is used in unit tests (and there it kind of fine because there is typically a single instance of the test case class).

  5. lihu-zhong commented on Nov 25, 2019

    @lihu-zhong
    Author

    That makes sense to me. Thank you for the feedback.

  6. rdbisme commented on Feb 24, 2020

    @rdbisme

    What about having an Enum as class attribute? It looks like I'm getting the same error for a class attribute

  7. Avasam commented on Feb 22, 2024

    @Avasam
    SponsorContributor

    What about having an Enum as class attribute? It looks like I'm getting the same error for a class attribute

    A similar question was asked in setuptools: how about at a module level?
    pypa/setuptools#3979 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions