diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cc7dc925..ff890ba8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,7 @@ latest ------ * Include wheel for Python 3.13.0-beta.1 in release. +* Upgrade PyO3 to 0.21. 3.2 (2024-1-8) -------------- diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 729e57df..2c2fb6a4 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -36,11 +36,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "indoc" -version = "1.0.9" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "itoa" @@ -108,6 +114,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "proc-macro2" version = "1.0.64" @@ -119,15 +131,16 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.19.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb88ae05f306b4bfcde40ac4a51dc0b05936a9207a4b75b798c7729c4258a59" +checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "parking_lot", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -136,9 +149,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.19.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554db24f0b3c180a9c0b1268f91287ab3f17c162e15b54caaae5a6b3773396b0" +checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" dependencies = [ "once_cell", "target-lexicon", @@ -146,9 +159,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.19.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922ede8759e8600ad4da3195ae41259654b9c55da4f7eec84a0ccc7d067a70a4" +checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" dependencies = [ "libc", "pyo3-build-config", @@ -156,9 +169,9 @@ dependencies = [ [[package]] name = "pyo3-log" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47b0777feb17f61eea78667d61103758b243a871edc09a7786500a50467b605" +checksum = "2af49834b8d2ecd555177e63b273b708dea75150abc6f5341d0a6e1a9623976c" dependencies = [ "arc-swap", "log", @@ -167,9 +180,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.19.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a5caec6a1dd355964a841fcbeeb1b89fe4146c87295573f94228911af3cc5a2" +checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -179,11 +192,13 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.19.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b78ccbb160db1556cdb6fd96c50334c5d4ec44dc5e0a968d0a1208fa0efa8b" +checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" dependencies = [ + "heck", "proc-macro2", + "pyo3-build-config", "quote", "syn", ] @@ -243,9 +258,9 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -266,9 +281,9 @@ checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unindent" -version = "0.1.11" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "windows-targets" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index bf4f1653..11a8b14d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,11 +9,11 @@ crate-type = ["cdylib", "rlib"] [dependencies] log = "0.4.19" -pyo3-log = "0.8.3" +pyo3-log = "0.10.0" serde_json = "1.0.103" [dependencies.pyo3] -version = "0.19.1" +version = "0.21.2" [features] extension-module = ["pyo3/extension-module"] diff --git a/rust/src/containers.rs b/rust/src/containers.rs index 63ad327a..6f17116d 100644 --- a/rust/src/containers.rs +++ b/rust/src/containers.rs @@ -1,10 +1,9 @@ use crate::importgraph::ImportGraph; use std::collections::HashSet; - pub fn check_containers_exist<'a>( graph: &'a ImportGraph, - containers: &'a HashSet<&'a str>, + containers: &'a HashSet, ) -> Result<(), String> { for container in containers { if !graph.contains_module(container) { diff --git a/rust/src/layers.rs b/rust/src/layers.rs index c490ca60..7b9c1a15 100644 --- a/rust/src/layers.rs +++ b/rust/src/layers.rs @@ -7,15 +7,15 @@ use std::time::Instant; /// A group of layers at the same level in the layering. #[derive(PartialEq, Eq, Hash, Debug)] -pub struct Level<'a> { - pub layers: Vec<&'a str>, +pub struct Level { + pub layers: Vec, pub independent: bool, } pub fn find_illegal_dependencies<'a>( graph: &'a ImportGraph, levels: &'a Vec, - containers: &'a HashSet<&'a str>, + containers: &'a HashSet, ) -> Vec { let mut dependencies: Vec = vec![]; let layers = _layers_from_levels(levels); @@ -50,7 +50,7 @@ pub fn find_illegal_dependencies<'a>( fn _generate_module_permutations<'a>( graph: &'a ImportGraph, levels: &'a [Level], - containers: &'a HashSet<&'a str>, + containers: &'a HashSet, ) -> Vec<(String, String, Option)> { let mut permutations: Vec<(String, String, Option)> = vec![]; @@ -71,7 +71,7 @@ fn _generate_module_permutations<'a>( continue; } - // Build the layers that mustn't import this higher layer. + // Build the layers that mustn't import this higher layer. // That includes: // * lower layers. // * sibling layers, if the layer is independent. @@ -164,7 +164,7 @@ fn _search_for_package_dependency<'a>( fn _layers_from_levels<'a>(levels: &'a Vec) -> Vec<&'a str> { let mut layers: Vec<&str> = vec![]; for level in levels { - layers.extend(level.layers.iter()); + layers.extend(level.layers.iter().map(|s| s.as_str())); } layers } @@ -341,15 +341,19 @@ mod tests { let levels = vec![ Level { independent: true, - layers: vec!["high"], + layers: vec!["high".to_string()], }, Level { independent: true, - layers: vec!["mid_a", "mid_b", "mid_c"], + layers: vec![ + "mid_a".to_string(), + "mid_b".to_string(), + "mid_c".to_string(), + ], }, Level { independent: true, - layers: vec!["low"], + layers: vec!["low".to_string()], }, ]; let containers = HashSet::new(); @@ -416,14 +420,14 @@ mod tests { let levels = vec![ Level { independent: true, - layers: vec!["high"], + layers: vec!["high".to_string()], }, Level { independent: true, - layers: vec!["low"], + layers: vec!["low".to_string()], }, ]; - let containers = HashSet::from(["mypackage"]); + let containers = HashSet::from(["mypackage".to_string()]); let dependencies = find_illegal_dependencies(&graph, &levels, &containers); @@ -473,18 +477,22 @@ mod tests { let levels = vec![ Level { independent: true, - layers: vec!["high"], + layers: vec!["high".to_string()], }, Level { independent: true, - layers: vec!["mid_a", "mid_b", "mid_c"], + layers: vec![ + "mid_a".to_string(), + "mid_b".to_string(), + "mid_c".to_string(), + ], }, Level { independent: true, - layers: vec!["low"], + layers: vec!["low".to_string()], }, ]; - let containers = HashSet::from(["mypackage"]); + let containers = HashSet::from(["mypackage".to_string()]); let perms = _generate_module_permutations(&graph, &levels, &containers); @@ -593,18 +601,22 @@ mod tests { let levels = vec![ Level { independent: true, - layers: vec!["high"], + layers: vec!["high".to_string()], }, Level { independent: false, - layers: vec!["mid_a", "mid_b", "mid_c"], + layers: vec![ + "mid_a".to_string(), + "mid_b".to_string(), + "mid_c".to_string(), + ], }, Level { independent: true, - layers: vec!["low"], + layers: vec!["low".to_string()], }, ]; - let containers = HashSet::from(["mypackage"]); + let containers = HashSet::from(["mypackage".to_string()]); let perms = _generate_module_permutations(&graph, &levels, &containers); @@ -663,15 +675,19 @@ mod tests { let levels = vec![ Level { independent: true, - layers: vec!["high"], + layers: vec!["high".to_string()], }, Level { independent: true, - layers: vec!["medium_a", "medium_b", "medium_c"], + layers: vec![ + "medium_a".to_string(), + "medium_b".to_string(), + "medium_c".to_string(), + ], }, Level { independent: true, - layers: vec!["low"], + layers: vec!["low".to_string()], }, ]; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 396dcbf5..5ab0d772 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -12,31 +12,35 @@ use log::info; use pyo3::create_exception; use pyo3::prelude::*; use pyo3::types::{PyDict, PyFrozenSet, PySet, PyString, PyTuple}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; #[pymodule] -fn _rustgrimp(_py: Python, m: &PyModule) -> PyResult<()> { +fn _rustgrimp(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { pyo3_log::init(); m.add_function(wrap_pyfunction!(find_illegal_dependencies, m)?)?; - m.add("NoSuchContainer", _py.get_type::())?; + m.add("NoSuchContainer", py.get_type_bound::())?; Ok(()) } create_exception!(_rustgrimp, NoSuchContainer, pyo3::exceptions::PyException); #[pyfunction] -pub fn find_illegal_dependencies<'a>( - py: Python<'a>, - levels: &'a PyTuple, - containers: &'a PySet, - importeds_by_importer: &'a PyDict, -) -> PyResult<&'a PyTuple> { +pub fn find_illegal_dependencies<'py>( + py: Python<'py>, + levels: &Bound<'py, PyTuple>, + containers: &Bound<'py, PySet>, + importeds_by_importer: &Bound<'py, PyDict>, +) -> PyResult> { info!("Using Rust to find illegal dependencies."); - let graph = ImportGraph::new(importeds_by_importer.extract()?); + let importeds_by_importer_strings: HashMap> = + importeds_by_importer.extract()?; + let importeds_by_importer_strs = strings_to_strs_hashmap(&importeds_by_importer_strings); + + let graph = ImportGraph::new(importeds_by_importer_strs); let levels_rust = rustify_levels(levels); - let containers_rust: HashSet<&str> = containers.extract()?; + let containers_rust: HashSet = containers.extract()?; if let Err(err) = check_containers_exist(&graph, &containers_rust) { return Err(NoSuchContainer::new_err(err)); @@ -47,12 +51,38 @@ pub fn find_illegal_dependencies<'a>( convert_dependencies_to_python(py, dependencies, &graph) } -fn rustify_levels(levels_python: &PyTuple) -> Vec { +fn strings_to_strs_hashmap<'a>( + string_map: &'a HashMap>, +) -> HashMap<&'a str, HashSet<&'a str>> { + let mut str_map: HashMap<&str, HashSet<&str>> = HashMap::new(); + + for (key, set) in string_map { + let mut str_set: HashSet<&str> = HashSet::new(); + for item in set.iter() { + str_set.insert(item); + } + str_map.insert(key.as_str(), str_set); + } + str_map +} + +fn rustify_levels<'a>(levels_python: &Bound<'a, PyTuple>) -> Vec { let mut rust_levels: Vec = vec![]; for level_python in levels_python.into_iter() { let level_dict = level_python.downcast::().unwrap(); - let layers: HashSet<&str> = level_dict.get_item("layers").unwrap().extract().unwrap(); - let independent: bool = level_dict.get_item("independent").unwrap().extract().unwrap(); + let layers: HashSet = level_dict + .get_item("layers") + .unwrap() + .unwrap() + .extract() + .unwrap(); + + let independent: bool = level_dict + .get_item("independent") + .unwrap() + .unwrap() + .extract() + .unwrap(); rust_levels.push(Level { independent, layers: layers.into_iter().collect(), @@ -61,47 +91,47 @@ fn rustify_levels(levels_python: &PyTuple) -> Vec { rust_levels } -fn convert_dependencies_to_python<'a>( - py: Python<'a>, +fn convert_dependencies_to_python<'py>( + py: Python<'py>, dependencies: Vec, graph: &ImportGraph, -) -> PyResult<&'a PyTuple> { - let mut python_dependencies: Vec<&PyDict> = vec![]; +) -> PyResult> { + let mut python_dependencies: Vec> = vec![]; for rust_dependency in dependencies { - let python_dependency = PyDict::new(py); + let python_dependency = PyDict::new_bound(py); python_dependency.set_item("imported", graph.names_by_id[&rust_dependency.imported])?; python_dependency.set_item("importer", graph.names_by_id[&rust_dependency.importer])?; - let mut python_routes: Vec<&PyDict> = vec![]; + let mut python_routes: Vec> = vec![]; for rust_route in rust_dependency.routes { - let route = PyDict::new(py); - let heads: Vec<&PyString> = rust_route + let route = PyDict::new_bound(py); + let heads: Vec> = rust_route .heads .iter() - .map(|i| PyString::new(py, graph.names_by_id[&i])) + .map(|i| PyString::new_bound(py, graph.names_by_id[&i])) .collect(); - route.set_item("heads", PyFrozenSet::new(py, &heads)?)?; - let middle: Vec<&PyString> = rust_route + route.set_item("heads", PyFrozenSet::new_bound(py, &heads)?)?; + let middle: Vec> = rust_route .middle .iter() - .map(|i| PyString::new(py, graph.names_by_id[&i])) + .map(|i| PyString::new_bound(py, graph.names_by_id[&i])) .collect(); - route.set_item("middle", PyTuple::new(py, &middle))?; - let tails: Vec<&PyString> = rust_route + route.set_item("middle", PyTuple::new_bound(py, &middle))?; + let tails: Vec> = rust_route .tails .iter() - .map(|i| PyString::new(py, graph.names_by_id[&i])) + .map(|i| PyString::new_bound(py, graph.names_by_id[&i])) .collect(); - route.set_item("tails", PyFrozenSet::new(py, &tails)?)?; + route.set_item("tails", PyFrozenSet::new_bound(py, &tails)?)?; python_routes.push(route); } - python_dependency.set_item("routes", PyTuple::new(py, python_routes))?; + python_dependency.set_item("routes", PyTuple::new_bound(py, python_routes))?; python_dependencies.push(python_dependency) } - Ok(PyTuple::new(py, python_dependencies)) + Ok(PyTuple::new_bound(py, python_dependencies)) } #[cfg(test)] @@ -113,7 +143,7 @@ mod tests { macro_rules! pydict { ($py: ident, {$($k: expr => $v: expr),*, $(,)?}) => { { - let dict = PyDict::new($py); + let dict = PyDict::new_bound($py); $( dict.set_item($k, $v)?; )* @@ -126,7 +156,7 @@ mod tests { fn test_rustify_levels_no_sibling_layers() { pyo3::prepare_freethreaded_python(); Python::with_gil(|py| -> PyResult<()> { - let elements: Vec<&PyDict> = vec![ + let elements = vec![ pydict! (py, { "independent" => true, "layers" => HashSet::from(["high"]), @@ -138,26 +168,26 @@ mod tests { pydict! (py, { "independent" => true, "layers" => HashSet::from(["low"]), - }) + }), ]; - let python_levels: &PyTuple = PyTuple::new(py, elements); + let python_levels = PyTuple::new_bound(py, elements); - let result = rustify_levels(python_levels); + let result = rustify_levels(&python_levels); assert_eq!( result, vec![ Level { independent: true, - layers: vec!["high"] + layers: vec!["high".to_string()] }, Level { independent: true, - layers: vec!["medium"] + layers: vec!["medium".to_string()] }, Level { independent: true, - layers: vec!["low"] + layers: vec!["low".to_string()] } ] ); @@ -171,7 +201,7 @@ mod tests { fn test_rustify_levels_sibling_layers() { pyo3::prepare_freethreaded_python(); Python::with_gil(|py| -> PyResult<()> { - let elements: Vec<&PyDict> = vec![ + let elements = vec![ pydict! (py, { "independent" => true, "layers" => HashSet::from(["high"]), @@ -187,11 +217,11 @@ mod tests { pydict! (py, { "independent" => true, "layers" => HashSet::from(["low"]), - }) + }), ]; - let python_levels: &PyTuple = PyTuple::new(py, elements); + let python_levels = PyTuple::new_bound(py, elements); - let mut result = rustify_levels(python_levels); + let mut result = rustify_levels(&python_levels); for level in &mut result { level.layers.sort(); @@ -201,19 +231,23 @@ mod tests { vec![ Level { independent: true, - layers: vec!["high"] + layers: vec!["high".to_string()] }, Level { independent: true, - layers: vec!["blue", "green", "orange"] + layers: vec![ + "blue".to_string(), + "green".to_string(), + "orange".to_string() + ] }, Level { independent: false, - layers: vec!["red", "yellow"] + layers: vec!["red".to_string(), "yellow".to_string()] }, Level { independent: true, - layers: vec!["low"] + layers: vec!["low".to_string()] } ] ); diff --git a/rust/tests/large.rs b/rust/tests/large.rs index fc030da0..64df80e3 100644 --- a/rust/tests/large.rs +++ b/rust/tests/large.rs @@ -22,26 +22,26 @@ fn test_large_graph() { let levels = vec![ Level { independent: true, - layers: vec!["plugins"], + layers: vec!["plugins".to_string()], }, Level { independent: true, - layers: vec!["interfaces"], + layers: vec!["interfaces".to_string()], }, Level { independent: true, - layers: vec!["application"], + layers: vec!["application".to_string()], }, Level { independent: true, - layers: vec!["domain"], + layers: vec!["domain".to_string()], }, Level { independent: true, - layers: vec!["data"], + layers: vec!["data".to_string()], }, ]; - let containers = HashSet::from(["mypackage"]); + let containers = HashSet::from(["mypackage".to_string()]); find_illegal_dependencies(&graph, &levels, &containers); }