Passed
Pull Request — master (#23)
by Ramon
55s
created

default_object_serializer()   A

Complexity

Conditions 3

Size

Total Lines 53
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 53
rs 9.1359
c 0
b 0
f 0
cc 3
nop 8

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
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 import default_datetime_serializer
8
from jsons.serializers.default_dict import default_dict_serializer
9
10
11
def default_object_serializer(
12
        obj: object,
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
        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 verbose: if ``True`` the resulting dict will contain meta
36
    information (e.g. on how to deserialize).
37
    :param kwargs: any keyword arguments that are to be passed to the
38
    serializer functions.
39
    :return: a Python dict holding the values of ``obj``.
40
    """
41
    cls = kwargs['cls'] or obj.__class__
42
    obj_dict = _get_dict_from_obj(obj, strip_privates, strip_properties,
43
                                  strip_class_variables, **kwargs)
44
    kwargs_ = {**kwargs}
45
    verbose = Verbosity.from_value(verbose)
46
    if Verbosity.WITH_CLASS_INFO in verbose:
47
        kwargs_['store_cls'] = True
48
    result = default_dict_serializer(
49
        obj_dict,
50
        key_transformer=key_transformer,
51
        strip_nulls=strip_nulls,
52
        strip_privates=strip_privates,
53
        strip_properties=strip_properties,
54
        strip_class_variables=strip_class_variables,
55
        **kwargs_)
56
    cls_name = get_class_name(cls, fully_qualified=True,
57
                              fork_inst=kwargs['fork_inst'])
58
    if kwargs.get('store_cls'):
59
        result['-cls'] = cls_name
60
    else:
61
        result = _get_dict_with_meta(result, cls_name, verbose,
62
                                     kwargs['fork_inst'])
63
    return result
64
65
66
def _get_dict_from_obj(obj,
67
                       strip_privates,
68
                       strip_properties,
69
                       strip_class_variables,
70
                       cls=None, *_, **__) -> dict:
71
    excluded_elems = dir(JsonSerializable)
72
    props, other_cls_vars = _get_class_props(obj.__class__)
73
    return {attr: obj.__getattribute__(attr) for attr in dir(obj)
74
            if not attr.startswith('__')
75
            and not (strip_privates and attr.startswith('_'))
76
            and not (strip_properties and attr in props)
77
            and not (strip_class_variables and attr in other_cls_vars)
78
            and attr != 'json'
79
            and not isinstance(obj.__getattribute__(attr), Callable)
80
            and (not cls or attr in cls.__slots__)
81
            and attr not in excluded_elems}
82
83
84
def _get_class_props(cls):
85
    props = []
86
    other_cls_vars = []
87
    for n, v in _get_complete_class_dict(cls).items():
88
        props.append(n) if type(v) is property else other_cls_vars.append(n)
89
    return props, other_cls_vars
90
91
92
def _get_complete_class_dict(cls):
93
    cls_dict = {}
94
    # Loop reversed so values of sub-classes override those of super-classes.
95
    for cls_or_elder in reversed(cls.mro()):
96
        cls_dict.update(cls_or_elder.__dict__)
97
    return cls_dict
98
99
100
def _get_dict_with_meta(
101
        obj: dict,
102
        cls_name: str,
103
        verbose: Verbosity,
104
        fork_inst: type) -> dict:
105
    # This function will add a -meta section to the given obj (provided that
106
    # the given obj has -cls attributes for all children).
107
    if verbose is Verbosity.WITH_NOTHING:
108
        return obj
109
110
    obj[META_ATTR] = {}
111
    if Verbosity.WITH_CLASS_INFO in verbose:
112
        collection_of_types = {}
113
        _fill_collection_of_types(obj, cls_name, '/', collection_of_types)
114
        collection_of_types['/'] = cls_name
115
        obj[META_ATTR]['classes'] = collection_of_types
116
    if Verbosity.WITH_DUMP_TIME in verbose:
117
        dump_time = to_str(datetime.now(tz=timezone.utc), True, fork_inst)
118
        obj[META_ATTR]['dump_time'] = dump_time
119
    return obj
120
121
122
def _fill_collection_of_types(obj_: dict,
123
                              cls_name_: Optional[str],
124
                              prefix: str,
125
                              collection_of_types_: dict) -> str:
126
    # This function loops through obj to fill collection_of_types_ with the
127
    # class names.
128
    cls_name_ = cls_name_ or obj_.pop('-cls')
129
    for attr in obj_:
130
        if attr != META_ATTR and isinstance(obj_[attr], dict):
131
            attr_class = _fill_collection_of_types(obj_[attr],
132
                                                   None,
133
                                                   prefix + attr + '/',
134
                                                   collection_of_types_)
135
            collection_of_types_[prefix + attr] = attr_class
136
    return cls_name_
137