From 9cdcf06de82f4f5d72a57b5c785bfc7a62f6c3b4 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 10 Mar 2022 18:14:23 -0500 Subject: [PATCH] pre-commit: pyupgrade with __future__.annotations --- .pre-commit-config.yaml | 17 +++++++++++------ nxontology/imports.py | 13 ++++++++----- nxontology/node.py | 39 ++++++++++++++------------------------- nxontology/ontology.py | 30 +++++++++++++++--------------- nxontology/similarity.py | 12 ++++++------ nxontology/utils.py | 8 +++++--- nxontology/viz.py | 10 ++++++---- pyproject.toml | 3 +++ 8 files changed, 68 insertions(+), 64 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4824a4..882ae2e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,28 +1,33 @@ +default_language_version: + python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: check-merge-conflict - id: debug-statements - id: mixed-line-ending - id: check-case-conflict - id: check-yaml + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [--py37-plus] - repo: https://github.com/timothycrosley/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/python/black - rev: 21.10b0 + rev: 22.1.0 hooks: - id: black - language_version: python3 - repo: https://gitlab.com/pycqa/flake8 rev: 3.9.2 hooks: - id: flake8 - language_version: python3 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.910-1 + rev: v0.931 hooks: - id: mypy args: ["--strict", "--show-error-codes"] diff --git a/nxontology/imports.py b/nxontology/imports.py index 8a3fa96..e609986 100644 --- a/nxontology/imports.py +++ b/nxontology/imports.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import logging from datetime import date from os import PathLike -from typing import AnyStr, BinaryIO, Counter, List, Optional, Tuple, Union, cast +from typing import AnyStr, BinaryIO, Counter, cast import networkx as nx from pronto import Ontology as Prontology # type: ignore [attr-defined] @@ -70,7 +72,7 @@ def from_obo_library(slug: str) -> NXOntology[str]: return nxo -def from_file(handle: Union[BinaryIO, str, "PathLike[AnyStr]"]) -> NXOntology[str]: +def from_file(handle: BinaryIO | str | PathLike[AnyStr]) -> NXOntology[str]: """ Read ontology in OBO, OWL, or JSON (OBO Graphs) format via pronto. @@ -84,7 +86,7 @@ def from_file(handle: Union[BinaryIO, str, "PathLike[AnyStr]"]) -> NXOntology[st def _pronto_edges_for_term( term: Term, default_rel_type: str = "is a" -) -> List[Tuple[Node, Node, str]]: +) -> list[tuple[Node, Node, str]]: """ Extract edges including "is a" relationships for a Pronto term. https://github.com/althonos/pronto/issues/119#issuecomment-956541286 @@ -158,7 +160,7 @@ def pronto_to_multidigraph( def multidigraph_to_digraph( graph: nx.MultiDiGraph, - rel_types: Optional[List[str]] = None, + rel_types: list[str] | None = None, reverse: bool = True, reduce: bool = False, ) -> nx.DiGraph: @@ -216,7 +218,8 @@ def multidigraph_to_digraph( def read_gene_ontology( release: str = "current", source_file: str = "go-basic.json.gz", - rel_types: Optional[List[str]] = [ + rel_types: list[str] + | None = [ "is a", "part of", "regulates", diff --git a/nxontology/node.py b/nxontology/node.py index af2c13b..6c80333 100644 --- a/nxontology/node.py +++ b/nxontology/node.py @@ -2,18 +2,7 @@ import math import warnings -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Generic, - Hashable, - Iterator, - List, - Optional, - Set, - TypeVar, -) +from typing import TYPE_CHECKING, Any, Generic, Hashable, Iterator, TypeVar import networkx as nx @@ -37,7 +26,7 @@ class Node_Info(Freezable, Generic[Node]): without requiring an external corpus to ascertain term frequency. """ - ic_metrics: List[str] = [ + ic_metrics: list[str] = [ "intrinsic_ic", "intrinsic_ic_sanchez", ] @@ -53,7 +42,7 @@ def __init__(self, nxo: NXOntology[Node], node: Node): self.node = node @property - def name(self) -> Optional[str]: + def name(self) -> str | None: """Human readable name / label.""" value = self._get_node_attribute( custom_field="node_name_attribute", default="name" @@ -66,7 +55,7 @@ def name(self) -> Optional[str]: return None if value is None else str(value) @property - def label(self) -> Optional[str]: + def label(self) -> str | None: """Human readable node name / label.""" warnings.warn( "Node_Info.label is deprecated and will be removed. Use Node_Info.name instead.", @@ -76,14 +65,14 @@ def label(self) -> Optional[str]: return self.name @property - def identifier(self) -> Optional[Any]: + def identifier(self) -> Any | None: """Database / machine identifier.""" return self._get_node_attribute( custom_field="node_identifier_attribute", default="identifier" ) @property - def url(self) -> Optional[str]: + def url(self) -> str | None: """Uniform Resource Locator (URL)""" value = self._get_node_attribute( custom_field="node_url_attribute", default="url" @@ -102,19 +91,19 @@ def frozen(self) -> bool: return self.nxo.frozen @property - def data(self) -> Dict[Any, Any]: + def data(self) -> dict[Any, Any]: """Dictionary of node data (properties) for `self.node` in the networkx graph.""" data = self.nxo.graph.nodes[self.node] assert isinstance(data, dict) return data @property - def parents(self) -> Set[Node]: + def parents(self) -> set[Node]: """Direct parent nodes of this node.""" return set(self.nxo.graph.predecessors(self.node)) @property - def parent(self) -> Optional[Node]: + def parent(self) -> Node | None: """ Sole parent of this node, or None if this node is a root. If this node has multiple parents, raise ValueError. @@ -129,13 +118,13 @@ def parent(self) -> Optional[Node]: raise ValueError(f"Node {self!r} has multiple parents.") @property - def children(self) -> Set[Node]: + def children(self) -> set[Node]: """Direct child nodes of this node.""" return set(self.nxo.graph.successors(self.node)) @property # type: ignore [misc] @cache_on_frozen - def ancestors(self) -> Set[Node]: + def ancestors(self) -> set[Node]: """ Get ancestors of node in graph, including the node itself. Ancestors refers to more general concepts in an ontology, @@ -148,7 +137,7 @@ def ancestors(self) -> Set[Node]: @property # type: ignore [misc] @cache_on_frozen - def descendants(self) -> Set[Node]: + def descendants(self) -> set[Node]: """ Get descendants of node in graph, including the node itself. Descendants refers to more specific concepts in an ontology, @@ -171,12 +160,12 @@ def n_descendants(self) -> int: @property # type: ignore [misc] @cache_on_frozen - def roots(self) -> Set[Node]: + def roots(self) -> set[Node]: """Ancestors of this node that are roots (top-level).""" return self.ancestors & self.nxo.roots @property - def leaves(self) -> Set[Node]: + def leaves(self) -> set[Node]: """Descendents of this node that are leaves.""" return self.descendants & self.nxo.leaves diff --git a/nxontology/ontology.py b/nxontology/ontology.py index 65cc5d6..46d906b 100644 --- a/nxontology/ontology.py +++ b/nxontology/ontology.py @@ -3,7 +3,7 @@ import itertools import json import logging -from typing import Any, Dict, Generic, Iterable, List, Optional, Set, cast +from typing import Any, Generic, Iterable, cast import fsspec import networkx as nx @@ -28,10 +28,10 @@ class NXOntology(Freezable, Generic[Node]): Edges should go from general to more specific. """ - def __init__(self, graph: Optional[nx.DiGraph] = None): + def __init__(self, graph: nx.DiGraph | None = None): self.graph = nx.DiGraph(graph) self.check_is_dag() - self._node_info_cache: Dict[Node, Node_Info[Node]] = {} + self._node_info_cache: dict[Node, Node_Info[Node]] = {} def check_is_dag(self) -> None: if nx.is_directed_acyclic_graph(self.graph): @@ -46,7 +46,7 @@ def check_is_dag(self) -> None: ) @property - def name(self) -> Optional[str]: + def name(self) -> str | None: """Short human-readable name for the ontology.""" key = self.graph.graph.get("graph_name_attribute", "name") name = self.graph.graph.get(key) @@ -101,7 +101,7 @@ def add_edge(self, u_of_edge: Node, v_of_edge: Node, **attr: Any) -> None: @property # type: ignore [misc] @cache_on_frozen - def roots(self) -> Set[Node]: + def roots(self) -> set[Node]: """ Return all top-level nodes, including isolates. """ @@ -127,7 +127,7 @@ def root(self) -> Node: @property # type: ignore [misc] @cache_on_frozen - def leaves(self) -> Set[Node]: + def leaves(self) -> set[Node]: """ Return all bottom-level nodes, including isolates. """ @@ -139,7 +139,7 @@ def leaves(self) -> Set[Node]: @property # type: ignore [misc] @cache_on_frozen - def isolates(self) -> Set[Node]: + def isolates(self) -> set[Node]: """ Return disconnected nodes. """ @@ -172,8 +172,8 @@ def similarity_metrics( node_0: Node, node_1: Node, ic_metric: str = "intrinsic_ic_sanchez", - keys: Optional[List[str]] = None, - ) -> Dict[str, Any]: + keys: list[str] | None = None, + ) -> dict[str, Any]: """ Compute intrinsic similarity metrics for two nodes. """ @@ -184,8 +184,8 @@ def compute_similarities( self, source_nodes: Iterable[Node], target_nodes: Iterable[Node], - ic_metrics: List[str] = ["intrinsic_ic_sanchez"], - ) -> Iterable[Dict[str, Any]]: + ic_metrics: list[str] = ["intrinsic_ic_sanchez"], + ) -> Iterable[dict[str, Any]]: """ Yield similarity metric dictionaries for all combinations of source_node-target_node-ic_metric. @@ -251,10 +251,10 @@ def n_edges(self) -> int: def set_graph_attributes( self, *, - graph_name_attribute: Optional[str] = None, - node_name_attribute: Optional[str] = None, - node_identifier_attribute: Optional[str] = None, - node_url_attribute: Optional[str] = None, + graph_name_attribute: str | None = None, + node_name_attribute: str | None = None, + node_identifier_attribute: str | None = None, + node_url_attribute: str | None = None, ) -> None: """ Convenience method to set attributes on the graph that are recognized by nxontology. diff --git a/nxontology/similarity.py b/nxontology/similarity.py index 8ff7974..f55de3f 100644 --- a/nxontology/similarity.py +++ b/nxontology/similarity.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Any, Dict, Generic, List, Optional, Set, Tuple +from typing import TYPE_CHECKING, Any, Generic if TYPE_CHECKING: from nxontology.ontology import NXOntology @@ -49,12 +49,12 @@ def node_1_subsumes_0(self) -> bool: @property # type: ignore [misc] @cache_on_frozen - def common_ancestors(self) -> Set[Node]: + def common_ancestors(self) -> set[Node]: return self.info_0.ancestors & self.info_1.ancestors @property # type: ignore [misc] @cache_on_frozen - def union_ancestors(self) -> Set[Node]: + def union_ancestors(self) -> set[Node]: return self.info_0.ancestors | self.info_1.ancestors @property @@ -91,7 +91,7 @@ def batet_log(self) -> float: # replace negative sign with abs to avoid returning -0.0. return abs(math.log(1 - self.batet) / math.log(self.n_union_ancestors)) - def results(self, keys: Optional[List[str]] = None) -> Dict[str, Any]: + def results(self, keys: list[str] | None = None) -> dict[str, Any]: if keys is None: keys = self.default_results return {key: getattr(self, key) for key in keys} @@ -155,7 +155,7 @@ def node_1_ic_scaled(self) -> float: @property # type: ignore [misc] @cache_on_frozen - def _resnik_mica(self) -> Tuple[float, Optional[Node]]: + def _resnik_mica(self) -> tuple[float, Node | None]: if not self.common_ancestors: return 0.0, None resnik, mica = max( @@ -166,7 +166,7 @@ def _resnik_mica(self) -> Tuple[float, Optional[Node]]: return resnik, mica @property - def mica(self) -> Optional[Node]: + def mica(self) -> Node | None: """ Most informative common ancestor. None if no common ancestors exist. diff --git a/nxontology/utils.py b/nxontology/utils.py index 5df9d9f..03aae02 100644 --- a/nxontology/utils.py +++ b/nxontology/utils.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import abc import functools -from typing import Callable, Dict, TypeVar +from typing import Callable, TypeVar class Freezable(abc.ABC): @@ -30,9 +32,9 @@ def wrapped(self: T_Freezable) -> T: if not self.frozen: return func(self) try: - method_cache: Dict[str, T] = getattr(self, "__method_cache") + method_cache: dict[str, T] = getattr(self, "__method_cache") except AttributeError: - method_cache: Dict[str, T] = {} # type: ignore [no-redef] + method_cache: dict[str, T] = {} # type: ignore [no-redef] setattr(self, "__method_cache", method_cache) if fname not in method_cache: method_cache[fname] = func(self) diff --git a/nxontology/viz.py b/nxontology/viz.py index ef47e21..6a3be1b 100644 --- a/nxontology/viz.py +++ b/nxontology/viz.py @@ -1,4 +1,6 @@ -from typing import Dict, Iterable, Optional +from __future__ import annotations + +from typing import Iterable from networkx.drawing.nx_agraph import to_agraph from pygraphviz.agraph import AGraph @@ -9,8 +11,8 @@ def create_similarity_graphviz( sim: SimilarityIC[Node], - nodes: Optional[Iterable[Node]] = None, -) -> "AGraph": + nodes: Iterable[Node] | None = None, +) -> AGraph: """ Create a pygraphviz AGraph to render the similarity subgraph with graphviz. Works by creating a subgraph in networkx with the relevant nodes. @@ -88,7 +90,7 @@ def get_verbose_node_label(info: Node_Info[Node]) -> str: return verbose_label -colormap: Dict[int, str] = { +colormap: dict[int, str] = { 0: "#f7fcf5", 1: "#f6fcf4", 2: "#f4fbf2", diff --git a/pyproject.toml b/pyproject.toml index 0e26a84..10d9900 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,9 @@ force_grid_wrap = 0 use_parentheses = true line_length = 88 +[tool.black] +target-version = ['py37', 'py38', 'py39', 'py310'] + # https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml-file [tool.mypy] python_version = "3.7"