[Python] Using typing.assert_never() with abstract base classes to ensure full coverage

Discussão em 'Python' iniciado por Stack, Abril 9, 2025.

  1. Stack

    Stack Membro Participativo

    I have an abstract base class ParserNode, with a bunch of concrete subclasses (StartTag, RawText, etc).

    I have a function that takes a ParserNode and does different things based on which subtype it is. I'd like to ensure that mypy will complain if I ever add a new ParserNode subclass without amending this function to handle the new type as well.

    Naively, I'd write this as:

    import typing

    def doTheThing(node: ParserNode) -> None:
    if isinstance(node, StartTag):
    ...
    elif isinstance(node, RawText):
    ...
    else:
    typing.assert_never(node)


    However, this doesn't work - mypy immediately complains that I'm not handling the ParserNode type. But that's an abstract base class, declared with meta=ABCMeta and with an @abstractmethod, so it's impossible for a variable to have that as its concrete type.

    So far, the only workaround I've found for this is to manually declare a union of subtypes and use that, like:

    import typing

    ParserNodeSubs: typing.TypeAlias = "StartTag | RawText"

    def doTheThing(node: ParserNode) -> None:
    node = typing.cast(ParserNodeSubs, node)
    if isinstance(node, StartTag):
    ...
    elif isinstance(node, RawText):
    ...
    else:
    typing.assert_never(node)


    This works as I want it to, but it's a little annoyingly verbose for a situation that I believe shouldn't require it, requiring me to manually maintain the list of ParserNodeSubs (tho only in one spot) and then manually cast variables when I need them. Is there a better way to declare these classes that makes it work automatically?

    Continue reading...

Compartilhe esta Página