Repository navigation
Pygments Formatter.__init__ overloads do not work as intended #7436
Description
Activity
- changed the title
[-]Pygments type stubs are broken[/-][+]Pygments `Formatter.__init__` overloads do not work as intended[/+]on Mar 4, 2022 The problem is most likely that
HtmlFormatteris not explicitly generic and does not override__init__, whichFormatter.__init__()annotatesselfwithFormatter[...]. Maybe that's something PEP 673 can help to fix, but I'm not sure. In the meantime, we should copyFormatter.__init__()toHtmlFormatter.Maybe that's something PEP 673 can help to fix, but I'm not sure.
It probably ultimately needs python/typing#548, right?
In the meantime, we should copy
Formatter.__init__()toHtmlFormatter.I'd be in favor of completely reverting #6819 instead because the following should type check with strict settings:
class Foo(pygments.formatter.Formatter): pass
but currently fails with
Missing type parameters for generic type "Formatter".And doing a dance like
from typing import TYPE_CHECKING if TYPE_CHECKING: _Base = pygments.formatter.Formatter[str] else: _Base = pygments.formatter.Formatter class Foo(_Base): pass
is IMHO completely ridiculous.
I attempted to upstream the generic base class to pygments but they're not interested (pygments/pygments#2081).
So I'd say revert #6819 till there is something like python/typing#956.
Cc. @Dreamsorcerer, who authored #6819
[D]oing a dance like
from typing import TYPE_CHECKING if TYPE_CHECKING: _Base = pygments.formatter.Formatter[str] else: _Base = pygments.formatter.Formatter class Foo(_Base): pass
is IMHO completely ridiculous.
Here's an alternative hack:
from pygments.formatter import Formatter from types import GenericAlias Formatter.__class_getitem__ = classmethod(GenericAlias) # type: ignore[attr-defined] class Foo(Formatter[str]): pass
If you don't care about runtime introspection, you don't even need the extra imports from
types. You can just do:from pygments.formatter import Formatter Formatter.__class_getitem__ = classmethod(lambda cls, val: cls) # type: ignore[attr-defined] class Foo(Formatter[str]): pass
Don't like the
# type: ignore? Just usesetattrinstead ;)from pygments.formatter import Formatter setattr(Formatter, "__class_getitem__", classmethod(lambda cls, val: cls)) class Foo(Formatter[str]): pass
I don't remember the details of the implementation, but this is what it fixed for me:
aio-libs/aiohttp-devtools@3405752Here's an alternative hack
IMHO using stubs from typeshed shouldn't force you to monkey patch the API on runtime just to create a subclass.
Here's an alternative hack
IMHO using stubs from typeshed shouldn't force you to monkey patch the API on runtime just to create a subclass.
Of course not. But this is a known problem that's been around for a while, and there's no easy solution here. Just trying to provide workarounds :)
In #6819
pygments.formatter.Formatterwas made generic to improve the type safety:Apparently this was never tested, since it does not actually work:
The intention of the overloads is that
HtmlFormatter(encoding='utf-8')should be of typeHtmlFormatter[builtins.bytes].I am not sure if this is a mypy bug or if such overload inference should even work in the first place (given that pyright also infers the type variable to be
Unknown: microsoft/pyright#3146).