Passed
Pull Request — master (#49)
by Ramon
01:20
created

default_object_serializer()   B

Complexity

Conditions 6

Size

Total Lines 64
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 64
rs 8.0106
c 0
b 0
f 0
cc 6
nop 9

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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