<filename>valtypes/parsing/utils.py
import inspect
from typing import Optional, Protocol, TypeVar, Union, cast

import regex

__all__ = ["parse_generic_alias_repr", "get_caller_namespace"]


T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)

F = TypeVar("F")

S = TypeVar("S")


generic_alias_repr_pattern = regex.compile(r"([^\[\], ]+)(?:\[(.+)])?")
generic_alias_repr_args_pattern = regex.compile(r"(?>[^\[\], ]+(?:\[(?R)(?:, (?R))*\])?)")


def split_generic_alias_repr(repr_: str) -> tuple[str, Optional[str]]:
    if match := generic_alias_repr_pattern.fullmatch(repr_):
        return cast(tuple[str, Optional[str]], match.groups())
    raise ValueError()


def split_generic_alias_repr_args(args: str) -> tuple[str, ...]:
    return tuple(generic_alias_repr_args_pattern.findall(args))


def parse_generic_alias_repr(repr_: str) -> tuple[str, Optional[tuple[str, ...]]]:
    origin, args = split_generic_alias_repr(repr_)
    return origin, split_generic_alias_repr_args(args) if args is not None else None


def get_caller_namespace() -> dict[str, object]:
    frame = inspect.stack()[2][0]
    return frame.f_builtins | frame.f_globals | frame.f_locals


class SupportsGetItem(Protocol[T_contra, T_co]):
    def __getitem__(self, __k: T_contra) -> T_co:
        ...


def get_item(obj: SupportsGetItem[T, F], key: T, default: S) -> Union[F, S]:
    try:
        return obj[key]
    except (IndexError, KeyError):
        return default

<filename>valtypes/parsing/utils.py
import inspect
from typing import Optional, Protocol, TypeVar, Union, cast

import regex

__all__ = ["parse_generic_alias_repr", "get_caller_namespace"]


T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)

F = TypeVar("F")

S = TypeVar("S")


generic_alias_repr_pattern = regex.compile(r"([^\[\], ]+)(?:\[(.+)])?")
generic_alias_repr_args_pattern = regex.compile(r"(?>[^\[\], ]+(?:\[(?R)(?:, (?R))*\])?)")


def split_generic_alias_repr(repr_: str) -> tuple[str, Optional[str]]:
    if match := generic_alias_repr_pattern.fullmatch(repr_):
        return cast(tuple[str, Optional[str]], match.groups())
    raise ValueError()


def split_generic_alias_repr_args(args: str) -> tuple[str, ...]:
    return tuple(generic_alias_repr_args_pattern.findall(args))


def parse_generic_alias_repr(repr_: str) -> tuple[str, Optional[tuple[str, ...]]]:
    origin, args = split_generic_alias_repr(repr_)
    return origin, split_generic_alias_repr_args(args) if args is not None else None


def get_caller_namespace() -> dict[str, object]:
    frame = inspect.stack()[2][0]
    return frame.f_builtins | frame.f_globals | frame.f_locals


class SupportsGetItem(Protocol[T_contra, T_co]):
    def __getitem__(self, __k: T_contra) -> T_co:
        ...


def get_item(obj: SupportsGetItem[T, F], key: T, default: S) -> Union[F, S]:
    try:
        return obj[key]
    except (IndexError, KeyError):
        return default
