Python: Import Enum Uniformly before Comparison
This is not considered a bug in Enum
but it’s quite subtle indeed.
1. ProblemPermalink
Example project structure:
.
├── src
│ ├── __init__.py
│ ├── peg_parser.py
│ └── peg_token.py
├── tests
│ ├── __init__.py
│ └── test_peg_parser.py
Both peg_parser.py
and test_peg_parser.py
imports enum TokenType
from peg_token.py
:
# src/peg_parser.py
from peg_token import TokenType
# tests/test_peg_parser.py
from src.peg_token import TokenType
and in test_peg_parser.py
, a unit test that parses var x = 5;
would finally call ToyPEGParser.consume_token()
:
def consume_token(self, token_type, value=None) -> Token | None:
if ((self.current_token.type == token_type) and
(value is None or self.current_token.value == value)):
consumed_token = self.current_token
self.advance_to_next_token()
return consumed_token
return None
The problem is that this method always return None
because self.current_token.type
and token_type
are NOT equal EVEN when they are both TokenType.VAR
.
This problem was also discussed in cpython - Issue#74730 and Python Enums across Modules.
2. SolutionPermalink
You can debug inside ToyPEGParser.consume_token()
method and probe __module__
fields like:
print(f"self.current_token.type.__module__: {self.current_token.type.__module__}")
print(f"token_type.__module__: {token_type.__module__}")
and you would get results like:
self.current_token.type.__module__: src.peg_token
token_type.__module__: peg_token
This is the root cause: the enum TokenType
is actually imported TWICE as TWO types, one src.peg_token.TokenType
and the other peg_token.TokenType
. Therefore the equality test always fails.
The solution is quite simple: import the enum UNIFORMLY:
# src/peg_parser.py
from src.peg_token import TokenType
# tests/test_peg_parser.py
from src.peg_token import TokenType
Comments