1 minute read

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

Categories:

Updated:

Comments