Completed
Push — master ( 7be5b2...0ef053 )
by Ramon
24s queued 10s
created

_get_dict_from_obj()   A

Complexity

Conditions 1

Size

Total Lines 20
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 20
rs 9.4
c 0
b 0
f 0
cc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
from datetime import datetime, timezone
2
from typing import Optional, Callable, Union, MutableSequence, Tuple
3
from jsons._common_impl import get_class_name, META_ATTR
4
from jsons._datetime_impl import to_str
5
from jsons.classes import JsonSerializable
6
from jsons.classes.verbosity import Verbosity
7
from jsons.serializers.default_dict import default_dict_serializer
8
9
10
def default_object_serializer(
11
        obj: object,
12
        *,
13
        key_transformer: Optional[Callable[[str], str]] = None,
14
        strip_nulls: bool = False,
15
        strip_privates: bool = False,
16
        strip_properties: bool = False,
17
        strip_class_variables: bool = False,
18
        strip_attr: Union[str, MutableSequence[str], Tuple[str]] = None,
19
        verbose: Union[Verbosity, bool] = False,
20
        **kwargs) -> dict:
21
    """
22
    Serialize the given ``obj`` to a dict. All values within ``obj`` are also
23
    serialized. If ``key_transformer`` is given, it will be used to transform
24
    the casing (e.g. snake_case) to a different format (e.g. camelCase).
25
    :param obj: the object that is to be serialized.
26
    :param key_transformer: a function that will be applied to all keys in the
27
    resulting dict.
28
    :param strip_nulls: if ``True`` the resulting dict will not contain null
29
    values.
30
    :param strip_privates: if ``True`` the resulting dict will not contain
31
    private attributes (i.e. attributes that start with an underscore).
32
    :param strip_properties: if ``True`` the resulting dict will not contain
33
    values from @properties.
34
    :param strip_class_variables: if ``True`` the resulting dict will not
35
    contain values from class variables.
36
    :param strip_attr: can be a name or a collection of names of attributes
37
    that are not to be included in the dump.
38
    dict will not contain attributes with
39
    :param verbose: if ``True`` the resulting dict will contain meta
40
    information (e.g. on how to deserialize).
41
    :param kwargs: any keyword arguments that are to be passed to the
42
    serializer functions.
43
    :return: a Python dict holding the values of ``obj``.
44
    """
45
    if obj is None:
46
        return obj
47
    strip_attr = strip_attr or []
48
    if (not isinstance(strip_attr, MutableSequence)
49
            and not isinstance(strip_attr, tuple)):
50
        strip_attr = (strip_attr,)
51
    cls = kwargs['cls'] or obj.__class__
52
    obj_dict = _get_dict_from_obj(obj, strip_privates, strip_properties,
53
                                  strip_class_variables, strip_attr, **kwargs)
54
    kwargs_ = {**kwargs, 'verbose': verbose}
55
    verbose = Verbosity.from_value(verbose)
56
    if Verbosity.WITH_CLASS_INFO in verbose:
57
        kwargs_['_store_cls'] = True
58
    result = default_dict_serializer(
59
        obj_dict,
60
        key_transformer=key_transformer,
61
        strip_nulls=strip_nulls,
62
        strip_privates=strip_privates,
63
        strip_properties=strip_properties,
64
        strip_class_variables=strip_class_variables,
65
        strip_attr=strip_attr,
66
        **kwargs_)
67
    cls_name = get_class_name(cls, fully_qualified=True,
68
                              fork_inst=kwargs['fork_inst'])
69
    if kwargs.get('_store_cls'):
70
        result['-cls'] = cls_name
71
    else:
72
        result = _get_dict_with_meta(result, cls_name, verbose,
73
                                     kwargs['fork_inst'])
74
    return result
75
76
77
def _get_dict_from_obj(
78
        obj,
79
        strip_privates,
80
        strip_properties,
81
        strip_class_variables,
82
        strip_attr,
83
        cls=None, *_, **__) -> dict:
84
    strip_attr = tuple(strip_attr) + _ABC_ATTRS
85
    excluded_elems = dir(JsonSerializable)
86
    props, other_cls_vars = _get_class_props(obj.__class__)
87
    return {attr: obj.__getattribute__(attr) for attr in dir(obj)
88
            if not attr.startswith('__')
89
            and not (strip_privates and attr.startswith('_'))
90
            and not (strip_properties and attr in props)
91
            and not (strip_class_variables and attr in other_cls_vars)
92
            and attr not in strip_attr
93
            and attr != 'json'
94
            and not isinstance(obj.__getattribute__(attr), Callable)
95
            and (not cls or attr in cls.__slots__)
96
            and attr not in excluded_elems}
97
98
99
def _get_class_props(cls):
100
    props = []
101
    other_cls_vars = []
102
    for n, v in _get_complete_class_dict(cls).items():
103
        list_to_append = props if isinstance(v, property) else other_cls_vars
104
        list_to_append.append(n)
105
    return props, other_cls_vars
106
107
108
def _get_complete_class_dict(cls):
109
    cls_dict = {}
110
    # Loop reversed so values of sub-classes override those of super-classes.
111
    for cls_or_elder in reversed(cls.mro()):
112
        cls_dict.update(cls_or_elder.__dict__)
113
    return cls_dict
114
115
116
def _get_dict_with_meta(
117
        obj: dict,
118
        cls_name: str,
119
        verbose: Verbosity,
120
        fork_inst: type) -> dict:
121
    # This function will add a -meta section to the given obj (provided that
122
    # the given obj has -cls attributes for all children).
123
    if verbose is Verbosity.WITH_NOTHING:
124
        return obj
125
126
    obj[META_ATTR] = {}
127
    if Verbosity.WITH_CLASS_INFO in verbose:
128
        collection_of_types = {}
129
        _fill_collection_of_types(obj, cls_name, '/', collection_of_types)
130
        collection_of_types['/'] = cls_name
131
        obj[META_ATTR]['classes'] = collection_of_types
132
    if Verbosity.WITH_DUMP_TIME in verbose:
133
        dump_time = to_str(datetime.now(tz=timezone.utc), True, fork_inst)
134
        obj[META_ATTR]['dump_time'] = dump_time
135
    return obj
136
137
138
def _fill_collection_of_types(
139
        obj_: dict,
140
        cls_name_: Optional[str],
141
        prefix: str,
142
        collection_of_types_: dict) -> str:
143
    # This function loops through obj to fill collection_of_types_ with the
144
    # class names.
145
    cls_name_ = cls_name_ or obj_.pop('-cls')
146
    for attr in obj_:
147
        if attr != META_ATTR and isinstance(obj_[attr], dict):
148
            attr_class = _fill_collection_of_types(obj_[attr],
149
                                                   None,
150
                                                   prefix + attr + '/',
151
                                                   collection_of_types_)
152
            collection_of_types_[prefix + attr] = attr_class
153
    return cls_name_
154
155
156
_ABC_ATTRS = ('_abc_registry', '_abc_cache', '_abc_negative_cache',
157
              '_abc_negative_cache_version', '_abc_impl')
158