Skip to content
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

Merged
merged 101 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
be25f8b
Initial check-in of stub files
IvanIsCoding Aug 2, 2021
1292561
Add types to PyDiGraph
IvanIsCoding Aug 2, 2021
cd98bda
Remove Union types
IvanIsCoding Aug 2, 2021
7cc697d
Start adding types to PyGraph
IvanIsCoding Aug 2, 2021
12b39e2
Tweak __init__
IvanIsCoding Aug 2, 2021
6fa0cf9
Finish adding types for PyGraph
IvanIsCoding Aug 3, 2021
aa3bcd9
Move PyGraph and PyDiGraph to own stub files
IvanIsCoding Aug 3, 2021
e799e0f
Import graph annotations in generators module
IvanIsCoding Aug 3, 2021
e6cec75
Start adding types to custom return types
IvanIsCoding Aug 3, 2021
ec48656
Conclude adding types to custom_return_types
IvanIsCoding Aug 3, 2021
3691bf4
Move stubs to their own package
IvanIsCoding Aug 3, 2021
78d458b
Declare package-dir for retworkx-stubs
IvanIsCoding Aug 3, 2021
fe73243
Use data_files in setup.py
IvanIsCoding Aug 3, 2021
530b26d
Remove print in setup.py
IvanIsCoding Aug 3, 2021
48b536e
Fix minor styling issues
IvanIsCoding Aug 3, 2021
05b2cf2
Merge branch 'main' into add-type-stubs
IvanIsCoding Aug 3, 2021
f6e37a9
Add simple stubs test case
IvanIsCoding Aug 4, 2021
0c3e735
Merge branch 'add-type-stubs' of github.com:IvanIsCoding/retworkx int…
IvanIsCoding Aug 4, 2021
87d0834
Run stub-tests in CI
IvanIsCoding Aug 4, 2021
1b3b747
Only run stub checks on linux with Python >= 3.7
IvanIsCoding Aug 4, 2021
bb1cf8f
Fix error in workflow
IvanIsCoding Aug 4, 2021
e460c11
Try to fix 3.6 build
IvanIsCoding Aug 4, 2021
acc7e4b
Improve custom return types
IvanIsCoding Aug 5, 2021
81aec08
Add PyDiGraph test
IvanIsCoding Aug 5, 2021
ff4139e
Merge remote-tracking branch 'upstream/main' into add-type-stubs
IvanIsCoding Aug 5, 2021
e31ea49
Add new merge functions
IvanIsCoding Aug 5, 2021
f36255f
Merge remote-tracking branch 'upstream/main' into add-type-stubs
IvanIsCoding Aug 7, 2021
69fd764
Merge branch 'main' into add-type-stubs
IvanIsCoding Aug 10, 2021
49eb24f
Merge branch 'main' into add-type-stubs
IvanIsCoding Aug 17, 2021
8777705
Merge branch 'main' into add-type-stubs
IvanIsCoding Aug 23, 2021
b448d34
Merge remote-tracking branch 'upstream/main' into add-type-stubs
IvanIsCoding Sep 5, 2021
e986c53
Merge branch 'main' into add-type-stubs
IvanIsCoding Oct 15, 2021
7ce2b9b
Add retworkx-stub to MANIFEST.in
IvanIsCoding Oct 16, 2021
e1feaf2
Move stubs to retworkx folder
IvanIsCoding Oct 16, 2021
837efb2
Reduce scope of the PR
IvanIsCoding Oct 16, 2021
0ceac47
Revert changes in setup.py
IvanIsCoding Oct 16, 2021
7e2f883
Pin mypy version
IvanIsCoding Oct 16, 2021
827a014
Merge branch 'main' into add-type-stubs
IvanIsCoding Oct 16, 2021
047daba
Minor improvements for annotations
IvanIsCoding Oct 19, 2021
68d3310
Add release note
IvanIsCoding Oct 19, 2021
44a59de
Add section to CONTRIBUTING.md about annotations
IvanIsCoding Oct 19, 2021
f8f9341
Merge branch 'main' into add-type-stubs
IvanIsCoding Oct 31, 2021
baa61e8
Merge branch 'main' into add-type-stubs
IvanIsCoding Nov 10, 2021
546f6ee
Merge branch 'main' into add-type-stubs
IvanIsCoding Nov 30, 2021
54b5c93
Merge branch 'main' into add-type-stubs
IvanIsCoding Dec 7, 2021
ac6825a
Apply suggestions from code review
IvanIsCoding Dec 8, 2021
00c8852
Merge branch 'main' into add-type-stubs
IvanIsCoding Dec 18, 2021
fa34487
Merge branch 'main' into add-type-stubs
IvanIsCoding Jan 6, 2022
4e64fa5
Merge branch 'main' into add-type-stubs
IvanIsCoding Jan 11, 2022
93685fa
Merge branch 'main' into add-type-stubs
IvanIsCoding Feb 3, 2022
9803970
Format with Black
IvanIsCoding Feb 3, 2022
c208f2d
Merge branch 'main' into add-type-stubs
IvanIsCoding Feb 16, 2022
7190bf5
Update dependencies
IvanIsCoding Feb 16, 2022
b35bb02
Correct pytest-mypy-testing version
IvanIsCoding Feb 16, 2022
16df41c
Merge branch 'main' into add-type-stubs
IvanIsCoding Apr 29, 2022
6506370
Merge branch 'main' into add-type-stubs
IvanIsCoding May 3, 2022
28ce1cc
Merge remote-tracking branch 'origin/main' into add-type-stubs
IvanIsCoding May 5, 2022
7d964af
Move tests to new folder
IvanIsCoding May 6, 2022
e025617
Black fmt
IvanIsCoding May 6, 2022
a83cce7
Add reveal_type to builtins to help flake8
IvanIsCoding May 6, 2022
59a9f90
Fix path to tests
IvanIsCoding May 6, 2022
c3dad55
Fix error due to line split
IvanIsCoding May 6, 2022
ed8ea1c
Do not test stubs on Python 3.7
IvanIsCoding May 6, 2022
63c87a6
Add test cases handling restricted methods
IvanIsCoding May 6, 2022
ed0c053
Handle lint
IvanIsCoding May 6, 2022
54a795c
Use Sequence instead of lists
IvanIsCoding May 6, 2022
8f22bb7
Minor updates in tests
IvanIsCoding May 6, 2022
342d2f5
Completely rewrite custom type annotations
IvanIsCoding May 6, 2022
937d049
Merge remote-tracking branch 'origin/main' into add-type-stubs
IvanIsCoding May 6, 2022
23c9a6d
Make PyDAG be generic
IvanIsCoding May 7, 2022
d0cb298
Shorten custom return types
IvanIsCoding May 7, 2022
fc16d41
Correct extend_from_weighted_edge_list signature
IvanIsCoding May 7, 2022
b34afe4
Fix signature
IvanIsCoding May 7, 2022
2bb0ac3
Yet another signature fix
IvanIsCoding May 7, 2022
42f77ac
Use Self from typing_extensions
IvanIsCoding May 17, 2022
ff58a0c
Address PR comments
IvanIsCoding May 17, 2022
0cab9fe
Merge remote-tracking branch 'origin/main' into add-type-stubs
IvanIsCoding May 17, 2022
a7aaf48
Address lint
IvanIsCoding May 17, 2022
20777a6
Add __str__ and from_complex_adjacency_matrix
IvanIsCoding May 17, 2022
e1a8e19
Merge branch 'main' into add-type-stubs
IvanIsCoding Jul 14, 2022
e1798e5
Merge remote-tracking branch 'origin/main' into add-type-stubs
IvanIsCoding Aug 1, 2022
f6bc49f
Rename to rustworkx
IvanIsCoding Aug 1, 2022
0cd9c9f
Change name of in custom_return_types.pyi
IvanIsCoding Aug 1, 2022
dc0d9b3
Skip tests if Python < 3.8
IvanIsCoding Aug 1, 2022
ad83bab
Ignore no tests found
IvanIsCoding Aug 2, 2022
2da1178
Merge branch 'main' into add-type-stubs
IvanIsCoding Sep 18, 2022
f91ab74
Merge remote-tracking branch 'upstream/main' into add-type-stubs
IvanIsCoding Feb 1, 2023
05c7d60
Try using pytest==7.1.3
IvanIsCoding Feb 1, 2023
37d3c23
Improvements after running mypy.stubtest
IvanIsCoding Feb 1, 2023
5e9c86e
More improvements after mypy.stubtest
IvanIsCoding Feb 1, 2023
c24f8f8
Make .pyi file names identical to Rust names
IvanIsCoding Feb 1, 2023
a441d39
Make mypy.stubtest happy
IvanIsCoding Feb 2, 2023
265fbe9
Use mypy.stubtest on CI
IvanIsCoding Feb 2, 2023
c16ae22
Run Black
IvanIsCoding Feb 2, 2023
9a21cae
Remove things that we do not need anymore
IvanIsCoding Feb 2, 2023
119cdb3
More things I forgot to remove
IvanIsCoding Feb 2, 2023
fb27883
Skip Python 3.7 for mypy
IvanIsCoding Feb 2, 2023
968f9d3
Add test stubs without Python 3.7
IvanIsCoding Feb 2, 2023
5ed884b
Don't use latest mypy release
IvanIsCoding Feb 21, 2023
4eab027
Merge branch 'main' into add-type-stubs
IvanIsCoding Feb 21, 2023
91a1f27
Merge branch 'main' into add-type-stubs
mergify[bot] Mar 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,31 @@ jobs:
if: runner.os == 'Linux'
- name: 'Run tests'
run: tox -epy
tests_stubs:
needs: [tests]
name: python-stubs-${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: x86_64-unknown-linux-gnu
profile: minimal
default: true
IvanIsCoding marked this conversation as resolved.
Show resolved Hide resolved
- name: 'Install dependencies'
run: python -m pip install --upgrade 'tox<4'
- name: 'Run rustworkx stub tests'
run: tox -estubs
tests_retworkx_compat:
needs: [build_lint]
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} ${{ matrix.msrv }}
Expand Down
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,21 @@ web browser by running:
cargo doc --open
```

### Type Annotations

If you have added new methods or changed method signatures, we encourage you to add annotations for
those methods in stub files. The stub files are in the `rustworkx` directory and have a `.pyi` file extension.
They contain annotated signatures for Python functions, stripped of their implementation.

While this step is optional, it is very helpful for end-users. Adding annotations lets users type check
their code with [mypy](http://mypy-lang.org/), which can be helpful for finding bugs.

Just like with tests for the code, annotations are also tested via tox.

```
tox -estubs
```

### Release Notes

It is important to document any end user facing changes when we release a new
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ include Cargo.toml
include Cargo.lock
recursive-include src *
recursive-include rustworkx-core *
recursive-include rustworkx *.pyi py.typed
6 changes: 6 additions & 0 deletions releasenotes/notes/graph-annotations-1d436930bf60c5c2.yaml
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>`__.
18 changes: 18 additions & 0 deletions rustworkx/__init__.pyi
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]): ...
174 changes: 174 additions & 0 deletions rustworkx/digraph.pyi
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]: ...
Comment on lines +34 to +38
Copy link
Member

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 for Vec<_>, but you're definitely correct with the hint here. I guess maybe PyO3 didn't want to do conversions that require multiple reallocations.

Copy link
Collaborator Author

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"

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Numpy technically provide stronger type hints now, and Unpack+TypeVarTuple give some sort of way of specifying ndarray shapes / dtypes. Unless you particularly want to look into that right now, though, I'd not worry about it in favour of just landing this PR.

@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
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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: ...
129 changes: 129 additions & 0 deletions rustworkx/graph.pyi
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: ...
Loading