Alternative Fix for Incompatible Dunder Method Error in Mypy
When a neat trick in Python becomes a side effect.
When cleaning up the code for my chess program that I’m writing in Python as a learning exercise, I fixed a problem by introducing another problem.
class Rank(list, Enum):
HIGH = [1, 8]
PAWN = [2, 7]
I created a multiple-inheritance class with
Enum that allowed access to the assigned values via the
in statement without adding
.value to the end.
<Rank.HIGH: (1, 8)>
>>> enums.Rank.HIGH. Value
>>> 8 in enums.Rank.HIGH
>>> 7 in enums.Rank.HIGH
That was a neat trick and the code worked. The mypy static checker complained about incompatible
__hash__() methods between
Enum. Since I wasn’t using that dunder method in my code, I slapped
# type: ignore to the class line to silence the error.
class Rank(list, Enum): # type: ignore
HIGH = [1, 8]
PAWN = [2, 7]
That worked until I used the Sphinx documentation generator and had to deal with the side effect of my previous fix.
I used a pair of Sphinx extensions:
sphinx.ext.autodoc to discover the doc strings in my modules and
sphinx.ext.viewcode to add source links to the code. Both extensions warned about side effects when importing the modules for processing into static HTML webpages.
If any modules have side effects on import, these will be executed when
The Sphinx wouldn’t generate an entry for the
I could force the class to appear by explicitly naming the classes with the
:members: in the
enums.rst file that specifies directives for Sphinx.
.. automodule:: enums
:members: Color, PieceType, Ranks
That worked until I noticed that the source link was missing for the class. After spending hours looking at Sphinx documentation, I concluded that this was a bug and opened a bug report.
I added this question at the end:
“Would the incompatible
__hash__()methods be a side effect that affects the docstring for a multiple-inheritance class from appearing?”
If the answer came back that this was a side effect impacting Sphinx, I would have removed
list from the class definition and used
enums.Rank.HIGH. value in my code to access the assigned value.
Two days after I opened the bug report, Medium presented this essay in my feed, “Use Perflint — A Performance Linter for Python” by Eldad Uzman, about the
perflint plugin for the
pylint static code analyzer. This plugin looks for performance issues in the code, say, using a global variable that took longer than using a local variable.
I ran the plugin against the
enums.py file and got the warning message for the
W8301: Use tuple instead of list for a non-mutated sequence (use-tuple-over-list)
A light bulb went off in my head. I tend to use
list for almost everything in my code. Unless the values in a
list need to be changed, a
tuple works just as well. Both are sequences that the
in statement can work with.
tuple in the class definition, removed the
# type: ignore from the class line, and replaced the square brackets with parentheses.
class Rank(tuple, Enum):
HIGH = (1, 8)
PAWN = (2, 7)
The code worked,
mypy no longer complained about the incompatible
__hash__() dunder methods, and, after I had Sphinx regenerate the static HTML files, the
enums.Rank class appeared with the source link.
With the side effect gone and everything working, I closed out the bug report.
Please follow me on Medium. Your support is greatly appreciated.