Client.start()   F
last analyzed

Complexity

Conditions 15

Size

Total Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
c 0
b 0
f 0
dl 0
loc 68
rs 2.5815

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 Client.start() 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
# pylint: disable=no-member
2
import json
3
import urllib
4
import signal
5
import time
6
import pycurl
7
8
POST_PARAMS = {}
9
10
11
class Client:
12
    def __init__(self, sensor, api_url, api_user, api_password):
13
        if sensor is not None:
14
            self._sensor = sensor
15
        else:
16
            self._sensor = None
17
        self.conn = None
18
        self.buffer = ''
19
        self.keep_trying = 1
20
        self.api_url = api_url
21
        self.api_user = api_user
22
        self.api_password = api_password
23
24
        signal.signal(signal.SIGINT, self.handle_ctrl_c)
25
26
    def handle_ctrl_c(self, signal, frame):
27
        if self._sensor is not None:
28
            self._sensor.logger.info('SIGINT receivied')
29
        else:
30
            print 'You pressed Ctrl+C!'
31
        self.abort_session()
32
33
    def abort_session(self):
34
        self.keep_trying = 0
35
        self.conn.close()
36
37
    def setup_connection(self):
38
39
        if self.conn:
40
            self.conn.close()
41
            self.buffer = ''
42
        self.headers = ['Accept: application/json', 'Expect:']
43
        self.conn = pycurl.Curl()
44
        self.conn.setopt(pycurl.SSL_VERIFYHOST, False)
45
        self.conn.setopt(pycurl.SSL_VERIFYPEER, False)
46
        self.conn.setopt(pycurl.USERPWD, "%s:%s" % (self.api_user, self.api_password))
47
        self.conn.setopt(pycurl.URL, self.api_url)
48
        self.conn.setopt(pycurl.VERBOSE, 1)
49
        self.conn.setopt(pycurl.WRITEFUNCTION, self.on_receive)
50
        self.conn.setopt(pycurl.NOSIGNAL, 1)
51
        self.conn.setopt(pycurl.NOPROGRESS, 0)
52
        self.conn.setopt(pycurl.PROGRESSFUNCTION, self.on_progress)
53
        self.conn.setopt(pycurl.HTTPHEADER, self.headers)
54
        self.conn.setopt(pycurl.POST, 1)
55
        self.conn.setopt(pycurl.POSTFIELDS, urllib.urlencode(POST_PARAMS))
56
57
    def on_receive(self, data):
58
        self.buffer += data
59
        if data.endswith('\n') and self.buffer.strip():
60
            # complete message received
61
            event = json.loads(self.buffer)
62
            self.buffer = ''
63
            if self._sensor is not None:
64
                self._sensor.process_event(event)
65
            else:
66
                print event
67
68
    def on_progress(self, d_total, downloaded, u_total, uploaded):
69
        pass
70
        # print "on_progress called"
71
72
    def __del__(self):
73
        self.conn.close()
74
75
    def start(self):
76
        if self._sensor is not None:
77
            self._sensor.logger.info('pyCurl started.')
78
        backoff_network_error = 0.25
79
        backoff_http_error = 5
80
        backoff_rate_limit = 60
81
        while True and self.keep_trying == 1:
82
            if self._sensor is not None:
83
                self._sensor.logger.info('keep trying = %i', self.keep_trying)
84
            else:
85
                print "keep trying = %i" % self.keep_trying
86
            self.setup_connection()
87
            try:
88
                self.conn.perform()
89
            except:
90
                # Network error, use linear back off up to 16 seconds
91
                if self.keep_trying == 0:
92
                    continue
93
                if self._sensor is not None:
94
                    self._sensor.logger.info('Network error: %s', self.conn.errstr())
95
                    self._sensor.logger.info('Waiting %s seconds before trying again',
96
                                             backoff_network_error)
97
                else:
98
                    print 'Network error: %s' % self.conn.errstr()
99
                    print 'Waiting %s seconds before trying again' % backoff_network_error
100
                time.sleep(backoff_network_error)
101
                backoff_network_error = min(backoff_network_error + 1, 16)
102
                continue
103
            # HTTP Error
104
            sc = self.conn.getinfo(pycurl.HTTP_CODE)
105
            if sc == 420:
106
                # Rate limit, use exponential back off starting with 1 minute, and doubling
107
                if self._sensor is not None:
108
                    self._sensor.logger.info('Rate limit, waiting %s seconds', backoff_rate_limit)
109
                else:
110
                    print 'Rate limit, waiting %s seconds' % backoff_rate_limit
111
                time.sleep(backoff_rate_limit)
112
                backoff_rate_limit *= 2
113
            elif sc == 401:
114
                # Authentication error
115
                if self._sensor is not None:
116
                    self._sensor.logger.info(
117
                        'Authentication error, check user/password, waiting %s seconds',
118
                        backoff_rate_limit)
119
                else:
120
                    print 'Authentication error, waiting %s seconds' % backoff_rate_limit
121
                time.sleep(backoff_rate_limit)
122
                backoff_rate_limit *= 2
123
            elif sc == 404:
124
                # Authorization error
125
                if self._sensor is not None:
126
                    self._sensor.logger.info(
127
                        'Authorization error, check permissions, waiting %s seconds',
128
                        backoff_rate_limit)
129
                else:
130
                    print 'Authorization error, waiting %s seconds' % backoff_rate_limit
131
                time.sleep(backoff_rate_limit)
132
                backoff_rate_limit *= 2
133
            else:
134
                # HTTP error, use exponential back off up to 320 seconds
135
                if self._sensor is not None:
136
                    self._sensor.logger.info('HTTP error %s, %s', sc, self.conn.errstr())
137
                    self._sensor.logger.info('Waiting %s seconds', backoff_http_error)
138
                else:
139
                    print 'HTTP error %s, %s' % (sc, self.conn.errstr())
140
                    print 'Waiting %s seconds' % backoff_http_error
141
                time.sleep(backoff_http_error)
142
                backoff_http_error = min(backoff_http_error * 2, 320)
143