Passed
Pull Request — master (#49)
by Ramon
50s
created

default_object_serializer()   B

Complexity

Conditions 6

Size

Total Lines 65
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 65
rs 7.9866
c 0
b 0
f 0
cc 6
nop 10

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
        *,
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
        props.append(n) if type(v) is property else other_cls_vars.append(n)
104
    return props, other_cls_vars
105
106
107
def _get_complete_class_dict(cls):
108
    cls_dict = {}
109
    # Loop reversed so values of sub-classes override those of super-classes.
110
    for cls_or_elder in reversed(cls.mro()):
111
        cls_dict.update(cls_or_elder.__dict__)
112
    return cls_dict
113
114
115
def _get_dict_with_meta(
116
        obj: dict,
117
        cls_name: str,
118
        verbose: Verbosity,
119
        fork_inst: type) -> dict:
120
    # This function will add a -meta section to the given obj (provided that
121
    # the given obj has -cls attributes for all children).
122
    if verbose is Verbosity.WITH_NOTHING:
123
        return obj
124
125
    obj[META_ATTR] = {}
126
    if Verbosity.WITH_CLASS_INFO in verbose:
127
        collection_of_types = {}
128
        _fill_collection_of_types(obj, cls_name, '/', collection_of_types)
129
        collection_of_types['/'] = cls_name
130
        obj[META_ATTR]['classes'] = collection_of_types
131
    if Verbosity.WITH_DUMP_TIME in verbose:
132
        dump_time = to_str(datetime.now(tz=timezone.utc), True, fork_inst)
133
        obj[META_ATTR]['dump_time'] = dump_time
134
    return obj
135
136
137
def _fill_collection_of_types(
138
        obj_: dict,
139
        cls_name_: Optional[str],
140
        prefix: str,
141
        collection_of_types_: dict) -> str:
142
    # This function loops through obj to fill collection_of_types_ with the
143
    # class names.
144
    cls_name_ = cls_name_ or obj_.pop('-cls')
145
    for attr in obj_:
146
        if attr != META_ATTR and isinstance(obj_[attr], dict):
147
            attr_class = _fill_collection_of_types(obj_[attr],
148
                                                   None,
149
                                                   prefix + attr + '/',
150
                                                   collection_of_types_)
151
            collection_of_types_[prefix + attr] = attr_class
152
    return cls_name_
153
154
155
_ABC_ATTRS = ('_abc_registry', '_abc_cache', '_abc_negative_cache',
156
              '_abc_negative_cache_version', '_abc_impl')
157