Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum created via functional API should be iterable #16936

Open
abravalheri opened this issue Feb 22, 2024 · 2 comments
Open

Enum created via functional API should be iterable #16936

abravalheri opened this issue Feb 22, 2024 · 2 comments
Labels
bug mypy got something wrong topic-enum

Comments

@abravalheri
Copy link

abravalheri commented Feb 22, 2024

Bug Report

The Python docs website document enum items as iterables:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

https://docs.python.org/3/howto/enum.html#functional-api

This definition is also used in typeshed: https://github.com/python/typeshed/blob/a2095002e446bd0e20f8a469a14e271cd6cc6ad9/stdlib/enum.pyi#L105.

However, mypy fails to recognise this characteristic when using the functional API.

To Reproduce

# Gist URL: https://gist.github.com/mypy-play/d41bcc40b2df9dbfb6f5a0cff7c18681
# Playground URL: https://mypy-play.net/?mypy=latest&python=3.12&gist=d41bcc40b2df9dbfb6f5a0cff7c18681
from enum import Enum

(LATEST,) = Enum("v", "LATEST")

DOWNLOADS = {
    "file1.txt": LATEST,
    "file2.html": 7,
    "file3.css": 3,
    "file4.txt": 5,
    "file5.txt": LATEST,
}

def download_all() -> None:
    for file, version in DOWNLOADS.items():
        if version is LATEST:
            url = f"http://latest.example.url/files/{file}"
        else:
            url = f"http://example.url/files/version/{version}/{file}"
        _download(url)

def _download(url: str) -> None:
    print(f"... pretend to be downloading {url} ...")


if __name__ == "__main__":
    download_all()

Note that I am using Enum in this example because it is much nicer to work with than LATEST = object(), it has a very good __repr__ and if at any point I need to add more special values, I can do that easily. Overall Enum's functional API is an all-rounder for defining constants.

Expected Behavior

Mypy should find no error when checking the example.

Indeed no runtime error can be found, and the program runs as expected:

$ python3.11 main.py
... pretend to be downloading http://latest.example.url/files/file1.txt ...
... pretend to be downloading http://example.url/files/version/7/file2.html ...
... pretend to be downloading http://example.url/files/version/3/file3.css ...
... pretend to be downloading http://example.url/files/version/5/file4.txt ...
... pretend to be downloading http://latest.example.url/files/file5.txt ...

Actual Behavior

main.py:3: error: "Enum" object is not iterable  [misc]
main.py:6: error: Cannot determine type of "LATEST"  [has-type]
main.py:10: error: Cannot determine type of "LATEST"  [has-type]
main.py:15: error: Cannot determine type of "LATEST"  [has-type]
Found 4 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.8.0
  • Mypy command-line flags: None (this is how I run pipx run --python python3.11 'mypy==1.8.0' main.py)
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: Python 3.11.6 (main, Oct 23 2023, 22:47:21) [GCC 9.4.0]

A related problem (but not identical use case) has been previously closed in the tracker #8009.
Please note, however, that the explanations (1, 2) for closing that other issue do not apply to this use case:

In summary, the other issue described a situation where an Enum was constantly re-defined as a class attribute inside of class' __init__ function. The report was dismissed because the constant re-definition of the Enum would cause subtle bugs.

In this use case however, the Enum is only defined once during the evaluation of the module by the import machinery. It is never re-evaluated and the Enum "class" itself is not assigned as a member of another object.

Footnotes

  1. https://github.com/python/mypy/issues/8009#issuecomment-558327474

  2. https://github.com/python/mypy/issues/8009#issuecomment-558335186

@abravalheri abravalheri added the bug mypy got something wrong label Feb 22, 2024
@abravalheri abravalheri changed the title Enum object created via functional API should be iterable Enum created via functional API should be iterable Feb 22, 2024
@erictraut
Copy link

Mypy is able to handle this pattern if you make the following small modification to your code.

v = Enum("v", "LATEST")
LATEST = v.LATEST

This approach is preferable from a static type checking perspective because it allows the type checker to evaluate a more specific (narrower) type for LATEST: Literal[v.LATEST]. By contrast, if you rely on the iterable interface to Enum, the evaluated type will be v.

@abravalheri
Copy link
Author

abravalheri commented Feb 22, 2024

Hi @erictraut, thank you very much for the suggestion.

Yes, this is a very good workaround for the bug in the question.

The unnecessary verboseness (and unnecessary introduction of an extra binding in the module namespace) are not really my cup of tea, so for the time being, I will just add an ignore comment until hopefully the problem goes away in some decades down the line 🤞.

Anyway, the error message is very misleading.

euri10 added a commit to euri10/litestar that referenced this issue Feb 27, 2024
euri10 added a commit to euri10/litestar that referenced this issue Mar 4, 2024
euri10 added a commit to euri10/litestar that referenced this issue Mar 4, 2024
Alc-Alc pushed a commit to litestar-org/litestar that referenced this issue Mar 4, 2024
euri10 added a commit to euri10/litestar that referenced this issue Mar 10, 2024
JacobCoffee added a commit to litestar-org/litestar that referenced this issue Mar 14, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
provinzkraut added a commit to litestar-org/litestar that referenced this issue Mar 16, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
provinzkraut added a commit to litestar-org/litestar that referenced this issue Mar 16, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
provinzkraut added a commit to litestar-org/litestar that referenced this issue Mar 16, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
provinzkraut added a commit to litestar-org/litestar that referenced this issue Mar 17, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
provinzkraut added a commit to litestar-org/litestar that referenced this issue Mar 18, 2024
* fix minijinja.py to mimic what we do on other templates so that any regitered callable can access the scope ie _transform_state is in the wrong spot

* added a flash function and its pending get_flashes one to be used in templates

* can't use StrEnum

* can't use StrEnum in tests as well

* remove StrEnum entirely

* enum not iterable ?

* silence mypy on this one, python/mypy#16936 related ?

* same

* TIL about ScopeState

* Silence another mypy error that works in test_temmplate...

* semi-ugly "fix"

* another way of looking at minjinja implementation

* Yes I debug with print sometimes

* still fails because get_flashes is not decorted properly in minijinja implementation, there should be a way to detect

* another "working" option, no preference vs other options

* another "working" option, no preference vs other options

* Removed the transformed kwrd, would be breaking probably

* Removed debug

* Removed docstring leftover

* Mypy

* Demonstrated superior-mypy-foo-skills by using type ignore

* Removed FLashCategory from plugin, shouldn't the app_init get it passed in the config ??

* docs: add plugin docs for flash messages

* docs: add plugin docs for flash messages

* Update docs/usage/index.rst

* fix: update file path

* fix: make sphinx gods happy

* docs: add more documentation

* docs: move api doc plugins into directory

* revert pdm.lock to it's original state

* living the dangerous life of not testing code

* typo

---------

Co-authored-by: Janek Nouvertné <25355197+provinzkraut@users.noreply.github.com>
Co-authored-by: Jacob Coffee <jacob@z7x.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-enum
Projects
None yet
Development

No branches or pull requests

3 participants