-
Notifications
You must be signed in to change notification settings - Fork 147
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
Add annotation stubs for PyGraph
and PyDiGraph
#401
Changes from 95 commits
be25f8b
1292561
cd98bda
7cc697d
12b39e2
6fa0cf9
aa3bcd9
e799e0f
e6cec75
ec48656
3691bf4
78d458b
fe73243
530b26d
48b536e
05b2cf2
f6e37a9
0c3e735
87d0834
1b3b747
bb1cf8f
e460c11
acc7e4b
81aec08
ff4139e
e31ea49
f36255f
69fd764
49eb24f
8777705
b448d34
e986c53
7ce2b9b
e1feaf2
837efb2
0ceac47
7e2f883
827a014
047daba
68d3310
44a59de
f8f9341
baa61e8
546f6ee
54b5c93
ac6825a
00c8852
fa34487
4e64fa5
93685fa
9803970
c208f2d
7190bf5
b35bb02
16df41c
6506370
28ce1cc
7d964af
e025617
a83cce7
59a9f90
c3dad55
ed8ea1c
63c87a6
ed0c053
54a795c
8f22bb7
342d2f5
937d049
23c9a6d
d0cb298
fc16d41
b34afe4
2bb0ac3
42f77ac
ff58a0c
0cab9fe
a7aaf48
20777a6
e1a8e19
e1798e5
f6bc49f
0cd9c9f
dc0d9b3
ad83bab
2da1178
f91ab74
05c7d60
37d3c23
5e9c86e
c24f8f8
a441d39
265fbe9
c16ae22
9a21cae
119cdb3
fb27883
968f9d3
5ed884b
4eab027
91a1f27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
features: | ||
- | | ||
Added type annotations to :class:`~retworkx.PyDiGraph` and | ||
:class:`~retworkx.PyGraph`. They can now be statically type checked | ||
with `mypy <http://mypy-lang.org>`__. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
# This file contains only type annotations for PyO3 functions and classes | ||
# For implementation details, see __init__.py and src/lib.rs | ||
|
||
from .rustworkx import * | ||
from typing import Generic, TypeVar | ||
|
||
S = TypeVar("S") | ||
T = TypeVar("T") | ||
|
||
class PyDAG(Generic[S, T], PyDiGraph[S, T]): ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
# This file contains only type annotations for PyO3 functions and classes | ||
# For implementation details, see __init__.py and src/digraph.rs | ||
|
||
import numpy as np | ||
from .iterators import * | ||
from .graph import PyGraph | ||
|
||
from typing import Any, Callable, Dict, Generic, TypeVar, Optional, List, Tuple, Sequence | ||
|
||
__all__ = ["PyDiGraph"] | ||
|
||
S = TypeVar("S") | ||
T = TypeVar("T") | ||
|
||
class PyDiGraph(Generic[S, T]): | ||
check_cycle: bool = ... | ||
multigraph: bool = ... | ||
def __init__( | ||
self, | ||
/, | ||
check_cycle: bool = ..., | ||
multigraph: bool = ..., | ||
) -> None: ... | ||
def add_child(self, parent: int, obj: S, edge: T, /) -> int: ... | ||
def add_edge(self, parent: int, child: int, edge: T, /) -> int: ... | ||
def add_edges_from( | ||
self, | ||
obj_list: Sequence[Tuple[int, int, T]], | ||
/, | ||
) -> List[int]: ... | ||
def add_edges_from_no_data( | ||
self: PyDiGraph[S, Optional[T]], obj_list: Sequence[Tuple[int, int]], / | ||
) -> List[int]: ... | ||
def add_node(self, obj: S, /) -> int: ... | ||
def add_nodes_from(self, obj_list: Sequence[S], /) -> NodeIndices: ... | ||
def add_parent(self, child: int, obj: S, edge: T, /) -> int: ... | ||
def adj(self, node: int, /) -> Dict[int, T]: ... | ||
def adj_direction(self, node: int, direction: bool, /) -> Dict[int, T]: ... | ||
def compose( | ||
self, | ||
other: PyDiGraph[S, T], | ||
node_map: Dict[int, Tuple[int, T]], | ||
/, | ||
node_map_func: Optional[Callable[[S], int]] = ..., | ||
edge_map_func: Optional[Callable[[T], int]] = ..., | ||
) -> Dict[int, int]: ... | ||
def copy(self) -> PyDiGraph[S, T]: ... | ||
def edge_index_map(self) -> EdgeIndexMap[T]: ... | ||
def edge_indices(self) -> EdgeIndices: ... | ||
def edge_list(self) -> EdgeList: ... | ||
def edges(self) -> List[T]: ... | ||
def extend_from_edge_list( | ||
self: PyDiGraph[Optional[S], Optional[T]], edge_list: Sequence[Tuple[int, int]], / | ||
) -> None: ... | ||
def extend_from_weighted_edge_list( | ||
self: PyDiGraph[Optional[S], T], | ||
edge_list: Sequence[Tuple[int, int, T]], | ||
/, | ||
) -> None: ... | ||
def find_adjacent_node_by_edge(self, node: int, predicate: Callable[[T], bool], /) -> S: ... | ||
def find_node_by_weight( | ||
self, | ||
obj: Callable[[S], bool], | ||
/, | ||
) -> Optional[int]: ... | ||
def find_predecessors_by_edge( | ||
self, node: int, filter_fn: Callable[[T], bool], / | ||
) -> List[S]: ... | ||
def find_successors_by_edge(self, node: int, filter_fn: Callable[[T], bool], /) -> List[S]: ... | ||
@staticmethod | ||
def from_adjacency_matrix( | ||
matrix: np.ndarray, /, null_value: float = ... | ||
) -> PyDiGraph[int, float]: ... | ||
Comment on lines
+79
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Numpy technically provide stronger type hints now, and |
||
@staticmethod | ||
def from_complex_adjacency_matrix( | ||
matrix: np.ndarray, /, null_value: complex = ... | ||
) -> PyDiGraph[int, complex]: ... | ||
def get_all_edge_data(self, node_a: int, node_b: int, /) -> List[T]: ... | ||
def get_edge_data(self, node_a: int, node_b: int, /) -> T: ... | ||
def get_node_data(self, node: int, /) -> S: ... | ||
def has_edge(self, node_a: int, node_b: int, /) -> bool: ... | ||
def in_degree(self, node: int, /) -> int: ... | ||
def in_edges(self, node: int, /) -> WeightedEdgeList[T]: ... | ||
def insert_node_on_in_edges(self, node: int, ref_node: int, /) -> None: ... | ||
def insert_node_on_in_edges_multiple(self, node: int, ref_nodes: Sequence[int], /) -> None: ... | ||
def insert_node_on_out_edges(self, node: int, ref_node: int, /) -> None: ... | ||
def insert_node_on_out_edges_multiple(self, node: int, ref_nodes: Sequence[int], /) -> None: ... | ||
def is_symmetric(self) -> bool: ... | ||
def merge_nodes(self, u: int, v: int, /) -> None: ... | ||
def neighbors(self, node: int, /) -> NodeIndices: ... | ||
def node_indexes(self) -> NodeIndices: ... | ||
def nodes(self) -> List[S]: ... | ||
def num_edges(self) -> int: ... | ||
def num_nodes(self) -> int: ... | ||
def out_degree(self, node: int, /) -> int: ... | ||
def out_edges(self, node: int, /) -> WeightedEdgeList[T]: ... | ||
def predecessor_indices(self, node: int, /) -> NodeIndices: ... | ||
def predecessors(self, node: int, /) -> List[S]: ... | ||
@staticmethod | ||
def read_edge_list( | ||
path: str, | ||
/, | ||
comment: Optional[str] = ..., | ||
deliminator: Optional[str] = ..., | ||
labels: bool = ..., | ||
) -> PyDiGraph: ... | ||
def remove_edge(self, parent: int, child: int, /) -> None: ... | ||
def remove_edge_from_index(self, edge: int, /) -> None: ... | ||
def remove_edges_from(self, index_list: Sequence[Tuple[int, int]], /) -> None: ... | ||
def remove_node(self, node: int, /) -> None: ... | ||
def remove_node_retain_edges( | ||
self, | ||
node: int, | ||
/, | ||
use_outgoing: Optional[bool] = ..., | ||
condition: Optional[Callable[[S, S], bool]] = ..., | ||
) -> None: ... | ||
def remove_nodes_from(self, index_list: Sequence[int], /) -> None: ... | ||
def subgraph(self, nodes: Sequence[int], /, preserve_attrs: bool = ...) -> PyDiGraph[S, T]: ... | ||
def substitute_node_with_subgraph( | ||
self, | ||
node: int, | ||
other: PyDiGraph[S, T], | ||
edge_map_fn: Callable[[int, int, T], Optional[int]], | ||
/, | ||
node_filter: Optional[Callable[[S], bool]] = ..., | ||
edge_weight_map: Optional[Callable[[T], T]] = ..., | ||
) -> NodeMap: ... | ||
def successor_indices(self, node: int, /) -> NodeIndices: ... | ||
def successors(self, node: int, /) -> List[S]: ... | ||
def to_dot( | ||
self, | ||
/, | ||
node_attr: Optional[Callable[[S], Dict[str, str]]] = ..., | ||
edge_attr: Optional[Callable[[T], Dict[str, str]]] = ..., | ||
graph_attr: Optional[Dict[str, str]] = ..., | ||
filename: Optional[str] = ..., | ||
) -> Optional[str]: ... | ||
Comment on lines
+139
to
+146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this could be done later if you'd rather just land the PR: I think there's a way to teach type checkers that the return type is dependent on the types of one of the arguments. It's made more complex because of the default value of the argument, so I don't precisely know the correct syntax, but it's something like @typing.overload
def to_dot(self, /, ..., filename: None = ...) -> str: ...
@typing.overload
def to_dot(self, /, ..., filename: str = ...) -> None: ...
def to_dot(self, /, ..., filename = None): ... I don't remember exactly how you sort out the syntax in a typing stub to combine the overload with the default value, but it's something along those lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this, I will try implementing it |
||
def to_undirected( | ||
self, | ||
/, | ||
multigraph: bool = ..., | ||
weight_combo_fn: Optional[Callable[[T, T], T]] = ..., | ||
) -> PyGraph[S, T]: ... | ||
def update_edge( | ||
self, | ||
source: int, | ||
target: int, | ||
edge: T, | ||
/, | ||
) -> None: ... | ||
def update_edge_by_index(self, edge_index: int, edge: T, /) -> None: ... | ||
def weighted_edge_list(self) -> WeightedEdgeList[T]: ... | ||
def write_edge_list( | ||
self, | ||
path: str, | ||
/, | ||
deliminator: Optional[str] = ..., | ||
weight_fn: Optional[Callable[[T], str]] = ..., | ||
) -> None: ... | ||
def __delitem__(self, idx: int, /) -> None: ... | ||
def __getitem__(self, idx: int, /) -> S: ... | ||
def __getstate__(self) -> Any: ... | ||
def __len__(self) -> int: ... | ||
def __setitem__(self, idx: int, value: S, /) -> None: ... | ||
def __setstate__(self, state, /) -> None: ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
# This file contains only type annotations for PyO3 functions and classes | ||
# For implementation details, see __init__.py and src/graph.rs | ||
|
||
import numpy as np | ||
from .iterators import * | ||
|
||
from typing import ( | ||
Any, | ||
Callable, | ||
Dict, | ||
Generic, | ||
TypeVar, | ||
Optional, | ||
List, | ||
Tuple, | ||
Sequence, | ||
) | ||
|
||
__all__ = ["PyGraph"] | ||
|
||
S = TypeVar("S") | ||
T = TypeVar("T") | ||
|
||
class PyGraph(Generic[S, T]): | ||
multigraph: bool = ... | ||
def __init__(self, /, multigraph: bool = ...) -> None: ... | ||
def add_edge(self, node_a: int, node_b: int, edge: T, /) -> int: ... | ||
def add_edges_from( | ||
self, | ||
obj_list: Sequence[Tuple[int, int, T]], | ||
/, | ||
) -> List[int]: ... | ||
def add_edges_from_no_data( | ||
self: PyGraph[S, Optional[T]], obj_list: Sequence[Tuple[int, int]], / | ||
) -> List[int]: ... | ||
def add_node(self, obj: S, /) -> int: ... | ||
def add_nodes_from(self, obj_list: Sequence[S], /) -> NodeIndices: ... | ||
def adj(self, node: int, /) -> Dict[int, T]: ... | ||
def compose( | ||
self, | ||
other: PyGraph[S, T], | ||
node_map: Dict[int, Tuple[int, T]], | ||
/, | ||
node_map_func: Optional[Callable[[S], int]] = ..., | ||
edge_map_func: Optional[Callable[[T], int]] = ..., | ||
) -> Dict[int, int]: ... | ||
def copy(self) -> PyGraph[S, T]: ... | ||
def degree(self, node: int, /) -> int: ... | ||
def edge_index_map(self) -> EdgeIndexMap[T]: ... | ||
def edge_indices(self) -> EdgeIndices: ... | ||
def edge_list(self) -> EdgeList: ... | ||
def edges(self) -> List[T]: ... | ||
def extend_from_edge_list( | ||
self: PyGraph[Optional[S], Optional[T]], edge_list: Sequence[Tuple[int, int]], / | ||
) -> None: ... | ||
def extend_from_weighted_edge_list( | ||
self: PyGraph[Optional[S], T], | ||
edge_list: Sequence[Tuple[int, int, T]], | ||
/, | ||
) -> None: ... | ||
@staticmethod | ||
def from_adjacency_matrix( | ||
matrix: np.ndarray, /, null_value: float = ... | ||
) -> PyGraph[int, float]: ... | ||
@staticmethod | ||
def from_complex_adjacency_matrix( | ||
matrix: np.ndarray, /, null_value: complex = ... | ||
) -> PyGraph[int, complex]: ... | ||
def get_all_edge_data(self, node_a: int, node_b: int, /) -> List[T]: ... | ||
def get_edge_data(self, node_a: int, node_b: int, /) -> T: ... | ||
def get_node_data(self, node: int, /) -> S: ... | ||
def has_edge(self, node_a: int, node_b: int, /) -> bool: ... | ||
def neighbors(self, node: int, /) -> NodeIndices: ... | ||
def node_indexes(self) -> NodeIndices: ... | ||
def nodes(self) -> List[S]: ... | ||
def num_edges(self) -> int: ... | ||
def num_nodes(self) -> int: ... | ||
@staticmethod | ||
def read_edge_list( | ||
path: str, | ||
/, | ||
comment: Optional[str] = ..., | ||
deliminator: Optional[str] = ..., | ||
labels: bool = ..., | ||
) -> PyGraph: ... | ||
def remove_edge(self, node_a: int, node_b: int, /) -> None: ... | ||
def remove_edge_from_index(self, edge: int, /) -> None: ... | ||
def remove_edges_from(self, index_list: Sequence[Tuple[int, int]], /) -> None: ... | ||
def remove_node(self, node: int, /) -> None: ... | ||
def remove_nodes_from(self, index_list: Sequence[int], /) -> None: ... | ||
def subgraph(self, nodes: Sequence[int], /, preserve_attrs: bool = ...) -> PyGraph[S, T]: ... | ||
def to_dot( | ||
self, | ||
/, | ||
node_attr: Optional[Callable[[S], Dict[str, str]]] = ..., | ||
edge_attr: Optional[Callable[[T], Dict[str, str]]] = ..., | ||
graph_attr: Optional[Dict[str, str]] = ..., | ||
filename: Optional[str] = ..., | ||
) -> Optional[str]: ... | ||
def update_edge( | ||
self, | ||
source: int, | ||
target: int, | ||
edge: T, | ||
/, | ||
) -> None: ... | ||
def update_edge_by_index(self, edge_index: int, edge: T, /) -> None: ... | ||
def weighted_edge_list(self) -> WeightedEdgeList[T]: ... | ||
def write_edge_list( | ||
self, | ||
path: str, | ||
/, | ||
deliminator: Optional[str] = ..., | ||
weight_fn: Optional[Callable[[T], str]] = ..., | ||
) -> None: ... | ||
def __delitem__(self, idx: int, /) -> None: ... | ||
def __getitem__(self, idx: int, /) -> S: ... | ||
def __getstate__(self) -> Any: ... | ||
def __len__(self) -> int: ... | ||
def __setitem__(self, idx: int, value: S, /) -> None: ... | ||
def __setstate__(self, state, /) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a little surprised to learn that PyO3 won't convert
Iterable
as a valid conversion type forVec<_>
, but you're definitely correct with the hint here. I guess maybe PyO3 didn't want to do conversions that require multiple reallocations.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PyO3 has some weird quirks with iterations (e.g. #614 that broke us) so sometimes I just accept the
Vec<_>
conversion and if it is not in our tests it is not "canonical"