Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Overconstrained type variable prevents declaring Protocols with shared invariant type #19068

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
butlscot opened this issue May 10, 2025 · 1 comment

Comments

@butlscot
Copy link

From #5775 I learned that

mypy enforces that when the TypeVar can be covariant, it must be covariant

This decision seems to prevent users from declaring Protocols that share an invariant generic type.

For a simple practical example, a depth-first search framework:

from typing import TypeVar, Protocol


NodePassthrough = TypeVar("NodePassthrough")


class Node(Protocol):
    def iter(self) -> list["Node"]:
        """Returns a list of child nodes."""
        ...


class DfsEnterNodeFn(Protocol[NodePassthrough]):
    def __call__(self, node: Node) -> NodePassthrough:
        """Called when entering a node during depth-first traversal."""
        ...


class DfsLeaveNodeFn(Protocol[NodePassthrough]):
    def __call__(self, node: Node, node_passthrough: NodePassthrough) -> None:
        """Called before leaving a node during depth-first traversal."""
        ...


def dfs(node: Node, upon_entering: DfsEnterNodeFn[NodePassthrough], before_leaving: DfsLeaveNodeFn[NodePassthrough]) -> None:
    """Depth-first search of a tree of nodes, calling upon_entering and before_leaving for each node encountered."""
    node_passthrough = upon_entering(node)

    for child in node.iter():
        dfs(child, upon_entering, before_leaving)

    before_leaving(node, node_passthrough)

This results in the following mypy errors:

example.py:12: error:
Invariant type variable "NodePassthrough" used in protocol where covariant one is expected  [misc]
    class DfsEnterNodeFn(Protocol[NodePassthrough]):
    ^

example.py:18: error:
Invariant type variable "NodePassthrough" used in protocol where contravariant one is expected  [misc]
    class DfsLeaveNodeFn(Protocol[NodePassthrough]):
    ^

Is there a workaround for this?

@JelleZijlstra
Copy link
Member

Yes, you can use different type variables.

"Sharing" type variables is a nebulous concept anyway. From the type checker's perspective, every generic class's type variables are completely independent. For this reason, the native generic syntax introduced in Python 3.12 makes it so every type variable is declared separately per generic class or function.

@JelleZijlstra JelleZijlstra closed this as not planned Won't fix, can't repro, duplicate, stale May 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants