From ae72a6a3dc3e748afb4914e82e3bde0b19871d92 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Mon, 21 Oct 2024 14:33:00 +0200 Subject: [PATCH] Activate additional ruff rules --- conftest.py | 12 ++++----- docs/conf.py | 4 +-- pyproject.toml | 30 ++++++++++++++++++++--- test/classes/test_colored_hypergraph.py | 4 +-- test/test_tutorials.py | 6 ++--- toponetx/algorithms/distance.py | 4 +-- toponetx/classes/cell.py | 4 +-- toponetx/classes/cell_complex.py | 6 ++--- toponetx/classes/colored_hypergraph.py | 2 +- toponetx/classes/combinatorial_complex.py | 4 +-- toponetx/classes/simplex.py | 3 +-- toponetx/classes/simplicial_complex.py | 8 +++--- toponetx/datasets/graph.py | 3 ++- toponetx/datasets/mesh.py | 7 +++--- toponetx/datasets/utils.py | 6 ++--- toponetx/readwrite/serialization.py | 11 +++++---- toponetx/utils/normalization.py | 4 ++- 17 files changed, 74 insertions(+), 44 deletions(-) diff --git a/conftest.py b/conftest.py index 5f78ae99..5aec8d14 100644 --- a/conftest.py +++ b/conftest.py @@ -1,10 +1,10 @@ """Configure our testing suite.""" -import networkx -import numpy +import networkx as nx +import numpy as np import pytest -import toponetx +import toponetx as tnx @pytest.fixture(autouse=True) @@ -26,6 +26,6 @@ def doctest_default_imports(doctest_namespace): doctest_namespace : dict The namespace of the doctest. """ - doctest_namespace["np"] = numpy - doctest_namespace["nx"] = networkx - doctest_namespace["tnx"] = toponetx + doctest_namespace["np"] = np + doctest_namespace["nx"] = nx + doctest_namespace["tnx"] = tnx diff --git a/docs/conf.py b/docs/conf.py index f1ab80d5..7d3df136 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ # -- Project information ----------------------------------------------------- project = "TopoNetX" -copyright = "2022-2023, PyT-Team, Inc." +copyright = "2022-2023, PyT-Team, Inc." # noqa: A001 author = "PyT-Team Authors" language = "en" @@ -66,7 +66,7 @@ "url": "https://github.com/pyt-team/TopoNetX", "icon": "fa-brands fa-github", "type": "fontawesome", - } + }, ], "use_edit_page_button": True, } diff --git a/pyproject.toml b/pyproject.toml index 53586814..69e19527 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,9 +80,6 @@ all = [ documentation="https://pyt-team.github.io/toponetx/" source="https://github.com/pyt-team/TopoNetX/" -[tool.ruff] -extend-include = ["*.ipynb"] - [tool.ruff.format] docstring-code-format = true @@ -92,23 +89,50 @@ select = [ "E", # code style "W", # warnings "I", # import order + "D", # pydocstyle rules "UP", # pyupgrade rules + "YTT", # flake8-2020 rules + "S", # bandit rules + "BLE", # blind except "B", # bugbear rules + "A", # builtin shadowing + "COM", # comma rules + "C4", # comprehensions + "DTZ", # datetime rules + "T10", # debugger calls + "FA", # future annotations + "ISC", # implicit str concatenation + "ICN", # import conventions + "LOG", # logging rules + "G", # logging format rules "PIE", # pie rules "Q", # quote rules + "RSE", # raise rules "RET", # return rules + "SLOT", # slot rules "SIM", # code simplifications + "TID", # tidy imports + "TCH", # type checking rules + "PTH", # use pathlib + "PD", # pandas rules + "PLC", # pylint conventions + "PLE", # pylint errors + "FLY", # flynt "NPY", # numpy rules "PERF", # performance rules + "FURB", # refurb "RUF", # miscellaneous rules ] ignore = [ "E501", # line too long + "COM812", # trailing commas; conflict with `ruff format` + "ISC001", # implicitly single-line str concat; conflict with `ruff format` "PERF203", # allow try-except within loops ] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F403"] +"test/**.py" = ["S101"] [tool.ruff.lint.pydocstyle] convention = "numpy" diff --git a/test/classes/test_colored_hypergraph.py b/test/classes/test_colored_hypergraph.py index 665de1e0..0b8f58bd 100644 --- a/test/classes/test_colored_hypergraph.py +++ b/test/classes/test_colored_hypergraph.py @@ -147,9 +147,9 @@ def test_chg_getitem(self): # invalid inputs should raise `KeyError`s as well with pytest.raises(KeyError): - _ = CHG[tuple()] + _ = CHG[()] with pytest.raises(KeyError): - _ = CHG[(tuple(), 0)] + _ = CHG[((), 0)] def test_add_cell(self): """Test adding a cell to a CHG.""" diff --git a/test/test_tutorials.py b/test/test_tutorials.py index ed8c39e8..aa6f53a7 100644 --- a/test/test_tutorials.py +++ b/test/test_tutorials.py @@ -1,8 +1,8 @@ """Unit tests for the tutorials.""" -import glob import subprocess import tempfile +from pathlib import Path import pytest @@ -28,11 +28,11 @@ def _exec_tutorial(path): tmp_file.name, path, ] - subprocess.check_call(args) + subprocess.check_call(args) # noqa: S603 TUTORIALS_DIR = "tutorials" -paths = sorted(glob.glob(f"{TUTORIALS_DIR}/*.ipynb")) +paths = sorted(Path(TUTORIALS_DIR).glob("*.ipynb")) @pytest.mark.parametrize("path", paths) diff --git a/toponetx/algorithms/distance.py b/toponetx/algorithms/distance.py index 75d20041..bb94813c 100644 --- a/toponetx/algorithms/distance.py +++ b/toponetx/algorithms/distance.py @@ -86,7 +86,7 @@ def distance( try: return nx.shortest_path_length(G, rowdict[source], rowdict[target]) except nx.NetworkXNoPath as exc: - raise TopoNetXNoPath() from exc + raise TopoNetXNoPath from exc def cell_distance( @@ -172,4 +172,4 @@ def cell_distance( try: return nx.shortest_path_length(G, cell_dict[source], cell_dict[target]) except nx.NetworkXNoPath as exc: - raise TopoNetXNoPath() from exc + raise TopoNetXNoPath from exc diff --git a/toponetx/classes/cell.py b/toponetx/classes/cell.py index 06ee0ba2..c42a9726 100644 --- a/toponetx/classes/cell.py +++ b/toponetx/classes/cell.py @@ -76,8 +76,8 @@ def __init__(self, elements: Collection, regular: bool = True, **kwargs) -> None for e in self._boundary: if e[0] in _adjdict: raise ValueError( - f" Node {e[0]} is repeated multiple times in the input cell." - + " Input cell violates the cell complex regularity condition." + f"Node {e[0]} is repeated multiple times in the input cell. " + "Input cell violates the cell complex regularity condition." ) _adjdict[e[0]] = e[1] else: diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 620c7f2b..331e42b7 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -324,9 +324,9 @@ def __iter__(self) -> Iterator: Iterator over nodes. """ return chain( - map(lambda x: (x,), self.nodes), + ((x,) for x in self.nodes), self.edges, - map(lambda cell: tuple(cell), self.cells), + (tuple(cell) for cell in self.cells), ) def __contains__(self, item) -> bool: @@ -624,7 +624,7 @@ def cell_neighbors(self, cell, s: int = 1): list List of cell neighbors. """ - raise NotImplementedError() + raise NotImplementedError def remove_node(self, node: Hashable) -> None: """Remove the given node from the cell complex. diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 74336dac..b23f7ac1 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -1355,7 +1355,7 @@ def from_trimesh(cls, mesh: trimesh.Trimesh): >>> CHG = tnx.ColoredHyperGraph.from_trimesh(mesh) >>> CHG.nodes """ - raise NotImplementedError() + raise NotImplementedError def restrict_to_cells(self, cell_set): """Construct a Colored Hypergraph using a subset of the cells. diff --git a/toponetx/classes/combinatorial_complex.py b/toponetx/classes/combinatorial_complex.py index f223f597..d05c39fb 100644 --- a/toponetx/classes/combinatorial_complex.py +++ b/toponetx/classes/combinatorial_complex.py @@ -599,13 +599,13 @@ def _CCC_condition(self, hyperedge_: set, rank: int) -> None: if rank > e_rank and existing_hyperedge.issuperset(hyperedge_): raise ValueError( "a violation of the combinatorial complex condition:" - + f"the hyperedge {existing_hyperedge} in the complex has rank {e_rank} is larger than {rank}, the rank of the input hyperedge {hyperedge_} " + f"the hyperedge {existing_hyperedge} in the complex has rank {e_rank} is larger than {rank}, the rank of the input hyperedge {hyperedge_} " ) if rank < e_rank and hyperedge_.issuperset(existing_hyperedge): raise ValueError( "violation of the combinatorial complex condition : " - + f"the hyperedge {existing_hyperedge} in the complex has rank {e_rank} is smaller than {rank}, the rank of the input hyperedge {hyperedge_} " + f"the hyperedge {existing_hyperedge} in the complex has rank {e_rank} is smaller than {rank}, the rank of the input hyperedge {hyperedge_} " ) def _add_hyperedge(self, hyperedge, rank, **attr): diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 0bb5236d..d28056d5 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -126,7 +126,6 @@ def construct_simplex_tree(elements: Collection) -> frozenset["Simplex"]: frozenset[Simplex] The set of faces of the simplex. """ - faceset = set() for r in range(len(elements), 0, -1): for face in combinations(elements, r): @@ -165,7 +164,7 @@ def sign(self, face) -> int: face : Simplex A face of the simplex. """ - raise NotImplementedError() + raise NotImplementedError @property @deprecated( diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 8c9c8e4a..66a3a4eb 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -877,7 +877,10 @@ def incidence_matrix( values.append((-1) ** i) face = frozenset(simplex).difference({left_out}) idx_faces.append(simplex_dict_d_minus_1[tuple(sorted(face))]) - assert len(values) == (rank + 1) * len(simplex_dict_d) + + if len(values) != (rank + 1) * len(simplex_dict_d): + raise RuntimeError("Error in computing the incidence matrix.") + boundary = csr_matrix( (values, (idx_faces, idx_simplices)), dtype=np.float32, @@ -1313,8 +1316,7 @@ def add_elements_from_nx_graph(self, G: nx.Graph) -> None: G : networkx.Graph A networkx graph instance. """ - _simplices = list(G.edges) + list(map(lambda n: [n], G.nodes)) - + _simplices = [[n] for n in G.nodes] + list(G.edges) self.add_simplices_from(_simplices) def restrict_to_simplices(self, cell_set) -> Self: diff --git a/toponetx/datasets/graph.py b/toponetx/datasets/graph.py index 0d484293..714d3e44 100644 --- a/toponetx/datasets/graph.py +++ b/toponetx/datasets/graph.py @@ -184,7 +184,8 @@ def coauthorship() -> SimplicialComplex: if not dataset_file.exists(): r = requests.get( - "https://github.com/pyt-team/topological-datasets/raw/main/resources/coauthorship.npy" + "https://github.com/pyt-team/topological-datasets/raw/main/resources/coauthorship.npy", + timeout=10, ) with dataset_file.open("wb") as f: f.write(r.content) diff --git a/toponetx/datasets/mesh.py b/toponetx/datasets/mesh.py index 6d743fe2..d87d3d62 100644 --- a/toponetx/datasets/mesh.py +++ b/toponetx/datasets/mesh.py @@ -78,7 +78,8 @@ def stanford_bunny( if not dataset_file.exists(): r = requests.get( - "https://github.com/pyt-team/topological-datasets/raw/main/resources/bunny.obj" + "https://github.com/pyt-team/topological-datasets/raw/main/resources/bunny.obj", + timeout=10, ) with dataset_file.open("wb") as f: f.write(r.content) @@ -145,7 +146,7 @@ def shrec_16(size: Literal["full", "small"] = "full"): if not training.exists() or not testing.exists(): print(f"downloading shrec 16 {size} dataset...\n") - r = requests.get(url) + r = requests.get(url, timeout=10) with zipfile.ZipFile(BytesIO(r.content)) as zip_ref: zip_ref.extractall(DIR) print("done!") @@ -211,7 +212,7 @@ def coseg(data: Literal["alien", "vase", "chair"] = "alien"): if not unziped_file.exists(): print(f"downloading {data} dataset...\n") - r = requests.get(url) + r = requests.get(url, timeout=10) with zipfile.ZipFile(BytesIO(r.content)) as zip_ref: zip_ref.extractall(DIR) print("done!") diff --git a/toponetx/datasets/utils.py b/toponetx/datasets/utils.py index 55230b49..a937b738 100644 --- a/toponetx/datasets/utils.py +++ b/toponetx/datasets/utils.py @@ -45,13 +45,13 @@ def load_ppi(): ] proteins = "%0d".join(protein_list) url = f"https://string-db.org/api/tsv/network?identifiers={proteins}&species=9606" - r = requests.get(url) + r = requests.get(url, timeout=10) lines = r.text.split("\n") data = [line.split("\t") for line in lines] - df = pd.DataFrame(data[1:-1], columns=data[0]) + protein_df = pd.DataFrame(data[1:-1], columns=data[0]) - interactions = df[["preferredName_A", "preferredName_B", "score"]] + interactions = protein_df[["preferredName_A", "preferredName_B", "score"]] G = nx.Graph(name="Protein Interaction Graph") interactions = np.array(interactions) diff --git a/toponetx/readwrite/serialization.py b/toponetx/readwrite/serialization.py index bb6c9043..54935d73 100644 --- a/toponetx/readwrite/serialization.py +++ b/toponetx/readwrite/serialization.py @@ -1,6 +1,7 @@ """Read and write complexes as pickled objects.""" import pickle +from pathlib import Path __all__ = ["to_pickle", "load_from_pickle"] @@ -12,10 +13,10 @@ def to_pickle(obj, filename: str) -> None: ---------- obj : object Object to write. - filename : str + filename : Path or str Filename. """ - with open(filename, "wb") as f: + with Path(filename).open("wb") as f: pickle.dump(obj, f) @@ -24,7 +25,7 @@ def load_from_pickle(filepath): Parameters ---------- - filepath : str + filepath : Path or str Filepath. Returns @@ -32,5 +33,5 @@ def load_from_pickle(filepath): object Object. """ - with open(filepath, "rb") as f: - return pickle.load(f) + with Path(filepath).open("rb") as f: + return pickle.load(f) # noqa: S301 diff --git a/toponetx/utils/normalization.py b/toponetx/utils/normalization.py index 6bd5abeb..a583f119 100644 --- a/toponetx/utils/normalization.py +++ b/toponetx/utils/normalization.py @@ -65,7 +65,9 @@ def compute_x_laplacian_normalized_matrix(L: csr_matrix, Lx: csr_matrix) -> csr_ This function normalizes the up or down Laplacian matrices by dividing them by the largest eigenvalue of the Laplacian matrix. """ - assert L.shape[0] == L.shape[1] + if not L.shape[0] == L.shape[1]: + raise ValueError("Laplacian matrix must be square.") + topeig = spl.eigsh(L.asfptype(), k=1, which="LM", return_eigenvectors=False)[0] return Lx * (1.0 / topeig)