Skip to content

Helpers

helpers

Functions:

Name Description
clean_string

Convert a value to a stripped string, returning an empty string for falsy input.

date_from_string

Convert an 8-character YYYYMMDD string into a date object.

deep_visit

Yield flattened (path, value) pairs by recursively walking a nested structure.

filter_and_clean

Filter comment lines and clean inline comments from a list of strings.

normalize_version

Ensure version is in X.0 format (e.g. '19' → '19.0').

removesuffix

Remove a suffix from a string if present, compatible with Python < 3.9.

slugify

Convert a human name to a lowercase, hyphen-separated ASCII slug.

str_to_list

Split a separated string into a list of cleaned, non-empty items.

clean_string

clean_string(raw: Any) -> str

Convert a value to a stripped string, returning an empty string for falsy input.

Parameters:

Name Type Description Default

raw

Any

Value to convert and clean.

required

Returns:

Type Description
str

Stripped string, or an empty string if raw is falsy.

Source code in src/oops/utils/helpers.py
def clean_string(raw: Any) -> str:
    """Convert a value to a stripped string, returning an empty string for falsy input.

    Args:
        raw: Value to convert and clean.

    Returns:
        Stripped string, or an empty string if raw is falsy.
    """

    return str(raw).strip().rstrip() if raw else ""

date_from_string

date_from_string(raw: str) -> date

Convert an 8-character YYYYMMDD string into a date object.

Parameters:

Name Type Description Default

raw

str

Date string in YYYYMMDD format (exactly 8 characters).

required

Returns:

Type Description
date

Corresponding date object.

Raises:

Type Description
ValueError

If raw is not exactly 8 characters long.

Source code in src/oops/utils/helpers.py
def date_from_string(raw: str) -> date:
    """Convert an 8-character YYYYMMDD string into a date object.

    Args:
        raw: Date string in YYYYMMDD format (exactly 8 characters).

    Returns:
        Corresponding date object.

    Raises:
        ValueError: If raw is not exactly 8 characters long.
    """

    if len(raw) != 8:  # noqa: PLR2004
        raise ValueError("The string does not have the correct length to be converted to a date.")

    y, m, d = int(raw[0:4]), int(raw[4:6]), int(raw[6:8])
    return date(y, m, d)

deep_visit

deep_visit(obj: Any, prefix: str = '') -> Generator[tuple[str, Any]]

Yield flattened (path, value) pairs by recursively walking a nested structure.

Dict keys become dot-separated segments; list indices become [n] segments. Example: assets.web.assets_backend[0]"/module/static/..."

Parameters:

Name Type Description Default

obj

Any

Nested dict, list, tuple, or scalar to walk.

required

prefix

str

Accumulated path prefix for the current node. Defaults to "".

''

Yields:

Type Description
Generator[tuple[str, Any]]

Tuple of (dotted_path_string, leaf_value) for each scalar encountered.

Source code in src/oops/utils/helpers.py
def deep_visit(obj: Any, prefix: str = "") -> Generator[tuple[str, Any]]:
    """Yield flattened (path, value) pairs by recursively walking a nested structure.

    Dict keys become dot-separated segments; list indices become ``[n]`` segments.
    Example: ``assets.web.assets_backend[0]`` → ``"/module/static/..."``

    Args:
        obj: Nested dict, list, tuple, or scalar to walk.
        prefix: Accumulated path prefix for the current node. Defaults to "".

    Yields:
        Tuple of (dotted_path_string, leaf_value) for each scalar encountered.
    """
    if isinstance(obj, dict):
        for k, v in obj.items():
            key = str(k)
            yield from deep_visit(v, f"{prefix}.{key}" if prefix else key)
    elif isinstance(obj, (list, tuple)):
        for i, v in enumerate(obj):
            yield from deep_visit(v, f"{prefix}[{i}]")
    else:
        yield prefix, obj

filter_and_clean

filter_and_clean(items: List[str], unique: bool = True) -> list

Filter comment lines and clean inline comments from a list of strings.

