Source code for generate_changelog.utilities

"""Utility methods."""
from typing import Any, Iterable, Optional


[docs] def is_action(value: Any) -> bool: """Returns ``True`` if the value is an action.""" return isinstance(value, dict) and "action" in value
[docs] def is_pipeline(value: Any) -> bool: """Returns ``True`` if the value is a pipeline.""" return value and isinstance(value, list) and is_action(value[0])
[docs] def eval_if_callable(value: Any) -> Any: """ Tries to evaluate ``value`` as an action, a pipeline, or a callable if possible. Args: value: A callable, action dictionary, list of action dictionaries, or other. Returns: The original value if it can not be evaluated further. """ from generate_changelog.configuration import get_config from generate_changelog.pipeline import pipeline_factory config = get_config() if is_action(value): # convert it into a single action and call it return pipeline_factory([value], **config.rendered_variables).run() elif is_pipeline(value): return pipeline_factory(value, **config.rendered_variables).run() return value() if callable(value) else value
[docs] def pairs(iterable: Iterable) -> Iterable: """ Return successive pairs taken from the input iterable. Like :py:func:`itertools.pairwise` in 3.10, but will always include the last element by itself. Example: >>> list(pairs("ABCD")) [("A", "B"), ("B", "C"), ("C", "D"), ("D", None)] >>> list(pairs("ABC")) [("A", "B"), ("B", "C"), ("C", None)] Args: iterable: The iterable to combine into pairs. Returns: An iterable of pairs. """ from itertools import tee, zip_longest a, b = tee(iterable) next(b, None) return zip_longest(a, b)
[docs] def resolve_name(obj: Any, name: str, default: Any = None) -> Any: """ Get a key or attr ``name`` from obj or default value. Copied and modified from Django Template variable resolutions Resolution methods: - Mapping key lookup - Attribute lookup - Sequence index Args: obj: The object to access name: A dotted name to the value, such as ``mykey.0.name`` default: If the name cannot be resolved from the object, return this value Returns: The value at the resolved name or the default value. Raises: TypeError, AttributeError: If accessing the property raises one of these exceptions. """ lookups = name.split(".") current = obj try: # catch-all for unexpected failures for bit in lookups: try: # dictionary lookup current = current[bit] # ValueError/IndexError are for numpy.array lookup on # numpy < 1.9 and 1.9+ respectively except (TypeError, AttributeError, KeyError, ValueError, IndexError): try: # attribute lookup current = getattr(current, bit) except (TypeError, AttributeError): # Reraise if the exception was raised by a @property if bit in dir(current): raise try: # list-index lookup current = current[int(bit)] except ( IndexError, # list index out of range ValueError, # invalid literal for int() KeyError, # current is a dict without `int(bit)` key TypeError, ): # un-subscript-able object return default return current except Exception: # NOQA: BLE001 pragma: no cover return default
[docs] def diff_index(iterable1: Iterable, iterable2: Iterable) -> Optional[int]: """Return the index where iterable2 is different from iterable1.""" return next((index for index, (item1, item2) in enumerate(zip(iterable1, iterable2)) if item1 != item2), None)