Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions anytree/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Powerful and Lightweight Python Tree Data Structure."""

__version__ = "2.12.1"
__author__ = "c0fec0de"
__author_email__ = "[email protected]"
__description__ = """Powerful and Lightweight Python Tree Data Structure."""
__url__ = "https://github.com/c0fec0de/anytree"

# pylint: disable=useless-import-alias
from . import cachedsearch as cachedsearch # noqa: PLC0414
from . import util as util # noqa: PLC0414
from .iterators import LevelOrderGroupIter as LevelOrderGroupIter # noqa: PLC0414
from .iterators import LevelOrderIter as LevelOrderIter # noqa: PLC0414
from .iterators import PostOrderIter as PostOrderIter # noqa: PLC0414
from .iterators import PreOrderIter as PreOrderIter # noqa: PLC0414
from .iterators import ZigZagGroupIter as ZigZagGroupIter # noqa: PLC0414
from .node import AnyNode as AnyNode # noqa: PLC0414
from .node import LightNodeMixin as LightNodeMixin # noqa: PLC0414
from .node import LoopError as LoopError # noqa: PLC0414
from .node import Node as Node # noqa: PLC0414
from .node import NodeMixin as NodeMixin # noqa: PLC0414
from .node import SymlinkNode as SymlinkNode # noqa: PLC0414
from .node import SymlinkNodeMixin as SymlinkNodeMixin # noqa: PLC0414
from .node import TreeError as TreeError # noqa: PLC0414
from .render import AbstractStyle as AbstractStyle # noqa: PLC0414
from .render import AsciiStyle as AsciiStyle # noqa: PLC0414
from .render import ContRoundStyle as ContRoundStyle # noqa: PLC0414
from .render import ContStyle as ContStyle # noqa: PLC0414
from .render import DoubleStyle as DoubleStyle # noqa: PLC0414
from .render import RenderTree as RenderTree # noqa: PLC0414
from .resolver import ChildResolverError as ChildResolverError # noqa: PLC0414
from .resolver import Resolver as Resolver # noqa: PLC0414
from .resolver import ResolverError as ResolverError # noqa: PLC0414
from .resolver import RootResolverError as RootResolverError # noqa: PLC0414
from .search import CountError as CountError # noqa: PLC0414
from .search import find as find # noqa: PLC0414
from .search import find_by_attr as find_by_attr # noqa: PLC0414
from .search import findall as findall # noqa: PLC0414
from .search import findall_by_attr as findall_by_attr # noqa: PLC0414
from .walker import Walker as Walker # noqa: PLC0414
from .walker import WalkError as WalkError # noqa: PLC0414

# legacy
LevelGroupOrderIter = LevelOrderGroupIter
37 changes: 35 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ classifiers = [
[dependency-groups]
dev = [
"coveralls>=3.3.1",
"mypy>=1.9.0",
"mypy>=1.17.0",
"pytest-cov>=5.0.0",
"ruff>=0.11.2",
"pre-commit>=4.2.0",
Expand Down Expand Up @@ -194,5 +194,38 @@ exclude_lines = [
]

[tool.mypy]
disable_error_code = "misc"
files = ["src/anytree"]
exclude = [
# TODO: Remove these exclusions add missing type annotations
"src/anytree/importer",
"src/anytree/exporter",
"src/anytree/iterators/preorderiter.py",
"src/anytree/iterators/postorderiter.py",
"src/anytree/iterators/levelorderiter.py",
"src/anytree/iterators/zigzaggroupiter.py",
"src/anytree/iterators/levelordergroupiter.py",
"src/anytree/util",
"src/anytree/dotexport.py",
"src/anytree/cachedsearch.py",
"src/anytree/resolver.py",
"src/anytree/search.py",
"src/anytree/render.py",
"src/anytree/walker.py",
]
check_untyped_defs = true
disallow_any_generics = true
disallow_untyped_calls = true
disallow_untyped_defs = true
ignore_missing_imports = true
no_implicit_optional = true
no_implicit_reexport = true
show_column_numbers = true
show_error_codes = true
show_traceback = true
strict = true
strict_equality = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
warn_unused_ignores = true
12 changes: 6 additions & 6 deletions src/anytree/iterators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
* :any:`ZigZagGroupIter`: iterate over tree using level-order strategy returning group for every level
"""

from .abstractiter import AbstractIter
from .levelordergroupiter import LevelOrderGroupIter
from .levelorderiter import LevelOrderIter
from .postorderiter import PostOrderIter
from .preorderiter import PreOrderIter
from .zigzaggroupiter import ZigZagGroupIter
from .abstractiter import AbstractIter as AbstractIter # noqa: PLC0414
from .levelordergroupiter import LevelOrderGroupIter as LevelOrderGroupIter # noqa: PLC0414
from .levelorderiter import LevelOrderIter as LevelOrderIter # noqa: PLC0414
from .postorderiter import PostOrderIter as PostOrderIter # noqa: PLC0414
from .preorderiter import PreOrderIter as PreOrderIter # noqa: PLC0414
from .zigzaggroupiter import ZigZagGroupIter as ZigZagGroupIter # noqa: PLC0414

