Source code for ferrosoft.strparse

"""
Tolerant string-to-type parsers for processing external and user-supplied data.

All parsers in this module are intentionally lenient:

* The ``parse_*_noexcept`` functions return ``None`` instead of raising on
  invalid input, making them safe to use with data from CSV files, query
  parameters, or any other source where the value may be absent or malformed.
* :func:`parse_boolean` treats every unrecognised string as ``True``, mirroring
  the JavaScript truthiness model and the convention used by HTML checkboxes
  (where a submitted field value always means "checked").

The :data:`_UNTRUTHY_VALUES` list deliberately covers representations from
multiple languages and data formats (Python's ``"none"``, JavaScript's
``"null"`` and ``"undefined"``, SQL's implicit empty string, numeric zeros)
so that the same parser can be used across heterogeneous data sources.
"""

import uuid
from decimal import Decimal
from uuid import UUID

#: String values that :func:`parse_boolean` treats as ``False``.
#: Comparison is case-insensitive (inputs are lower-cased before lookup).
#: The set mirrors JavaScript's falsy strings plus common null/zero
#: representations from Python, SQL, and JSON.
_UNTRUTHY_VALUES = [
    "",
    "false",
    "0",
    "-0",
    "0n",
    "null",
    "undefined",
    "none",
    "nan",
]


[docs] def get_untruthy_values() -> list[str]: """Return the list of string values that :func:`parse_boolean` treats as ``False``. Callers can use this to display or validate the set of accepted falsy representations without hard-coding the list themselves. Returns: A list of lowercase strings considered untruthy by :func:`parse_boolean`. """ return _UNTRUTHY_VALUES
[docs] def parse_boolean(value: str) -> bool: """Parse a string as a boolean, treating all unrecognised values as ``True``. Converts ``value`` to lowercase and checks it against :data:`_UNTRUTHY_VALUES`. Any string not in that list — including ``"yes"``, ``"on"``, ``"1"``, ``"true"``, or any arbitrary non-empty string — is considered ``True``. This permissive convention is intentional: it matches the behaviour of HTML form checkboxes (where the presence of any submitted value means "enabled") and JavaScript's general truthiness rules. Args: value: The raw string to interpret as a boolean. Returns: ``False`` if ``value.lower()`` is in :data:`_UNTRUTHY_VALUES`, ``True`` otherwise. Example: :: parse_boolean("true") # True parse_boolean("yes") # True parse_boolean("1") # True parse_boolean("false") # False parse_boolean("0") # False parse_boolean("none") # False parse_boolean("") # False """ return value.lower() not in _UNTRUTHY_VALUES
[docs] def parse_decimal_noexcept(value: str) -> Decimal | None: """Parse a string as a :class:`~decimal.Decimal`, returning ``None`` on failure. Args: value: The raw string to parse. Returns: A :class:`~decimal.Decimal` on success, or ``None`` if ``value`` cannot be converted (e.g. it is empty, contains letters, or is otherwise not a valid decimal literal). """ try: return Decimal(value) except ValueError: return None
[docs] def parse_int_noexcept(value: str | None) -> int | None: """Parse a string as an integer, returning ``None`` on failure or absent input. Args: value: The raw string to parse, or ``None``. Returns: An ``int`` on success, or ``None`` if ``value`` is ``None`` or cannot be converted to an integer. """ if value is None: return None try: return int(value) except ValueError: return None
[docs] def parse_uuid_noexcept(value: str | None) -> UUID | None: """Parse a string as a :class:`~uuid.UUID`, returning ``None`` on failure or absent input. Accepts any UUID format recognised by :class:`uuid.UUID` (with or without hyphens, upper or lower case). Args: value: The raw string to parse, or ``None``. Returns: A :class:`~uuid.UUID` on success, or ``None`` if ``value`` is ``None`` or not a valid UUID representation. """ if value is None: return None try: return UUID(value) except ValueError: return None