Repository: Qiskit/rustworkx (language: python)

Issue:
Update PyO3 to 0.22.x
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?


This is mostly a reminder that we need to update to 0.22.1 (or later) following https://pyo3.rs/v0.22.0/migration.html


Discussion / hints:
We're going to be blocked on rust-numpy for this. There a couple of open PRs there working on pyo3 0.22.x compatibility. Once that's released then we can start this.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7de789d50..f81e20123 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,12 +47,6 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
-[[package]]
-name = "bitflags"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
-
 [[package]]
 name = "byteorder"
 version = "1.5.0"
@@ -119,12 +113,6 @@ dependencies = [
  "wasi",
 ]
 
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
 [[package]]
 name = "hashbrown"
 version = "0.14.5"
@@ -144,9 +132,9 @@ checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
 
 [[package]]
 name = "heck"
-version = "0.4.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
@@ -154,16 +142,6 @@ version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
-]
-
 [[package]]
 name = "indexmap"
 version = "2.6.0"
@@ -183,18 +161,18 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
 
 [[package]]
 name = "itertools"
-version = "0.10.5"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
 dependencies = [
  "either",
 ]
 
 [[package]]
 name = "itertools"
-version = "0.11.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
 dependencies = [
  "either",
 ]
@@ -217,16 +195,6 @@ version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
 [[package]]
 name = "matrixmultiply"
 version = "0.3.9"