Strips full-line comments (starting with #), blank lines, and inline comments (everything after # on a line).

Parameters:

Name Type Description Default

items

List[str]

Lines of text to process.

required

unique

bool

Whether to deduplicate the result. Defaults to True.

True

Returns:

Type Description
list

List of cleaned, non-empty, non-comment strings or the raw list.

Source code in src/oops/utils/helpers.py
def filter_and_clean(items: List[str], unique: bool = True) -> list:
    """Filter comment lines and clean inline comments from a list of strings.

    Strips full-line comments (starting with ``#``), blank lines, and inline
    comments (everything after ``#`` on a line).

    Args:
        items: Lines of text to process.
        unique (bool): Whether to deduplicate the result. Defaults to True.

    Returns:
        List of cleaned, non-empty, non-comment strings or the raw list.
    """

    def clean(item):
        if "#" not in item:
            return item.strip()

        return item.split("#")[0].strip()

    cleaned = [clean(item) for item in items if item.strip() and not item.startswith("#")]

    return list(set(cleaned)) if unique else cleaned

normalize_version

normalize_version(ctx: Context, param: Parameter, value: str) -> str

Ensure version is in X.0 format (e.g. '19' → '19.0').

Source code in src/oops/utils/helpers.py
def normalize_version(ctx: click.Context, param: click.Parameter, value: str) -> str:
    """Ensure version is in X.0 format (e.g. '19' → '19.0')."""
    return value if "." in value else f"{value}.0"

removesuffix

removesuffix(raw: Any, suffix: str) -> str

Remove a suffix from a string if present, compatible with Python < 3.9.

Parameters:

Name Type Description Default

raw

Any

Input string to process.

required

suffix

str

Suffix to strip if present.

required

Returns:

Type Description
str

String with the suffix removed, or the original string if not present.

Source code in src/oops/utils/helpers.py
def removesuffix(raw: Any, suffix: str) -> str:
    """Remove a suffix from a string if present, compatible with Python < 3.9.

    Args:
        raw: Input string to process.
        suffix: Suffix to strip if present.

    Returns:
        String with the suffix removed, or the original string if not present.
    """

    # str.removesuffix added in 3.8
    if PY38:
        return raw[: len(raw) - len(suffix)] if raw[-len(suffix) :] == suffix else raw
    return raw.removesuffix(suffix)

slugify

slugify(name: str) -> str

Convert a human name to a lowercase, hyphen-separated ASCII slug.

Strips accents (é → e, à → a) via Unicode NFKD decomposition before replacing any run of non-alphanumeric characters with a single hyphen.

Source code in src/oops/utils/helpers.py
def slugify(name: str) -> str:
    """Convert a human name to a lowercase, hyphen-separated ASCII slug.

    Strips accents (é → e, à → a) via Unicode NFKD decomposition before
    replacing any run of non-alphanumeric characters with a single hyphen.
    """
    # Decompose accented characters and drop the combining marks
    normalized = unicodedata.normalize("NFKD", name)
    ascii_name = "".join(c for c in normalized if unicodedata.category(c) != "Mn")
    slug = ascii_name.lower().strip()
    slug = re.sub(r"[^a-z0-9]+", "-", slug)
    return slug.strip("-")

str_to_list

str_to_list(raw: str, sep: str = ',') -> list

Split a separated string into a list of cleaned, non-empty items.

Parameters:

Name Type Description Default

raw

str

Input string to split.

required

sep

str

Separator character or string. Defaults to ",".

','

Returns:

Type Description
list

List of stripped, non-empty strings.

Source code in src/oops/utils/helpers.py
def str_to_list(raw: str, sep: str = ",") -> list:
    """Split a separated string into a list of cleaned, non-empty items.

    Args:
        raw: Input string to split.
        sep: Separator character or string. Defaults to ",".

    Returns:
        List of stripped, non-empty strings.
    """

    if not raw:
        return []
    return list(filter(bool, (clean_string(item) for item in raw.split(sep))))