stringify()   F
last analyzed

Complexity

Conditions 18

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
cc 18
dl 0
loc 58
rs 3.2528
c 5
b 1
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A requests_models_PreparedRequest_builder() 0 9 3
A bottle_builder() 0 10 3
B requests_models_Response_builder() 0 16 6

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like stringify() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
from __future__ import absolute_import, division, print_function, unicode_literals
3
from json import JSONEncoder
4
from logging import getLogger, INFO, Formatter, StreamHandler, DEBUG
5
from pprint import pformat
6
from sys import stderr
7
8
from . import NullHandler
9
from .compat import text_type
10
11
log = getLogger(__name__)
12
root_log = getLogger()
13
14
NullHandler = NullHandler
15
16
DEBUG_FORMATTER = Formatter(
17
    "[%(levelname)s] [%(asctime)s.%(msecs)03d] %(process)d %(name)s:%(funcName)s(%(lineno)d):\n"
18
    "%(message)s\n",
19
    "%Y-%m-%d %H:%M:%S")
20
21
INFO_FORMATTER = Formatter(
22
    "[%(levelname)s] [%(asctime)s.%(msecs)03d] %(process)d %(name)s(%(lineno)d): %(message)s\n",
23
    "%Y-%m-%d %H:%M:%S")
24
25
26
def set_root_level(level=INFO):
27
    root_log.setLevel(level)
28
29
30
def attach_stderr(level=INFO):
31
    has_stderr_handler = any(handler.name == 'stderr' for handler in root_log.handlers)
32
    if not has_stderr_handler:
33
        handler = StreamHandler(stderr)
34
        handler.name = 'stderr'
35
        if level is not None:
36
            handler.setLevel(level)
37
        handler.setFormatter(DEBUG_FORMATTER if level == DEBUG else INFO_FORMATTER)
38
        root_log.addHandler(handler)
39
        return True
40
    else:
41
        return False
42
43
44
def detach_stderr():
45
    for handler in root_log.handlers:
46
        if handler.name == 'stderr':
47
            root_log.removeHandler(handler)
48
            return True
49
    return False
50
51
52
def initialize_logging(level=INFO):
53
    attach_stderr(level)
54
55
56
class DumpEncoder(JSONEncoder):
57
    def default(self, obj):
58
        if hasattr(obj, 'dump'):
59
            return obj.dump()
60
        # Let the base class default method raise the TypeError
61
        return super(DumpEncoder, self).default(obj)
62
_DUMPS = DumpEncoder(indent=2, ensure_ascii=False, sort_keys=True).encode
63
64
65
def jsondumps(obj):
66
    return _DUMPS(obj)
67
68
69
def fullname(obj):
70
    return obj.__module__ + "." + obj.__class__.__name__
71
72
73
request_header_sort_dict = {
74
    'Host': '\x00\x00',
75
    'User-Agent': '\x00\x01',
76
}
77
def request_header_sort_key(item):
78
    return request_header_sort_dict.get(item[0], item[0].lower())
79
80
81
response_header_sort_dict = {
82
    'Content-Length': '\x7e\x7e\x61',
83
    'Connection': '\x7e\x7e\x62',
84
}
85
def response_header_sort_key(item):
86
    return response_header_sort_dict.get(item[0], item[0].lower())
87
88
89
def stringify(obj):
90
    def bottle_builder(builder, bottle_object):
91
        builder.append("{0} {1}{2} {3}".format(bottle_object.method,
92
                                               bottle_object.path,
93
                                               bottle_object.environ.get('QUERY_STRING', ''),
94
                                               bottle_object.get('SERVER_PROTOCOL')))
95
        builder += ["{0}: {1}".format(key, value) for key, value in bottle_object.headers.items()]
96
        builder.append('')
97
        body = bottle_object.body.read().strip()
98
        if body:
99
            builder.append(body)
100
101
    def requests_models_PreparedRequest_builder(builder, request_object):
102
        builder.append(">>{0} {1} {2}".format(request_object.method, request_object.path_url,
103
                                              request_object.url.split(':', 1)[0].upper()))
104
        builder.extend("> {0}: {1}".format(key, value)
105
                       for key, value in sorted(request_object.headers.items(),
106
                                                key=request_header_sort_key))
107
        builder.append('')
108
        if request_object.body:
109
            builder.append(request_object.body)
110
111
    def requests_models_Response_builder(builder, response_object, include_content=False):
112
        builder.append("<<{0} {1} {2}".format(response_object.url.split(':', 1)[0].upper(),
113
                                              response_object.status_code, response_object.reason))
114
        builder.extend("< {0}: {1}".format(key, value)
115
                       for key, value in sorted(response_object.headers.items(),
116
                                                key=response_header_sort_key))
117
        elapsed = text_type(response_object.elapsed).split(':', 1)[-1]
118
        builder.append('< Elapsed: {0}'.format(elapsed))
119
        if include_content:
120
            builder.append('')
121
            content_type = response_object.headers.get('Content-Type')
122
            if content_type == 'application/json':
123
                builder.append(pformat(response_object.json(), indent=2))
124
                builder.append('')
125
            elif content_type is not None and content_type.startswith('text/'):
126
                builder.append(response_object.text)
127
128
    try:
129
        name = fullname(obj)
130
        builder = ['']  # start with new line
131
        if name.startswith('bottle.'):
132
            bottle_builder(builder, obj)
133
        elif name.endswith('requests.models.PreparedRequest'):
134
            requests_models_PreparedRequest_builder(builder, obj)
135
        elif name.endswith('requests.models.Response'):
136
            if getattr(obj, 'request'):
137
                requests_models_PreparedRequest_builder(builder, obj.request)
138
            else:
139
                log.info("request is 'None' for Response object with url %s", obj.url)
140
            requests_models_Response_builder(builder, obj)
141
        else:
142
            return None
143
        builder.append('')  # end with new line
144
        return "\n".join(builder)
145
    except Exception as e:
146
        log.exception(e)
147