"""Miscellaneous general-purpose utility functions.
This module provides a set of utility functions for various common
tasks, including flattening nested iterables, summing tuples, and
extracting summary lines from docstrings. It also includes functions for
validating class attributes and string representations of floats,
printing verbose messages, as well as a a no-op function for
placeholder purposes.
To import the module:
>>> from smufolib import stdUtils
"""
import operator
from typing import Any
from collections.abc import Generator, Iterable
# pylint: disable=C0103
[docs]
def flatten(iterable: Iterable[Any], depth: int | None = None) -> Generator:
"""Flatten irregularly nested iterables of any depth.
:param iterable: The :term:`iterable` to flatten.
:param depth: The number of levels to flatten. `depth=None` employs
maximum flattening. Defaults to :obj:`None`.
Examples:
>>> list(stdUtils.flatten([1, [2, [3, [4, 5]]]], depth=2))
[1, 2, 3, [4, 5]]
>>> list(stdUtils.flatten([1, [2, [3, [4, [5]]]]], depth=None))
[1, 2, 3, 4, 5]
"""
for item in iterable:
if (
not isinstance(item, Iterable)
or isinstance(item, (str, bytes))
or depth == 0
):
yield item
elif depth is None:
yield from flatten(item)
else:
yield from flatten(item, depth - 1)
[docs]
def addTuples(*tuples: tuple[int | float, ...]) -> tuple[int | float, ...]:
r"""Sumize tuple values.
:param \*tuples: tuples to add.
Example:
>>> stdUtils.addTuples((2, 4), (2, 4))
(4, 8)
"""
return tuple(map(sum, zip(*tuples)))
[docs]
def getSummary(docstring: str | None) -> str | None:
'''Extract the summary line from a docstring.
:param docstring: The docstring from which to get the summary line.
Example::
>>> def showcaseGetSummary():
... """Provide an example for the `getSummary()` function.
...
... This function provides an example to show how to
... extract a summary line from a docstring.
...
... """
...
>>> docstring = showcaseGetSummary.__doc__
>>> stdUtils.getSummary(docstring)
"Give example for `getSummary()` function."
'''
if docstring is None:
return None
return docstring.split("\n", maxsplit=1)[0]
[docs]
def isFloat(string: str) -> bool:
"""Check if string represents a :class:`float`.
:param string: The string to check.
Examples:
>>> stdUtils.isFloat("3.14")
True
>>> stdUtils.isFloat("314")
False
"""
if "." not in string:
return False
try:
float(string)
return True
except ValueError:
return False
[docs]
def isInUnicodeRange(codepoint: int) -> bool:
"""Check if an integer is within the Unicode range (U+0000 to U+10FFFF)."""
if 0x0 <= codepoint <= 0x10FFFF:
return True
return False
[docs]
def validateAttr(obj, attributes: Iterable[str] | None = None) -> bool:
"""Validate `obj` based on whether the given attributes have been set.
This function returns :obj:`False` if any given attribute is not :obj:`None`,
otherwise itreturns :obj:`True`. It supports validation of nested
attributes, e.g.: ``"attr1.attr2.attr3"``.
:param obj: The object to validate.
:param attributes: The attribute names to check.
Example:
>>> class MyClass:
... def __init__(self):
... self.attr1 = 1
... self.attr2 = None
... self.attr3 = False
...
>>> obj = MyClass()
>>> stdUtils.validateAttr(obj, "attr1")
True
>>> stdUtils.validateAttr(obj, "attr2")
False
>>> stdUtils.validateAttr(obj, "attr3")
True
>>> stdUtils.validateAttr(obj, ["attr1", "attr2"])
False
"""
if not attributes:
return False
if isinstance(attributes, str):
attributes = (attributes,)
return all(operator.attrgetter(attr)(obj) is not None for attr in attributes)
[docs]
def doNothing(*args: Any, **kwargs: Any) -> None:
# pylint: disable=W0613
r"""Do nothing at all.
This function accepts any arguments but does nothing with them.
:param \*args: Positional arguments.
:param \**kwargs: Keyword arguments.
Example:
>>> stdUtils.doNothing(1, 2, 3)
>>> stdUtils.doNothing(a=1, b=2)
"""
[docs]
def verbosePrint(message: str, verbose: bool, *args: Any, **kwargs: Any) -> None:
r"""Print a message if `verbose` is :obj:`True`.
This function behaves like the built-in :func:`print` function, but
only prints the message if `verbose` is :obj:`True`. If `verbose`
is :obj:`False`, it does nothing.
:param message: The message to be printed.
:param verbose: A flag indicating whether to print the message.
:param \*args: Additional positional arguments passed to
the :func:`print` function.
:param \**kwargs: Additional keyword arguments passed to
the :func:`print` function.
Example:
>>> stdUtils.verbosePrint("Hello, world!", True)
Hello, world!
>>> stdUtils.verbosePrint("Hello, world!", False)
"""
if verbose:
print(message, *args, **kwargs)
else:
doNothing(message, *args, **kwargs)