@@ -254,26 +222,28 @@ dependencies = [
 
 [[package]]
 name = "ndarray"
-version = "0.15.6"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
 dependencies = [
  "matrixmultiply",
  "num-complex 0.4.6",
  "num-integer",
  "num-traits",
+ "portable-atomic",
+ "portable-atomic-util",
  "rawpointer",
  "rayon",
 ]
 
 [[package]]
 name = "ndarray-stats"
-version = "0.5.1"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400"
+checksum = "17ebbe97acce52d06aebed4cd4a87c0941f4b2519b59b82b4feb5bd0ce003dfd"
 dependencies = [
- "indexmap 1.9.3",
- "itertools 0.10.5",
+ "indexmap",
+ "itertools 0.13.0",
  "ndarray",
  "noisy_float",
  "num-integer",
@@ -350,9 +320,9 @@ dependencies = [
 
 [[package]]
 name = "numpy"
-version = "0.21.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec170733ca37175f5d75a5bea5911d6ff45d2cd52849ce98b685394e4f2f37f4"
+checksum = "cf314fca279e6e6ac2126a4ff98f26d88aa4ad06bc68fb6ae5cf4bd706758311"
 dependencies = [
  "libc",
  "ndarray",
@@ -369,29 +339,6 @@ version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets",
-]
-
 [[package]]
 name = "petgraph"
 version = "0.6.5"
@@ -399,7 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
 dependencies = [
  "fixedbitset",
- "indexmap 2.6.0",
+ "indexmap",
 ]
 
 [[package]]
@@ -408,6 +355,15 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
 
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d"
+dependencies = [
+ "portable-atomic",
+]
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.20"
@@ -425,7 +381,7 @@ checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d"
 dependencies = [
  "autocfg",
  "equivalent",
- "indexmap 2.6.0",
+ "indexmap",
 ]
 
 [[package]]
@@ -439,19 +395,19 @@ dependencies = [
 
 [[package]]
 name = "pyo3"
-version = "0.21.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
+checksum = "15ee168e30649f7f234c3d49ef5a7a6cbf5134289bc46c29ff3155fa3221c225"
 dependencies = [
  "cfg-if",
  "hashbrown 0.14.5",
- "indexmap 2.6.0",
+ "indexmap",
  "indoc",
  "libc",
  "memoffset",
  "num-bigint",
  "num-complex 0.4.6",
- "parking_lot",
+ "once_cell",
  "portable-atomic",
  "pyo3-build-config",
  "pyo3-ffi",
@@ -461,9 +417,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-build-config"
-version = "0.21.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
+checksum = "e61cef80755fe9e46bb8a0b8f20752ca7676dcc07a5277d8b7768c6172e529b3"
 dependencies = [
  "once_cell",
  "target-lexicon",
@@ -471,9 +427,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-ffi"
-version = "0.21.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
+checksum = "67ce096073ec5405f5ee2b8b31f03a68e02aa10d5d4f565eca04acc41931fa1c"
 dependencies = [
  "libc",
  "pyo3-build-config",
@@ -481,9 +437,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-macros"
-version = "0.21.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
+checksum = "2440c6d12bc8f3ae39f1e775266fa5122fd0c8891ce7520fa6048e683ad3de28"
 dependencies = [
  "proc-macro2",
  "pyo3-macros-backend",
@@ -493,9 +449,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-macros-backend"
-version = "0.21.2"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
+checksum = "1be962f0e06da8f8465729ea2cb71a416d2257dff56cbe40a70d3e62a93ae5d1"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -598,15 +554,6 @@ dependencies = [
  "crossbeam-utils",
 ]
 
-[[package]]
-name = "redox_syscall"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
-dependencies = [
- "bitflags",
-]
-
 [[package]]
 name = "rustc-hash"
 version = "1.1.0"
@@ -620,7 +567,7 @@ dependencies = [
  "ahash",
  "fixedbitset",
  "hashbrown 0.14.5",
- "indexmap 2.6.0",
+ "indexmap",
  "ndarray",
  "ndarray-stats",
  "num-bigint",
@@ -647,7 +594,7 @@ dependencies = [
  "ahash",
  "fixedbitset",
  "hashbrown 0.14.5",
- "indexmap 2.6.0",
+ "indexmap",
  "ndarray",
  "num-traits",
  "petgraph",
@@ -664,12 +611,6 @@ version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
 [[package]]
 name = "serde"
 version = "1.0.210"
@@ -764,70 +705,6 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
 [[package]]
 name = "zerocopy"
 version = "0.7.35"
diff --git a/Cargo.toml b/Cargo.toml
index 667d17de7..b9826507a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,9 +28,9 @@ ahash = "0.8.6"
 fixedbitset = "0.4.2"
 hashbrown = { version = ">=0.13, <0.15", features = ["rayon"] }
 indexmap = { version = ">=1.9, <3", features = ["rayon"] }
-ndarray = { version = "0.15.6", features = ["rayon"] }
+ndarray = { version = "0.16.1", features = ["rayon"] }
 num-traits = "0.2"
-numpy = "0.21.0"
+numpy = "0.22"
 petgraph = "0.6.5"
 rand = "0.8"
 rand_pcg = "0.3"
@@ -46,7 +46,7 @@ fixedbitset.workspace = true
 hashbrown.workspace = true
 indexmap.workspace = true
 ndarray.workspace = true
-ndarray-stats = "0.5.1"
+ndarray-stats = "0.6.0"
 num-bigint = "0.4"
 num-complex = "0.4"
 num-traits.workspace = true
@@ -62,8 +62,8 @@ smallvec = { version = "1.0", features = ["union"] }
 rustworkx-core = { path = "rustworkx-core", version = "=0.16.0" }
 
 [dependencies.pyo3]
-version = "0.21.2"
-features = ["abi3-py39", "extension-module", "hashbrown", "num-bigint", "num-complex", "indexmap"]
+version = "0.22.3"
+features = ["abi3-py39", "extension-module", "hashbrown", "num-bigint", "num-complex", "indexmap", "py-clone"]
 
 [dependencies.sprs]
 version = "^0.11"
diff --git a/src/coloring.rs b/src/coloring.rs
index cf844b1a2..25b96ebd3 100644
--- a/src/coloring.rs
+++ b/src/coloring.rs
@@ -42,7 +42,7 @@ pub use rustworkx_core::coloring::ColoringStrategy as ColoringStrategyCore;
 ///       - `GIS` strategy in [1] (section 1.2.2.9)
 ///
 /// [1] Adrian Kosowski, and Krzysztof Manuszewski, Classical Coloring of Graphs, Graph Colorings, 2-19, 2004. ISBN 0-8218-3458-4.
-#[pyclass(module = "rustworkx")]
+#[pyclass(module = "rustworkx", eq, eq_int)]
 #[derive(Clone, PartialEq)]
 pub enum ColoringStrategy {
     Degree,
diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs
index c3023d6d8..353a57fe2 100644
--- a/src/connectivity/mod.rs
+++ b/src/connectivity/mod.rs
@@ -71,7 +71,7 @@ use rustworkx_core::dag_algo::longest_path;
 /// .. [1] Paton, K. An algorithm for finding a fundamental set of
 ///    cycles of a graph. Comm. ACM 12, 9 (Sept 1969), 514-518.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, root=None)")]
+#[pyo3(text_signature = "(graph, /, root=None)", signature = (graph, root=None))]
 pub fn cycle_basis(graph: &graph::PyGraph, root: Option<usize>) -> Vec<Vec<usize>> {
     connectivity::cycle_basis(&graph.graph, root.map(NodeIndex::new))
         .into_iter()
@@ -125,7 +125,7 @@ pub fn strongly_connected_components(graph: &digraph::PyDiGraph) -> Vec<Vec<usiz
 ///     forms a cycle (loop) in the input graph
 /// :rtype: EdgeList
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, source=None)")]
+#[pyo3(text_signature = "(graph, /, source=None)", signature=(graph, source=None))]
 pub fn digraph_find_cycle(graph: &digraph::PyDiGraph, source: Option<usize>) -> EdgeList {
     EdgeList {
         edges: connectivity::find_cycle(&graph.graph, source.map(NodeIndex::new))
@@ -575,7 +575,7 @@ pub fn digraph_complement(py: Python, graph: &digraph::PyDiGraph) -> PyResult<di
 /// :returns: A list of lists where each inner list is a path of node indices
 /// :rtype: list
 #[pyfunction]
-#[pyo3(text_signature = "(graph, origin, to, /, min_depth=None, cutoff=None)")]
+#[pyo3(text_signature = "(graph, origin, to, /, min_depth=None, cutoff=None)", signature = (graph, origin, to, min_depth=None, cutoff=None))]
 pub fn graph_all_simple_paths(
     graph: &graph::PyGraph,
     origin: usize,
@@ -629,7 +629,7 @@ pub fn graph_all_simple_paths(
 /// :returns: A list of lists where each inner list is a path
 /// :rtype: list
 #[pyfunction]
-#[pyo3(text_signature = "(graph, origin, to, /, min_depth=None, cutoff=None)")]
+#[pyo3(text_signature = "(graph, origin, to, /, min_depth=None, cutoff=None)", signature = (graph, origin, to, min_depth=None, cutoff=None))]
 pub fn digraph_all_simple_paths(
     graph: &digraph::PyDiGraph,
     origin: usize,
@@ -687,7 +687,7 @@ pub fn digraph_all_simple_paths(
 ///
 /// :raises ValueError: If ``min_depth`` or ``cutoff`` are < 2
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, min_depth=None, cutoff=None)")]
+#[pyo3(text_signature = "(graph, /, min_depth=None, cutoff=None)", signature = (graph, min_depth=None, cutoff=None))]
 pub fn digraph_all_pairs_all_simple_paths(
     graph: &digraph::PyDiGraph,
     min_depth: Option<usize>,
@@ -749,7 +749,7 @@ pub fn connected_subgraphs(graph: &PyGraph, k: usize) -> PyResult<Vec<Vec<usize>
 ///
 /// :raises ValueError: If ``min_depth`` or ``cutoff`` are < 2.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, min_depth=None, cutoff=None)")]
+#[pyo3(text_signature = "(graph, /, min_depth=None, cutoff=None)", signature = (graph, min_depth=None, cutoff=None))]
 pub fn graph_all_pairs_all_simple_paths(
     graph: &graph::PyGraph,
     min_depth: Option<usize>,
@@ -939,7 +939,7 @@ pub fn digraph_core_number(py: Python, graph: &digraph::PyDiGraph) -> PyResult<P
 /// .. [stoer_simple_1997] Stoer, Mechthild and Frank Wagner, "A simple min-cut
 ///     algorithm". Journal of the ACM 44 (4), 585-591, 1997.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, weight_fn=None)")]
+#[pyo3(text_signature = "(graph, /, weight_fn=None)", signature = (graph, weight_fn=None))]
 pub fn stoer_wagner_min_cut(
     py: Python,
     graph: &graph::PyGraph,
@@ -1081,7 +1081,7 @@ pub fn biconnected_components(graph: &graph::PyGraph) -> BiconnectedComponents {
 ///       and 2-edge-connectivity." *Information Processing Letters*,
 ///       113, 241–244. Elsevier. <https://doi.org/10.1016/j.ipl.2013.01.016>
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, source=None)")]
+#[pyo3(text_signature = "(graph, /, source=None)", signature = (graph, source=None))]
 pub fn chain_decomposition(graph: graph::PyGraph, source: Option<usize>) -> Chains {
     let chains = connectivity::chain_decomposition(&graph.graph, source.map(NodeIndex::new));
     Chains {
diff --git a/src/dag_algo/mod.rs b/src/dag_algo/mod.rs
index 65df6afcc..769582ce1 100644
--- a/src/dag_algo/mod.rs
+++ b/src/dag_algo/mod.rs
@@ -111,7 +111,7 @@ pub fn traversal_directions(reverse: bool) -> (petgraph::Direction, petgraph::Di
 /// :raises Exception: If an unexpected error occurs or a path can't be found
 /// :raises DAGHasCycle: If the input PyDiGraph has a cycle
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, weight_fn=None)")]
+#[pyo3(text_signature = "(graph, /, weight_fn=None)", signature = (graph, weight_fn=None))]
 pub fn dag_longest_path(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -151,7 +151,7 @@ pub fn dag_longest_path(
 /// :raises Exception: If an unexpected error occurs or a path can't be found
 /// :raises DAGHasCycle: If the input PyDiGraph has a cycle
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, weight_fn=None)")]
+#[pyo3(text_signature = "(graph, /, weight_fn=None)", signature = (graph, weight_fn=None))]
 pub fn dag_longest_path_length(
     py: Python,
     graph: &digraph::PyDiGraph,
diff --git a/src/digraph.rs b/src/digraph.rs
index dd5025080..07aa2be14 100644
--- a/src/digraph.rs
+++ b/src/digraph.rs
@@ -2132,7 +2132,8 @@ impl PyDiGraph {
     ///   image
     ///
     #[pyo3(
-        text_signature = "(self, /, node_attr=None, edge_attr=None, graph_attr=None, filename=None)"
+        text_signature = "(self, /, node_attr=None, edge_attr=None, graph_attr=None, filename=None)",
+        signature = (node_attr=None, edge_attr=None, graph_attr=None, filename=None)
     )]
     pub fn to_dot(
         &self,
@@ -2309,7 +2310,7 @@ impl PyDiGraph {
     ///     with open(path, 'rt') as edge_file:
     ///         print(edge_file.read())
     ///
-    #[pyo3(text_signature = "(self, path, /, deliminator=None, weight_fn=None)")]
+    #[pyo3(text_signature = "(self, path, /, deliminator=None, weight_fn=None)", signature = (path, deliminator=None, weight_fn=None))]
     pub fn write_edge_list(
         &self,
         py: Python,
@@ -2476,7 +2477,7 @@ impl PyDiGraph {
     ///   graph.compose(other_graph, node_map)
     ///   mpl_draw(graph, with_labels=True, labels=str, edge_labels=str)
     ///
-    #[pyo3(text_signature = "(self, other, node_map, /, node_map_func=None, edge_map_func=None)")]
+    #[pyo3(text_signature = "(self, other, node_map, /, node_map_func=None, edge_map_func=None)", signature = (other, node_map, node_map_func=None, edge_map_func=None))]
     pub fn compose(
         &mut self,
         py: Python,
@@ -2553,7 +2554,8 @@ impl PyDiGraph {
     ///    order when iterated over multiple times).
     ///
     #[pyo3(
-        text_signature = "(self, node, other, edge_map_fn, /, node_filter=None, edge_weight_map=None)"
+        text_signature = "(self, node, other, edge_map_fn, /, node_filter=None, edge_weight_map=None)",
+        signature = (node, other, edge_map_fn, node_filter=None, edge_weight_map=None)
     )]
     fn substitute_node_with_subgraph(
         &mut self,
@@ -2695,7 +2697,7 @@ impl PyDiGraph {
     /// :returns: The index of the newly created node.
     /// :raises DAGWouldCycle: The cycle check is enabled and the
     ///     contraction would introduce cycle(s).
-    #[pyo3(text_signature = "(self, nodes, obj, /, check_cycle=None, weight_combo_fn=None)")]
+    #[pyo3(text_signature = "(self, nodes, obj, /, check_cycle=None, weight_combo_fn=None)", signature = (nodes, obj, check_cycle=None, weight_combo_fn=None))]
     pub fn contract_nodes(
         &mut self,
         py: Python,
@@ -2867,6 +2869,7 @@ impl PyDiGraph {
     ///     then by default the data payload will be copied when the reverse edge is added.
     ///     If there are parallel edges, then one of the edges (typically the one with the lower
     ///     index, but this is not a guarantee) will be copied.
+    #[pyo3(signature = (edge_payload_fn=None))]
     pub fn make_symmetric(
         &mut self,
         py: Python,
diff --git a/src/graph.rs b/src/graph.rs
index 41cf02bac..f74933928 100644
--- a/src/graph.rs
+++ b/src/graph.rs
@@ -1254,7 +1254,8 @@ impl PyGraph {
     ///   image
     ///
     #[pyo3(
-        text_signature = "(self, /, node_attr=None, edge_attr=None, graph_attr=None, filename=None)"
+        text_signature = "(self, /, node_attr=None, edge_attr=None, graph_attr=None, filename=None)",
+        signature = (node_attr=None, edge_attr=None, graph_attr=None, filename=None)
     )]
     pub fn to_dot(
         &self,
@@ -1428,7 +1429,7 @@ impl PyGraph {
     ///     with open(path, 'rt') as edge_file:
     ///         print(edge_file.read())
     ///
-    #[pyo3(text_signature = "(self, path, /, deliminator=None, weight_fn=None)")]
+    #[pyo3(text_signature = "(self, path, /, deliminator=None, weight_fn=None)", signature = (path, deliminator=None, weight_fn=None))]
     pub fn write_edge_list(
         &self,
         py: Python,
@@ -1602,7 +1603,7 @@ impl PyGraph {
     ///   graph.compose(other_graph, node_map)
     ///   mpl_draw(graph, with_labels=True, labels=str, edge_labels=str)
     ///
-    #[pyo3(text_signature = "(self, other, node_map, /, node_map_func=None, edge_map_func=None)")]
+    #[pyo3(text_signature = "(self, other, node_map, /, node_map_func=None, edge_map_func=None)", signature = (other, node_map, node_map_func=None, edge_map_func=None))]
     pub fn compose(
         &mut self,
         py: Python,
@@ -1679,7 +1680,8 @@ impl PyGraph {
     ///    order when iterated over multiple times).
     ///
     #[pyo3(
-        text_signature = "(self, node, other, edge_map_fn, /, node_filter=None, edge_weight_map=None"
+        text_signature = "(self, node, other, edge_map_fn, /, node_filter=None, edge_weight_map=None",
+        signature = (node, other, edge_map_fn, node_filter=None, edge_weight_map=None)
     )]
     fn substitute_node_with_subgraph(
         &mut self,
@@ -1825,7 +1827,7 @@ impl PyGraph {
     ///     combined by choosing one of the edge's weights arbitrarily based
     ///     on an internal iteration order, subject to change.
     /// :returns: The index of the newly created node.
-    #[pyo3(text_signature = "(self, nodes, obj, /, weight_combo_fn=None)")]
+    #[pyo3(text_signature = "(self, nodes, obj, /, weight_combo_fn=None)", signature = (nodes, obj, weight_combo_fn=None))]
     pub fn contract_nodes(
         &mut self,
         py: Python,
diff --git a/src/iterators.rs b/src/iterators.rs
index 64d0c644e..6eecbbaeb 100644
--- a/src/iterators.rs
+++ b/src/iterators.rs
@@ -39,7 +39,6 @@
 #![allow(clippy::float_cmp, clippy::upper_case_acronyms)]
 
 use std::collections::hash_map::DefaultHasher;
-use std::convert::TryInto;
 use std::hash::Hasher;
 
 use num_bigint::BigUint;
@@ -410,10 +409,26 @@ trait PyGCProtocol {
     fn __clear__(&mut self) {}
 }
 
-#[derive(FromPyObject)]
-enum SliceOrInt<'a> {
+/// A Python-space indexer for the standard `PySequence` type; a single integer or a slice.
+///
+/// These come in as `isize`s from Python space, since Python typically allows negative indices.
+/// Copied from https://github.com/Qiskit/qiskit/pull/12669
+pub enum PySequenceIndex<'py> {
     Int(isize),
-    Slice(&'a PySlice),
+    Slice(Bound<'py, PySlice>),
+}
+
+impl<'py> FromPyObject<'py> for PySequenceIndex<'py> {
+    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
+        // `slice` can't be subclassed in Python, so it's safe (and faster) to check for it exactly.
+        // The `downcast_exact` check is just a pointer comparison, so while `slice` is the less
+        // common input, doing that first has little-to-no impact on the speed of the `isize` path,
+        // while the reverse makes `slice` inputs significantly slower.
+        if let Ok(slice) = ob.downcast_exact::<PySlice>() {
+            return Ok(Self::Slice(slice.clone()));
+        }
+        Ok(Self::Int(ob.extract()?))
+    }
 }
 
 trait PyConvertToPyArray {
@@ -542,9 +557,9 @@ macro_rules! custom_vec_iter_impl {
                 Ok(self.$data.len())
             }
 
-            fn __getitem__(&self, py: Python, idx: SliceOrInt) -> PyResult<PyObject> {
+            fn __getitem__(&self, py: Python, idx: PySequenceIndex) -> PyResult<PyObject> {
                 match idx {
-                    SliceOrInt::Slice(slc) => {
+                    PySequenceIndex::Slice(slc) => {
                         let len = self.$data.len().try_into().unwrap();
                         let indices = slc.indices(len)?;
                         let mut out_vec: Vec<$T> = Vec::new();
@@ -571,7 +586,7 @@ macro_rules! custom_vec_iter_impl {
                         }
                         Ok(out_vec.into_py(py))
                     }
-                    SliceOrInt::Int(idx) => {
+                    PySequenceIndex::Int(idx) => {
                         let len = self.$data.len() as isize;
                         if idx >= len || idx < -len {
                             Err(PyIndexError::new_err(format!("Invalid index, {}", idx)))
@@ -599,6 +614,7 @@ macro_rules! custom_vec_iter_impl {
                 }
             }
 
+            #[pyo3(signature = (dtype=None, copy=None))]
             fn __array__(
                 &self,
                 py: Python,
diff --git a/src/json/mod.rs b/src/json/mod.rs
index 4f6ee50e2..2ad2e8b6b 100644
--- a/src/json/mod.rs
+++ b/src/json/mod.rs
@@ -43,6 +43,7 @@ use pyo3::Python;
 /// :returns: The graph represented by the node link JSON
 /// :rtype: PyGraph | PyDiGraph
 #[pyfunction]
+#[pyo3(signature = (path, graph_attrs=None, node_attrs=None, edge_attrs=None))]
 pub fn from_node_link_json_file(
     py: Python,
     path: &str,
@@ -120,6 +121,7 @@ pub fn from_node_link_json_file(
 /// :returns: The graph represented by the node link JSON
 /// :rtype: PyGraph | PyDiGraph
 #[pyfunction]
+#[pyo3(signature = (data, graph_attrs=None, node_attrs=None, edge_attrs=None))]
 pub fn parse_node_link_json(
     py: Python,
     data: &str,
@@ -196,7 +198,8 @@ pub fn parse_node_link_json(
 /// :rtype: str
 #[pyfunction]
 #[pyo3(
-    text_signature = "(graph, /, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)"
+    text_signature = "(graph, /, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)",
+    signature = (graph, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)
 )]
 pub fn digraph_node_link_json(
     py: Python,
@@ -243,7 +246,8 @@ pub fn digraph_node_link_json(
 /// :rtype: str
 #[pyfunction]
 #[pyo3(
-    text_signature = "(graph, /, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)"
+    text_signature = "(graph, /, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)",
+    signature = (graph, path=None, graph_attrs=None, node_attrs=None, edge_attrs=None)
 )]
 pub fn graph_node_link_json(
     py: Python,
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 72c4155e8..802207b1c 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -205,7 +205,7 @@ pub fn digraph_spring_layout(
 /// :returns: The random layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, / center=None, seed=None)")]
+#[pyo3(text_signature = "(graph, / center=None, seed=None)", signature = (graph, center=None, seed=None))]
 pub fn graph_random_layout(
     graph: &graph::PyGraph,
     center: Option<[f64; 2]>,
@@ -224,7 +224,7 @@ pub fn graph_random_layout(
 /// :returns: The random layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, / center=None, seed=None)")]
+#[pyo3(text_signature = "(graph, / center=None, seed=None)", signature = (graph, center=None, seed=None))]
 pub fn digraph_random_layout(
     graph: &digraph::PyDiGraph,
     center: Option<[f64; 2]>,
@@ -319,7 +319,7 @@ pub fn digraph_bipartite_layout(
 /// :returns: The circular layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, scale=1, center=None)")]
+#[pyo3(text_signature = "(graph, /, scale=1, center=None)", signature = (graph, scale=None, center=None))]
 pub fn graph_circular_layout(
     graph: &graph::PyGraph,
     scale: Option<f64>,
@@ -338,7 +338,7 @@ pub fn graph_circular_layout(
 /// :returns: The circular layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, scale=1, center=None)")]
+#[pyo3(text_signature = "(graph, /, scale=1, center=None)", signature = (graph, scale=None, center=None))]
 pub fn digraph_circular_layout(
     graph: &digraph::PyDiGraph,
     scale: Option<f64>,
@@ -361,7 +361,7 @@ pub fn digraph_circular_layout(
 /// :returns: The shell layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, nlist=None, rotate=None, scale=1, center=None)")]
+#[pyo3(text_signature = "(graph, /, nlist=None, rotate=None, scale=1, center=None)", signature = (graph, nlist=None, rotate=None, scale=None, center=None))]
 pub fn graph_shell_layout(
     graph: &graph::PyGraph,
     nlist: Option<Vec<Vec<usize>>>,
@@ -385,7 +385,7 @@ pub fn graph_shell_layout(
 /// :returns: The shell layout of the graph.
 /// :rtype: Pos2DMapping
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, nlist=None, rotate=None, scale=1, center=None)")]
+#[pyo3(text_signature = "(graph, /, nlist=None, rotate=None, scale=1, center=None)", signature = (graph, nlist=None, rotate=None, scale=None, center=None))]
 pub fn digraph_shell_layout(
     graph: &digraph::PyDiGraph,
     nlist: Option<Vec<Vec<usize>>>,
diff --git a/src/random_graph.rs b/src/random_graph.rs
index 7c981c5ee..66451dc22 100644
--- a/src/random_graph.rs
+++ b/src/random_graph.rs
@@ -61,7 +61,7 @@ use rustworkx_core::generators as core_generators;
 ///    Phys. Rev. E, 71, 036113, 2005.
 /// .. [2] https://github.com/networkx/networkx/blob/networkx-2.4/networkx/generators/random_graphs.py#L49-L120
 #[pyfunction]
-#[pyo3(text_signature = "(num_nodes, probability, /, seed=None)")]
+#[pyo3(text_signature = "(num_nodes, probability, /, seed=None)", signature = (num_nodes, probability, seed=None))]
 pub fn directed_gnp_random_graph(
     py: Python,
     num_nodes: usize,
@@ -129,7 +129,7 @@ pub fn directed_gnp_random_graph(
 ///    Phys. Rev. E, 71, 036113, 2005.
 /// .. [2] https://github.com/networkx/networkx/blob/networkx-2.4/networkx/generators/random_graphs.py#L49-L120
 #[pyfunction]
-#[pyo3(text_signature = "(num_nodes, probability, /, seed=None)")]
+#[pyo3(text_signature = "(num_nodes, probability, /, seed=None)", signature = (num_nodes, probability, seed=None))]
 pub fn undirected_gnp_random_graph(
     py: Python,
     num_nodes: usize,
@@ -187,7 +187,7 @@ pub fn undirected_gnp_random_graph(
 /// :rtype: PyDiGraph
 ///
 #[pyfunction]
-#[pyo3(text_signature = "(num_nodes, num_edges, /, seed=None)")]
+#[pyo3(text_signature = "(num_nodes, num_edges, /, seed=None)", signature = (num_nodes, num_edges, seed=None))]
 pub fn directed_gnm_random_graph(
     py: Python,
     num_nodes: usize,
@@ -243,7 +243,7 @@ pub fn directed_gnm_random_graph(
 /// :rtype: PyGraph
 
 #[pyfunction]
-#[pyo3(text_signature = "(num_nodes, num_edges, /, seed=None)")]
+#[pyo3(text_signature = "(num_nodes, num_edges, /, seed=None)", signature = (num_nodes, num_edges, seed=None))]
 pub fn undirected_gnm_random_graph(
     py: Python,
     num_nodes: usize,
@@ -297,7 +297,7 @@ pub fn undirected_gnm_random_graph(
 /// :return: A PyDiGraph object
 /// :rtype: PyDiGraph
 #[pyfunction]
-#[pyo3(text_signature = "(sizes, probabilities, loops, /, seed=None)")]
+#[pyo3(text_signature = "(sizes, probabilities, loops, /, seed=None)", signature = (sizes, probabilities, loops, seed=None))]
 pub fn directed_sbm_random_graph<'p>(
     py: Python<'p>,
     sizes: Vec<usize>,
@@ -353,7 +353,7 @@ pub fn directed_sbm_random_graph<'p>(
 /// :return: A PyGraph object
 /// :rtype: PyGraph
 #[pyfunction]
-#[pyo3(text_signature = "(sizes, probabilities, loops, /, seed=None)")]
+#[pyo3(text_signature = "(sizes, probabilities, loops, /, seed=None)", signature = (sizes, probabilities, loops, seed=None))]
 pub fn undirected_sbm_random_graph<'p>(
     py: Python<'p>,
     sizes: Vec<usize>,
@@ -527,7 +527,7 @@ pub fn random_geometric_graph(
 /// :return: A PyGraph object
 /// :rtype: PyGraph
 #[pyfunction]
-#[pyo3(text_signature = "(pos, beta, r, /, seed=None)")]
+#[pyo3(text_signature = "(pos, beta, r, /, seed=None)", signature = (pos, r, beta=None, seed=None))]
 pub fn hyperbolic_random_graph(
     py: Python,
     pos: Vec<Vec<f64>>,
@@ -572,6 +572,7 @@ pub fn hyperbolic_random_graph(
 /// :return: A PyGraph object
 /// :rtype: PyGraph
 #[pyfunction]
+#[pyo3(signature = (n, m, seed=None, initial_graph=None))]
 pub fn barabasi_albert_graph(
     py: Python,
     n: usize,
@@ -633,6 +634,7 @@ pub fn barabasi_albert_graph(
 /// :return: A PyDiGraph object
 /// :rtype: PyDiGraph
 #[pyfunction]
+#[pyo3(signature = (n, m, seed=None, initial_graph=None))]
 pub fn directed_barabasi_albert_graph(
     py: Python,
     n: usize,
@@ -691,7 +693,7 @@ pub fn directed_barabasi_albert_graph(
 /// :return: A PyDiGraph object
 /// :rtype: PyDiGraph
 #[pyfunction]
-#[pyo3(text_signature = "(num_l_nodes, num_r_nodes, probability, /, seed=None)")]
+#[pyo3(text_signature = "(num_l_nodes, num_r_nodes, probability, /, seed=None)", signature = (num_l_nodes, num_r_nodes, probability, seed=None))]
 pub fn directed_random_bipartite_graph(
     py: Python,
     num_l_nodes: usize,
@@ -744,7 +746,7 @@ pub fn directed_random_bipartite_graph(
 /// :return: A PyGraph object
 /// :rtype: PyGraph
 #[pyfunction]
-#[pyo3(text_signature = "(num_l_nodes, num_r_nodes, probability, /, seed=None)")]
+#[pyo3(text_signature = "(num_l_nodes, num_r_nodes, probability, /, seed=None)", signature = (num_l_nodes, num_r_nodes, probability, seed=None))]
 pub fn undirected_random_bipartite_graph(
     py: Python,
     num_l_nodes: usize,
diff --git a/src/token_swapper.rs b/src/token_swapper.rs
index a4b460a86..a2ee68e21 100644
--- a/src/token_swapper.rs
+++ b/src/token_swapper.rs
@@ -48,7 +48,10 @@ use rustworkx_core::token_swapper;
 ///      the tokens.
 /// :rtype: EdgeList
 #[pyfunction]
-#[pyo3(text_signature = "(graph, mapping, /, trials=None, seed=None, parallel_threshold=50)")]
+#[pyo3(
+    text_signature = "(graph, mapping, /, trials=None, seed=None, parallel_threshold=50)", 
+    signature = (graph, mapping, trials=None, seed=None, parallel_threshold=None)
+)]
 pub fn graph_token_swapper(
     graph: &graph::PyGraph,
     mapping: HashMap<usize, usize>,
diff --git a/src/traversal/mod.rs b/src/traversal/mod.rs
index f6ce66a76..2d8531b48 100644
--- a/src/traversal/mod.rs
+++ b/src/traversal/mod.rs
@@ -71,7 +71,7 @@ use crate::iterators::EdgeList;
 ///     depth-first order
 /// :rtype: EdgeList
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, source=None)")]
+#[pyo3(text_signature = "(graph, /, source=None)", signature = (graph, source=None))]
 pub fn digraph_dfs_edges(graph: &digraph::PyDiGraph, source: Option<usize>) -> EdgeList {
     EdgeList {
         edges: dfs_edges(&graph.graph, source.map(NodeIndex::new)),
@@ -116,7 +116,7 @@ pub fn digraph_dfs_edges(graph: &digraph::PyDiGraph, source: Option<usize>) -> E
 ///     depth-first order
 /// :rtype: EdgeList
 #[pyfunction]
-#[pyo3(text_signature = "(graph, /, source=None)")]
+#[pyo3(text_signature = "(graph, /, source=None)", signature = (graph, source=None))]
 pub fn graph_dfs_edges(graph: &graph::PyGraph, source: Option<usize>) -> EdgeList {
     EdgeList {
         edges: dfs_edges(&graph.graph, source.map(NodeIndex::new)),
@@ -318,6 +318,7 @@ pub fn descendants(graph: &digraph::PyDiGraph, node: usize) -> HashSet<usize> {
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, visitor=None))]
 pub fn digraph_bfs_search(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -410,6 +411,7 @@ pub fn digraph_bfs_search(
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, visitor=None))]
 pub fn graph_bfs_search(
     py: Python,
     graph: &graph::PyGraph,
@@ -500,6 +502,7 @@ pub fn graph_bfs_search(
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, visitor=None))]
 pub fn digraph_dfs_search(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -590,6 +593,7 @@ pub fn digraph_dfs_search(
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, visitor=None))]
 pub fn graph_dfs_search(
     py: Python,
     graph: &graph::PyGraph,
@@ -666,6 +670,7 @@ pub fn graph_dfs_search(
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, weight_fn=None, visitor=None))]
 pub fn digraph_dijkstra_search(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -747,6 +752,7 @@ pub fn digraph_dijkstra_search(
 ///     preserve argument ordering from an earlier version) but it is a required argument
 ///     and will raise a ``TypeError`` if not specified.
 #[pyfunction]
+#[pyo3(signature = (graph, source=None, weight_fn=None, visitor=None))]
 pub fn graph_dijkstra_search(
     py: Python,
     graph: &graph::PyGraph,


Test patch:
diff --git a/src/shortest_path/mod.rs b/src/shortest_path/mod.rs
index fb9aec4e1..1b9970327 100644
--- a/src/shortest_path/mod.rs
+++ b/src/shortest_path/mod.rs
@@ -375,7 +375,7 @@ pub fn digraph_has_path(
 /// :raises ValueError: when an edge weight with NaN or negative value
 ///     is provided.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)")]
+#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)", signature = (graph, node, edge_cost_fn, goal=None))]
 pub fn graph_dijkstra_shortest_path_lengths(
     py: Python,
     graph: &graph::PyGraph,
@@ -450,7 +450,7 @@ pub fn graph_dijkstra_shortest_path_lengths(
 /// :raises ValueError: when an edge weight with NaN or negative value
 ///     is provided.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)")]
+#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)", signature = (graph, node, edge_cost_fn, goal=None))]
 pub fn digraph_dijkstra_shortest_path_lengths(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -802,7 +802,7 @@ pub fn graph_astar_shortest_path(
 /// :raises ValueError: when an edge weight with NaN or negative value
 ///     is provided.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, start, k, edge_cost, /, goal=None)")]
+#[pyo3(text_signature = "(graph, start, k, edge_cost, /, goal=None)", signature = (graph, start, k, edge_cost, goal=None))]
 pub fn digraph_k_shortest_path_lengths(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -862,7 +862,7 @@ pub fn digraph_k_shortest_path_lengths(
 /// :raises ValueError: when an edge weight with NaN or negative value
 ///     is provided.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, start, k, edge_cost, /, goal=None)")]
+#[pyo3(text_signature = "(graph, start, k, edge_cost, /, goal=None)", signature = (graph, start, k, edge_cost, goal=None))]
 pub fn graph_k_shortest_path_lengths(
     py: Python,
     graph: &graph::PyGraph,
@@ -1578,7 +1578,7 @@ pub fn graph_unweighted_average_shortest_path_length(
 /// :raises: :class:`~rustworkx.NegativeCycle`: when there is a negative cycle and the shortest
 ///     path is not defined.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)")]
+#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)", signature = (graph, node, edge_cost_fn, goal=None))]
 pub fn digraph_bellman_ford_shortest_path_lengths(
     py: Python,
     graph: &digraph::PyDiGraph,
@@ -1663,7 +1663,7 @@ pub fn digraph_bellman_ford_shortest_path_lengths(
 /// :raises: :class:`~rustworkx.NegativeCycle`: when there is a negative cycle and the shortest
 ///     path is not defined.
 #[pyfunction]
-#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)")]
+#[pyo3(text_signature = "(graph, node, edge_cost_fn, /, goal=None)", signature = (graph, node, edge_cost_fn, goal=None))]
 pub fn graph_bellman_ford_shortest_path_lengths(
     py: Python,
     graph: &graph::PyGraph,