Source code for poseinterface.utils
"""General utility functions for ``poseinterface``."""
from collections.abc import Iterator
from itertools import islice
from pathlib import Path
[docs]
def tree(
dir_path: Path,
*,
level: int = -1,
limit_to_directories: bool = False,
exclude_hidden: bool = False,
length_limit: int = 1000,
) -> str:
"""Return a visual tree structure of a directory as a string.
Parameters
----------
dir_path
Path to the root directory.
level
Maximum depth to display. ``-1`` means no limit. Default is ``-1``.
limit_to_directories
If ``True``, only directories are shown. Default is ``False``.
exclude_hidden
If ``True``, files and directories starting with ``.`` are excluded.
Default is ``False``.
length_limit
Maximum number of lines to include before truncating.
Default is ``1000``.
Returns
-------
str
Tree representation of the directory structure, including a
summary line with the count of directories and files.
Notes
-----
Based on https://stackoverflow.com/a/59109706 by Aaron Hall, modified
by community (see post 'Timeline' for change history).
Retrieved 2026-03-27. License: CC BY-SA 4.0.
Examples
--------
>>> from pathlib import Path
>>> from poseinterface.utils import tree
>>> print(tree(Path(".")))
"""
space = " "
branch = "│ "
tee = "├── "
last = "└── "
dir_path = Path(dir_path)
files = 0
directories = 0
def _inner(
dir_path: Path, prefix: str = "", level: int = -1
) -> Iterator[str]:
nonlocal files, directories
if not level:
return
contents = [
d
for d in sorted(dir_path.iterdir(), key=lambda d: d.name)
if not (exclude_hidden and d.name.startswith("."))
]
if limit_to_directories:
contents = [d for d in contents if d.is_dir()]
last_index = len(contents) - 1
for index, path in enumerate(contents):
pointer = last if index == last_index else tee
if path.is_dir():
yield prefix + pointer + path.name + "/"
directories += 1
extension = branch if pointer == tee else space
yield from _inner(
path, prefix=prefix + extension, level=level - 1
)
elif not limit_to_directories:
yield prefix + pointer + path.name
files += 1
lines: list[str] = [dir_path.name + "/"]
iterator = _inner(dir_path, level=level)
for line in islice(iterator, length_limit):
lines.append(line)
if next(iterator, None):
lines.append(f"... length_limit, {length_limit}, reached, counted:")
summary = f"\n{directories} directories"
if files:
summary += f", {files} files"
lines.append(summary)
return "\n".join(lines)