Completed
Pull Request — master (#552)
by
unknown
02:27
created

Client.start()   F

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