Alternative Fix for Incompatible Dunder Method Error in Mypy

Photo by Phil Hearing on Unsplash

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 list and Enum that allowed access to the assigned values via the in statement without adding .value to the end.

>>> enums.Rank.HIGH
<Rank.HIGH: (1, 8)>
>>> enums.Rank.HIGH. Value
(1, 8)
>>> 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 list and 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 sphinx-build is run.

The Sphinx wouldn’t generate an entry for the enums.Rank class.

Missing Rank class in the static HTML page. Image by C.D. Reimer.

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
Rank class with missing source link in the static HTML page. Image by C.D. Reimer.

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 file and got the warning message for the enums.Rank class.

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.

I changed list to 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.

Rank class updated to appear with source link in the static HTML page. Image by C.D. Reimer.

With the side effect gone and everything working, I closed out the bug report.

Please follow me on Medium. Your support is greatly appreciated.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store