$73 GRAYBYTE WORDPRESS FILE MANAGER $79

SERVER : in-mum-web1330.main-hosting.eu #1 SMP Mon Feb 10 22:45:17 UTC 2025
SERVER IP : 88.222.243.126 | ADMIN IP 216.73.216.215
OPTIONS : CRL = ON | WGT = ON | SDO = OFF | PKEX = OFF
DEACTIVATED : NONE

/opt/alt/python311/lib/python3.11/site-packages/typish/

HOME
Current File : /opt/alt/python311/lib/python3.11/site-packages/typish//_functions.py
"""
PRIVATE MODULE: do not import (from) it directly.

This module contains the implementation of all functions of typish.
"""
import inspect
import types
import typing
from collections import deque, defaultdict
from collections.abc import Set
from functools import lru_cache
from inspect import getmro

from typish._types import T, KT, VT, NoneType, Unknown, Empty


def subclass_of(cls: type, *args: type) -> bool:
    """
    Return whether ``cls`` is a subclass of all types in ``args`` while also
    considering generics.
    :param cls: the subject.
    :param args: the super types.
    :return: True if ``cls`` is a subclass of all types in ``args`` while also
    considering generics.
    """
    if args and _is_literal(args[0]):
        return _check_literal(cls, subclass_of, *args)

    if len(args) > 1:
        result = subclass_of(cls, args[0]) and subclass_of(cls, *args[1:])
    else:
        if args[0] == cls:
            return True
        result = _subclass_of(cls, args[0])
    return result


def instance_of(obj: object, *args: type) -> bool:
    """
    Check whether ``obj`` is an instance of all types in ``args``, while also
    considering generics.
    :param obj: the object in subject.
    :param args: the type(s) of which ``obj`` is an instance or not.
    :return: ``True`` if ``obj`` is an instance of all types in ``args``.
    """
    if args and _is_literal(args[0]):
        return _check_literal(obj, instance_of, *args)

    type_ = get_type(obj, use_union=True)
    return subclass_of(type_, *args)


def get_origin(t: type) -> type:
    """
    Return the origin of the given (generic) type. For example, for
    ``t=List[str]``, the result would be ``list``.
    :param t: the type of which the origin is to be found.
    :return: the origin of ``t`` or ``t`` if it is not generic.
    """
    simple_name = _get_simple_name(t)
    result = _type_per_alias.get(simple_name, None)
    if not result:
        result = getattr(typing, simple_name, t)
    return result


def get_args(t: type) -> typing.Tuple[type, ...]:
    """
    Get the arguments from a collection type (e.g. ``typing.List[int]``) as a
    ``tuple``.
    :param t: the collection type.
    :return: a ``tuple`` containing types.
    """
    args_ = getattr(t, '__args__', tuple()) or tuple()
    args = tuple([attr for attr in args_
                  if type(attr) != typing.TypeVar])
    return args


@lru_cache()
def get_alias(cls: T) -> typing.Optional[T]:
    """
    Return the alias from the ``typing`` module for ``cls``. For example, for
    ``cls=list``, the result would be ``typing.List``. If no alias exists for
    ``cls``, then ``None`` is returned.
    :param cls: the type for which the ``typing`` equivalent is to be found.
    :return: the alias from ``typing``.
    """
    return _alias_per_type.get(cls.__name__, None)


def get_type(inst: T, use_union: bool = False) -> typing.Type[T]:
    """
    Return a type, complete with generics for the given ``inst``.
    :param inst: the instance for which a type is to be returned.
    :param use_union: if ``True``, the resulting type can contain a union.
    :return: the type of ``inst``.
    """
    if inst is typing.Any:
        return typing.Any

    if str(inst).startswith('typing.Union'):
        return getattr(typing, '_GenericAlias', getattr(typing, 'GenericMeta', type))

    result = type(inst)
    super_types = [
        (dict, _get_type_dict),
        (tuple, _get_type_tuple),
        (str, lambda inst_, _: result),
        (typing.Iterable, _get_type_iterable),
        (types.FunctionType, _get_type_callable),
        (types.MethodType, _get_type_callable),
        (type, lambda inst_, _: typing.Type[inst]),
    ]

    try:
        for super_type, func in super_types:
            if isinstance(inst, super_type):
                result = func(inst, use_union)
                break
    except Exception:
        # If anything went wrong, return the regular type.
        # This is to support 3rd party libraries.
        return type(inst)
    return result


