Completed
Push — master ( 8f3758...257d3c )
by Ramon
29s queued 11s
created

_get_subclasses()   A

Complexity

Conditions 5

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 16
rs 9.2833
c 0
b 0
f 0
cc 5
nop 2
1
from collections.abc import Iterable
2
from multiprocessing import Process
3
from typing import Tuple, Optional
4
5
from typish import get_args, get_type
6
7
from jsons._dump_impl import dump
8
from jsons._multitasking import multi_task
9
from jsons.exceptions import SerializationError
10
11
12
def default_iterable_serializer(
13
        obj: Iterable,
14
        cls: type = None,
15
        *,
16
        strict: bool = False,
17
        tasks: int = 1,
18
        task_type: type = Process,
19
        **kwargs) -> list:
20
    """
21
    Serialize the given ``obj`` to a list of serialized objects.
22
    :param obj: the iterable that is to be serialized.
23
    :param cls: the (subscripted) type of the iterable.
24
    :param strict: a bool to determine if the serializer should be strict
25
    (i.e. only dumping stuff that is known to ``cls``).
26
    :param tasks: the allowed number of tasks (threads or processes).
27
    :param task_type: the type that is used for multitasking.
28
    :param kwargs: any keyword arguments that may be given to the serialization
29
    process.
30
    :return: a list of which all elements are serialized.
31
    """
32
    # The meta kwarg store_cls is filtered out, because an iterable should have
33
    # its own -meta attribute.
34
    kwargs_ = {**kwargs, 'strict': strict}
35
    kwargs_.pop('_store_cls', None)
36
    if strict:
37
        cls_ = determine_cls(obj, cls)
38
        # cls_ = cls or get_type(obj)  # Get the List[T] type from the instance.
39
        subclasses = _get_subclasses(obj, cls_)
40
    else:
41
        subclasses = _get_subclasses(obj, None)
42
43
    if tasks < 2:
44
        result = [dump(elem, cls=subclasses[i], **kwargs_)
45
                  for i, elem in enumerate(obj)]
46
    else:
47
        zipped_objs = list(zip(obj, subclasses))
48
        result = multi_task(do_dump, zipped_objs, tasks, task_type, **kwargs_)
49
50
    return result
51
52
53
def _get_subclasses(obj: Iterable, cls: type = None) -> Tuple[type, ...]:
54
    subclasses = (None,) * len(obj)
55
    if cls:
56
        args = get_args(cls)
57
        if len(args) == 1:
58
            # E.g. List[int]
59
            subclasses = args * len(obj)
60
        elif len(args) > 1:
61
            # E.g. Tuple[int, str, str]
62
            subclasses = args
63
    if len(subclasses) != len(obj):
64
        msg = ('Not enough generic types ({}) in {}, expected {} to match '
65
               'the iterable of length {}'
66
               .format(len(subclasses), cls, len(obj), len(obj)))
67
        raise SerializationError(msg)
68
    return subclasses
69
70
71
def do_dump(obj_cls_tuple: Tuple[object, type], *args, **kwargs):
72
    kwargs_ = {**kwargs}
73
    kwargs_['tasks'] = 1
74
    return dump(*obj_cls_tuple, *args, **kwargs_)
75
76
77
def determine_cls(obj: Iterable, cls: Optional[type]) -> Optional[type]:
78
    cls_ = cls
79
    if not cls and hasattr(obj, '__getitem__') and len(obj) > 0:
80
        obj_with_only_one_elem = obj.__getitem__(slice(0, 1))
81
        cls_ = get_type(obj_with_only_one_elem)
82
    return cls_
83