Completed
Pull Request — master (#469)
by
unknown
02:24
created

Client.start()   F

Complexity

Conditions 14

Size

Total Lines 63

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 63
rs 2.8462
cc 14

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