Source code for topo_metrics.utils

from __future__ import annotations

import io
import logging
import os
import sys
import warnings
from typing import Any, Iterable


[docs] class hushed: """ A context manager to suppress stdout and stderr. Notes ----- - I have made this as aggressive as possible to suppress all warnings and logging messages. This is because the function `instantiate_julia`brings in a lot of noise that irritates me. """ def __enter__(self): assert sys.__stdout__ is not None assert sys.__stderr__ is not None self._stdout_fd = sys.__stdout__.fileno() self._stderr_fd = sys.__stderr__.fileno() self._saved_stdout_fd = os.dup(self._stdout_fd) self._saved_stderr_fd = os.dup(self._stderr_fd) self._devnull = open(os.devnull, "w") os.dup2(self._devnull.fileno(), self._stdout_fd) os.dup2(self._devnull.fileno(), self._stderr_fd) # ALSO redirect Python-level output streams self._saved_stdout_obj = sys.stdout self._saved_stderr_obj = sys.stderr sys.stdout = io.StringIO() sys.stderr = io.StringIO() warnings.simplefilter("ignore") logging.disable(logging.CRITICAL) return self def __exit__(self, exc_type, exc_value, traceback): os.dup2(self._saved_stdout_fd, self._stdout_fd) os.dup2(self._saved_stderr_fd, self._stderr_fd) os.close(self._saved_stdout_fd) os.close(self._saved_stderr_fd) self._devnull.close() sys.stdout = self._saved_stdout_obj sys.stderr = self._saved_stderr_obj warnings.resetwarnings() logging.disable(logging.NOTSET)
[docs] def to_tuple(not_tuple: list | Any) -> Any: """ Recursively converts a list (or nested lists) to a tuple. This function is designed to handle lists that may contain nested lists, and it will recursively convert all levels of lists into tuples. Parameters ---------- not_tuple The input to be converted. If the input is a list, it will be recursively converted to a tuple. Otherwise, it will be returned as-is. Returns ------- A tuple equivalent of the input list, or the original element if it is not a list. Example ------- >>> totuple([1, 2, [3, 4, [5, 6]], 7]) (1, 2, (3, 4, (5, 6)), 7) >>> totuple('string') 'string' """ if isinstance(not_tuple, str): return not_tuple if isinstance(not_tuple, Iterable): return tuple(to_tuple(i) for i in not_tuple) return not_tuple
[docs] def uniform_repr( object_name: str, *positional_args: Any, max_width: int = 60, stringify: bool = True, indent_size: int = 2, **keyword_args: Any, ) -> str: """ Generates a uniform string representation of an object, supporting both positional and keyword arguments. """ def format_value(value: Any) -> str: """ Converts a value to a string, optionally wrapping strings in quotes. """ if isinstance(value, str) and stringify: return f'"{value}"' return str(value) # Format positional and keyword arguments components = [format_value(arg) for arg in positional_args] components += [ f"{key}={format_value(value)}" for key, value in keyword_args.items() ] # Construct a single-line representation single_line_repr = f"{object_name}({', '.join(components)})" if len(single_line_repr) < max_width and "\n" not in single_line_repr: return single_line_repr # If exceeding max width, format as a multi-line representation. def indent(text: str) -> str: """Indents text with a specified number of spaces.""" indentation = " " * indent_size return "\n".join(f"{indentation}{line}" for line in text.split("\n")) # Build multi-line representation multi_line_repr = f"{object_name}(\n" multi_line_repr += ",\n".join(indent(component) for component in components) multi_line_repr += "\n)" return multi_line_repr
[docs] def min_count(x: list[int]) -> tuple[int, int]: """ Count the number of times the minimum value appears in the list `X`. """ x = list(x) if not x: raise ValueError("The list X cannot be empty") min_value = min(x) return min_value, x.count(min_value)