Skip to content

Module omnipy.util.helpers

Overview

View Source
from collections.abc import Hashable, Iterable

import inspect

import locale as pkg_locale

from types import UnionType

from typing import (Annotated,

                    Any,

                    cast,

                    ClassVar,

                    get_args,

                    get_origin,

                    Mapping,

                    Protocol,

                    Type,

                    TypeVar,

                    Union)

from typing_inspect import get_generic_bases, is_generic_type

from omnipy.api.typedefs import LocaleType

KeyT = TypeVar('KeyT', bound=Hashable)

Dictable = Mapping[KeyT, Any] | Iterable[tuple[KeyT, Any]]

def as_dictable(obj: object) -> Dictable | None:

    def _is_iterable_of_tuple_pairs(obj_inner: object) -> bool:

        return isinstance(obj_inner, Iterable) and \

            all(isinstance(el, tuple) and len(el) == 2 for el in obj_inner)

    if isinstance(obj, Mapping) or _is_iterable_of_tuple_pairs(obj):

        return cast(Dictable, obj)

    else:

        return None

def create_merged_dict(dictable_1: Dictable[KeyT], dictable_2: Dictable[KeyT]) -> dict[KeyT, Any]:

    merged_dict = dictable_1 if isinstance(dictable_1, dict) else dict(dictable_1)

    dict_2 = dictable_2 if isinstance(dictable_2, dict) else dict(dictable_2)

    merged_dict |= dict_2

    return merged_dict

def remove_none_vals(**kwargs: object) -> dict[object, object]:

    return {key: val for key, val in kwargs.items() if val is not None}

def get_datetime_format(locale: LocaleType | None = None) -> str:

    pkg_locale.setlocale(pkg_locale.LC_ALL, locale)

    if hasattr(pkg_locale, 'nl_langinfo'):  # noqa

        datetime_format = pkg_locale.nl_langinfo(pkg_locale.D_T_FMT)

    else:

        datetime_format = '%a %b %e %X %Y'

    return datetime_format

async def resolve(val):

    return await val if inspect.isawaitable(val) else val

def repr_max_len(data: object, max_len: int = 200):

    repr_str = repr(data)

    return f'{repr_str[:max_len]}...' if len(repr_str) > max_len else repr_str

def get_bases(cls):

    return get_generic_bases(cls) if is_generic_type(cls) else cls.__bases__

def generic_aware_issubclass_ignore_args(cls, cls_or_tuple):

    try:

        return issubclass(cls, cls_or_tuple)

    except TypeError:

        return issubclass(get_origin(cls), cls_or_tuple)

def transfer_generic_args_to_cls(to_cls, from_generic_type):

    try:

        return to_cls[get_args(from_generic_type)]

    except (TypeError, AttributeError):

        return to_cls

def is_iterable(obj: object) -> bool:

    try:

        iter(obj)

        return True

    except TypeError:

        return False

def is_union(cls_or_type: type | UnionType | None | object) -> bool:

    return get_origin(cls_or_type) in [Union, UnionType]

def is_optional(cls_or_type: type | UnionType | None | object) -> bool:

    return is_union(cls_or_type) and type(None) in get_args(cls_or_type)

def is_strict_subclass(

        __cls: type,

        __class_or_tuple: type | UnionType | tuple[type | UnionType | tuple[Any, ...], ...]

) -> bool:

    if issubclass(__cls, __class_or_tuple):

        if isinstance(__class_or_tuple, Iterable):

            return __cls not in __class_or_tuple

        else:

            return __cls != __class_or_tuple

    return False

class IsDataclass(Protocol):

    __dataclass_fields__: ClassVar[dict]

def remove_annotated_plus_optional_if_present(

        type_or_class: Type | UnionType | object) -> Type | UnionType | object:

    if get_origin(type_or_class) == Annotated:

        type_or_class = get_args(type_or_class)[0]

        if is_optional(type_or_class):

            args = get_args(type_or_class)

            if len(args) == 2:

                type_or_class = args[0]

            else:

                type_or_class = Union[args[:-1]]

    return type_or_class

Variables

Dictable
KeyT

Functions

as_dictable

def as_dictable(
    obj: object
) -> Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]], NoneType]

Parameters:

Name Type Description Default
obj object

Returns:

Type Description
Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]], NoneType]
View Source
def as_dictable(obj: object) -> Dictable | None:

    def _is_iterable_of_tuple_pairs(obj_inner: object) -> bool:

        return isinstance(obj_inner, Iterable) and \

            all(isinstance(el, tuple) and len(el) == 2 for el in obj_inner)

    if isinstance(obj, Mapping) or _is_iterable_of_tuple_pairs(obj):

        return cast(Dictable, obj)

    else:

        return None

create_merged_dict

def create_merged_dict(
    dictable_1: Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]]],
    dictable_2: Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]]]
) -> dict[~KeyT, typing.Any]

Parameters:

Name Type Description Default
dictable_1 Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]]]
dictable_2 Union[Mapping[~KeyT, Any], collections.abc.Iterable[tuple[~KeyT, Any]]]

Returns:

Type Description
dict[~KeyT, typing.Any]
View Source
def create_merged_dict(dictable_1: Dictable[KeyT], dictable_2: Dictable[KeyT]) -> dict[KeyT, Any]:

    merged_dict = dictable_1 if isinstance(dictable_1, dict) else dict(dictable_1)

    dict_2 = dictable_2 if isinstance(dictable_2, dict) else dict(dictable_2)

    merged_dict |= dict_2

    return merged_dict

