diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bae36930..4b0bdc95 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,11 @@ repos: rev: v3.18.3 hooks: - id: commitizen + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.13.0 + hooks: + - id: ruff + args: [--fix] - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d68847a..8a30894f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- General: Switch to use Python 3.10 conventions. ## [0.31.0] - 2025-09-11 ### Changed: diff --git a/bigtree/binarytree/construct.py b/bigtree/binarytree/construct.py index 6deda2ea..250d1ca2 100644 --- a/bigtree/binarytree/construct.py +++ b/bigtree/binarytree/construct.py @@ -1,4 +1,4 @@ -from typing import Sequence, Type, TypeVar +from typing import Sequence, TypeVar from bigtree.node import binarynode from bigtree.utils import assertions @@ -10,7 +10,7 @@ def list_to_binarytree( heapq_list: Sequence[int], - node_type: Type[T] = binarynode.BinaryNode, # type: ignore[assignment] + node_type: type[T] = binarynode.BinaryNode, # type: ignore[assignment] ) -> T: """Construct tree from a list of numbers (int or float) in heapq format. diff --git a/bigtree/dag/construct.py b/bigtree/dag/construct.py index f2b0d007..99eca167 100644 --- a/bigtree/dag/construct.py +++ b/bigtree/dag/construct.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Collection, Dict, List, Mapping, Optional, Tuple, Type, TypeVar +from typing import Any, Collection, Mapping, TypeVar from bigtree.node import dagnode from bigtree.utils import assertions, common, exceptions @@ -18,8 +18,8 @@ def list_to_dag( - relations: Collection[Tuple[str, str]], - node_type: Type[T] = dagnode.DAGNode, # type: ignore[assignment] + relations: Collection[tuple[str, str]], + node_type: type[T] = dagnode.DAGNode, # type: ignore[assignment] ) -> T: """Construct DAG from list of tuples containing parent-child names. Note that node names must be unique. @@ -39,7 +39,7 @@ def list_to_dag( """ assertions.assert_length_not_empty(relations, "Input list", "relations") - node_dict: Dict[str, T] = dict() + node_dict: dict[str, T] = dict() parent_name: str = "" for parent_name, child_name in relations: @@ -53,7 +53,7 @@ def list_to_dag( def dict_to_dag( relation_attrs: Mapping[str, Any], parent_key: str = "parents", - node_type: Type[T] = dagnode.DAGNode, # type: ignore[assignment] + node_type: type[T] = dagnode.DAGNode, # type: ignore[assignment] ) -> T: """Construct DAG from nested dictionary, ``key``: child name, ``value``: dictionary of parent names and attributes. Note that node names must be unique. @@ -82,8 +82,8 @@ def dict_to_dag( """ assertions.assert_length_not_empty(relation_attrs, "Dictionary", "relation_attrs") - node_dict: Dict[str, T] = dict() - _parent_name: Optional[str] = None + node_dict: dict[str, T] = dict() + _parent_name: str | None = None for child_name, node_attrs in relation_attrs.items(): node_attrs = node_attrs.copy() @@ -109,10 +109,10 @@ def dict_to_dag( @exceptions.optional_dependencies_pandas def dataframe_to_dag( data: pd.DataFrame, - child_col: Optional[str] = None, - parent_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, - node_type: Type[T] = dagnode.DAGNode, # type: ignore[assignment] + child_col: str | None = None, + parent_col: str | None = None, + attribute_cols: list[str] | None = None, + node_type: type[T] = dagnode.DAGNode, # type: ignore[assignment] ) -> T: """Construct DAG from pandas DataFrame. Note that node names must be unique. @@ -178,8 +178,8 @@ def dataframe_to_dag( if sum(data[child_col].isnull()): raise ValueError(f"Child name cannot be empty, check column: {child_col}") - node_dict: Dict[str, T] = dict() - _parent_name: Optional[str] = None + node_dict: dict[str, T] = dict() + _parent_name: str | None = None for row in data.reset_index(drop=True).to_dict(orient="index").values(): child_name = row[child_col] diff --git a/bigtree/dag/export.py b/bigtree/dag/export.py index b89f5451..647a7aba 100644 --- a/bigtree/dag/export.py +++ b/bigtree/dag/export.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List, Mapping, Optional, Tuple, TypeVar, Union +from typing import Any, Mapping, TypeVar from bigtree.node import dagnode from bigtree.utils import assertions, common, exceptions, iterators @@ -27,7 +27,7 @@ def dag_to_list( dag: T, -) -> List[Tuple[str, str]]: +) -> list[tuple[str, str]]: """Export DAG to list of tuples containing parent-child names. Examples: @@ -55,9 +55,9 @@ def dag_to_list( def dag_to_dict( dag: T, parent_key: str = "parents", - attr_dict: Optional[Mapping[str, str]] = None, + attr_dict: Mapping[str, str] | None = None, all_attrs: bool = False, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Export DAG to dictionary. Exported dictionary will have key as child name, and values as a dictionary of parent names and node attributes. @@ -106,7 +106,7 @@ def dag_to_dataframe( dag: T, name_col: str = "name", parent_col: str = "parent", - attr_dict: Optional[Mapping[str, str]] = None, + attr_dict: Mapping[str, str] | None = None, all_attrs: bool = False, ) -> pd.DataFrame: """Export DAG to pandas DataFrame. @@ -139,7 +139,7 @@ def dag_to_dataframe( pandas DataFrame of DAG information """ dag = dag.copy() - data_list: List[Dict[str, Any]] = [] + data_list: list[dict[str, Any]] = [] for parent_node, child_node in iterators.dag_iterator(dag): if parent_node.is_root: @@ -165,14 +165,14 @@ def dag_to_dataframe( @exceptions.optional_dependencies_image("pydot") def dag_to_dot( - dag: Union[T, List[T]], + dag: T | list[T], rankdir: str = "TB", - bg_colour: Optional[str] = None, - node_colour: Optional[str] = None, - node_shape: Optional[str] = None, - edge_colour: Optional[str] = None, - node_attr: Optional[str] = None, - edge_attr: Optional[str] = None, + bg_colour: str | None = None, + node_colour: str | None = None, + node_shape: str | None = None, + edge_colour: str | None = None, + node_attr: str | None = None, + edge_attr: str | None = None, ) -> pydot.Dot: r"""Export DAG or list of DAGs to image. Note that node names must be unique. Possible node attributes include style, fillcolor, or shape. diff --git a/bigtree/dag/parsing.py b/bigtree/dag/parsing.py index 64da6d24..d5d83ed7 100644 --- a/bigtree/dag/parsing.py +++ b/bigtree/dag/parsing.py @@ -1,4 +1,4 @@ -from typing import List, Optional, TypeVar +from typing import TypeVar from bigtree.node import dagnode from bigtree.utils import exceptions @@ -10,7 +10,7 @@ T = TypeVar("T", bound=dagnode.DAGNode) -def get_path_dag(from_node: T, to_node: T) -> List[List[T]]: +def get_path_dag(from_node: T, to_node: T) -> list[list[T]]: """Get path from origin node to destination node. Path is inclusive of origin and destination nodes. Examples: @@ -52,9 +52,9 @@ def get_path_dag(from_node: T, to_node: T) -> List[List[T]]: if to_node not in from_node.descendants: raise exceptions.TreeError(f"It is not possible to go to {to_node}") - paths: List[List[T]] = [] + paths: list[list[T]] = [] - def _recursive_path(_node: T, _path: List[T]) -> Optional[List[T]]: + def _recursive_path(_node: T, _path: list[T]) -> list[T] | None: """Get path to specified node. Args: diff --git a/bigtree/node/basenode.py b/bigtree/node/basenode.py index e6f53d24..65042bce 100644 --- a/bigtree/node/basenode.py +++ b/bigtree/node/basenode.py @@ -2,17 +2,7 @@ import copy import heapq -from typing import ( - Any, - Generator, - Iterable, - List, - Mapping, - Optional, - Set, - Tuple, - TypeVar, -) +from typing import Any, Generator, Iterable, Mapping, TypeVar from bigtree.globals import ASSERTIONS from bigtree.utils import exceptions, iterators @@ -126,7 +116,7 @@ class BaseNode: 3. ``set_attrs(attrs: dict)``: Set node attribute name(s) and value(s) 4. ``go_to(node: Self)``: Get a path from own node to another node from same tree 5. ``append(node: Self)``: Add child to node - 6. ``extend(nodes: List[Self])``: Add multiple children to node + 6. ``extend(nodes: list[Self])``: Add multiple children to node 7. ``copy()``: Deep copy self 8. ``sort()``: Sort child nodes 9. ``plot()``: Plot tree in line form @@ -138,12 +128,12 @@ class BaseNode: def __init__( self, - parent: Optional[T] = None, - children: Optional[List[T]] = None, + parent: T | None = None, + children: list[T] | None = None, **kwargs: Any, ): - self.__parent: Optional[T] = None - self.__children: List[T] = [] + self.__parent: T | None = None + self.__children: list[T] = [] if children is None: children = [] self.parent = parent @@ -187,7 +177,7 @@ def __check_parent_loop(self, new_parent: T) -> None: ) @property - def parent(self: T) -> Optional[T]: + def parent(self: T) -> T | None: """Get parent node. Returns: @@ -330,7 +320,7 @@ def __check_children_loop(self: T, new_children: Iterable[T]) -> None: seen_children.append(id(new_child)) @property - def children(self: T) -> Tuple[T, ...]: + def children(self: T) -> tuple[T, ...]: """Get child nodes. Returns: @@ -339,7 +329,7 @@ def children(self: T) -> Tuple[T, ...]: return tuple(self.__children) @children.setter - def children(self: T, new_children: List[T] | Tuple[T] | Set[T]) -> None: + def children(self: T, new_children: list[T] | tuple[T] | set[T]) -> None: """Set child nodes. Args: @@ -457,7 +447,7 @@ def siblings(self: T) -> Iterable[T]: return tuple(child for child in self.parent.children if child is not self) @property - def left_sibling(self: T) -> Optional[T]: + def left_sibling(self: T) -> T | None: """Get sibling left of self. Returns: @@ -470,7 +460,7 @@ def left_sibling(self: T) -> Optional[T]: return self.parent.children[child_idx - 1] @property - def right_sibling(self: T) -> Optional[T]: + def right_sibling(self: T) -> T | None: """Get sibling right of self. Returns: @@ -594,7 +584,7 @@ def from_dict(cls, input_dict: Mapping[str, Any]) -> BaseNode: def describe( self, exclude_attributes: Iterable[str] = (), exclude_prefix: str = "" - ) -> List[Tuple[str, Any]]: + ) -> list[tuple[str, Any]]: """Get node information sorted by attribute name, returns list of tuples. Examples: @@ -679,7 +669,7 @@ def append(self: T, other: T) -> T: other.parent = self return self - def extend(self: T, others: List[T]) -> T: + def extend(self: T, others: list[T]) -> T: """Add others as children of self. Can be chained. Args: @@ -743,7 +733,7 @@ def plot(self, *args: Any, **kwargs: Any) -> plt.Figure: reingold_tilford(self) return plot_tree(self, *args, **kwargs) - def query(self, query: str, debug: bool = False) -> List[T]: + def query(self, query: str, debug: bool = False) -> list[T]: """Query tree using Tree Definition Language. Examples: diff --git a/bigtree/node/binarynode.py b/bigtree/node/binarynode.py index 28650230..1fc6d574 100644 --- a/bigtree/node/binarynode.py +++ b/bigtree/node/binarynode.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Optional, Tuple, TypeVar, Union +from typing import Any, TypeVar from bigtree.globals import ASSERTIONS from bigtree.node import node @@ -62,21 +62,21 @@ class BinaryNode(node.Node): def __init__( self, - name: Union[str, int] = "", - left: Optional[T] = None, - right: Optional[T] = None, - parent: Optional[T] = None, - children: Optional[List[Optional[T]]] = None, + name: str | int = "", + left: T | None = None, + right: T | None = None, + parent: T | None = None, + children: list[T | None] | None = None, **kwargs: Any, ): try: - self.val: Union[str, int] = int(name) + self.val: str | int = int(name) except ValueError: self.val = str(name) self.name = str(name) self._sep = "/" - self.__parent: Optional[T] = None - self.__children: List[Optional[T]] = [None, None] + self.__parent: T | None = None + self.__children: list[T | None] = [None, None] if not children: children = [] if len(children): @@ -112,7 +112,7 @@ def left(self: T) -> T: return self.__children[0] @left.setter - def left(self: T, left_child: Optional[T]) -> None: + def left(self: T, left_child: T | None) -> None: """Set left children. Args: @@ -130,7 +130,7 @@ def right(self: T) -> T: return self.__children[1] @right.setter - def right(self: T, right_child: Optional[T]) -> None: + def right(self: T, right_child: T | None) -> None: """Set right children. Args: @@ -139,7 +139,7 @@ def right(self: T, right_child: Optional[T]) -> None: self.children = [self.left, right_child] # type: ignore @staticmethod - def __check_parent_type(new_parent: Optional[T]) -> None: + def __check_parent_type(new_parent: T | None) -> None: """Check parent type. Args: @@ -151,7 +151,7 @@ def __check_parent_type(new_parent: Optional[T]) -> None: ) @property - def parent(self: T) -> Optional[T]: + def parent(self: T) -> T | None: """Get parent node. Returns: @@ -160,7 +160,7 @@ def parent(self: T) -> Optional[T]: return self.__parent @parent.setter - def parent(self: T, new_parent: Optional[T]) -> None: + def parent(self: T, new_parent: T | None) -> None: """Set parent node. Args: @@ -214,7 +214,7 @@ def parent(self: T, new_parent: Optional[T]) -> None: current_parent.__children[current_child_idx] = self raise exceptions.TreeError(exc_info) from None - def __pre_assign_parent(self: T, new_parent: Optional[T]) -> None: + def __pre_assign_parent(self: T, new_parent: T | None) -> None: """Custom method to check before attaching parent. Can be overridden with `_BinaryNode__pre_assign_parent()`. Args: @@ -222,7 +222,7 @@ def __pre_assign_parent(self: T, new_parent: Optional[T]) -> None: """ pass - def __post_assign_parent(self: T, new_parent: Optional[T]) -> None: + def __post_assign_parent(self: T, new_parent: T | None) -> None: """Custom method to check after attaching parent. Can be overridden with `_BinaryNode__post_assign_parent()`. Args: @@ -230,9 +230,7 @@ def __post_assign_parent(self: T, new_parent: Optional[T]) -> None: """ pass - def __check_children_type( - self: T, new_children: List[Optional[T]] - ) -> List[Optional[T]]: + def __check_children_type(self: T, new_children: list[T | None]) -> list[T | None]: """Check child type. Args: @@ -247,7 +245,7 @@ def __check_children_type( raise ValueError("Children input must have length 2") return new_children - def __check_children_loop(self: T, new_children: List[Optional[T]]) -> None: + def __check_children_loop(self: T, new_children: list[T | None]) -> None: """Check child loop. Args: @@ -281,7 +279,7 @@ def __check_children_loop(self: T, new_children: List[Optional[T]]) -> None: seen_children.append(id(new_child)) @property - def children(self: T) -> Tuple[T, ...]: + def children(self: T) -> tuple[T, ...]: """Get child nodes. Returns: @@ -290,7 +288,7 @@ def children(self: T) -> Tuple[T, ...]: return tuple(self.__children) @children.setter - def children(self: T, _new_children: List[Optional[T]]) -> None: + def children(self: T, _new_children: list[T | None]) -> None: """Set child nodes. Args: @@ -355,7 +353,7 @@ def children(self) -> None: child.parent.__children.remove(child) # type: ignore child.__parent = None - def __pre_assign_children(self: T, new_children: List[Optional[T]]) -> None: + def __pre_assign_children(self: T, new_children: list[T | None]) -> None: """Custom method to check before attaching children. Can be overridden with `_BinaryNode__pre_assign_children()`. Args: @@ -363,7 +361,7 @@ def __pre_assign_children(self: T, new_children: List[Optional[T]]) -> None: """ pass - def __post_assign_children(self: T, new_children: List[Optional[T]]) -> None: + def __post_assign_children(self: T, new_children: list[T | None]) -> None: """Custom method to check after attaching children. Can be overridden with `_BinaryNode__post_assign_children()`. Args: diff --git a/bigtree/node/dagnode.py b/bigtree/node/dagnode.py index 81a8418b..ede174b8 100644 --- a/bigtree/node/dagnode.py +++ b/bigtree/node/dagnode.py @@ -1,7 +1,7 @@ from __future__ import annotations import copy -from typing import Any, Generator, Iterable, List, Mapping, Optional, Tuple, TypeVar +from typing import Any, Generator, Iterable, Mapping, TypeVar from bigtree.globals import ASSERTIONS from bigtree.utils import exceptions, iterators @@ -97,7 +97,7 @@ class DAGNode: 3. ``set_attrs(attrs: dict)``: Set node attribute name(s) and value(s) 4. ``go_to(node: Self)``: Get a path from own node to another node from same DAG 5. ``append(node: Self)``: Add child to node - 6. ``extend(nodes: List[Self])``: Add multiple children to node + 6. ``extend(nodes: list[Self])``: Add multiple children to node 7. ``copy()``: Deep copy self ---- @@ -107,13 +107,13 @@ class DAGNode: def __init__( self, name: str = "", - parents: Optional[List[T]] = None, - children: Optional[List[T]] = None, + parents: list[T] | None = None, + children: list[T] | None = None, **kwargs: Any, ): self.name = name - self.__parents: List[T] = [] - self.__children: List[T] = [] + self.__parents: list[T] = [] + self.__children: list[T] = [] if parents is None: parents = [] if children is None: @@ -152,7 +152,7 @@ def parent(self, new_parent: T) -> None: ) @staticmethod - def __check_parent_type(new_parents: List[T]) -> None: + def __check_parent_type(new_parents: list[T]) -> None: """Check parent type. Args: @@ -163,7 +163,7 @@ def __check_parent_type(new_parents: List[T]) -> None: f"Parents input should be list type, received input type {type(new_parents)}" ) - def __check_parent_loop(self: T, new_parents: List[T]) -> None: + def __check_parent_loop(self: T, new_parents: list[T]) -> None: """Check parent type. Args: @@ -206,7 +206,7 @@ def parents(self: T) -> Iterable[T]: return tuple(self.__parents) @parents.setter - def parents(self: T, new_parents: List[T]) -> None: + def parents(self: T, new_parents: list[T]) -> None: """Set parent node. Args: @@ -236,7 +236,7 @@ def parents(self: T, new_parents: List[T]) -> None: new_parent.__children.remove(self) raise exceptions.TreeError(exc_info) from None - def __pre_assign_parents(self: T, new_parents: List[T]) -> None: + def __pre_assign_parents(self: T, new_parents: list[T]) -> None: """Custom method to check before attaching parent. Can be overridden with `_DAGNode__pre_assign_parent()`. Args: @@ -244,7 +244,7 @@ def __pre_assign_parents(self: T, new_parents: List[T]) -> None: """ pass - def __post_assign_parents(self: T, new_parents: List[T]) -> None: + def __post_assign_parents(self: T, new_parents: list[T]) -> None: """Custom method to check after attaching parent. Can be overridden with `_DAGNode__post_assign_parent()`. Args: @@ -457,7 +457,7 @@ def from_dict(cls, input_dict: Mapping[str, Any]) -> DAGNode: def describe( self, exclude_attributes: Iterable[str] = (), exclude_prefix: str = "" - ) -> List[Tuple[str, Any]]: + ) -> list[tuple[str, Any]]: """Get node information sorted by attribute name, returns list of tuples. Args: @@ -504,7 +504,7 @@ def set_attrs(self, attrs: Mapping[str, Any]) -> None: """ self.__dict__.update(attrs) - def go_to(self: T, node: T) -> List[List[T]]: + def go_to(self: T, node: T) -> list[list[T]]: """Get list of possible paths from current node to specified node from same tree, uses `get_path_dag` function. Args: @@ -526,7 +526,7 @@ def append(self: T, other: T) -> T: other.parents = [self] return self - def extend(self: T, others: List[T]) -> T: + def extend(self: T, others: list[T]) -> T: """Add others as children of self. Can be chained. Args: diff --git a/bigtree/node/node.py b/bigtree/node/node.py index ce3e2999..734cb787 100644 --- a/bigtree/node/node.py +++ b/bigtree/node/node.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections import Counter -from typing import Any, List, TypeVar +from typing import Any, TypeVar from bigtree.node import basenode from bigtree.utils import exceptions @@ -126,16 +126,16 @@ def path_name(self) -> str: sep = ancestors[-1].sep return sep + sep.join([str(node.node_name) for node in reversed(ancestors)]) - def __pre_assign_children(self: T, new_children: List[T]) -> None: + def __pre_assign_children(self: T, new_children: list[T]) -> None: """Custom method to check before attaching children Can be overridden with `_Node__pre_assign_children()` Args: - new_children (List[Self]): new children to be added + new_children (list[Self]): new children to be added """ pass - def __post_assign_children(self: T, new_children: List[T]) -> None: + def __post_assign_children(self: T, new_children: list[T]) -> None: """Custom method to check after attaching children. Can be overridden with `_Node__post_assign_children()`. Args: @@ -184,7 +184,7 @@ def _BaseNode__post_assign_parent(self: T, new_parent: T) -> None: """ self.__post_assign_parent(new_parent) - def _BaseNode__pre_assign_children(self: T, new_children: List[T]) -> None: + def _BaseNode__pre_assign_children(self: T, new_children: list[T]) -> None: """Do not allow duplicate nodes of same path. Args: @@ -204,7 +204,7 @@ def _BaseNode__pre_assign_children(self: T, new_children: List[T]) -> None: f"Attempting to add nodes with same path {duplicate_names_str}" ) - def _BaseNode__post_assign_children(self: T, new_children: List[T]) -> None: + def _BaseNode__post_assign_children(self: T, new_children: list[T]) -> None: """No rules. Args: diff --git a/bigtree/tree/_query.py b/bigtree/tree/_query.py index 96b3210c..bfcabc54 100644 --- a/bigtree/tree/_query.py +++ b/bigtree/tree/_query.py @@ -73,38 +73,38 @@ class QueryTransformer(Transformer): # type: ignore } @staticmethod - def or_clause(args: List[Callable[[T], bool]]) -> Callable[[T], bool]: + def or_clause(args: list[Callable[[T], bool]]) -> Callable[[T], bool]: return lambda node: any(cond(node) for cond in args) @staticmethod - def and_clause(args: List[Callable[[T], bool]]) -> Callable[[T], bool]: + def and_clause(args: list[Callable[[T], bool]]) -> Callable[[T], bool]: return lambda node: all(cond(node) for cond in args) - def condition(self, args: List[Token]) -> Callable[[T], bool]: + def condition(self, args: list[Token]) -> Callable[[T], bool]: attr, op, value = args op_func = self.OPERATORS[op] return lambda node: op_func(attr(node), value) - def string_condition(self, args: List[Token]) -> Callable[[T], bool]: + def string_condition(self, args: list[Token]) -> Callable[[T], bool]: attr, op, value = args op_func = self.OPERATORS[op] return lambda node: op_func(attr(node) or "", value) - def between_condition(self, args: List[Token]) -> Callable[[T], bool]: + def between_condition(self, args: list[Token]) -> Callable[[T], bool]: attr, op, value_from, value_to = args op_func = self.OPERATOR_BETWEEN[op] return lambda node: op_func(attr(node) or float("inf"), value_from, value_to) - def unary(self, args: List[Token]) -> Callable[[T], bool]: + def unary(self, args: list[Token]) -> Callable[[T], bool]: attr = args[0] return lambda node: bool(attr(node)) - def not_predicate(self, args: List[Token]) -> Callable[[T], bool]: + def not_predicate(self, args: list[Token]) -> Callable[[T], bool]: attr = args[0] return lambda node: not attr(node) @staticmethod - def object_attr(args: List[Token]) -> Callable[[T], Any]: + def object_attr(args: list[Token]) -> Callable[[T], Any]: # e.g., ['parent', 'name'] => lambda node: node.parent.name def accessor(node: T) -> Any: obj = node @@ -117,7 +117,7 @@ def accessor(node: T) -> Any: return accessor @staticmethod - def list(args: List[Token]) -> Any: + def list(args: list[Token]) -> Any: return list(args) @staticmethod diff --git a/bigtree/tree/construct/dataframes.py b/bigtree/tree/construct/dataframes.py index c705d87b..4c62a594 100644 --- a/bigtree/tree/construct/dataframes.py +++ b/bigtree/tree/construct/dataframes.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional, Type, TypeVar +from typing import Any, TypeVar from bigtree.node import node from bigtree.tree.construct.dictionaries import add_dict_to_tree_by_name @@ -38,8 +38,8 @@ def add_dataframe_to_tree_by_path( tree: T, data: pd.DataFrame, - path_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + path_col: str | None = None, + attribute_cols: list[str] | None = None, sep: str = "/", duplicate_name_allowed: bool = True, ) -> T: @@ -134,8 +134,8 @@ def add_dataframe_to_tree_by_path( def add_dataframe_to_tree_by_name( tree: T, data: pd.DataFrame, - name_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + name_col: str | None = None, + attribute_cols: list[str] | None = None, ) -> T: """Add attributes to existing tree *in-place*. Adds to existing tree from pandas DataFrame. @@ -201,8 +201,8 @@ def add_dataframe_to_tree_by_name( def add_polars_to_tree_by_path( tree: T, data: pl.DataFrame, - path_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + path_col: str | None = None, + attribute_cols: list[str] | None = None, sep: str = "/", duplicate_name_allowed: bool = True, ) -> T: @@ -299,8 +299,8 @@ def add_polars_to_tree_by_path( def add_polars_to_tree_by_name( tree: T, data: pl.DataFrame, - name_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + name_col: str | None = None, + attribute_cols: list[str] | None = None, ) -> T: """Add attributes to existing tree *in-place*. Adds to existing tree from polars DataFrame. @@ -363,11 +363,11 @@ def add_polars_to_tree_by_name( def dataframe_to_tree( data: pd.DataFrame, - path_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + path_col: str | None = None, + attribute_cols: list[str] | None = None, sep: str = "/", duplicate_name_allowed: bool = True, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from pandas DataFrame using path, return root of tree. @@ -470,11 +470,11 @@ def dataframe_to_tree( def dataframe_to_tree_by_relation( data: pd.DataFrame, - child_col: Optional[str] = None, - parent_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + child_col: str | None = None, + parent_col: str | None = None, + attribute_cols: list[str] | None = None, allow_duplicates: bool = False, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from pandas DataFrame using parent and child names, return root of tree. @@ -553,7 +553,7 @@ def dataframe_to_tree_by_relation( ) root_name = list(root_names)[0] - def _retrieve_attr(_row: Dict[str, Any]) -> Dict[str, Any]: + def _retrieve_attr(_row: dict[str, Any]) -> dict[str, Any]: """Retrieve node attributes from dictionary, remove parent and child column from dictionary. Args: @@ -594,11 +594,11 @@ def _recursive_add_child(parent_node: T) -> None: def polars_to_tree( data: pl.DataFrame, - path_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + path_col: str | None = None, + attribute_cols: list[str] | None = None, sep: str = "/", duplicate_name_allowed: bool = True, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from polars DataFrame using path, return root of tree. @@ -702,11 +702,11 @@ def polars_to_tree( def polars_to_tree_by_relation( data: pl.DataFrame, - child_col: Optional[str] = None, - parent_col: Optional[str] = None, - attribute_cols: Optional[List[str]] = None, + child_col: str | None = None, + parent_col: str | None = None, + attribute_cols: list[str] | None = None, allow_duplicates: bool = False, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from polars DataFrame using parent and child names, return root of tree. @@ -785,7 +785,7 @@ def polars_to_tree_by_relation( ) root_name = list(root_names)[0] - def _retrieve_attr(_row: Dict[str, Any]) -> Dict[str, Any]: + def _retrieve_attr(_row: dict[str, Any]) -> dict[str, Any]: """Retrieve node attributes from dictionary, remove parent and child column from dictionary. Args: diff --git a/bigtree/tree/construct/dictionaries.py b/bigtree/tree/construct/dictionaries.py index 5763e719..263afb57 100644 --- a/bigtree/tree/construct/dictionaries.py +++ b/bigtree/tree/construct/dictionaries.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Mapping, Optional, Type, TypeVar +from typing import Any, Mapping, TypeVar from bigtree.node import node from bigtree.tree.construct.strings import add_path_to_tree @@ -139,7 +139,7 @@ def dict_to_tree( path_attrs: Mapping[str, Any], sep: str = "/", duplicate_name_allowed: bool = True, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from nested dictionary using path, ``key``: path, ``value``: dict of attribute name and attribute value. @@ -229,7 +229,7 @@ def nested_dict_to_tree( node_attrs: Mapping[str, Any], name_key: str = "name", child_key: str = "children", - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from nested recursive dictionary. @@ -265,7 +265,7 @@ def nested_dict_to_tree( node_attrs: node, children, and node attribute information, key: `name_key` and `child_key` value of `name_key` (str): node name - value of `child_key` (List[Mapping[str, Any]]): list of dict containing `name_key` and `child_key` (recursive) + value of `child_key` (list[Mapping[str, Any]]): list of dict containing `name_key` and `child_key` (recursive) name_key: key of node name, value is type str child_key: key of child list, value is type list node_type: node type of tree to be created @@ -276,7 +276,7 @@ def nested_dict_to_tree( assertions.assert_length_not_empty(node_attrs, "Dictionary", "node_attrs") def _recursive_add_child( - child_dict: Mapping[str, Any], parent_node: Optional[T] = None + child_dict: Mapping[str, Any], parent_node: T | None = None ) -> T: """Recursively add child to tree, given child attributes and parent node. @@ -290,7 +290,7 @@ def _recursive_add_child( child_dict = dict(child_dict) node_name = child_dict.pop(name_key) node_children = child_dict.pop(child_key, []) - if not isinstance(node_children, List): + if not isinstance(node_children, list): raise TypeError( f"child_key {child_key} should be List type, received {node_children}" ) @@ -305,8 +305,8 @@ def _recursive_add_child( def nested_dict_key_to_tree( node_attrs: Mapping[str, Mapping[str, Any]], - child_key: Optional[str] = "children", - node_type: Type[T] = node.Node, # type: ignore[assignment] + child_key: str | None = "children", + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from nested recursive dictionary, where the keys are node names. @@ -386,7 +386,7 @@ def nested_dict_key_to_tree( assertions.assert_length(node_attrs, 1, "Dictionary", "node_attrs") def _recursive_add_child( - child_name: str, child_dict: Mapping[str, Any], parent_node: Optional[T] = None + child_name: str, child_dict: Mapping[str, Any], parent_node: T | None = None ) -> T: """Recursively add child to tree, given child attributes and parent node. diff --git a/bigtree/tree/construct/lists.py b/bigtree/tree/construct/lists.py index dec9fbd6..2490c071 100644 --- a/bigtree/tree/construct/lists.py +++ b/bigtree/tree/construct/lists.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections import OrderedDict -from typing import List, Tuple, Type, TypeVar +from typing import TypeVar from bigtree.node import node from bigtree.tree.construct.dataframes import dataframe_to_tree_by_relation @@ -24,10 +24,10 @@ def list_to_tree( - paths: List[str], + paths: list[str], sep: str = "/", duplicate_name_allowed: bool = True, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from list of path strings. @@ -85,9 +85,9 @@ def list_to_tree( @exceptions.optional_dependencies_pandas def list_to_tree_by_relation( - relations: List[Tuple[str, str]], + relations: list[tuple[str, str]], allow_duplicates: bool = False, - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from list of tuple containing parent-child names. diff --git a/bigtree/tree/construct/render.py b/bigtree/tree/construct/render.py index 332cbdb7..434aa158 100644 --- a/bigtree/tree/construct/render.py +++ b/bigtree/tree/construct/render.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from bigtree.node import node @@ -27,8 +27,8 @@ def __init__(self, master: tk.Tk, **kwargs: Any): self.bind("", self.on_button_release) self.tag_configure("highlight", background="lightblue") - self._dragging_item: Optional[str] = None - self._drop_target: Optional[str] = None + self._dragging_item: str | None = None + self._drop_target: str | None = None def on_button_press(self, event: TkEvent) -> None: """Assign dragging item to pressed object""" diff --git a/bigtree/tree/construct/strings.py b/bigtree/tree/construct/strings.py index efe8516d..e72662c2 100644 --- a/bigtree/tree/construct/strings.py +++ b/bigtree/tree/construct/strings.py @@ -2,7 +2,7 @@ import re from collections import defaultdict -from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, Type, TypeVar +from typing import Any, Iterable, Mapping, TypeVar from bigtree.node import node from bigtree.tree import search @@ -22,7 +22,7 @@ def add_path_to_tree( path: str, sep: str = "/", duplicate_name_allowed: bool = True, - node_attrs: Optional[Mapping[str, Any]] = None, + node_attrs: Mapping[str, Any] | None = None, ) -> T: """Add nodes and attributes to existing tree *in-place*, return node of path added. Adds to existing tree from list of path strings. @@ -105,7 +105,7 @@ def add_path_to_tree( def str_to_tree( tree_string: str, tree_prefix_list: Iterable[str] = (), - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: r"""Construct tree from tree string. @@ -183,7 +183,7 @@ def newick_to_tree( tree_string: str, length_attr: str = "length", attr_prefix: str = "&&NHX:", - node_type: Type[T] = node.Node, # type: ignore[assignment] + node_type: type[T] = node.Node, # type: ignore[assignment] ) -> T: """Construct tree from Newick notation, return root of tree. @@ -241,24 +241,24 @@ def newick_to_tree( assertions.assert_length_not_empty(tree_string, "Tree string", "tree_string") # Store results (for tracking) - depth_nodes: Dict[int, List[T]] = defaultdict(list) + depth_nodes: dict[int, list[T]] = defaultdict(list) unlabelled_node_counter: int = 0 current_depth: int = 1 tree_string_idx: int = 0 # Store states (for assertions and checks) current_state: constants.NewickState = constants.NewickState.PARSE_STRING - current_node: Optional[T] = None + current_node: T | None = None cumulative_string: str = "" cumulative_string_value: str = "" def _create_node( - _new_node: Optional[T], + _new_node: T | None, _cumulative_string: str, _unlabelled_node_counter: int, - _depth_nodes: Dict[int, List[T]], + _depth_nodes: dict[int, list[T]], _current_depth: int, - ) -> Tuple[T, int]: + ) -> tuple[T, int]: """Create node at checkpoint. Args: diff --git a/bigtree/tree/export/_stdout.py b/bigtree/tree/export/_stdout.py index 304bc8fc..359283d3 100644 --- a/bigtree/tree/export/_stdout.py +++ b/bigtree/tree/export/_stdout.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import List, Optional, TypeVar, Union +from typing import TypeVar from bigtree.node import node from bigtree.utils.constants import BaseHPrintStyle, BaseVPrintStyle, BorderStyle @@ -35,11 +35,11 @@ def format_node( _node: T, alias: str = "node_name", intermediate_node_name: bool = True, - style: Union[BaseHPrintStyle, BaseVPrintStyle] = default_vstyle, - border_style: Optional[BorderStyle] = None, + style: BaseHPrintStyle | BaseVPrintStyle = default_vstyle, + border_style: BorderStyle | None = None, min_width: int = 0, add_buffer: bool = True, -) -> List[str]: +) -> list[str]: """Format node to be same width, able to customise whether to add border. Args: @@ -72,7 +72,7 @@ def format_node( ) height = len(node_title_lines) - node_display_lines: List[str] = [] + node_display_lines: list[str] = [] if border_style: width += 2 node_display_lines.append( @@ -136,7 +136,7 @@ def format_node( return node_display_lines -def horizontal_join(node_displays: List[List[str]], spacing: int = 0) -> List[str]: +def horizontal_join(node_displays: list[list[str]], spacing: int = 0) -> list[str]: """Horizontally join multiple node displays, for displaying tree vertically. Args: @@ -166,7 +166,7 @@ def horizontal_join(node_displays: List[List[str]], spacing: int = 0) -> List[st return [row_display[k] for k in sorted(row_display)] -def vertical_join(node_displays: List[List[str]]) -> List[str]: +def vertical_join(node_displays: list[list[str]]) -> list[str]: """Vertically join multiple node displays, for displaying tree horizontally. Args: diff --git a/bigtree/tree/export/_yield_tree.py b/bigtree/tree/export/_yield_tree.py index 35f44d35..90a68b41 100644 --- a/bigtree/tree/export/_yield_tree.py +++ b/bigtree/tree/export/_yield_tree.py @@ -1,5 +1,5 @@ import collections -from typing import Iterable, List, Optional, Tuple, Type, TypeVar, Union +from typing import Iterable, TypeVar, Union from bigtree.node import node from bigtree.tree.export._stdout import ( @@ -30,8 +30,8 @@ def _get_style_class( - base_style: Type[TStyle], - style: Union[str, Iterable[str], TStyle], + base_style: type[TStyle], + style: str | Iterable[str] | TStyle, param_name: str, ) -> TStyle: """Get style class from style, which can be a string, style_class, or list of input to style_class. @@ -60,7 +60,7 @@ class BaseYieldTree: def __init__( self, tree: T, - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, style: Union[ str, @@ -69,7 +69,7 @@ def __init__( constants.BaseHPrintStyle, constants.BaseVPrintStyle, ] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = None, + border_style: str | Iterable[str] | constants.BorderStyle | None = None, ): """Initialise yield tree class. @@ -80,13 +80,13 @@ def __init__( style: style of print border_style: style of border """ - self._style_class: Type[constants.BaseStyle] + self._style_class: type[constants.BaseStyle] if node_name_or_path or max_depth: tree = get_subtree(tree, node_name_or_path, max_depth) # Set style style_class = _get_style_class(self._style_class, style, "style") - border_style_class: Optional[constants.BorderStyle] = None + border_style_class: constants.BorderStyle | None = None if border_style: border_style_class = _get_style_class( constants.BorderStyle, border_style, "border_style" @@ -97,7 +97,7 @@ def __init__( self.border_style_class = border_style_class self.space = " " - def yield_tree(self, strip: bool) -> Union[List[str], Iterable[Tuple[str, str, T]]]: + def yield_tree(self, strip: bool) -> list[str] | Iterable[tuple[str, str, T]]: """Yield tree. Args: @@ -113,9 +113,9 @@ class YieldTree(BaseYieldTree): def __init__( self, tree: T, - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, - style: Union[str, Iterable[str], constants.BasePrintStyle] = "const", + style: str | Iterable[str] | constants.BasePrintStyle = "const", ): """Initialise yield tree class. @@ -129,7 +129,7 @@ def __init__( super().__init__(tree, node_name_or_path, max_depth, style, None) self.style_class: constants.BasePrintStyle - def yield_tree(self, strip: bool = True) -> Iterable[Tuple[str, str, T]]: + def yield_tree(self, strip: bool = True) -> Iterable[tuple[str, str, T]]: """Yield tree. Args: @@ -175,12 +175,12 @@ def __init__( self, tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 0, - style: Union[str, Iterable[str], constants.BaseHPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = None, + style: str | Iterable[str] | constants.BaseHPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = None, ): """Initialise yield tree class, yields tree in horizontal fashion. @@ -221,9 +221,7 @@ def __init__( self.intermediate_node_name = intermediate_node_name self.spacing = spacing - def recursive( - self, _node: Union[T, node.Node], _cur_depth: int - ) -> Tuple[List[str], int]: + def recursive(self, _node: T | node.Node, _cur_depth: int) -> tuple[list[str], int]: """Get string for tree horizontally. Recursively iterate the nodes in post-order traversal manner. Args: @@ -325,7 +323,7 @@ def recursive( ) return result, mid + line_buffer - def yield_tree(self, strip: bool = True) -> List[str]: + def yield_tree(self, strip: bool = True) -> list[str]: """Yield tree. Args: @@ -345,12 +343,12 @@ def __init__( self, tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 2, - style: Union[str, Iterable[str], constants.BaseVPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = None, + style: str | Iterable[str] | constants.BaseVPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = None, ): """Initialise yield tree class, yields tree in vertical fashion. @@ -371,7 +369,7 @@ def __init__( self.intermediate_node_name = intermediate_node_name self.spacing = spacing - def recursive(self, _node: Union[T, node.Node]) -> Tuple[List[str], int]: + def recursive(self, _node: T | node.Node) -> tuple[list[str], int]: """Get string for tree vertically. Recursively iterate the nodes in post-order traversal manner. Args: @@ -452,7 +450,7 @@ def recursive(self, _node: Union[T, node.Node]) -> Tuple[List[str], int]: ) return result, mid + line_buffer - def yield_tree(self, strip: bool = False) -> List[str]: + def yield_tree(self, strip: bool = False) -> list[str]: """Yield tree. Args: diff --git a/bigtree/tree/export/dataframes.py b/bigtree/tree/export/dataframes.py index ec8d312b..2354fa82 100644 --- a/bigtree/tree/export/dataframes.py +++ b/bigtree/tree/export/dataframes.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, Optional, TypeVar +from typing import TypeVar from bigtree.node import node from bigtree.utils import common, exceptions @@ -31,10 +31,10 @@ @exceptions.optional_dependencies_pandas def tree_to_dataframe( tree: T, - path_col: Optional[str] = "path", - name_col: Optional[str] = "name", - parent_col: Optional[str] = None, - attr_dict: Optional[Dict[str, str]] = None, + path_col: str | None = "path", + name_col: str | None = "name", + parent_col: str | None = None, + attr_dict: dict[str, str] | None = None, all_attrs: bool = False, max_depth: int = 0, skip_depth: int = 0, @@ -114,10 +114,10 @@ def _recursive_append(_node: T) -> None: @exceptions.optional_dependencies_polars def tree_to_polars( tree: T, - path_col: Optional[str] = "path", - name_col: Optional[str] = "name", - parent_col: Optional[str] = None, - attr_dict: Optional[Dict[str, str]] = None, + path_col: str | None = "path", + name_col: str | None = "name", + parent_col: str | None = None, + attr_dict: dict[str, str] | None = None, all_attrs: bool = False, max_depth: int = 0, skip_depth: int = 0, diff --git a/bigtree/tree/export/dictionaries.py b/bigtree/tree/export/dictionaries.py index afcc95fd..49d39f0b 100644 --- a/bigtree/tree/export/dictionaries.py +++ b/bigtree/tree/export/dictionaries.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional, TypeVar +from typing import Any, TypeVar from bigtree.node import node from bigtree.utils import common @@ -16,14 +16,14 @@ def tree_to_dict( tree: T, - name_key: Optional[str] = "name", - parent_key: Optional[str] = None, - attr_dict: Optional[Dict[str, str]] = None, + name_key: str | None = "name", + parent_key: str | None = None, + attr_dict: dict[str, str] | None = None, all_attrs: bool = False, max_depth: int = 0, skip_depth: int = 0, leaf_only: bool = False, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Export tree to dictionary. All descendants from `tree` will be exported, `tree` can be the root node or child node of tree. @@ -91,10 +91,10 @@ def tree_to_nested_dict( tree: T, name_key: str = "name", child_key: str = "children", - attr_dict: Optional[Dict[str, str]] = None, + attr_dict: dict[str, str] | None = None, all_attrs: bool = False, max_depth: int = 0, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Export tree to nested dictionary. All descendants from `tree` will be exported, `tree` can be the root node or child node of tree. @@ -122,9 +122,9 @@ def tree_to_nested_dict( Returns: Dictionary containing tree information """ - data_dict: Dict[str, List[Dict[str, Any]]] = {} + data_dict: dict[str, list[dict[str, Any]]] = {} - def _recursive_append(_node: T, parent_dict: Dict[str, Any]) -> None: + def _recursive_append(_node: T, parent_dict: dict[str, Any]) -> None: """Recursively iterate through node and its children to export to nested dictionary. Args: @@ -150,11 +150,11 @@ def _recursive_append(_node: T, parent_dict: Dict[str, Any]) -> None: def tree_to_nested_dict_key( tree: T, - child_key: Optional[str] = "children", - attr_dict: Optional[Dict[str, str]] = None, + child_key: str | None = "children", + attr_dict: dict[str, str] | None = None, all_attrs: bool = False, max_depth: int = 0, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Export tree to nested dictionary, where the keys are node names. All descendants from `tree` will be exported, `tree` can be the root node or child node of tree. @@ -185,9 +185,9 @@ def tree_to_nested_dict_key( Returns: Dictionary containing tree information """ - data_dict: Dict[str, Dict[str, Any]] = {} + data_dict: dict[str, dict[str, Any]] = {} - def _recursive_append(_node: T, parent_dict: Dict[str, Any]) -> None: + def _recursive_append(_node: T, parent_dict: dict[str, Any]) -> None: """Recursively iterate through node and its children to export to nested dictionary. Args: diff --git a/bigtree/tree/export/images.py b/bigtree/tree/export/images.py index a59caa5f..b83f7bb7 100644 --- a/bigtree/tree/export/images.py +++ b/bigtree/tree/export/images.py @@ -2,7 +2,7 @@ import collections import re -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar, Union +from typing import Any, Callable, TypeVar from bigtree.node import node from bigtree.tree.export.stdout import yield_tree @@ -44,15 +44,15 @@ @exceptions.optional_dependencies_image("pydot") def tree_to_dot( - tree: Union[T, List[T]], + tree: T | list[T], directed: bool = True, rankdir: str = "TB", - bg_colour: Optional[str] = None, - node_colour: Optional[str] = None, - node_shape: Optional[str] = None, - edge_colour: Optional[str] = None, - node_attr: Callable[[T], Dict[str, Any]] | Optional[str] = None, - edge_attr: Callable[[T], Dict[str, Any]] | Optional[str] = None, + bg_colour: str | None = None, + node_colour: str | None = None, + node_shape: str | None = None, + edge_colour: str | None = None, + node_attr: Callable[[T], dict[str, Any]] | str | None = None, + edge_attr: Callable[[T], dict[str, Any]] | str | None = None, ) -> pydot.Dot: r"""Export tree(s) to pydot.Dot object. Object can be converted to other format, such as png, dot file or dot string. Dot string can be imported to work with networkx. @@ -173,12 +173,12 @@ def tree_to_dot( for _tree in tree: assertions.assert_tree_type(_tree, node.Node, "Node") - name_dict: Dict[str, List[str]] = collections.defaultdict(list) + name_dict: dict[str, list[str]] = collections.defaultdict(list) def _recursive_append( - parent_name: Optional[str], + parent_name: str | None, child_node: T, - _name_dict: Dict[str, List[str]] = name_dict, + _name_dict: dict[str, list[str]] = name_dict, ) -> None: """Recursively iterate through node and its children to export to dot by creating node and edges. @@ -222,7 +222,7 @@ def _recursive_append( def _load_font( - font_family: Optional[str] = None, font_size: int = 12 + font_family: str | None = None, font_size: int = 12 ) -> ImageFont.truetype: if not font_family: from urllib.request import urlopen @@ -243,18 +243,18 @@ def tree_to_pillow_graph( tree: T, node_content: str = "{node_name}", *, - margin: Optional[Dict[str, int]] = None, - height_buffer: Union[int, float] = 20, - width_buffer: Union[int, float] = 10, - font_family: Optional[str] = None, + margin: dict[str, int] | None = None, + height_buffer: int | float = 20, + width_buffer: int | float = 10, + font_family: str | None = None, font_size: int = 12, - font_colour: Union[Tuple[int, int, int], str] = "black", + font_colour: tuple[int, int, int] | str = "black", text_align: str = "center", - bg_colour: Union[Tuple[int, int, int], str] = "white", - rect_margin: Optional[Dict[str, int]] = None, - rect_fill: Union[Tuple[int, int, int], str, mpl.colors.Colormap] = "white", - rect_cmap_attr: Optional[str] = None, - rect_outline: Union[Tuple[int, int, int], str] = "black", + bg_colour: tuple[int, int, int] | str = "white", + rect_margin: dict[str, int] | None = None, + rect_fill: tuple[int, int, int] | str | mpl.colors.Colormap = "white", + rect_cmap_attr: str | None = None, + rect_outline: tuple[int, int, int] | str = "black", rect_width: int = 1, **kwargs: Any, ) -> Image.Image: @@ -334,7 +334,7 @@ def get_node_text(_node: T, _node_content: str) -> str: ) return _node_content - cmap_range: Set[Union[float, int]] = set() + cmap_range: set[float | int] = set() for _, _, _node in yield_tree(tree, **kwargs): l, t, r, b = _draw.multiline_textbbox( (0, 0), get_node_text(_node, node_content), font=font @@ -438,11 +438,11 @@ def tree_to_pillow( tree: T, width: int = 0, height: int = 0, - start_pos: Tuple[int, int] = (10, 10), - font_family: Optional[str] = None, + start_pos: tuple[int, int] = (10, 10), + font_family: str | None = None, font_size: int = 12, - font_colour: Union[Tuple[int, int, int], str] = "black", - bg_colour: Union[Tuple[int, int, int], str] = "white", + font_colour: tuple[int, int, int] | str = "black", + bg_colour: tuple[int, int, int] | str = "white", **kwargs: Any, ) -> Image.Image: """Export tree to PIL.Image.Image object. Object can be converted to other formats, such as jpg, or png. Image will @@ -487,8 +487,8 @@ def tree_to_pillow( # Calculate image dimension from text, otherwise override with argument def get_list_of_text_dimensions( - text_lines: List[str], - ) -> List[Tuple[int, int, int, int]]: + text_lines: list[str], + ) -> list[tuple[int, int, int, int]]: """Get list dimensions. Args: @@ -523,19 +523,19 @@ def get_list_of_text_dimensions( def tree_to_mermaid( tree: T, - title: Optional[str] = None, - theme: Optional[str] = None, + title: str | None = None, + theme: str | None = None, rankdir: str = "TB", line_shape: str = "basis", - node_colour: Optional[str] = None, - node_border_colour: Optional[str] = None, + node_colour: str | None = None, + node_border_colour: str | None = None, node_border_width: float = 1, node_shape: str = "rounded_edge", - node_shape_attr: Callable[[T], str] | Optional[str] = None, + node_shape_attr: Callable[[T], str] | str | None = None, edge_arrow: str = "normal", - edge_arrow_attr: Callable[[T], str] | Optional[str] = None, - edge_label: Optional[str] = None, - node_attr: Callable[[T], str] | Optional[str] = None, + edge_arrow_attr: Callable[[T], str] | str | None = None, + edge_label: str | None = None, + node_attr: Callable[[T], str] | str | None = None, **kwargs: Any, ) -> str: r"""Export tree to mermaid Markdown text. Accepts additional keyword arguments as input to `yield_tree`. diff --git a/bigtree/tree/export/stdout.py b/bigtree/tree/export/stdout.py index 221c35c4..bd85a1dd 100644 --- a/bigtree/tree/export/stdout.py +++ b/bigtree/tree/export/stdout.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Collection, Iterable, List, Optional, Tuple, TypeVar, Union +from typing import Any, Collection, Iterable, TypeVar from bigtree.node import node from bigtree.utils import common, constants @@ -21,13 +21,13 @@ def print_tree( tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, all_attrs: bool = False, - attr_list: Optional[Iterable[str]] = None, + attr_list: Iterable[str] | None = None, attr_omit_null: bool = False, attr_bracket: Collection[str] = ("[", "]"), - style: Union[str, Iterable[str], constants.BasePrintStyle] = "const", + style: str | Iterable[str] | constants.BasePrintStyle = "const", **kwargs: Any, ) -> None: """Print tree to console, starting from `tree`. Accepts kwargs for print() function. @@ -38,12 +38,12 @@ def print_tree( - Able to choose which attributes to show or show all attributes, using `all_attrs` and `attr_list` - Able to omit showing of attributes if it is null, using `attr_omit_null` - Able to customise open and close brackets if attributes are shown, using `attr_bracket` - - Able to customise style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` + - Able to customise style, to choose from str, list[str], or inherit from constants.BasePrintStyle, using `style` For style, - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - - (List[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters + - (list[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, `RoundedPrintStyle`, `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` @@ -219,20 +219,20 @@ def print_tree( def yield_tree( tree: T, - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, - style: Union[str, Iterable[str], constants.BasePrintStyle] = "const", -) -> Iterable[Tuple[str, str, T]]: + style: str | Iterable[str] | constants.BasePrintStyle = "const", +) -> Iterable[tuple[str, str, T]]: """Generator method for customizing printing of tree, starting from `tree`. - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - Able to customise for maximum depth to print, using `max_depth` - - Able to customise style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` + - Able to customise style, to choose from str, list[str], or inherit from constants.BasePrintStyle, using `style` For style, - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - - (List[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters + - (list[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, `RoundedPrintStyle`, `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` @@ -361,12 +361,12 @@ def yield_tree( def hprint_tree( tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 0, - style: Union[str, Iterable[str], constants.BaseHPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = None, + style: str | Iterable[str] | constants.BaseHPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = None, strip: bool = True, **kwargs: Any, ) -> None: @@ -524,14 +524,14 @@ def hprint_tree( def hyield_tree( tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 0, - style: Union[str, Iterable[str], constants.BaseHPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = None, + style: str | Iterable[str] | constants.BaseHPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = None, strip: bool = True, -) -> List[str]: +) -> list[str]: """Yield tree in horizontal orientation to console, starting from `tree`. - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` @@ -688,12 +688,12 @@ def hyield_tree( def vprint_tree( tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 2, - style: Union[str, Iterable[str], constants.BaseVPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = "const", + style: str | Iterable[str] | constants.BaseVPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = "const", strip: bool = False, **kwargs: Any, ) -> None: @@ -909,14 +909,14 @@ def vprint_tree( def vyield_tree( tree: T, alias: str = "node_name", - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, intermediate_node_name: bool = True, spacing: int = 2, - style: Union[str, Iterable[str], constants.BaseVPrintStyle] = "const", - border_style: Optional[Union[str, Iterable[str], constants.BorderStyle]] = "const", + style: str | Iterable[str] | constants.BaseVPrintStyle = "const", + border_style: str | Iterable[str] | constants.BorderStyle | None = "const", strip: bool = False, -) -> List[str]: +) -> list[str]: """Yield tree in vertical orientation to console, starting from `tree`. - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` @@ -1123,11 +1123,11 @@ def vyield_tree( def tree_to_newick( tree: T, intermediate_node_name: bool = True, - length_attr: Optional[str] = None, - length_sep: Union[str, constants.NewickCharacter] = constants.NewickCharacter.SEP, - attr_list: Optional[Iterable[str]] = None, + length_attr: str | None = None, + length_sep: str | constants.NewickCharacter = constants.NewickCharacter.SEP, + attr_list: Iterable[str] | None = None, attr_prefix: str = "&&NHX:", - attr_sep: Union[str, constants.NewickCharacter] = constants.NewickCharacter.SEP, + attr_sep: str | constants.NewickCharacter = constants.NewickCharacter.SEP, ) -> str: """Export tree to Newick notation. Useful for describing phylogenetic tree. diff --git a/bigtree/tree/export/vis.py b/bigtree/tree/export/vis.py index 803bf4ef..a5f42caa 100644 --- a/bigtree/tree/export/vis.py +++ b/bigtree/tree/export/vis.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Optional, TypeVar +from typing import Any, TypeVar from bigtree.node import node from bigtree.tree.export.stdout import yield_tree @@ -25,12 +25,12 @@ def tree_to_vis( tree: T, alias: str = "node_name", - plot_kwargs: Optional[Dict[str, Any]] = None, - custom_node_kwargs: Optional[Dict[str, str]] = None, - node_kwargs: Optional[Dict[str, Any]] = None, - custom_edge_kwargs: Optional[Dict[str, str]] = None, - edge_kwargs: Optional[Dict[str, Any]] = None, - network_kwargs: Optional[Dict[str, Any]] = None, + plot_kwargs: dict[str, Any] | None = None, + custom_node_kwargs: dict[str, str] | None = None, + node_kwargs: dict[str, Any] | None = None, + custom_edge_kwargs: dict[str, str] | None = None, + edge_kwargs: dict[str, Any] | None = None, + network_kwargs: dict[str, Any] | None = None, **kwargs: Any, ) -> pyvis.network.Network: """Export tree to pyvis for visualisations. diff --git a/bigtree/tree/helper.py b/bigtree/tree/helper.py index ee08dfc9..233d0882 100644 --- a/bigtree/tree/helper.py +++ b/bigtree/tree/helper.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Iterable, List, Optional, Set, Type, TypeVar, Union +from typing import Any, Iterable, TypeVar from bigtree.node import basenode, binarynode, node from bigtree.tree import construct, export, search @@ -23,7 +23,7 @@ NodeT = TypeVar("NodeT", bound=node.Node) -def clone_tree(tree: basenode.BaseNode, node_type: Type[BaseNodeT]) -> BaseNodeT: +def clone_tree(tree: basenode.BaseNode, node_type: type[BaseNodeT]) -> BaseNodeT: """Clone tree to another ``Node`` type. If the same type is needed, simply do a tree.copy(). Examples: @@ -68,7 +68,7 @@ def _recursive_add_child( def get_subtree( tree: NodeT, - node_name_or_path: Optional[str] = None, + node_name_or_path: str | None = None, max_depth: int = 0, ) -> NodeT: """Get subtree based on node name or node path, and/or maximum depth of tree. Subtrees are smaller trees with @@ -124,12 +124,12 @@ def get_subtree( def prune_tree( - tree: Union[BinaryNodeT, NodeT], - prune_path: Optional[Union[Iterable[str], str]] = None, + tree: BinaryNodeT | NodeT, + prune_path: Iterable[str] | str | None = None, exact: bool = False, sep: str = "/", max_depth: int = 0, -) -> Union[BinaryNodeT, NodeT]: +) -> BinaryNodeT | NodeT: """Prune tree by path or depth. Pruned trees are smaller trees with same root. Returns a copy of the tree; does not affect original tree. @@ -215,8 +215,8 @@ def prune_tree( # Prune by path (prune bottom-up) if prune_path: - ancestors_to_prune: Set[Union[BinaryNodeT, NodeT]] = set() - nodes_to_prune: Set[Union[BinaryNodeT, NodeT]] = set() + ancestors_to_prune: set[BinaryNodeT | NodeT] = set() + nodes_to_prune: set[BinaryNodeT | NodeT] = set() for path in prune_path: path = path.replace(sep, tree.sep) child = search.find_path(tree_copy, path) @@ -257,7 +257,7 @@ def get_tree_diff_dataframe( only_diff: bool = True, detail: bool = False, aggregate: bool = False, - attr_list: Optional[List[str]] = None, + attr_list: list[str] | None = None, fallback_sep: str = "/", name_col: str = "name", path_col: str = "path", @@ -437,7 +437,7 @@ def get_tree_diff( only_diff: bool = True, detail: bool = False, aggregate: bool = False, - attr_list: Optional[Iterable[str]] = None, + attr_list: Iterable[str] | None = None, fallback_sep: str = "/", ) -> node.Node: """Get difference of `tree` to `other_tree`, changes are relative to `tree`. @@ -637,7 +637,7 @@ def get_tree_diff( path_to_suffix = dict(zip(data_diff[path_col], data_diff[suffix_col], strict=True)) # Check tree attribute difference - path_attr_diff: Dict[str, Dict[str, Any]] = {} + path_attr_diff: dict[str, dict[str, Any]] = {} if attr_list: data_both = data_diff_all[data_diff_all[indicator_col] == "both"] condition_attr_diff = ( diff --git a/bigtree/tree/modify.py b/bigtree/tree/modify.py index f1cbd9e4..fd01bffb 100644 --- a/bigtree/tree/modify.py +++ b/bigtree/tree/modify.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Collection, Dict, Optional, Sequence, TypeVar +from typing import Any, Collection, Sequence, TypeVar from bigtree.node import node from bigtree.tree import construct, search @@ -125,7 +125,7 @@ def merge_trees( **kwargs, # type: ignore ) else: - path_attrs: Dict[str, Dict[str, Any]] = {} + path_attrs: dict[str, dict[str, Any]] = {} for tree in trees: tree_attr = dict(tree.describe(exclude_prefix="_")) path_attrs[tree.path_name] = { @@ -139,7 +139,7 @@ def merge_trees( def shift_nodes( tree: T, from_paths: Collection[str], - to_paths: Collection[Optional[str]], + to_paths: Collection[str | None], sep: str = "/", skippable: bool = False, overriding: bool = False, @@ -406,7 +406,7 @@ def shift_nodes( def copy_nodes( tree: T, from_paths: Collection[str], - to_paths: Collection[Optional[str]], + to_paths: Collection[str | None], sep: str = "/", skippable: bool = False, overriding: bool = False, @@ -771,7 +771,7 @@ def copy_nodes_from_tree_to_tree( from_tree: T, to_tree: T, from_paths: Collection[str], - to_paths: Collection[Optional[str]], + to_paths: Collection[str | None], sep: str = "/", skippable: bool = False, overriding: bool = False, @@ -1180,7 +1180,7 @@ def _merge_attribute( def copy_or_shift_logic( tree: T, from_paths: Collection[str], - to_paths: Collection[Optional[str]], + to_paths: Collection[str | None], sep: str = "/", copy: bool = False, skippable: bool = False, @@ -1189,7 +1189,7 @@ def copy_or_shift_logic( merge_children: bool = False, merge_leaves: bool = False, delete_children: bool = False, - to_tree: Optional[T] = None, + to_tree: T | None = None, with_full_path: bool = False, ) -> None: """Shift or copy nodes from `from_paths` to `to_paths` *in-place*. @@ -1477,7 +1477,7 @@ def replace_logic( copy: bool = False, skippable: bool = False, delete_children: bool = False, - to_tree: Optional[T] = None, + to_tree: T | None = None, with_full_path: bool = False, ) -> None: """Shift or copy nodes from `from_paths` to *replace* `to_paths` *in-place*. diff --git a/bigtree/tree/parsing.py b/bigtree/tree/parsing.py index e5c77766..5446d6fa 100644 --- a/bigtree/tree/parsing.py +++ b/bigtree/tree/parsing.py @@ -1,4 +1,4 @@ -from typing import Iterable, List, Sequence, TypeVar +from typing import Iterable, Sequence, TypeVar from bigtree.node import basenode from bigtree.utils import exceptions @@ -11,7 +11,7 @@ T = TypeVar("T", bound=basenode.BaseNode) -def get_common_ancestors(nodes: Sequence[T]) -> List[T]: +def get_common_ancestors(nodes: Sequence[T]) -> list[T]: """Get common ancestors between different branches of the same tree. Args: diff --git a/bigtree/tree/query.py b/bigtree/tree/query.py index 082cd6f4..808a9831 100644 --- a/bigtree/tree/query.py +++ b/bigtree/tree/query.py @@ -1,4 +1,4 @@ -from typing import List, TypeVar +from typing import TypeVar from bigtree.node import basenode from bigtree.tree._query import QUERY_GRAMMAR, QueryTransformer @@ -19,7 +19,7 @@ @exceptions.optional_dependencies_query -def query_tree(tree_node: T, query: str, debug: bool = False) -> List[T]: +def query_tree(tree_node: T, query: str, debug: bool = False) -> list[T]: """Query tree using Tree Definition Language. - Supports clauses: AND, OR, NOT diff --git a/bigtree/tree/search.py b/bigtree/tree/search.py index 1c9cf66c..b0fb70a6 100644 --- a/bigtree/tree/search.py +++ b/bigtree/tree/search.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Iterable, List, Tuple, TypeVar, Union +from typing import Any, Callable, Iterable, TypeVar from bigtree.node import basenode, dagnode, node from bigtree.utils import exceptions, iterators @@ -27,7 +27,7 @@ def __check_result_count( - result: Tuple[Any, ...], min_count: int, max_count: int + result: tuple[Any, ...], min_count: int, max_count: int ) -> None: """Check result fulfil min_count and max_count requirements. @@ -54,7 +54,7 @@ def findall( max_depth: int = 0, min_count: int = 0, max_count: int = 0, -) -> Tuple[T, ...]: +) -> tuple[T, ...]: """ Search tree for one or more nodes matching condition (callable function). @@ -209,7 +209,7 @@ def find_relative_paths( path_name: str, min_count: int = 0, max_count: int = 0, -) -> Tuple[NodeT, ...]: +) -> tuple[NodeT, ...]: r""" Search tree for one or more nodes matching relative path attribute. @@ -255,7 +255,7 @@ def find_relative_paths( path_name = path_name.rstrip(sep).lstrip(sep) path_list = path_name.split(sep) wildcard_indicator = "*" in path_name - resolved_nodes: List[NodeT] = [] + resolved_nodes: list[NodeT] = [] def resolve(_node: NodeT, path_idx: int) -> None: """Resolve node based on path name. @@ -455,11 +455,11 @@ def find_attrs( def find_children( - tree: Union[T, DAGNodeT], - condition: Callable[[Union[T, DAGNodeT]], bool], + tree: T | DAGNodeT, + condition: Callable[[T | DAGNodeT], bool], min_count: int = 0, max_count: int = 0, -) -> Tuple[Union[T, DAGNodeT], ...]: +) -> tuple[T | DAGNodeT, ...]: """ Search children for one or more nodes matching condition (callable function). @@ -489,9 +489,9 @@ def find_children( def find_child( - tree: Union[T, DAGNodeT], - condition: Callable[[Union[T, DAGNodeT]], bool], -) -> Union[T, DAGNodeT]: + tree: T | DAGNodeT, + condition: Callable[[T | DAGNodeT], bool], +) -> T | DAGNodeT: """ Search children for a single node matching condition (callable function). @@ -516,9 +516,7 @@ def find_child( return result[0] -def find_child_by_name( - tree: Union[NodeT, DAGNodeT], name: str -) -> Union[NodeT, DAGNodeT]: +def find_child_by_name(tree: NodeT | DAGNodeT, name: str) -> NodeT | DAGNodeT: """ Search tree for a single node matching name attribute. diff --git a/bigtree/utils/assertions.py b/bigtree/utils/assertions.py index d6d8e1c2..28576890 100644 --- a/bigtree/utils/assertions.py +++ b/bigtree/utils/assertions.py @@ -1,16 +1,6 @@ from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Any, - Collection, - Iterable, - List, - Mapping, - Sequence, - Type, - Union, -) +from typing import TYPE_CHECKING, Any, Collection, Iterable, Mapping, Sequence try: import pandas as pd @@ -81,7 +71,7 @@ def assert_str_in_list( def assert_not_reserved_keywords( - parameter_dict_or_df: Union[Iterable[str], Mapping[str, Any], pd.DataFrame], + parameter_dict_or_df: Iterable[str] | Mapping[str, Any] | pd.DataFrame, reserved_keywords: Sequence[str], ) -> None: """Raise ValueError is parameter is in list/dictionary/dataframe. @@ -161,10 +151,10 @@ def assert_dataframe_not_empty(data: pd.DataFrame) -> None: def assert_dataframe_no_duplicate_attribute( - data: Union[pd.DataFrame, pl.DataFrame], + data: pd.DataFrame | pl.DataFrame, id_type: str, id_col: str, - attribute_cols: List[str], + attribute_cols: list[str], ) -> None: """Raise ValueError is dataframe contains different attributes for same path. @@ -195,7 +185,7 @@ def assert_dataframe_no_duplicate_attribute( def assert_dataframe_no_duplicate_children( - data: Union[pd.DataFrame, pl.DataFrame], + data: pd.DataFrame | pl.DataFrame, child_col: str, parent_col: str, ) -> None: @@ -234,8 +224,8 @@ def assert_dataframe_no_duplicate_children( def assert_tree_type( - tree: Union[BaseNode, Node, DAGNode], - tree_type: Union[Type[BaseNode], Type[Node], Type[DAGNode]], + tree: BaseNode | Node | DAGNode, + tree_type: type[BaseNode] | type[Node] | type[DAGNode], tree_type_name: str, ) -> None: """Raise TypeError is tree is not of `tree_type`. diff --git a/bigtree/utils/common.py b/bigtree/utils/common.py index 8cfa1063..51f158ae 100644 --- a/bigtree/utils/common.py +++ b/bigtree/utils/common.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Collection, Dict, Mapping, Optional, Tuple, TypeVar, Union +from typing import Any, Collection, Mapping, TypeVar, Union from bigtree.node import dagnode, node @@ -33,7 +33,7 @@ def filter_attributes( node_attributes: Mapping[str, Any], omit_keys: Collection[str], omit_null_values: bool, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Filter node attributes to remove certain keys and/or values. Args: @@ -55,12 +55,12 @@ def filter_attributes( def assemble_attributes( _node: T, - attr_dict: Optional[Mapping[str, str]], + attr_dict: Mapping[str, str] | None, all_attrs: bool, - path_col: Optional[str] = None, - name_col: Optional[str] = None, - parent_col: Optional[Union[str, Tuple[str, Any]]] = None, -) -> Dict[str, Any]: + path_col: str | None = None, + name_col: str | None = None, + parent_col: str | tuple[str, Any] | None = None, +) -> dict[str, Any]: """Assemble attributes of node into a dictionary. Args: diff --git a/bigtree/utils/constants.py b/bigtree/utils/constants.py index eb64732b..43202149 100644 --- a/bigtree/utils/constants.py +++ b/bigtree/utils/constants.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from enum import Enum, auto -from typing import Dict, List, Tuple from bigtree.utils import assertions @@ -47,7 +46,7 @@ class ExportConstants: HORIZONTAL_UP_DOUBLE = "\u2569" HORIZONTAL_DOWN_DOUBLE = "\u2566" - BORDER_STYLES: Dict[str, Tuple[str, str, str, str, str, str]] = { + BORDER_STYLES: dict[str, tuple[str, str, str, str, str, str]] = { "ansi": ("`", "`", "`", "`", "-", "|"), "ascii": ("+", "+", "+", "+", "-", "|"), "const": (DOWN_RIGHT, DOWN_LEFT, UP_RIGHT, UP_LEFT, HORIZONTAL, VERTICAL), @@ -77,7 +76,7 @@ class ExportConstants: ), } - PRINT_STYLES: Dict[str, Tuple[str, str, str]] = { + PRINT_STYLES: dict[str, tuple[str, str, str]] = { "ansi": ("| ", "|-- ", "`-- "), "ascii": ("| ", "|-- ", "+-- "), "const": ( @@ -102,7 +101,7 @@ class ExportConstants: ), } - HPRINT_STYLES: Dict[str, Tuple[str, str, str, str, str, str, str]] = { + HPRINT_STYLES: dict[str, tuple[str, str, str, str, str, str, str]] = { "ansi": ("/", "+", "+", "+", "\\", "|", "-"), "ascii": ("+", "+", "+", "+", "+", "|", "-"), "const": ( @@ -143,7 +142,7 @@ class ExportConstants: ), } - VPRINT_STYLES: Dict[str, Tuple[str, str, str, str, str, str, str]] = { + VPRINT_STYLES: dict[str, tuple[str, str, str, str, str, str, str]] = { "ansi": ("/", "+", "+", "+", "\\", "-", "|"), "ascii": ("+", "+", "+", "+", "+", "-", "|"), "const": ( @@ -333,9 +332,9 @@ def __post_init__(self) -> None: class MermaidConstants: - THEMES: List[str] = ["default", "neutral", "dark", "forest", "base"] - RANK_DIR: List[str] = ["TB", "BT", "LR", "RL"] - LINE_SHAPES: List[str] = [ + THEMES: list[str] = ["default", "neutral", "dark", "forest", "base"] + RANK_DIR: list[str] = ["TB", "BT", "LR", "RL"] + LINE_SHAPES: list[str] = [ "basis", "bumpX", "bumpY", @@ -349,7 +348,7 @@ class MermaidConstants: "stepAfter", "stepBefore", ] - NODE_SHAPES: Dict[str, str] = { + NODE_SHAPES: dict[str, str] = { "rounded_edge": """("{label}")""", "stadium": """(["{label}"])""", "subroutine": """[["{label}"]]""", @@ -364,7 +363,7 @@ class MermaidConstants: "trapezoid_alt": """[\\"{label}"/]""", "double_circle": """((("{label}")))""", } - EDGE_ARROWS: Dict[str, str] = { + EDGE_ARROWS: dict[str, str] = { "normal": "-->", "bold": "==>", "dotted": "-.->", @@ -397,7 +396,7 @@ class NewickCharacter(str, Enum): NODE_SEP = "," @classmethod - def values(cls) -> List[str]: + def values(cls) -> list[str]: return [c.value for c in cls] diff --git a/bigtree/utils/iterators.py b/bigtree/utils/iterators.py index 9bbd8a63..63d3752b 100644 --- a/bigtree/utils/iterators.py +++ b/bigtree/utils/iterators.py @@ -1,15 +1,6 @@ from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Callable, - Iterable, - List, - Optional, - Tuple, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Callable, Iterable, TypeVar, Union if TYPE_CHECKING: from bigtree.node import basenode, binarynode, dagnode @@ -33,7 +24,7 @@ def inorder_iter( tree: BinaryNodeT, - filter_condition: Optional[Callable[[BinaryNodeT], bool]] = None, + filter_condition: Callable[[BinaryNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[BinaryNodeT]: """Iterate through all children of a tree. @@ -83,8 +74,8 @@ def inorder_iter( def preorder_iter( tree: T, - filter_condition: Optional[Callable[[T], bool]] = None, - stop_condition: Optional[Callable[[T], bool]] = None, + filter_condition: Callable[[T], bool] | None = None, + stop_condition: Callable[[T], bool] | None = None, max_depth: int = 0, ) -> Iterable[T]: """Iterate through all children of a tree. @@ -144,8 +135,8 @@ def preorder_iter( def postorder_iter( tree: BaseNodeT, - filter_condition: Optional[Callable[[BaseNodeT], bool]] = None, - stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, + filter_condition: Callable[[BaseNodeT], bool] | None = None, + stop_condition: Callable[[BaseNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. @@ -205,8 +196,8 @@ def postorder_iter( def levelorder_iter( tree: BaseNodeT, - filter_condition: Optional[Callable[[BaseNodeT], bool]] = None, - stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, + filter_condition: Callable[[BaseNodeT], bool] | None = None, + stop_condition: Callable[[BaseNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. @@ -250,7 +241,7 @@ def levelorder_iter( Iterable of nodes """ - def _levelorder_iter(trees: List[BaseNodeT]) -> Iterable[BaseNodeT]: + def _levelorder_iter(trees: list[BaseNodeT]) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. Args: @@ -276,8 +267,8 @@ def _levelorder_iter(trees: List[BaseNodeT]) -> Iterable[BaseNodeT]: def levelordergroup_iter( tree: BaseNodeT, - filter_condition: Optional[Callable[[BaseNodeT], bool]] = None, - stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, + filter_condition: Callable[[BaseNodeT], bool] | None = None, + stop_condition: Callable[[BaseNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. @@ -321,7 +312,7 @@ def levelordergroup_iter( List of iterable of nodes """ - def _levelordergroup_iter(trees: List[BaseNodeT]) -> Iterable[Iterable[BaseNodeT]]: + def _levelordergroup_iter(trees: list[BaseNodeT]) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. Args: @@ -348,8 +339,8 @@ def _levelordergroup_iter(trees: List[BaseNodeT]) -> Iterable[Iterable[BaseNodeT def zigzag_iter( tree: BaseNodeT, - filter_condition: Optional[Callable[[BaseNodeT], bool]] = None, - stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, + filter_condition: Callable[[BaseNodeT], bool] | None = None, + stop_condition: Callable[[BaseNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[BaseNodeT]: """ "Iterate through all children of a tree. @@ -394,7 +385,7 @@ def zigzag_iter( """ def _zigzag_iter( - trees: List[BaseNodeT], reverse_indicator: bool = False + trees: list[BaseNodeT], reverse_indicator: bool = False ) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. @@ -427,8 +418,8 @@ def _zigzag_iter( def zigzaggroup_iter( tree: BaseNodeT, - filter_condition: Optional[Callable[[BaseNodeT], bool]] = None, - stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, + filter_condition: Callable[[BaseNodeT], bool] | None = None, + stop_condition: Callable[[BaseNodeT], bool] | None = None, max_depth: int = 0, ) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. @@ -474,7 +465,7 @@ def zigzaggroup_iter( """ def _zigzaggroup_iter( - trees: List[BaseNodeT], reverse_indicator: bool = False + trees: list[BaseNodeT], reverse_indicator: bool = False ) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. @@ -506,7 +497,7 @@ def _zigzaggroup_iter( yield from _zigzaggroup_iter([tree]) -def dag_iterator(dag: DAGNodeT) -> Iterable[Tuple[DAGNodeT, DAGNodeT]]: +def dag_iterator(dag: DAGNodeT) -> Iterable[tuple[DAGNodeT, DAGNodeT]]: """Iterate through all nodes of a Directed Acyclic Graph (DAG). Note that node names must be unique. Note that DAG must at least have two nodes to be shown on graph. @@ -533,7 +524,7 @@ def dag_iterator(dag: DAGNodeT) -> Iterable[Tuple[DAGNodeT, DAGNodeT]]: """ visited_nodes = set() - def _dag_iterator(node: DAGNodeT) -> Iterable[Tuple[DAGNodeT, DAGNodeT]]: + def _dag_iterator(node: DAGNodeT) -> Iterable[tuple[DAGNodeT, DAGNodeT]]: """Iterate through all children of a DAG. Args: diff --git a/bigtree/utils/plot.py b/bigtree/utils/plot.py index e6f8c7b2..7d0a851e 100644 --- a/bigtree/utils/plot.py +++ b/bigtree/utils/plot.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, TypeVar +from typing import Any, TypeVar from bigtree.node import basenode from bigtree.utils import exceptions, iterators @@ -91,7 +91,7 @@ def reingold_tilford( @exceptions.optional_dependencies_matplotlib def plot_tree( - tree_node: T, *args: Any, ax: Optional[plt.Axes] = None, **kwargs: Any + tree_node: T, *args: Any, ax: plt.Axes | None = None, **kwargs: Any ) -> plt.Figure: """Plot tree in line form. Tree should have `x` and `y` attribute from Reingold Tilford. Accepts existing matplotlib Axes. Accepts args and kwargs for matplotlib.pyplot.plot() function. @@ -346,9 +346,9 @@ def _second_pass( x_offset: float, y_offset: float, reverse: bool, - cum_mod: Optional[float] = 0.0, - max_depth: Optional[int] = None, - x_adjustment: Optional[float] = 0.0, + cum_mod: float | None = 0.0, + max_depth: int | None = None, + x_adjustment: float | None = 0.0, ) -> float: """ Performs pre-order traversal of tree and determines the final `x` and `y` values for each node. Modifies tree in-place. diff --git a/bigtree/workflows/app_calendar.py b/bigtree/workflows/app_calendar.py index 48ee0004..41e73cde 100644 --- a/bigtree/workflows/app_calendar.py +++ b/bigtree/workflows/app_calendar.py @@ -1,7 +1,7 @@ from __future__ import annotations import datetime as dt -from typing import Any, Optional, Union +from typing import Any from bigtree.node import node from bigtree.tree import construct, export, search @@ -63,7 +63,7 @@ def __init__(self, name: str): def add_event( self, event_name: str, - event_datetime: Union[str, dt.datetime], + event_datetime: str | dt.datetime, date_format: str = "%Y-%m-%d %H:%M", **kwargs: Any, ) -> None: @@ -96,9 +96,7 @@ def add_event( ) self.__sorted = False - def delete_event( - self, event_name: str, event_date: Optional[dt.date] = None - ) -> None: + def delete_event(self, event_name: str, event_date: dt.date | None = None) -> None: """Delete event from calendar. Args: diff --git a/bigtree/workflows/app_todo.py b/bigtree/workflows/app_todo.py index 739cf89e..aee535c3 100644 --- a/bigtree/workflows/app_todo.py +++ b/bigtree/workflows/app_todo.py @@ -2,7 +2,7 @@ import json import logging -from typing import Any, List, Union +from typing import Any from bigtree.node import node from bigtree.tree import construct, export, search @@ -125,7 +125,7 @@ def prioritize_list(self, list_name: str) -> None: self._root.children = current_children # type: ignore def add_item( - self, item_name: Union[str, List[str]], list_name: str = "", **kwargs: Any + self, item_name: str | list[str], list_name: str = "", **kwargs: Any ) -> None: """Add items to list. @@ -149,9 +149,7 @@ def add_item( _ = node.Node(_item, parent=list_node, **kwargs) logging.info(f"Created item(s) {', '.join(item_name)}") - def remove_item( - self, item_name: Union[str, List[str]], list_name: str = "" - ) -> None: + def remove_item(self, item_name: str | list[str], list_name: str = "") -> None: """Remove items from list. Args: diff --git a/docs/others/nodes.md b/docs/others/nodes.md index 3bd288e9..660b5563 100644 --- a/docs/others/nodes.md +++ b/docs/others/nodes.md @@ -51,7 +51,6 @@ print_tree(root, attr_list=["population", "percentage"]) import pytest from bigtree import Node -from typing import List class ReadOnlyNode(Node): @@ -65,7 +64,7 @@ class ReadOnlyNode(Node): if self.__readonly: raise RuntimeError("Nodes cannot be reassigned for ReadOnlyNode") - def _Node__pre_assign_children(self, new_children: List[Node]): + def _Node__pre_assign_children(self, new_children: list[Node]): if self.__readonly: raise RuntimeError("Nodes cannot be reassigned for ReadOnlyNode") diff --git a/pyproject.toml b/pyproject.toml index 8c46a3f5..c73c5771 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,18 +171,16 @@ disallow_untyped_calls = false [tool.ruff] line-length = 88 target-version = "py38" +include = ["*.py", "*.pyi"] +exclude = ["assets", "dist", "docs*", "tests"] + +[tool.ruff.lint] select = ["B", "E", "F", "I", "N", "UP", "W"] ignore = [ "E501", # line too long "E741", # ambiguous variable name "F401", # imported but not used / explicit reexport "N802", # function should be lowercase - "UP006", # type annotation use object instead of typing - "UP007", # use | instead of Union "UP015", # unnecessry mode argument for open - "UP035", # typing.List is deprecated "UP037", # Remove quotes from type annotation - "UP045", # use | instead of Optional ] -include = ["*.py", "*.pyi"] -exclude = ["dist", "docs*", "tests"] diff --git a/tests/conftest.py b/tests/conftest.py index 54674228..6cf31242 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import io import logging import sys -from typing import List, Union +from typing import Union import pytest @@ -196,7 +196,7 @@ def assert_print_statement(func, expected, *args, **kwargs): assert actual == expected, f"Expected\n{expected}\nReceived\n{actual}" -def assert_console_output(expected: Union[List[str], str]): +def assert_console_output(expected: Union[list[str], str]): def _decorator(func): def wrapper(caplog, *args, **kwargs): caplog.set_level(logging.DEBUG) diff --git a/tests/utils/test_plot.py b/tests/utils/test_plot.py index 3cd15f6f..79f9ce28 100644 --- a/tests/utils/test_plot.py +++ b/tests/utils/test_plot.py @@ -1,5 +1,5 @@ import unittest -from typing import Any, List +from typing import Any import matplotlib.pyplot as plt import pytest @@ -856,7 +856,7 @@ def test_reingold_tilford(self): assert_x_y_coordinate(self.root, expected, approx=True) -def assert_x_y_coordinate(tree: node.Node, expected: List[Any], approx: bool = False): +def assert_x_y_coordinate(tree: node.Node, expected: list[Any], approx: bool = False): actual = [ ( _node.node_name, @@ -874,7 +874,7 @@ def assert_x_y_coordinate(tree: node.Node, expected: List[Any], approx: bool = F assert actual == expected, f"Expected\n{expected}\nReceived\n{actual}" -def assert_x_mod_shift(tree: node.Node, expected: List[Any], approx: bool = False): +def assert_x_mod_shift(tree: node.Node, expected: list[Any], approx: bool = False): actual = [ ( _node.node_name,