def common_ancestor(*args: object) -> type:
    """
    Get the closest common ancestor of the given objects.
    :param args: any objects.
    :return: the ``type`` of the closest common ancestor of the given ``args``.
    """
    return _common_ancestor(args, False)


def common_ancestor_of_types(*args: type) -> type:
    """
    Get the closest common ancestor of the given classes.
    :param args: any classes.
    :return: the ``type`` of the closest common ancestor of the given ``args``.
    """
    return _common_ancestor(args, True)


def get_args_and_return_type(hint: typing.Type[typing.Callable]) \
        -> typing.Tuple[typing.Optional[typing.Tuple[type]], typing.Optional[type]]:
    """
    Get the argument types and the return type of a callable type hint
    (e.g. ``Callable[[int], str]``).

    Example:
    ```
    arg_types, return_type = get_args_and_return_type(Callable[[int], str])
    # args_types is (int, )
    # return_type is str
    ```

    Example for when ``hint`` has no generics:
    ```
    arg_types, return_type = get_args_and_return_type(Callable)
    # args_types is None
    # return_type is None
    ```
    :param hint: the callable type hint.
    :return: a tuple of the argument types (as a tuple) and the return type.
    """
    if hint in (callable, typing.Callable):
        arg_types = None
        return_type = None
    elif hasattr(hint, '__result__'):
        arg_types = hint.__args__
        return_type = hint.__result__
    else:
        arg_types = hint.__args__[0:-1]
        return_type = hint.__args__[-1]
    return arg_types, return_type


def get_type_hints_of_callable(
        func: typing.Callable) -> typing.Dict[str, type]:
    """
    Return the type hints of the parameters of the given callable.
    :param func: the callable of which the type hints are to be returned.
    :return: a dict with parameter names and their types.
    """
    # Python3.5: get_type_hints raises on classes without explicit constructor
    try:
        result = typing.get_type_hints(func)
    except AttributeError:
        result = {}
    return result


def is_type_annotation(item: typing.Any) -> bool:
    """
    Return whether item is a type annotation (a ``type`` or a type from
    ``typing``, such as ``List``).
    :param item: the item in question.
    :return: ``True`` is ``item`` is a type annotation.
    """
    # Use _GenericAlias for Python 3.7+ and use GenericMeta for the rest.
    super_cls = getattr(typing, '_GenericAlias',
                        getattr(typing, 'GenericMeta', None))
    return (item is typing.Any
            or instance_of(item, type)
            or instance_of(item, super_cls))


def _subclass_of_generic(
        cls: type,
        info_generic_type: type,
        info_args: typing.Tuple[type, ...]) -> bool:
    # Check if cls is a subtype of info_generic_type, knowing that the latter
    # is a generic type.
    result = False
    cls_origin, cls_args = _split_generic(cls)
    if info_generic_type is tuple:
        # Special case.
        result = (subclass_of(cls_origin, tuple)
                  and _subclass_of_tuple(cls_args, info_args))
    elif cls_origin is tuple and info_generic_type is typing.Iterable:
        # Another special case.
        args = get_args(cls)
        if len(args) > 1 and args[1] is ...:
            args = [args[0]]
        ancestor = common_ancestor_of_types(*args)
        result = subclass_of(typing.Iterable[ancestor],
                             typing.Iterable[args[0]])
    elif info_generic_type is typing.Union:
        # Another special case.
        result = _subclass_of_union(cls, info_args)
    elif (subclass_of(cls_origin, info_generic_type) and cls_args
            and len(cls_args) == len(info_args)):
        for tup in zip(cls_args, info_args):
            if not subclass_of(*tup):
                result = False
                break
        else:
            result = True
    # Note that issubtype(list, List[...]) is always False.
    # Note that the number of arguments must be equal.
    return result


def _subclass_of_tuple(
        cls_args: typing.Tuple[type, ...],
        info_args: typing.Tuple[type, ...]) -> bool:
    result = False
    if len(info_args) == 2 and info_args[1] is ...:
        type_ = get_origin(info_args[0])
        if type_ is typing.Union:
            # A heterogeneous tuple: check each element if it subclasses the
            # union.
            result = all([subclass_of(elem, info_args[0]) for elem in cls_args])
        else:
            result = subclass_of(common_ancestor_of_types(*cls_args), info_args[0])
    elif len(cls_args) == len(info_args):
        for c1, c2 in zip(cls_args, info_args):
            if not subclass_of(c1, c2):
                break
        else:
            result = True
    return result


def _split_generic(t: type) -> \
        typing.Tuple[type, typing.Optional[typing.Tuple[type, ...]]]:
    # Split the given generic type into the type and its args.
    return get_origin(t), get_args(t)


def _get_type_iterable(inst: typing.Iterable, use_union: bool):
    typing_type = get_alias(type(inst))
    common_cls = Unknown
    if inst:
        if use_union:
            types = [get_type(elem) for elem in inst]
            common_cls = typing.Union[tuple(types)]
        else:
            common_cls = common_ancestor(*inst)
            if typing_type:
                if issubclass(common_cls, typing.Iterable) and typing_type is not str:
                    # Get to the bottom of it; obtain types recursively.
                    common_cls = get_type(common_cls(_flatten(inst)))
    result = typing_type[common_cls]
    return result


def _get_type_tuple(inst: tuple, use_union: bool) -> typing.Dict[KT, VT]:
    args = [get_type(elem) for elem in inst]
    return typing.Tuple[tuple(args)]


def _get_type_callable(
        inst: typing.Callable,
        use_union: bool) -> typing.Type[typing.Dict[KT, VT]]:
    if 'lambda' in str(inst):
        result = _get_type_lambda(inst, use_union)
    else:
        result = typing.Callable
        sig = inspect.signature(inst)
        args = [_map_empty(param.annotation)
                for param in sig.parameters.values()]
        return_type = NoneType
        if sig.return_annotation != Empty:
            return_type = sig.return_annotation
        if args or return_type != NoneType:
            if inspect.iscoroutinefunction(inst):
                return_type = typing.Awaitable[return_type]
            result = typing.Callable[args, return_type]
    return result


def _map_empty(annotation: type) -> type:
    result = annotation
    if annotation == Empty:
        result = typing.Any
    return result


def _get_type_lambda(
        inst: typing.Callable,
        use_union: bool) -> typing.Type[typing.Dict[KT, VT]]:
    args = [Unknown for _ in inspect.signature(inst).parameters]
    return_type = Unknown
    return typing.Callable[args, return_type]


def _get_type_dict(inst: typing.Dict[KT, VT],
                   use_union: bool) -> typing.Type[typing.Dict[KT, VT]]:
    t_list_k = _get_type_iterable(list(inst.keys()), use_union)
    t_list_v = _get_type_iterable(list(inst.values()), use_union)
    _, t_k_tuple = _split_generic(t_list_k)
    _, t_v_tuple = _split_generic(t_list_v)
    return typing.Dict[t_k_tuple[0], t_v_tuple[0]]


def _flatten(l: typing.Iterable[typing.Iterable[typing.Any]]) -> typing.List[typing.Any]:
    result = []
    for x in l:
        result += [*x]
    return result


def _common_ancestor(args: typing.Sequence[object], types: bool) -> type:
    if len(args) < 1:
        raise TypeError('common_ancestor() requires at least 1 argument')
    tmap = (lambda x: x) if types else get_type
    mros = [_get_mro(tmap(elem)) for elem in args]
    for cls in mros[0]:
        for mro in mros:
            if cls not in mro:
                break
        else:
            # cls is in every mro; a common ancestor is found!
            return cls


def _subclass_of(cls: type, clsinfo: type) -> bool:
    # Check whether cls is a subtype of clsinfo.
    clsinfo_origin, info_args = _split_generic(clsinfo)
    cls_origin = get_origin(cls)
    if cls is Unknown or clsinfo in (typing.Any, object):
        result = True
    elif cls_origin is typing.Union:
        # cls is a Union; all options of that Union must subclass clsinfo.
        _, cls_args = _split_generic(cls)
        result = all([subclass_of(elem, clsinfo) for elem in cls_args])
    elif info_args:
        result = _subclass_of_generic(cls, clsinfo_origin, info_args)
    else:
        try:
            result = issubclass(cls_origin, clsinfo_origin)
        except TypeError:
            result = False
    return result


def _subclass_of_union(
        cls: type,
        info_args: typing.Tuple[type, ...]) -> bool:
    # Handle subclass_of(*, union)
    result = True
    for cls_ in info_args:
        if subclass_of(cls, cls_):
            break
    else:
        result = False
    return result


@lru_cache()
def _get_simple_name(cls: type) -> str:
    if cls is None:
        cls = type(cls)
    cls_name = getattr(cls, '__name__', None)
    if not cls_name:
        cls_name = getattr(cls, '_name', None)
    if not cls_name:
        cls_name = repr(cls)
        cls_name = cls_name.split('[')[0]  # Remove generic types.
        cls_name = cls_name.split('.')[-1]  # Remove any . caused by repr.
        cls_name = cls_name.split(r"'>")[0]  # Remove any '>.
    return cls_name


def _get_mro(cls: type) -> typing.Tuple[type, ...]:
    # Wrapper around ``getmro`` to allow types from ``Typing``.
    if cls is ...:
        return Ellipsis, object
    elif cls is typing.Union:
        # For Python <3.7, we cannot use mro.
        super_cls = getattr(typing, '_GenericAlias',
                            getattr(typing, 'GenericMeta', None))
        return (typing.Union, super_cls, object)

    origin, args = _split_generic(cls)
    if origin != cls:
        return _get_mro(origin)

    return getmro(cls)


def _is_literal(arg: typing.Any) -> bool:
    # Return True if arg is a Literal.
    origin = get_origin(arg)
    return getattr(origin, '_name', None) == 'Literal'


def _check_literal(obj: object, func: typing.Callable, *args: type) -> bool:
    # Instance or subclass check for Literal.
    literal = args[0]
    leftovers = args[1:]
    literal_args = getattr(literal, '__args__', None)
    if literal_args:
        literal_arg = literal_args[0]
        return obj == literal_arg and (not leftovers or func(obj, *leftovers))
    return False


_alias_per_type = {
    'list': typing.List,
    'tuple': typing.Tuple,
    'dict': typing.Dict,
    'set': typing.Set,
    'frozenset': typing.FrozenSet,
    'deque': typing.Deque,
    'defaultdict': typing.DefaultDict,
    'type': typing.Type,
    'Set': typing.AbstractSet,
}

_type_per_alias = {
    'List': list,
    'Tuple': tuple,
    'Dict': dict,
    'Set': set,
    'FrozenSet': frozenset,
    'Deque': deque,
    'DefaultDict': defaultdict,
    'Type': type,
    'AbstractSet': Set,
}

Current_dir [ NOT WRITEABLE ] Document_root [ WRITEABLE ]


[ Back ]
NAME
SIZE
LAST TOUCH
USER
CAN-I?
FUNCTIONS
..
--
5 Sep 2025 9.32 AM
root / 996
0755
__pycache__
--
8 May 2024 6.34 PM
root / root
0755
classes
--
8 May 2024 6.34 PM
root / root
0755
decorators
--
8 May 2024 6.34 PM
root / root
0755
functions
--
8 May 2024 6.34 PM
root / root
0755
__init__.py
1.452 KB
8 May 2024 6.34 PM
root / root
0644
_classes.py
11.346 KB
8 May 2024 6.34 PM
root / root
0644
_decorators.py
0.717 KB
8 May 2024 6.34 PM
root / root
0644
_functions.py
15.254 KB
8 May 2024 6.34 PM
root / root
0644
_meta.py
0.239 KB
8 May 2024 6.34 PM
root / root
0644
_state.py
1.271 KB
8 May 2024 6.34 PM
root / root
0644
_types.py
0.436 KB
8 May 2024 6.34 PM
root / root
0644
effe.py
0.821 KB
8 May 2024 6.34 PM
root / root
0644

GRAYBYTE WORDPRESS FILE MANAGER @ 2026 CONTACT ME
Static GIF