Skip to content

Cannot infer type of generic subclass within a function call #2044

@Michael0x2a

Description

@Michael0x2a

Sorry about the bland title -- I think I lack the vocabulary to concisely describe what exactly is going on and to determine if this is a duplicate issue or not. Feel free to change it to something more descriptive.

Here's a somewhat boiled down snippit of code:

from typing import Generic, TypeVar, Set, List, Iterable

T = TypeVar('T')

class Node:
    def __init__(self, children: List['Node']) -> None:
        self.children = children

    def accept(self, visitor: 'Visitor[T]') -> T:
        pass

class NodeChild(Node):
    def accept(self, visitor: 'Visitor[T]') -> T:
        return visitor.visit_node_child(self)

class Visitor(Generic[T]):
    def visit_node_child(self, node: NodeChild) -> T:
        pass

class ExampleVisitor(Visitor[Set[str]]):
    def _visit(self, nodes: List[Node]) -> Set[str]:
        output = set()  # type: Set[str]
        for node in nodes:
            output.update(node.accept(self))  # Error here
        return output

    def visit_node_child(self, node: NodeChild) -> Set[str]:
        return {'a'} | self._visit(node.children)

When I try running this snippit of code, I get the following unexpected error:

test.py: note: In member "_visit" of class "ExampleVisitor":
test.py:24: error: Argument 1 to "accept" of "Node" has incompatible type "ExampleVisitor"; expected Visitor[Iterable[str]]

However, when I try modifying the definition of _visit to look like the following:

    def _visit(self, nodes: List[Node]) -> Set[str]:
        output = set()  # type: Set[str]
        for node in nodes:
            data = node.accept(self)   # Extract this into a separate variable
            output.update(data)
        return output

...mypy behaves as expected and does not report any errors.

I think this bug is also related to the type signature of set.update -- the type signature is update(self, *x: Iterable[_T]) -> None. The same bug still exists if I try swapping out the call to output.update(...) with a dummy function with a signature of fake_update(x: Iterable[T]) -> None, but goes away if I try changing the dummy signature to look like fake_update(x: Set[T]) -> None.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions