jacked._typing._without_generic()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
"""
2
PRIVATE MODULE: do not import (from) it directly.
3
4
This module contains types that are not available by default.
5
"""
6
import sys
7
import typing
8
9
10
T = typing.TypeVar('T')
11
Module = type(typing)
12
NoneType = type(None)
13
14
15
class AttrDict(dict):
16
    """
17
    A simple wrapper around the default ``dict`` type that allows object-like
18
    access to attributes.
19
    """
20
    def __init__(self, *args, **kwargs):
21
        """
22
        Constructor.
23
        :param args: any args.
24
        :param kwargs: any kwargs.
25
        """
26
        super(AttrDict, self).__init__(*args, **kwargs)
27
        self.__dict__ = self
28
29
30
def issubtype(cls: type, clsinfo: type) -> bool:
31
    """
32
    Return whether ``cls`` is a subclass of ``clsinfo`` while also considering
33
    generics.
34
    :param cls: the subject.
35
    :param clsinfo: the object.
36
    :return: True if ``cls`` is a subclass of ``clsinfo`` considering generics.
37
    """
38
    info_generic_type, info_args = _split_generic(clsinfo)
39
    if clsinfo in (typing.Any, object):
40
        result = True
41
    elif info_args:
42
        result = _issubtype_generic(cls, info_generic_type, info_args)
43
    else:
44
        result = issubclass(_without_generic(cls), _without_generic(clsinfo))
45
    return result
46
47
48
def _issubtype_generic(
49
        cls: type,
50
        info_generic_type: type,
51
        info_args: tuple) -> bool:
52
    # Check if cls is a subtype of info_generic_type, knowing that the latter
53
    # is a generic type.
54
    result = False
55
    cls_generic_type, cls_args = _split_generic(cls)
56
    if (cls_generic_type == info_generic_type and cls_args
57
            and len(cls_args) == len(info_args)):
58
        args_do_correspond = True
59
        for tup in zip(cls_args, info_args):
60
            args_do_correspond &= issubtype(*tup)
61
        result = args_do_correspond
62
    # Note that issubtype(list, List[...]) is always False.
63
    # Note that the number of arguments must be equal.
64
    return result
65
66
67
def _split_generic(t: type) -> \
68
        typing.Tuple[type, typing.Optional[typing.Tuple[type, ...]]]:
69
    # Split the given generic type into the type and its args.
70
    origin = getattr(t, '__origin__', t)
71
    args_ = getattr(t, '__args__', tuple()) or tuple()
72
    args = tuple([attr for attr in args_
73
                  if type(attr) != typing.TypeVar])
74
    return origin, args
75
76
77
def _without_generic(t: type) -> type:
78
    # Return type t without any generic type.
79
    attr = '__origin__'
80
    if sys.version_info[1] in (5, 6):
81
        attr = '__extra__'
82
    return getattr(t, attr, t)
83