From 4150e09338896a33c6bbf0292d114e894f226e52 Mon Sep 17 00:00:00 2001 From: ThePiep Date: Tue, 21 Apr 2026 20:14:26 +0200 Subject: [PATCH 1/2] networkx: Add generic TypeVar for node and edge data --- stubs/networkx/networkx/algorithms/clique.pyi | 11 +- stubs/networkx/networkx/algorithms/dag.pyi | 12 +- .../networkx/algorithms/planarity.pyi | 6 +- .../traversal/breadth_first_search.pyi | 6 +- .../traversal/depth_first_search.pyi | 6 +- stubs/networkx/networkx/classes/digraph.pyi | 18 +-- stubs/networkx/networkx/classes/function.pyi | 12 +- stubs/networkx/networkx/classes/graph.pyi | 63 +++++----- .../networkx/networkx/classes/graphviews.pyi | 50 +++++--- .../networkx/classes/multidigraph.pyi | 18 +-- .../networkx/networkx/classes/multigraph.pyi | 22 ++-- .../networkx/networkx/classes/reportviews.pyi | 108 +++++++++--------- stubs/networkx/networkx/convert.pyi | 8 +- 13 files changed, 190 insertions(+), 150 deletions(-) diff --git a/stubs/networkx/networkx/algorithms/clique.pyi b/stubs/networkx/networkx/algorithms/clique.pyi index e053da521ca9..23ffbb7355f8 100644 --- a/stubs/networkx/networkx/algorithms/clique.pyi +++ b/stubs/networkx/networkx/algorithms/clique.pyi @@ -2,7 +2,7 @@ from _typeshed import Incomplete from collections.abc import Generator, Iterable, Iterator from typing import overload -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _EdgeData, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = [ @@ -23,10 +23,15 @@ def find_cliques(G: Graph[_Node], nodes: Iterable[Incomplete] | None = None) -> @_dispatchable def find_cliques_recursive(G: Graph[_Node], nodes: Iterable[Incomplete] | None = None) -> Iterator[list[_Node]]: ... @_dispatchable -def make_max_clique_graph(G: Graph[_Node], create_using: Graph[_Node] | None = None) -> Graph[_Node]: ... +def make_max_clique_graph( + G: Graph[_Node], create_using: Graph[_Node, _NodeData, _EdgeData] | None = None +) -> Graph[_Node, _NodeData, _EdgeData]: ... @_dispatchable def make_clique_bipartite( - G: Graph[_Node], fpos: bool | None = None, create_using: Graph[_Node] | None = None, name=None + G: Graph[_Node, _NodeData, _EdgeData], + fpos: bool | None = None, + create_using: Graph[_Node, _NodeData, _EdgeData] | None = None, + name=None, ) -> Graph[_Node]: ... @overload def node_clique_number( diff --git a/stubs/networkx/networkx/algorithms/dag.pyi b/stubs/networkx/networkx/algorithms/dag.pyi index cd31778ee3bf..58e1bce57ca6 100644 --- a/stubs/networkx/networkx/algorithms/dag.pyi +++ b/stubs/networkx/networkx/algorithms/dag.pyi @@ -2,7 +2,7 @@ from _typeshed import Incomplete from collections.abc import Callable, Generator, Iterable from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _EdgeData, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = [ @@ -40,11 +40,13 @@ def all_topological_sorts(G: DiGraph[_Node]) -> Generator[list[_Node]]: ... @_dispatchable def is_aperiodic(G: DiGraph[_Node]) -> bool: ... @_dispatchable -def transitive_closure(G: Graph[_Node], reflexive=False) -> Graph[_Node]: ... +def transitive_closure(G: Graph[_Node, _NodeData, _EdgeData], reflexive=False) -> Graph[_Node, _NodeData, _EdgeData]: ... @_dispatchable -def transitive_closure_dag(G: DiGraph[_Node], topo_order: Iterable[Incomplete] | None = None) -> DiGraph[_Node]: ... +def transitive_closure_dag( + G: DiGraph[_Node, _NodeData, _EdgeData], topo_order: Iterable[Incomplete] | None = None +) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @_dispatchable -def transitive_reduction(G: DiGraph[_Node]) -> DiGraph[_Node]: ... +def transitive_reduction(G: DiGraph[_Node, _NodeData, _EdgeData]) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @_dispatchable def antichains(G: DiGraph[_Node], topo_order: Iterable[Incomplete] | None = None) -> Generator[list[_Node]]: ... @_dispatchable @@ -57,4 +59,4 @@ def dag_longest_path( @_dispatchable def dag_longest_path_length(G: DiGraph[_Node], weight: str | None = "weight", default_weight: int | None = 1) -> int: ... @_dispatchable -def dag_to_branching(G: DiGraph[_Node]) -> DiGraph[_Node]: ... +def dag_to_branching(G: DiGraph[_Node, _NodeData, _EdgeData]) -> DiGraph[_Node, _NodeData, _EdgeData]: ... diff --git a/stubs/networkx/networkx/algorithms/planarity.pyi b/stubs/networkx/networkx/algorithms/planarity.pyi index 3eb6000d971a..e731f2d953be 100644 --- a/stubs/networkx/networkx/algorithms/planarity.pyi +++ b/stubs/networkx/networkx/algorithms/planarity.pyi @@ -4,7 +4,7 @@ from decimal import Decimal from typing import NoReturn from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _EdgePlus, _Node +from networkx.classes.graph import Graph, _EdgeData, _EdgePlus, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = ["check_planarity", "is_planar", "PlanarEmbedding"] @@ -14,9 +14,9 @@ def is_planar(G: Graph[_Node]) -> bool: ... @_dispatchable def check_planarity(G: Graph[_Node], counterexample: bool = False): ... @_dispatchable -def get_counterexample(G: Graph[_Node]) -> Graph[_Node]: ... +def get_counterexample(G: Graph[_Node, _NodeData, _EdgeData]) -> Graph[_Node, _NodeData, _EdgeData]: ... @_dispatchable -def get_counterexample_recursive(G: Graph[_Node]) -> Graph[_Node]: ... +def get_counterexample_recursive(G: Graph[_Node, _NodeData, _EdgeData]) -> Graph[_Node, _NodeData, _EdgeData]: ... class Interval: low: Incomplete diff --git a/stubs/networkx/networkx/algorithms/traversal/breadth_first_search.pyi b/stubs/networkx/networkx/algorithms/traversal/breadth_first_search.pyi index 696874b92a17..60d27065db69 100644 --- a/stubs/networkx/networkx/algorithms/traversal/breadth_first_search.pyi +++ b/stubs/networkx/networkx/algorithms/traversal/breadth_first_search.pyi @@ -2,7 +2,7 @@ from collections.abc import Callable, Generator, Iterable, Iterator from typing import Final, Literal from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _EdgeData, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = [ @@ -30,12 +30,12 @@ def bfs_edges( ) -> Generator[tuple[_Node, _Node]]: ... @_dispatchable def bfs_tree( - G: Graph[_Node], + G: Graph[_Node, _NodeData, _EdgeData], source: _Node, reverse: bool | None = False, depth_limit: int | None = None, sort_neighbors: Callable[[Iterator[_Node]], Iterable[_Node]] | None = None, -) -> DiGraph[_Node]: ... +) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @_dispatchable def bfs_predecessors( G: Graph[_Node], diff --git a/stubs/networkx/networkx/algorithms/traversal/depth_first_search.pyi b/stubs/networkx/networkx/algorithms/traversal/depth_first_search.pyi index 5c9341993e02..083e25e1561d 100644 --- a/stubs/networkx/networkx/algorithms/traversal/depth_first_search.pyi +++ b/stubs/networkx/networkx/algorithms/traversal/depth_first_search.pyi @@ -2,7 +2,7 @@ from collections.abc import Callable, Generator, Iterable, Iterator from typing import Literal from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _EdgeData, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = [ @@ -25,12 +25,12 @@ def dfs_edges( ) -> Generator[tuple[_Node, _Node]]: ... @_dispatchable def dfs_tree( - G: Graph[_Node], + G: Graph[_Node, _NodeData, _EdgeData], source: _Node | None = None, depth_limit: int | None = None, *, sort_neighbors: Callable[[Iterator[_Node]], Iterable[_Node]] | None = None, -) -> DiGraph[_Node]: ... +) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @_dispatchable def dfs_predecessors( G: Graph[_Node], diff --git a/stubs/networkx/networkx/classes/digraph.pyi b/stubs/networkx/networkx/classes/digraph.pyi index 281ee1415740..e6a0afb41288 100644 --- a/stubs/networkx/networkx/classes/digraph.pyi +++ b/stubs/networkx/networkx/classes/digraph.pyi @@ -4,7 +4,7 @@ from typing import Any from typing_extensions import Self from networkx.classes.coreviews import AdjacencyView -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _EdgeData, _Node, _NodeData from networkx.classes.reportviews import ( DiDegreeView, InDegreeView, @@ -21,7 +21,7 @@ __all__ = ["DiGraph"] # NOTE: Graph subclasses relationships are so complex # we're only overriding methods that differ in signature from the base classes # to use inheritance to our advantage and reduce complexity -class DiGraph(Graph[_Node]): +class DiGraph(Graph[_Node, _NodeData, _EdgeData]): @cached_property def succ(self) -> AdjacencyView[_Node, _Node, dict[str, Any]]: ... @cached_property @@ -34,19 +34,19 @@ class DiGraph(Graph[_Node]): def predecessors(self, n: _Node) -> Iterator[_Node]: ... @cached_property - def edges(self) -> OutEdgeView[_Node]: ... + def edges(self) -> OutEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def out_edges(self) -> OutEdgeView[_Node]: ... + def out_edges(self) -> OutEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property # Including subtypes' possible return types for LSP - def in_edges(self) -> InEdgeView[_Node] | InMultiEdgeView[_Node]: ... + def in_edges(self) -> InEdgeView[_Node, _NodeData, _EdgeData] | InMultiEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def degree(self) -> DiDegreeView[_Node]: ... + def degree(self) -> DiDegreeView[_Node, _NodeData, _EdgeData]: ... @cached_property # Including subtypes' possible return types for LSP - def in_degree(self) -> InDegreeView[_Node] | InMultiDegreeView[_Node]: ... + def in_degree(self) -> InDegreeView[_Node, _NodeData, _EdgeData] | InMultiDegreeView[_Node, _NodeData, _EdgeData]: ... @cached_property # Including subtypes' possible return types for LSP - def out_degree(self) -> OutDegreeView[_Node] | OutMultiDegreeView[_Node]: ... - def to_undirected(self, reciprocal: bool = False, as_view: bool = False) -> Graph[_Node]: ... # type: ignore[override] # Has an additional `reciprocal` keyword argument + def out_degree(self) -> OutDegreeView[_Node, _NodeData, _EdgeData] | OutMultiDegreeView[_Node, _NodeData, _EdgeData]: ... + def to_undirected(self, reciprocal: bool = False, as_view: bool = False) -> Graph[_Node, _NodeData, _EdgeData]: ... # type: ignore[override] # Has an additional `reciprocal` keyword argument def reverse(self, copy: bool = True) -> Self: ... diff --git a/stubs/networkx/networkx/classes/function.pyi b/stubs/networkx/networkx/classes/function.pyi index ad5818fae47a..0c6422005e53 100644 --- a/stubs/networkx/networkx/classes/function.pyi +++ b/stubs/networkx/networkx/classes/function.pyi @@ -5,7 +5,7 @@ from typing import Literal, TypeVar, overload from networkx import _dispatchable from networkx.algorithms.planarity import PlanarEmbedding from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _NBunch, _Node +from networkx.classes.graph import Graph, _EdgeData, _NBunch, _Node, _NodeData from networkx.classes.multigraph import MultiGraph __all__ = [ @@ -73,7 +73,7 @@ def add_star(G_to_add_to, nodes_for_star, **attr) -> None: ... def add_path(G_to_add_to, nodes_for_path, **attr) -> None: ... def add_cycle(G_to_add_to, nodes_for_cycle, **attr) -> None: ... def subgraph(G: Graph[_Node], nbunch): ... -def induced_subgraph(G: Graph[_Node], nbunch: _NBunch[_Node]) -> Graph[_Node]: ... +def induced_subgraph(G: Graph[_Node, _NodeData, _EdgeData], nbunch: _NBunch[_Node]) -> Graph[_Node, _NodeData, _EdgeData]: ... def edge_subgraph(G: Graph[_Node], edges): ... def restricted_view(G: Graph[_Node], nodes, edges): ... def to_directed(graph): ... @@ -146,8 +146,8 @@ def selfloop_edges( ) -> Generator[tuple[_Node, _Node]]: ... @overload def selfloop_edges( - G: Graph[_Node], data: Literal[True], keys: Literal[False] = False, default=None -) -> Generator[tuple[_Node, _Node, dict[str, Incomplete]]]: ... + G: Graph[_Node, _NodeData, _EdgeData], data: Literal[True], keys: Literal[False] = False, default=None +) -> Generator[tuple[_Node, _Node, _EdgeData]]: ... @overload def selfloop_edges( G: Graph[_Node], data: str, keys: Literal[False] = False, default: _U | None = None @@ -162,8 +162,8 @@ def selfloop_edges( ) -> Generator[tuple[_Node, _Node, int]]: ... @overload def selfloop_edges( - G: Graph[_Node], data: Literal[True], keys: Literal[True], default=None -) -> Generator[tuple[_Node, _Node, int, dict[str, Incomplete]]]: ... + G: Graph[_Node, _NodeData, _EdgeData], data: Literal[True], keys: Literal[True], default=None +) -> Generator[tuple[_Node, _Node, int, _EdgeData]]: ... @overload def selfloop_edges( G: Graph[_Node], data: str, keys: Literal[True], default: _U | None = None diff --git a/stubs/networkx/networkx/classes/graph.pyi b/stubs/networkx/networkx/classes/graph.pyi index 32eab69e42cd..eb3c7d2501c6 100644 --- a/stubs/networkx/networkx/classes/graph.pyi +++ b/stubs/networkx/networkx/classes/graph.pyi @@ -1,7 +1,7 @@ -from collections.abc import Callable, Collection, Hashable, Iterable, Iterator, MutableMapping +from collections.abc import Callable, Collection, Hashable, Iterable, Iterator, Mapping, MutableMapping from decimal import Decimal from functools import cached_property -from typing import Any, ClassVar, TypeVar, overload +from typing import Any, ClassVar, Generic, TypeVar, overload from typing_extensions import Self, TypeAlias import numpy @@ -9,26 +9,31 @@ from networkx.classes.coreviews import AdjacencyView, AtlasView from networkx.classes.digraph import DiGraph from networkx.classes.reportviews import DegreeView, DiDegreeView, EdgeView, NodeView, OutEdgeView +_DataBound: TypeAlias = Mapping[str, Any] + _Node = TypeVar("_Node", bound=Hashable) -_NodeWithData: TypeAlias = tuple[_Node, dict[str, Any]] -_NodePlus: TypeAlias = _Node | _NodeWithData[_Node] +_NodeData = TypeVar("_NodeData", bound=_DataBound, default=dict[str, Any]) +_EdgeData = TypeVar("_EdgeData", bound=_DataBound, default=dict[str, Any]) + +_NodeWithData: TypeAlias = tuple[_Node, _NodeData] +_NodePlus: TypeAlias = _Node | _NodeWithData[_Node, _NodeData] _Edge: TypeAlias = tuple[_Node, _Node] -_EdgeWithData: TypeAlias = tuple[_Node, _Node, dict[str, Any]] -_EdgePlus: TypeAlias = _Edge[_Node] | _EdgeWithData[_Node] +_EdgeWithData: TypeAlias = tuple[_Node, _Node, _EdgeData] +_EdgePlus: TypeAlias = _Edge[_Node] | _EdgeWithData[_Node, _EdgeData] _MapFactory: TypeAlias = Callable[[], MutableMapping[str, Any]] _NBunch: TypeAlias = _Node | Iterable[_Node] | None _Data: TypeAlias = ( - Graph[_Node] - | dict[_Node, dict[_Node, dict[str, Any]]] + Graph[_Node, _NodeData, _EdgeData] + | dict[_Node, dict[_Node, _NodeData]] | dict[_Node, Iterable[_Node]] - | Iterable[_EdgePlus[_Node]] + | Iterable[_EdgePlus[_Node, _EdgeData]] | numpy.ndarray[Any, Any] # | scipy.sparse.base.spmatrix ) __all__ = ["Graph"] -class Graph(Collection[_Node]): +class Graph(Collection[_Node], Generic[_Node, _NodeData, _EdgeData]): __networkx_backend__: ClassVar[str] node_dict_factory: ClassVar[_MapFactory] node_attr_dict_factory: ClassVar[_MapFactory] @@ -40,14 +45,16 @@ class Graph(Collection[_Node]): graph: dict[str, Any] __networkx_cache__: dict[str, Any] - def to_directed_class(self) -> type[DiGraph[_Node]]: ... - def to_undirected_class(self) -> type[Graph[_Node]]: ... + def to_directed_class(self) -> type[DiGraph[_Node, _NodeData, _EdgeData]]: ... + def to_undirected_class(self) -> type[Graph[_Node, _NodeData, _EdgeData]]: ... # @_dispatchable adds `backend` argument, but this decorated is unsupported constructor type here # and __init__() ignores this argument def __new__(cls, *args, backend=None, **kwargs) -> Self: ... - def __init__(self, incoming_graph_data: _Data[_Node] | None = None, **attr: Any) -> None: ... # attr: key=value pairs + def __init__( + self, incoming_graph_data: _Data[_Node, _NodeData, _EdgeData] | None = None, **attr: Any + ) -> None: ... # attr: key=value pairs @cached_property - def adj(self) -> AdjacencyView[_Node, _Node, dict[str, Any]]: ... + def adj(self) -> AdjacencyView[_Node, _Node, _EdgeData]: ... # This object is a read-only dict-like structure @property def name(self) -> str: ... @@ -58,49 +65,53 @@ class Graph(Collection[_Node]): def __len__(self) -> int: ... def __getitem__(self, n: _Node) -> AtlasView[_Node, str, Any]: ... def add_node(self, node_for_adding: _Node, **attr: Any) -> None: ... # attr: Set or change node attributes using key=value - def add_nodes_from(self, nodes_for_adding: Iterable[_NodePlus[_Node]], **attr: Any) -> None: ... # attr: key=value pairs + def add_nodes_from( + self, nodes_for_adding: Iterable[_NodePlus[_Node, _NodeData]], **attr: Any + ) -> None: ... # attr: key=value pairs def remove_node(self, n: _Node) -> None: ... def remove_nodes_from(self, nodes: Iterable[_Node]) -> None: ... @cached_property - def nodes(self) -> NodeView[_Node]: ... + def nodes(self) -> NodeView[_Node, _NodeData, _EdgeData]: ... def number_of_nodes(self) -> int: ... def order(self) -> int: ... def has_node(self, n: _Node) -> bool: ... # Including subtypes' possible return types for LSP def add_edge(self, u_of_edge: _Node, v_of_edge: _Node, **attr: Any) -> Hashable | None: ... # attr: Edge data (or labels or objects) can be assigned using keyword arguments - def add_edges_from(self, ebunch_to_add: Iterable[_EdgePlus[_Node]], **attr: Any) -> None: ... + def add_edges_from(self, ebunch_to_add: Iterable[_EdgePlus[_Node, _EdgeData]], **attr: Any) -> None: ... # attr: Edge data (or labels or objects) can be assigned using keyword arguments def add_weighted_edges_from( self, ebunch_to_add: Iterable[tuple[_Node, _Node, float | Decimal | None]], weight: str = "weight", **attr: Any ) -> None: ... # attr: Edge attributes to add/update for all edges. def remove_edge(self, u: _Node, v: _Node) -> None: ... - def remove_edges_from(self, ebunch: Iterable[_EdgePlus[_Node]]) -> None: ... + def remove_edges_from(self, ebunch: Iterable[_EdgePlus[_Node, _EdgeData]]) -> None: ... @overload - def update(self, edges: Graph[_Node], nodes: None = None) -> None: ... + def update(self, edges: Graph[_Node, _NodeData, _EdgeData], nodes: None = None) -> None: ... @overload def update( - self, edges: Graph[_Node] | Iterable[_EdgePlus[_Node]] | None = None, nodes: Iterable[_Node] | None = None + self, + edges: Graph[_Node, _NodeData, _EdgeData] | Iterable[_EdgePlus[_Node, _EdgeData]] | None = None, + nodes: Iterable[_Node] | None = None, ) -> None: ... def has_edge(self, u: _Node, v: _Node) -> bool: ... def neighbors(self, n: _Node) -> Iterator[_Node]: ... @cached_property # Including subtypes' possible return types for LSP - def edges(self) -> EdgeView[_Node] | OutEdgeView[_Node]: ... - def get_edge_data(self, u: _Node, v: _Node, default: Any = None) -> dict[str, Any]: ... + def edges(self) -> EdgeView[_Node, _NodeData, _EdgeData] | OutEdgeView[_Node, _NodeData, _EdgeData]: ... + def get_edge_data(self, u: _Node, v: _Node, default: Any = None) -> _EdgeData: ... # default: any Python object - def adjacency(self) -> Iterator[tuple[_Node, dict[_Node, dict[str, Any]]]]: ... + def adjacency(self) -> Iterator[tuple[_Node, dict[_Node, _EdgeData]]]: ... @cached_property # Including subtypes' possible return types for LSP - def degree(self) -> DegreeView[_Node] | DiDegreeView[_Node]: ... + def degree(self) -> DegreeView[_Node, _NodeData, _EdgeData] | DiDegreeView[_Node, _NodeData, _EdgeData]: ... def clear(self) -> None: ... def clear_edges(self) -> None: ... def is_multigraph(self) -> bool: ... def is_directed(self) -> bool: ... def copy(self, as_view: bool = False) -> Self: ... - def to_directed(self, as_view: bool = False) -> DiGraph[_Node]: ... - def to_undirected(self, as_view: bool = False) -> Graph[_Node]: ... + def to_directed(self, as_view: bool = False) -> DiGraph[_Node, _NodeData, _EdgeData]: ... + def to_undirected(self, as_view: bool = False) -> Graph[_Node, _NodeData, _EdgeData]: ... def subgraph(self, nodes: _NBunch[_Node]) -> Self: ... def edge_subgraph(self, edges: Iterable[_Edge[_Node]]) -> Self: ... @overload diff --git a/stubs/networkx/networkx/classes/graphviews.pyi b/stubs/networkx/networkx/classes/graphviews.pyi index 460e2df058c9..a1b228711d9e 100644 --- a/stubs/networkx/networkx/classes/graphviews.pyi +++ b/stubs/networkx/networkx/classes/graphviews.pyi @@ -2,39 +2,59 @@ from collections.abc import Callable, Hashable from typing import TypeVar, overload from networkx.classes.digraph import DiGraph -from networkx.classes.graph import Graph, _Node +from networkx.classes.graph import Graph, _DataBound, _EdgeData, _Node, _NodeData from networkx.classes.multidigraph import MultiDiGraph from networkx.classes.multigraph import MultiGraph -_G = TypeVar("_G", bound=Graph[Hashable]) -_D = TypeVar("_D", bound=DiGraph[Hashable]) +_G = TypeVar("_G", bound=Graph[Hashable, _DataBound, _DataBound]) +_D = TypeVar("_D", bound=DiGraph[Hashable, _DataBound, _DataBound]) __all__ = ["generic_graph_view", "subgraph_view", "reverse_view"] @overload def generic_graph_view(G: _G, create_using: None = None) -> _G: ... @overload -def generic_graph_view(G: Graph[_Node], create_using: type[MultiDiGraph[_Node]]) -> MultiDiGraph[_Node]: ... +def generic_graph_view( + G: Graph[_Node, _NodeData, _EdgeData], create_using: type[MultiDiGraph[_Node, _NodeData, _EdgeData]] +) -> MultiDiGraph[_Node, _NodeData, _EdgeData]: ... @overload -def generic_graph_view(G: Graph[_Node], create_using: type[DiGraph[_Node]]) -> DiGraph[_Node]: ... +def generic_graph_view( + G: Graph[_Node, _NodeData, _EdgeData], create_using: type[DiGraph[_Node, _NodeData, _EdgeData]] +) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @overload -def generic_graph_view(G: Graph[_Node], create_using: type[MultiGraph[_Node]]) -> MultiGraph[_Node]: ... +def generic_graph_view( + G: Graph[_Node, _NodeData, _EdgeData], create_using: type[MultiGraph[_Node, _NodeData, _EdgeData]] +) -> MultiGraph[_Node, _NodeData, _EdgeData]: ... @overload -def generic_graph_view(G: Graph[_Node], create_using: type[Graph[_Node]]) -> Graph[_Node]: ... +def generic_graph_view( + G: Graph[_Node, _NodeData, _EdgeData], create_using: type[Graph[_Node, _NodeData, _EdgeData]] +) -> Graph[_Node, _NodeData, _EdgeData]: ... @overload def subgraph_view( - G: MultiDiGraph[_Node], *, filter_node: Callable[[_Node], bool] = ..., filter_edge: Callable[[_Node, _Node, int], bool] = ... -) -> MultiDiGraph[_Node]: ... + G: MultiDiGraph[_Node, _NodeData, _EdgeData], + *, + filter_node: Callable[[_Node], bool] = ..., + filter_edge: Callable[[_Node, _Node, int], bool] = ..., +) -> MultiDiGraph[_Node, _NodeData, _EdgeData]: ... @overload def subgraph_view( - G: MultiGraph[_Node], *, filter_node: Callable[[_Node], bool] = ..., filter_edge: Callable[[_Node, _Node, int], bool] = ... -) -> MultiGraph[_Node]: ... + G: MultiGraph[_Node, _NodeData, _EdgeData], + *, + filter_node: Callable[[_Node], bool] = ..., + filter_edge: Callable[[_Node, _Node, int], bool] = ..., +) -> MultiGraph[_Node, _NodeData, _EdgeData]: ... @overload def subgraph_view( - G: DiGraph[_Node], *, filter_node: Callable[[_Node], bool] = ..., filter_edge: Callable[[_Node, _Node], bool] = ... -) -> DiGraph[_Node]: ... + G: DiGraph[_Node, _NodeData, _EdgeData], + *, + filter_node: Callable[[_Node], bool] = ..., + filter_edge: Callable[[_Node, _Node], bool] = ..., +) -> DiGraph[_Node, _NodeData, _EdgeData]: ... @overload def subgraph_view( - G: Graph[_Node], *, filter_node: Callable[[_Node], bool] = ..., filter_edge: Callable[[_Node, _Node], bool] = ... -) -> Graph[_Node]: ... + G: Graph[_Node, _NodeData, _EdgeData], + *, + filter_node: Callable[[_Node], bool] = ..., + filter_edge: Callable[[_Node, _Node], bool] = ..., +) -> Graph[_Node, _NodeData, _EdgeData]: ... def reverse_view(G: _D) -> _D: ... diff --git a/stubs/networkx/networkx/classes/multidigraph.pyi b/stubs/networkx/networkx/classes/multidigraph.pyi index 190d5c645c54..324f033ac0e6 100644 --- a/stubs/networkx/networkx/classes/multidigraph.pyi +++ b/stubs/networkx/networkx/classes/multidigraph.pyi @@ -3,7 +3,7 @@ from typing import Any from networkx.classes.coreviews import MultiAdjacencyView from networkx.classes.digraph import DiGraph -from networkx.classes.graph import _Node +from networkx.classes.graph import _EdgeData, _Node, _NodeData from networkx.classes.multigraph import MultiGraph from networkx.classes.reportviews import ( DiMultiDegreeView, @@ -18,21 +18,21 @@ __all__ = ["MultiDiGraph"] # NOTE: Graph subclasses relationships are so complex # we're only overriding methods that differ in signature from the base classes # to use inheritance to our advantage and reduce complexity -class MultiDiGraph(MultiGraph[_Node], DiGraph[_Node]): +class MultiDiGraph(MultiGraph[_Node, _NodeData, _EdgeData], DiGraph[_Node, _NodeData, _EdgeData]): @cached_property def succ(self) -> MultiAdjacencyView[_Node, _Node, dict[str, Any]]: ... @cached_property def pred(self) -> MultiAdjacencyView[_Node, _Node, dict[str, Any]]: ... @cached_property - def edges(self) -> OutMultiEdgeView[_Node]: ... + def edges(self) -> OutMultiEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def out_edges(self) -> OutMultiEdgeView[_Node]: ... + def out_edges(self) -> OutMultiEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def in_edges(self) -> InMultiEdgeView[_Node]: ... + def in_edges(self) -> InMultiEdgeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def degree(self) -> DiMultiDegreeView[_Node]: ... + def degree(self) -> DiMultiDegreeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def in_degree(self) -> InMultiDegreeView[_Node]: ... + def in_degree(self) -> InMultiDegreeView[_Node, _NodeData, _EdgeData]: ... @cached_property - def out_degree(self) -> OutMultiDegreeView[_Node]: ... - def to_undirected(self, reciprocal: bool = False, as_view: bool = False) -> MultiGraph[_Node]: ... # type: ignore[override] # Has an additional `reciprocal` keyword argument + def out_degree(self) -> OutMultiDegreeView[_Node, _NodeData, _EdgeData]: ... + def to_undirected(self, reciprocal: bool = False, as_view: bool = False) -> MultiGraph[_Node, _NodeData, _EdgeData]: ... # type: ignore[override] # Has an additional `reciprocal` keyword argument diff --git a/stubs/networkx/networkx/classes/multigraph.pyi b/stubs/networkx/networkx/classes/multigraph.pyi index 41f35d269050..3e1ef3823509 100644 --- a/stubs/networkx/networkx/classes/multigraph.pyi +++ b/stubs/networkx/networkx/classes/multigraph.pyi @@ -4,7 +4,7 @@ from typing import Any, ClassVar, overload from typing_extensions import Self, TypeAlias, TypeVar from networkx.classes.coreviews import MultiAdjacencyView -from networkx.classes.graph import Graph, _MapFactory, _Node +from networkx.classes.graph import Graph, _EdgeData, _MapFactory, _Node, _NodeData from networkx.classes.multidigraph import MultiDiGraph from networkx.classes.reportviews import DiMultiDegreeView, MultiDegreeView, MultiEdgeView, OutMultiEdgeView @@ -18,10 +18,10 @@ __all__ = ["MultiGraph"] # NOTE: Graph subclasses relationships are so complex # we're only overriding methods that differ in signature from the base classes # to use inheritance to our advantage and reduce complexity -class MultiGraph(Graph[_Node]): +class MultiGraph(Graph[_Node, _NodeData, _EdgeData]): edge_key_dict_factory: ClassVar[_MapFactory] - def to_directed_class(self) -> type[MultiDiGraph[_Node]]: ... - def to_undirected_class(self) -> type[MultiGraph[_Node]]: ... + def to_directed_class(self) -> type[MultiDiGraph[_Node, _NodeData, _EdgeData]]: ... + def to_undirected_class(self) -> type[MultiGraph[_Node, _NodeData, _EdgeData]]: ... # @_dispatchable adds `backend` argument, but this decorated is unsupported constructor type here # and __init__() ignores this argument def __new__(cls, *args, backend=None, **kwargs) -> Self: ... @@ -38,23 +38,21 @@ class MultiGraph(Graph[_Node]): def has_edge(self, u: _Node, v: _Node, key: Hashable | None = None) -> bool: ... @cached_property # Including subtypes' possible return types for LSP - def edges(self) -> MultiEdgeView[_Node] | OutMultiEdgeView[_Node]: ... + def edges(self) -> MultiEdgeView[_Node, _NodeData, _EdgeData] | OutMultiEdgeView[_Node, _NodeData, _EdgeData]: ... # key : hashable identifier, optional (default=None). # default : any Python object (default=None). Value to return if the specific edge (u, v, key) is not found. # Returns: The edge attribute dictionary. @overload # type: ignore[override] - def get_edge_data( - self, u: _Node, v: _Node, key: Hashable, default: _DefaultT | None = None - ) -> dict[str, Any] | _DefaultT: ... + def get_edge_data(self, u: _Node, v: _Node, key: Hashable, default: _DefaultT | None = None) -> _EdgeData | _DefaultT: ... # default : any Python object (default=None). Value to return if there are no edges between u and v and no key is specified. # Returns: A dictionary mapping edge keys to attribute dictionaries for each of those edges if no specific key is provided. @overload def get_edge_data( self, u: _Node, v: _Node, key: None = None, default: _DefaultT | None = None - ) -> dict[Hashable, dict[str, Any] | _DefaultT]: ... + ) -> dict[Hashable, _EdgeData | _DefaultT]: ... def copy(self, as_view: bool = False) -> Self: ... @cached_property # Including subtypes' possible return types for LSP - def degree(self) -> MultiDegreeView[_Node] | DiMultiDegreeView[_Node]: ... - def to_directed(self, as_view: bool = False) -> MultiDiGraph[_Node]: ... - def to_undirected(self, as_view: bool = False) -> MultiGraph[_Node]: ... + def degree(self) -> MultiDegreeView[_Node, _NodeData, _EdgeData] | DiMultiDegreeView[_Node, _NodeData, _EdgeData]: ... + def to_directed(self, as_view: bool = False) -> MultiDiGraph[_Node, _NodeData, _EdgeData]: ... + def to_undirected(self, as_view: bool = False) -> MultiGraph[_Node, _NodeData, _EdgeData]: ... diff --git a/stubs/networkx/networkx/classes/reportviews.pyi b/stubs/networkx/networkx/classes/reportviews.pyi index b141f395feac..dba4d056ffd2 100644 --- a/stubs/networkx/networkx/classes/reportviews.pyi +++ b/stubs/networkx/networkx/classes/reportviews.pyi @@ -1,10 +1,10 @@ from _typeshed import Incomplete, Unused from abc import ABC from collections.abc import Iterable, Iterator, Mapping, Set as AbstractSet -from typing import Any, Generic, Literal, TypeVar, overload +from typing import Generic, Literal, TypeVar, overload from typing_extensions import Self -from networkx.classes.graph import Graph, _Edge, _NBunch, _Node +from networkx.classes.graph import Graph, _Edge, _EdgeData, _NBunch, _Node, _NodeData _D = TypeVar("_D") _U = TypeVar("_U") @@ -34,32 +34,34 @@ __all__ = [ "OutMultiDegreeView", ] -class NodeView(Mapping[_Node, dict[str, Any]], AbstractSet[_Node]): +class NodeView(Mapping[_Node, _NodeData], AbstractSet[_Node], Generic[_Node, _NodeData, _EdgeData]): __slots__ = ("_nodes",) - def __init__(self, graph: Graph[_Node]) -> None: ... + def __init__(self, graph: Graph[_Node, _NodeData, _EdgeData]) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_Node]: ... - def __getitem__(self, n: _Node) -> dict[str, Any]: ... + def __getitem__(self, n: _Node) -> _NodeData: ... def __contains__(self, n: object) -> bool: ... @overload def __call__(self, data: Literal[False] = False, default=None) -> Self: ... @overload - def __call__(self, data: Literal[True] | str, default=None) -> NodeDataView[_Node]: ... + def __call__(self, data: Literal[True] | str, default=None) -> NodeDataView[_Node, _NodeData, _EdgeData]: ... @overload def data(self, data: Literal[False], default=None) -> Self: ... @overload - def data(self, data: Literal[True] | str = True, default=None) -> NodeDataView[_Node]: ... + def data(self, data: Literal[True] | str = True, default=None) -> NodeDataView[_Node, _NodeData, _EdgeData]: ... -class NodeDataView(AbstractSet[_Node]): +class NodeDataView(AbstractSet[_Node], Generic[_Node, _NodeData, _EdgeData]): __slots__ = ("_nodes", "_data", "_default") - def __init__(self, nodedict: Mapping[str, Incomplete], data: bool | str = False, default=None) -> None: ... + def __init__(self, nodedict: _NodeData, data: bool | str = False, default=None) -> None: ... def __len__(self) -> int: ... - def __iter__(self) -> Iterator[tuple[_Node, Incomplete]]: ... # type: ignore[override] + def __iter__(self) -> Iterator[tuple[_Node, _NodeData]]: ... # type: ignore[override] def __contains__(self, n: object) -> bool: ... - def __getitem__(self, n: _Node): ... + def __getitem__(self, n: _Node) -> _NodeData | Incomplete: ... -class DiDegreeView(Generic[_Node]): - def __init__(self, G: Graph[_Node], nbunch: _NBunch[_Node] = None, weight: None | bool | str = None) -> None: ... +class DiDegreeView(Generic[_Node, _NodeData, _EdgeData]): + def __init__( + self, G: Graph[_Node, _NodeData, _EdgeData], nbunch: _NBunch[_Node] = None, weight: None | bool | str = None + ) -> None: ... @overload # Use this overload first in case _Node=str, since `str` matches `Iterable[str]` def __call__(self, nbunch: _Node, weight: None | bool | str = None) -> int: ... # type: ignore[overload-overlap] @overload @@ -68,13 +70,13 @@ class DiDegreeView(Generic[_Node]): def __iter__(self) -> Iterator[tuple[_Node, int]]: ... def __len__(self) -> int: ... -class DegreeView(DiDegreeView[_Node]): ... -class OutDegreeView(DiDegreeView[_Node]): ... -class InDegreeView(DiDegreeView[_Node]): ... -class MultiDegreeView(DiDegreeView[_Node]): ... -class DiMultiDegreeView(DiDegreeView[_Node]): ... -class InMultiDegreeView(DiDegreeView[_Node]): ... -class OutMultiDegreeView(DiDegreeView[_Node]): ... +class DegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class OutDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class InDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class MultiDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class DiMultiDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class InMultiDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... +class OutMultiDegreeView(DiDegreeView[_Node, _NodeData, _EdgeData]): ... class EdgeViewABC(ABC): ... class OutEdgeDataView(EdgeViewABC, Generic[_Node, _D]): @@ -103,13 +105,13 @@ class MultiEdgeDataView(OutEdgeDataView[_Node, _D]): class InMultiEdgeDataView(OutEdgeDataView[_Node, _D]): __slots__ = () -class OutEdgeView(AbstractSet[Incomplete], Mapping[Incomplete, Incomplete], EdgeViewABC, Generic[_Node]): +class OutEdgeView(AbstractSet[Incomplete], Mapping[Incomplete, Incomplete], EdgeViewABC, Generic[_Node, _NodeData, _EdgeData]): __slots__ = ("_adjdict", "_graph", "_nodes_nbrs") - def __init__(self, G: Graph[_Node]) -> None: ... + def __init__(self, G: Graph[_Node, _NodeData, _EdgeData]) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[tuple[_Node, _Node]]: ... def __contains__(self, e: _Edge[_Node]) -> bool: ... # type: ignore[override] - def __getitem__(self, e: _Edge[_Node]) -> dict[str, Any]: ... + def __getitem__(self, e: _Edge[_Node]) -> _EdgeData: ... dataview = OutEdgeDataView @overload def __call__(self, nbunch: None = None, data: Literal[False] = False, *, default: Unused = None) -> Self: ... # type: ignore[overload-overlap] @@ -120,11 +122,11 @@ class OutEdgeView(AbstractSet[Incomplete], Mapping[Incomplete, Incomplete], Edge @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None - ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None - ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None @@ -138,13 +140,13 @@ class OutEdgeView(AbstractSet[Incomplete], Mapping[Incomplete, Incomplete], Edge @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None - ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None ) -> OutEdgeDataView[_Node, tuple[_Node, _Node, _U]]: ... -class EdgeView(OutEdgeView[_Node]): +class EdgeView(OutEdgeView[_Node, _NodeData, _EdgeData]): __slots__ = () dataview = EdgeDataView # Have to override parent's overloads with the proper return type based on dataview @@ -157,11 +159,11 @@ class EdgeView(OutEdgeView[_Node]): @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None - ) -> EdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> EdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None - ) -> EdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> EdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None @@ -175,13 +177,13 @@ class EdgeView(OutEdgeView[_Node]): @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None - ) -> EdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> EdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None ) -> EdgeDataView[_Node, tuple[_Node, _Node, _U]]: ... -class InEdgeView(OutEdgeView[_Node]): +class InEdgeView(OutEdgeView[_Node, _NodeData, _EdgeData]): __slots__ = () dataview = InEdgeDataView # Have to override parent's overloads with the proper return type based on dataview @@ -194,11 +196,11 @@ class InEdgeView(OutEdgeView[_Node]): @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None - ) -> InEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> InEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None - ) -> InEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> InEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None @@ -212,16 +214,16 @@ class InEdgeView(OutEdgeView[_Node]): @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None - ) -> InEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> InEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None ) -> InEdgeDataView[_Node, tuple[_Node, _Node, _U]]: ... -class OutMultiEdgeView(OutEdgeView[_Node]): +class OutMultiEdgeView(OutEdgeView[_Node, _NodeData, _EdgeData]): __slots__ = () def __iter__(self) -> Iterator[tuple[_Node, _Node, Incomplete]]: ... # type: ignore[override] - def __getitem__(self, e: tuple[_Node, _Node, Incomplete]) -> dict[str, Any]: ... # type: ignore[override] + def __getitem__(self, e: tuple[_Node, _Node, Incomplete]) -> _EdgeData: ... # type: ignore[override] dataview = OutMultiEdgeDataView @overload # type: ignore[override] # Has an additional `keys` keyword argument def __call__( # type: ignore[overload-overlap] @@ -238,15 +240,15 @@ class OutMultiEdgeView(OutEdgeView[_Node]): @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[True] - ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None, keys: Literal[True] - ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[False] = False - ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None, keys: Literal[False] = False @@ -268,11 +270,11 @@ class OutMultiEdgeView(OutEdgeView[_Node]): @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False - ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, *, keys: Literal[True] - ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False @@ -282,7 +284,7 @@ class OutMultiEdgeView(OutEdgeView[_Node]): self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None, *, keys: Literal[True] ) -> OutMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _U]]: ... -class MultiEdgeView(OutMultiEdgeView[_Node]): +class MultiEdgeView(OutMultiEdgeView[_Node, _NodeData, _EdgeData]): __slots__ = () dataview = MultiEdgeDataView # type: ignore[assignment] # Have to override parent's overloads with the proper return type based on dataview @@ -301,15 +303,15 @@ class MultiEdgeView(OutMultiEdgeView[_Node]): @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[True] - ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None, keys: Literal[True] - ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[False] = False - ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None, keys: Literal[False] = False @@ -331,11 +333,11 @@ class MultiEdgeView(OutMultiEdgeView[_Node]): @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False - ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, *, keys: Literal[True] - ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False @@ -345,7 +347,7 @@ class MultiEdgeView(OutMultiEdgeView[_Node]): self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None, *, keys: Literal[True] ) -> MultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _U]]: ... -class InMultiEdgeView(OutMultiEdgeView[_Node]): +class InMultiEdgeView(OutMultiEdgeView[_Node, _NodeData, _EdgeData]): __slots__ = () dataview = InMultiEdgeDataView # type: ignore[assignment] # Have to override parent's overloads with the proper return type based on dataview @@ -364,15 +366,15 @@ class InMultiEdgeView(OutMultiEdgeView[_Node]): @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[True] - ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: Literal[True], *, default: None = None, keys: Literal[True] - ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node] = None, *, data: Literal[True], default: None = None, keys: Literal[False] = False - ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def __call__( self, nbunch: _NBunch[_Node], data: str, *, default: _U | None = None, keys: Literal[False] = False @@ -394,11 +396,11 @@ class InMultiEdgeView(OutMultiEdgeView[_Node]): @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False - ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, dict[str, Incomplete]]]: ... + ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, _EdgeData]]: ... @overload def data( self, data: Literal[True] = True, default: None = None, nbunch: _NBunch[_Node] = None, *, keys: Literal[True] - ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, dict[str, Incomplete]]]: ... + ) -> InMultiEdgeDataView[_Node, tuple[_Node, _Node, Incomplete, _EdgeData]]: ... @overload def data( self, data: str, default: _U | None = None, nbunch: _NBunch[_Node] = None, keys: Literal[False] = False diff --git a/stubs/networkx/networkx/convert.pyi b/stubs/networkx/networkx/convert.pyi index 0f25eabffa87..0d4ddfa929a5 100644 --- a/stubs/networkx/networkx/convert.pyi +++ b/stubs/networkx/networkx/convert.pyi @@ -1,7 +1,7 @@ from _typeshed import Incomplete from collections.abc import Callable, Collection, Iterable -from networkx.classes.graph import Graph, _Data, _Node +from networkx.classes.graph import Graph, _Data, _EdgeData, _Node, _NodeData from networkx.utils.backends import _dispatchable __all__ = [ @@ -15,8 +15,10 @@ __all__ = [ ] def to_networkx_graph( - data: _Data[_Node], create_using: Graph[_Node] | Callable[[], Graph[_Node]] | None = None, multigraph_input: bool = False -) -> Graph[_Node]: ... + data: _Data[_Node], + create_using: Graph[_Node, _NodeData, _EdgeData] | Callable[[], Graph[_Node, _NodeData, _EdgeData]] | None = None, + multigraph_input: bool = False, +) -> Graph[_Node, _NodeData, _EdgeData]: ... @_dispatchable def to_dict_of_lists(G: Graph[_Node], nodelist: Collection[_Node] | None = None) -> dict[_Node, list[_Node]]: ... @_dispatchable From 4128cb9f2d933f477b4fc182fa8e3e996b1b5179 Mon Sep 17 00:00:00 2001 From: ThePiep Date: Tue, 21 Apr 2026 20:49:11 +0200 Subject: [PATCH 2/2] Minor fix in multigraph --- stubs/networkx/networkx/classes/multigraph.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/networkx/networkx/classes/multigraph.pyi b/stubs/networkx/networkx/classes/multigraph.pyi index 3e1ef3823509..dcccac0c6d69 100644 --- a/stubs/networkx/networkx/classes/multigraph.pyi +++ b/stubs/networkx/networkx/classes/multigraph.pyi @@ -27,7 +27,7 @@ class MultiGraph(Graph[_Node, _NodeData, _EdgeData]): def __new__(cls, *args, backend=None, **kwargs) -> Self: ... def __init__(self, incoming_graph_data=None, multigraph_input: bool | None = None, **attr: Any) -> None: ... @cached_property - def adj(self) -> MultiAdjacencyView[_Node, _Node, dict[str, Any]]: ... # data can be any type + def adj(self) -> MultiAdjacencyView[_Node, _Node, _EdgeData]: ... # data can be any type def new_edge_key(self, u: _Node, v: _Node) -> int: ... # key : hashable identifier, optional (default=lowest unused integer) @overload # type: ignore[override] # More complex overload