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

Introduce build.rs to reduce boilerplate #1253

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fbc83db
Start using rustworkx_modules functions
IvanIsCoding Jul 14, 2024
7603a25
Add rustworkx_modules for layout and more
IvanIsCoding Jul 14, 2024
c7ff3f0
Even more rustworkx_modules
IvanIsCoding Jul 14, 2024
2f499c0
Even more rustworkx_modules (again!)
IvanIsCoding Jul 14, 2024
99bc71f
Conclude rustworkx_modules refactor
IvanIsCoding Jul 14, 2024
b7ea061
Address two warnings
IvanIsCoding Jul 14, 2024
9eae241
Use build.rs to reduce boilerplate
IvanIsCoding Jul 14, 2024
42c172e
Generate register_rustworkx_modules function
IvanIsCoding Jul 14, 2024
a42f4db
Clippy fix
IvanIsCoding Jul 14, 2024
e5d241c
Minor documentation improvement
IvanIsCoding Jul 15, 2024
ab14db6
Better naming
IvanIsCoding Jul 15, 2024
86018b4
Fix CONTRIBUTING.md
IvanIsCoding Jul 15, 2024
0226161
Merge branch 'smarter-boilerplate' into build-rs
IvanIsCoding Jul 15, 2024
829ff46
Update build.rs to reflect name changes
IvanIsCoding Jul 15, 2024
cd58d4a
Cargo fmt
IvanIsCoding Jul 15, 2024
68dd946
Cargo fmt
IvanIsCoding Jul 15, 2024
8926b5c
Merge branch 'smarter-boilerplate' into build-rs
IvanIsCoding Jul 15, 2024
023145b
Merge branch 'main' into smarter-boilerplate
IvanIsCoding Sep 3, 2024
8d3d562
Merge branch 'main' into build-rs
IvanIsCoding Sep 3, 2024
6048fce
Merge branch 'main' into smarter-boilerplate
IvanIsCoding Sep 5, 2024
09aa7f1
Merge branch 'main' into build-rs
IvanIsCoding Sep 5, 2024
da72eaa
Merge branch 'main' into smarter-boilerplate
IvanIsCoding Sep 19, 2024
be38d3d
Merge branch 'main' into build-rs
IvanIsCoding Sep 19, 2024
fabcb4b
Merge branch 'main' into smarter-boilerplate
IvanIsCoding Oct 4, 2024
e5a741e
Merge branch 'main' into build-rs
IvanIsCoding Oct 4, 2024
7cabc0b
Merge branch 'main' into smarter-boilerplate
IvanIsCoding Oct 11, 2024
8aecd39
Merge branch 'main' into build-rs
IvanIsCoding Oct 11, 2024
f629906
Merge branch 'smarter-boilerplate' into build-rs
IvanIsCoding Oct 13, 2024
7ad4e1d
Merge branch 'main' into build-rs
IvanIsCoding Oct 14, 2024
4754c31
Merge branch 'main' into build-rs
IvanIsCoding Oct 16, 2024
aee290b
Merge branch 'main' into build-rs
IvanIsCoding Oct 31, 2024
99698db
Merge branch 'main' into build-rs
IvanIsCoding Nov 10, 2024
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
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ you for an overview of our simplified source tree:
│ │ └── more_pure_rust_code.rs
```

#### Module exports in `lib.rs`
#### Exporting new functions

To add new functions, you will need to export them in `lib.rs`. `lib.rs` will
import functions defined in Rust modules (see the next section), and export
them to Python using `m.add_wrapped(wrap_pyfunction!(your_new_function))?;`
To add new functions, you will need to export them in the
`export_rustworkx_functions!` statement in the Rust file you are editing.
If the function is not added to that statement, the Rust compiler
will complain about dead-code and Python will not find the function.

#### Adding and changing functions in modules

Expand Down Expand Up @@ -66,7 +67,6 @@ pub fn your_new_function(
> __NOTE:__ If you create a new `your_module.rs`, remember to declare and import it in `lib.rs`:
> ```rust
> mod your_module;
> use your_module::*;
> ```

#### Module directories: when a single file is not enough
Expand Down
93 changes: 93 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::collections::BTreeSet;
use std::env;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::path;

fn main() {
let src_dir_path = env::var("CARGO_MANIFEST_DIR").unwrap();
let src_dir_path = format!("{}/src/", src_dir_path);
let out_dir = fs::read_dir(src_dir_path).expect("could not read src/ directory");

let mut modules = BTreeSet::new();
for entry in out_dir {
let entry = entry.expect("could not read entry");
let path = entry.path();
// Top-level .rs files
if path.is_file() {
let file_name = path.to_str().unwrap();
if file_name.ends_with(".rs") && file_name != "lib.rs" {
modules.insert(file_name.to_string());
}
}
if path.is_dir() {
let sub_dir = fs::read_dir(path).expect("could not read subdirectory");
for entry in sub_dir {
let entry = entry.expect("could not read entry");
let path = entry.path();
if path.is_file() {
let file_name = path.to_str().unwrap();
if file_name.ends_with("mod.rs") {
let module_name = file_name.to_string();
modules.insert(module_name);
}
}
}
}
}

// Create the generated file with the modules
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = path::PathBuf::from(out_dir).join("generated_include_rustworkx_modules.rs");

// Create the file and write the contents to it
let mut f = fs::File::create(dest_path).unwrap();

let mut rustworkx_modules = BTreeSet::new();

for path in modules {
let mut file = fs::File::open(path.clone())
.expect("could not open file to check if it declares a rustworkx module");
let mut content = String::new();
file.read_to_string(&mut content)
.expect("could not read contents of the file");
if content.contains("export_rustworkx_functions!") {
rustworkx_modules.insert(module_name_from_file_name(path.clone()));
}
}

writeln!(f, "fn register_rustworkx_everything(m: &pyo3::Bound<pyo3::types::PyModule>) -> pyo3::prelude::PyResult<()>").expect("could not write function signature");
writeln!(f, "{{").expect("could not write function body");

for module in rustworkx_modules {
writeln!(f, "{}::register_rustworkx_functions(m)?;", module.clone())
.expect("could not write to file");
}
writeln!(f, "Ok(())").expect("could not write function body");
writeln!(f, "}}").expect("could not write function body");
}

fn module_name_from_file_name(filename: String) -> String {
if filename.ends_with("mod.rs") {
let parent = path::Path::new(&filename)
.parent()
.expect("could not get parent directory");
let module_name = parent
.file_name()
.expect("could not get file name")
.to_str()
.expect("could not convert to string");
return module_name.to_string();
}
let module_name = path::Path::new(&filename)
.file_name()
.expect("could not get file name")
.to_str()
.expect("could not convert to string");
return module_name
.to_string()
.strip_suffix(".rs")
.expect("could not strip suffix")
.to_string();
}
4 changes: 3 additions & 1 deletion src/bisimulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use hashbrown::hash_map::Entry;
use hashbrown::{HashMap, HashSet};

use crate::iterators::{IndexPartitionBlock, RelationalCoarsestPartition};
use crate::{digraph, Directed, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, Directed, StablePyGraph};
use petgraph::graph;
use petgraph::Direction::{Incoming, Outgoing};

export_rustworkx_functions!(digraph_maximum_bisimulation);

type Block = Vec<graph::NodeIndex>;
type Counterimage = HashMap<graph::NodeIndex, Vec<graph::NodeIndex>>;
type NodeToBlockVec = Vec<Rc<RefCell<FineBlock>>>;
Expand Down
4 changes: 3 additions & 1 deletion src/cartesian_product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// under the License.

use crate::iterators::ProductNodeMap;
use crate::{digraph, graph, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, graph, StablePyGraph};

use hashbrown::HashMap;

Expand All @@ -21,6 +21,8 @@ use petgraph::{algo, EdgeType};
use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(graph_cartesian_product, digraph_cartesian_product);

fn cartesian_product<Ty: EdgeType>(
py: Python,
first: &StablePyGraph<Ty>,
Expand Down
14 changes: 14 additions & 0 deletions src/centrality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use std::convert::TryFrom;

use crate::digraph;
use crate::export_rustworkx_functions;
use crate::graph;
use crate::iterators::{CentralityMapping, EdgeCentralityMapping};
use crate::CostFn;
Expand All @@ -29,6 +30,19 @@ use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use rustworkx_core::centrality;

export_rustworkx_functions!(
graph_betweenness_centrality,
digraph_betweenness_centrality,
graph_closeness_centrality,
digraph_closeness_centrality,
graph_edge_betweenness_centrality,
digraph_edge_betweenness_centrality,
graph_eigenvector_centrality,
digraph_eigenvector_centrality,
graph_katz_centrality,
digraph_katz_centrality
);

/// Compute the betweenness centrality of all nodes in a PyGraph.
///
/// Betweenness centrality of a node :math:`v` is the sum of the
Expand Down
11 changes: 10 additions & 1 deletion src/coloring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// under the License.

use crate::GraphNotBipartite;
use crate::{digraph, graph, EdgeIndex, NodeIndex};
use crate::{digraph, export_rustworkx_functions, graph, EdgeIndex, NodeIndex};

use pyo3::prelude::*;
use pyo3::types::PyDict;
Expand All @@ -27,6 +27,15 @@ use rustworkx_core::coloring::{

pub use rustworkx_core::coloring::ColoringStrategy as ColoringStrategyCore;

export_rustworkx_functions!(
graph_greedy_color,
graph_misra_gries_edge_color,
graph_greedy_edge_color,
graph_bipartite_edge_color,
graph_two_color,
digraph_two_color
);

/// Greedy coloring strategies available for `graph_greedy_color`
///
/// .. list-table:: Strategy description
Expand Down
40 changes: 39 additions & 1 deletion src/connectivity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ mod johnson_simple_cycles;
mod subgraphs;

use super::{
digraph, get_edge_iter_with_weights, graph, score, weight_callable, InvalidNode, NullGraph,
digraph, export_rustworkx_functions, get_edge_iter_with_weights, graph, score, weight_callable,
InvalidNode, NullGraph,
};

use hashbrown::{HashMap, HashSet};
Expand Down Expand Up @@ -46,6 +47,43 @@ use rustworkx_core::coloring::two_color;
use rustworkx_core::connectivity;
use rustworkx_core::dag_algo::longest_path;

export_rustworkx_functions!(
cycle_basis,
simple_cycles,
strongly_connected_components,
digraph_find_cycle,
number_connected_components,
connected_components,
node_connected_component,
is_connected,
number_weakly_connected_components,
weakly_connected_components,
is_weakly_connected,
is_semi_connected,
digraph_adjacency_matrix,
graph_adjacency_matrix,
graph_complement,
digraph_complement,
digraph_is_bipartite,
graph_is_bipartite,
graph_isolates,
digraph_isolates,
chain_decomposition,
biconnected_components,
bridges,
articulation_points,
stoer_wagner_min_cut,
graph_all_simple_paths,
digraph_all_simple_paths,
graph_all_pairs_all_simple_paths,
digraph_all_pairs_all_simple_paths,
graph_longest_simple_path,
digraph_longest_simple_path,
graph_core_number,
digraph_core_number,
connected_subgraphs
);

/// Return a list of cycles which form a basis for cycles of a given PyGraph
///
/// A basis for cycles of a graph is a minimal collection of
Expand Down
19 changes: 18 additions & 1 deletion src/dag_algo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use rustworkx_core::dag_algo::layers as core_layers;
use rustworkx_core::dictmap::InitWithHasher;

use super::iterators::NodeIndices;
use crate::{digraph, DAGHasCycle, InvalidNode, RxPyResult, StablePyGraph};
use crate::{
digraph, export_rustworkx_functions, DAGHasCycle, InvalidNode, RxPyResult, StablePyGraph,
};

use rustworkx_core::dag_algo::collect_bicolor_runs as core_collect_bicolor_runs;
use rustworkx_core::dag_algo::collect_runs as core_collect_runs;
Expand All @@ -37,6 +39,21 @@ use petgraph::visit::NodeIndexable;

use num_traits::{Num, Zero};

export_rustworkx_functions!(
dag_longest_path,
dag_longest_path_length,
dag_weighted_longest_path,
dag_weighted_longest_path_length,
is_directed_acyclic_graph,
transitive_reduction,
topological_sort,
topological_generations,
lexicographical_topological_sort,
collect_runs,
collect_bicolor_runs,
layers
);

/// Calculate the longest path in a directed acyclic graph (DAG).
///
/// This function interfaces with the Python `PyDiGraph` object to compute the longest path
Expand Down
4 changes: 3 additions & 1 deletion src/graphml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use pyo3::exceptions::PyException;
use pyo3::prelude::*;
use pyo3::PyErr;

use crate::{digraph::PyDiGraph, graph::PyGraph, StablePyGraph};
use crate::{digraph::PyDiGraph, export_rustworkx_functions, graph::PyGraph, StablePyGraph};

export_rustworkx_functions!(read_graphml);

pub enum Error {
Xml(String),
Expand Down
11 changes: 10 additions & 1 deletion src/isomorphism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,22 @@

mod vf2;

use crate::{digraph, graph};
use crate::{digraph, export_rustworkx_functions, graph};

use std::cmp::Ordering;

use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(
graph_is_isomorphic,
digraph_is_isomorphic,
graph_is_subgraph_isomorphic,
digraph_is_subgraph_isomorphic,
digraph_vf2_mapping,
graph_vf2_mapping
);

/// Determine if 2 directed graphs are isomorphic
///
/// This checks if 2 graphs are isomorphic both structurally and also
Expand Down
9 changes: 8 additions & 1 deletion src/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@ mod node_link_data;
use std::fs::File;
use std::io::BufReader;

use crate::{digraph, graph, JSONDeserializationError, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, graph, JSONDeserializationError, StablePyGraph};
use petgraph::{algo, Directed, Undirected};

use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(
digraph_node_link_json,
graph_node_link_json,
from_node_link_json_file,
parse_node_link_json
);

/// Parse a node-link format JSON file to generate a graph
///
/// :param path str: The path to the JSON file to load
Expand Down
17 changes: 16 additions & 1 deletion src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod shell;
mod spiral;
mod spring;

use crate::{digraph, graph};
use crate::{digraph, export_rustworkx_functions, graph};
use spring::Point;

use hashbrown::{HashMap, HashSet};
Expand All @@ -27,6 +27,21 @@ use pyo3::Python;

use crate::iterators::Pos2DMapping;

export_rustworkx_functions!(
graph_random_layout,
digraph_random_layout,
graph_bipartite_layout,
digraph_bipartite_layout,
graph_circular_layout,
digraph_circular_layout,
graph_shell_layout,
digraph_shell_layout,
graph_spiral_layout,
digraph_spiral_layout,
graph_spring_layout,
digraph_spring_layout
);

/// Position nodes using Fruchterman-Reingold force-directed algorithm.
///
/// The algorithm simulates a force-directed representation of the network
Expand Down
Loading