__all__ = [
"AbstractIter",
Expand Down
51 changes: 39 additions & 12 deletions src/anytree/iterators/abstractiter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
class AbstractIter:
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Generic, TypeVar

if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator

from typing_extensions import Self

from anytree.node.lightnodemixin import LightNodeMixin
from anytree.node.nodemixin import NodeMixin


NodeT_co = TypeVar("NodeT_co", bound="NodeMixin[Any] | LightNodeMixin[Any]", covariant=True)


class AbstractIter(Generic[NodeT_co]):
# pylint: disable=R0205
"""
Iterate over tree starting at `node`.
Expand All @@ -11,14 +27,20 @@ class AbstractIter:
maxlevel (int): maximum descending in the node hierarchy.
"""

def __init__(self, node, filter_=None, stop=None, maxlevel=None):
def __init__(
self,
node: NodeT_co,
filter_: Callable[[NodeT_co], bool] | None = None,
stop: Callable[[NodeT_co], bool] | None = None,
maxlevel: int | None = None,
) -> None:
self.node = node
self.filter_ = filter_
self.stop = stop
self.maxlevel = maxlevel
self.__iter = None
self.__iter: Iterator[NodeT_co] | None = None

def __init(self):
def __init(self) -> Iterator[NodeT_co]:
node = self.node
maxlevel = self.maxlevel
filter_ = self.filter_ or AbstractIter.__default_filter
Expand All @@ -27,31 +49,36 @@ def __init(self):
return self._iter(children, filter_, stop, maxlevel)

@staticmethod
def __default_filter(node):
def __default_filter(node: NodeT_co) -> bool:
# pylint: disable=W0613
return True

@staticmethod
def __default_stop(node):
def __default_stop(node: NodeT_co) -> bool:
# pylint: disable=W0613
return False

def __iter__(self):
def __iter__(self) -> Self:
return self

def __next__(self):
def __next__(self) -> NodeT_co:
if self.__iter is None:
self.__iter = self.__init()
return next(self.__iter)

@staticmethod
def _iter(children, filter_, stop, maxlevel):
raise NotImplementedError
def _iter(
children: Iterable[NodeT_co],
filter_: Callable[[NodeT_co], bool],
stop: Callable[[NodeT_co], bool],
maxlevel: int | None,
) -> Iterator[NodeT_co]:
raise NotImplementedError # pragma: no cover

@staticmethod
def _abort_at_level(level, maxlevel):
def _abort_at_level(level: int, maxlevel: int | None) -> bool:
return maxlevel is not None and level > maxlevel

@staticmethod
def _get_children(children, stop):
def _get_children(children: Iterable[NodeT_co], stop: Callable[[NodeT_co], bool]) -> list[Any]:
return [child for child in children if not stop(child)]
15 changes: 8 additions & 7 deletions src/anytree/node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
* :any:`LightNodeMixin`: A :any:`NodeMixin` using slots.
"""

from .anynode import AnyNode
from .exceptions import LoopError, TreeError
from .lightnodemixin import LightNodeMixin
from .node import Node
from .nodemixin import NodeMixin
from .symlinknode import SymlinkNode
from .symlinknodemixin import SymlinkNodeMixin
from .anynode import AnyNode as AnyNode # noqa: PLC0414
from .exceptions import LoopError as LoopError # noqa: PLC0414
from .exceptions import TreeError as TreeError # noqa: PLC0414
from .lightnodemixin import LightNodeMixin as LightNodeMixin # noqa: PLC0414
from .node import Node as Node # noqa: PLC0414
from .nodemixin import NodeMixin as NodeMixin # noqa: PLC0414
from .symlinknode import SymlinkNode as SymlinkNode # noqa: PLC0414
from .symlinknodemixin import SymlinkNodeMixin as SymlinkNodeMixin # noqa: PLC0414

__all__ = [
"AnyNode",
Expand Down
13 changes: 10 additions & 3 deletions src/anytree/node/anynode.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from .nodemixin import NodeMixin
from .util import _repr

if TYPE_CHECKING:
from collections.abc import Iterable


class AnyNode(NodeMixin):
class AnyNode(NodeMixin["AnyNode"]):
"""
A generic tree node with any `kwargs`.

Expand Down Expand Up @@ -90,11 +97,11 @@ class AnyNode(NodeMixin):
... ])
"""

def __init__(self, parent=None, children=None, **kwargs):
def __init__(self, parent: AnyNode | None = None, children: Iterable[AnyNode] | None = None, **kwargs: Any) -> None:
self.__dict__.update(kwargs)
self.parent = parent
if children:
self.children = children

def __repr__(self):
def __repr__(self) -> str:
return _repr(self)
Loading