generic_aware_issubclass_ignore_args

def generic_aware_issubclass_ignore_args(
    cls,
    cls_or_tuple
)

Parameters:

Name Type Description Default
cls_or_tuple
View Source
def generic_aware_issubclass_ignore_args(cls, cls_or_tuple):

    try:

        return issubclass(cls, cls_or_tuple)

    except TypeError:

        return issubclass(get_origin(cls), cls_or_tuple)

get_bases

def get_bases(
    cls
)
View Source
def get_bases(cls):

    return get_generic_bases(cls) if is_generic_type(cls) else cls.__bases__

get_datetime_format

def get_datetime_format(
    locale: str | tuple[str | None, str | None] | None = None
) -> str

Parameters:

Name Type Description Default
locale str tuple[str None, str

Returns:

Type Description
str
View Source
def get_datetime_format(locale: LocaleType | None = None) -> str:

    pkg_locale.setlocale(pkg_locale.LC_ALL, locale)

    if hasattr(pkg_locale, 'nl_langinfo'):  # noqa

        datetime_format = pkg_locale.nl_langinfo(pkg_locale.D_T_FMT)

    else:

        datetime_format = '%a %b %e %X %Y'

    return datetime_format

is_iterable

def is_iterable(
    obj: object
) -> bool

Parameters:

Name Type Description Default
obj object

Returns:

Type Description
bool
View Source
def is_iterable(obj: object) -> bool:

    try:

        iter(obj)

        return True

    except TypeError:

        return False

is_optional

def is_optional(
    cls_or_type: type | types.UnionType | None | object
) -> bool

Parameters:

Name Type Description Default
cls_or_type type types.UnionType None

Returns:

Type Description
bool
View Source
def is_optional(cls_or_type: type | UnionType | None | object) -> bool:

    return is_union(cls_or_type) and type(None) in get_args(cls_or_type)

is_strict_subclass

def is_strict_subclass(
    __cls: type,
    __class_or_tuple: type | types.UnionType | tuple[type | types.UnionType | tuple[typing.Any, ...], ...]
) -> bool

Parameters:

Name Type Description Default
__cls type
__class_or_tuple type types.UnionType tuple[type

Returns:

Type Description
bool
View Source
def is_strict_subclass(

        __cls: type,

        __class_or_tuple: type | UnionType | tuple[type | UnionType | tuple[Any, ...], ...]

) -> bool:

    if issubclass(__cls, __class_or_tuple):

        if isinstance(__class_or_tuple, Iterable):

            return __cls not in __class_or_tuple

        else:

            return __cls != __class_or_tuple

    return False

is_union

def is_union(
    cls_or_type: type | types.UnionType | None | object
) -> bool

Parameters:

Name Type Description Default
cls_or_type type types.UnionType None

Returns:

Type Description
bool
View Source
def is_union(cls_or_type: type | UnionType | None | object) -> bool:

    return get_origin(cls_or_type) in [Union, UnionType]

remove_annotated_plus_optional_if_present

def remove_annotated_plus_optional_if_present(
    type_or_class: Union[Type, types.UnionType, object]
) -> Union[Type, types.UnionType, object]

Parameters:

Name Type Description Default
type_or_class Union[Type, types.UnionType, object]

Returns:

Type Description
Union[Type, types.UnionType, object]
View Source
def remove_annotated_plus_optional_if_present(

        type_or_class: Type | UnionType | object) -> Type | UnionType | object:

    if get_origin(type_or_class) == Annotated:

        type_or_class = get_args(type_or_class)[0]

        if is_optional(type_or_class):

            args = get_args(type_or_class)

            if len(args) == 2:

                type_or_class = args[0]

            else:

                type_or_class = Union[args[:-1]]

    return type_or_class

remove_none_vals

def remove_none_vals(
    **kwargs: object
) -> dict[object, object]

Parameters:

Name Type Description Default
kwargs object

Returns:

Type Description
dict[object, object]
View Source
def remove_none_vals(**kwargs: object) -> dict[object, object]:

    return {key: val for key, val in kwargs.items() if val is not None}

repr_max_len

def repr_max_len(
    data: object,
    max_len: int = 200
)

Parameters:

Name Type Description Default
data object
max_len int 200
View Source
def repr_max_len(data: object, max_len: int = 200):

    repr_str = repr(data)

    return f'{repr_str[:max_len]}...' if len(repr_str) > max_len else repr_str

resolve

def resolve(
    val
)

Parameters:

Name Type Description Default
val
View Source
async def resolve(val):

    return await val if inspect.isawaitable(val) else val

transfer_generic_args_to_cls

def transfer_generic_args_to_cls(
    to_cls,
    from_generic_type
)

Parameters:

Name Type Description Default
to_cls
from_generic_type
View Source
def transfer_generic_args_to_cls(to_cls, from_generic_type):

    try:

        return to_cls[get_args(from_generic_type)]

    except (TypeError, AttributeError):

        return to_cls

Classes

IsDataclass

class IsDataclass(
    *args,
    **kwargs
)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with

View Source
class IsDataclass(Protocol):

    __dataclass_fields__: ClassVar[dict]