diff --git a/gcm/gcm.py b/gcm/gcm.py index 3b38111..cb853e8 100644 --- a/gcm/gcm.py +++ b/gcm/gcm.py @@ -8,20 +8,54 @@ GCM_URL = 'https://android.googleapis.com/gcm/send' -class GCMException(Exception): pass -class GCMMalformedJsonException(GCMException): pass -class GCMConnectionException(GCMException): pass -class GCMAuthenticationException(GCMException): pass -class GCMTooManyRegIdsException(GCMException): pass -class GCMInvalidTtlException(GCMException): pass +class GCMException(Exception): + pass + + +class GCMMalformedJsonException(GCMException): + pass + + +class GCMConnectionException(GCMException): + pass + + +class GCMAuthenticationException(GCMException): + pass + + +class GCMTooManyRegIdsException(GCMException): + pass + + +class GCMInvalidTtlException(GCMException): + pass # Exceptions from Google responses -class GCMMissingRegistrationException(GCMException): pass -class GCMMismatchSenderIdException(GCMException): pass -class GCMNotRegisteredException(GCMException): pass -class GCMMessageTooBigException(GCMException): pass -class GCMInvalidRegistrationException(GCMException): pass -class GCMUnavailableException(GCMException): pass + + +class GCMMissingRegistrationException(GCMException): + pass + + +class GCMMismatchSenderIdException(GCMException): + pass + + +class GCMNotRegisteredException(GCMException): + pass + + +class GCMMessageTooBigException(GCMException): + pass + + +class GCMInvalidRegistrationException(GCMException): + pass + + +class GCMUnavailableException(GCMException): + pass # TODO: Refactor this to be more human-readable @@ -29,22 +63,16 @@ def group_response(response, registration_ids, key): # Pair up results and reg_ids mapping = zip(registration_ids, response['results']) # Filter by key - filtered = filter(lambda x: key in x[1], mapping) - # Only consider the value in the dict - tupled = [(s[0], s[1][key]) for s in filtered] + filtered = ((reg_id, res[key]) for reg_id, res in mapping if key in res) # Grouping of errors and mapping of ids if key is 'registration_id': - grouping = {} - for k, v in tupled: - grouping[k] = v + grouping = dict(filtered) else: grouping = defaultdict(list) - for k, v in tupled: + for k, v in filtered: grouping[v].append(k) - if len(grouping) == 0: - return - return grouping + return grouping or None def urlencode_utf8(params): @@ -56,7 +84,7 @@ def urlencode_utf8(params): if hasattr(params, 'items'): params = params.items() - params = ( + params = ( '='.join(( urllib.quote_plus(k.encode('utf8'), safe='/'), urllib.quote_plus(v.encode('utf8'), safe='/') @@ -69,8 +97,8 @@ def urlencode_utf8(params): class GCM(object): # Timeunit is milliseconds. - BACKOFF_INITIAL_DELAY = 1000; - MAX_BACKOFF_DELAY = 1024000; + BACKOFF_INITIAL_DELAY = 1000 + MAX_BACKOFF_DELAY = 1024000 def __init__(self, api_key, url=GCM_URL, proxy=None): """ api_key : google api key @@ -80,17 +108,17 @@ def __init__(self, api_key, url=GCM_URL, proxy=None): self.api_key = api_key self.url = url if proxy: - if isinstance(proxy,basestring): + if isinstance(proxy, basestring): protocol = url.split(':')[0] - proxy={protocol:proxy} + proxy = {protocol: proxy} auth = urllib2.HTTPBasicAuthHandler() - opener = urllib2.build_opener(urllib2.ProxyHandler(proxy), auth, urllib2.HTTPHandler) + opener = urllib2.build_opener( + urllib2.ProxyHandler(proxy), auth, urllib2.HTTPHandler) urllib2.install_opener(opener) - def construct_payload(self, registration_ids, data=None, collapse_key=None, - delay_while_idle=False, time_to_live=None, is_json=True, dry_run=False): + delay_while_idle=False, time_to_live=None, is_json=True, dry_run=False): """ Construct the dictionary mapping of parameters. Encodes the dictionary into JSON if for json requests. @@ -101,7 +129,8 @@ def construct_payload(self, registration_ids, data=None, collapse_key=None, """ if time_to_live: - if time_to_live > 2419200 or time_to_live < 0: + four_weeks_in_secs = 2419200 + if not (0 <= time_to_live <= four_weeks_in_secs): raise GCMInvalidTtlException("Invalid time to live value") if is_json: @@ -119,7 +148,7 @@ def construct_payload(self, registration_ids, data=None, collapse_key=None, if delay_while_idle: payload['delay_while_idle'] = delay_while_idle - if time_to_live >= 0: + if time_to_live: payload['time_to_live'] = time_to_live if collapse_key: @@ -146,7 +175,8 @@ def make_request(self, data, is_json=True): headers = { 'Authorization': 'key=%s' % self.api_key, } - # Default Content-Type is defaulted to application/x-www-form-urlencoded;charset=UTF-8 + # Default Content-Type is defaulted to + # application/x-www-form-urlencoded;charset=UTF-8 if is_json: headers['Content-Type'] = 'application/json' @@ -158,16 +188,19 @@ def make_request(self, data, is_json=True): response = urllib2.urlopen(req).read() except urllib2.HTTPError as e: if e.code == 400: - raise GCMMalformedJsonException("The request could not be parsed as JSON") + raise GCMMalformedJsonException( + "The request could not be parsed as JSON") elif e.code == 401: - raise GCMAuthenticationException("There was an error authenticating the sender account") + raise GCMAuthenticationException( + "There was an error authenticating the sender account") elif e.code == 503: raise GCMUnavailableException("GCM service is unavailable") else: error = "GCM service error: %d" % e.code raise GCMUnavailableException(error) except urllib2.URLError as e: - raise GCMConnectionException("There was an internal error in the GCM server while trying to process the request") + raise GCMConnectionException( + "There was an internal error in the GCM server while trying to process the request") if is_json: response = json.loads(response) @@ -179,11 +212,14 @@ def raise_error(self, error): elif error == 'Unavailable': # Plain-text requests will never return Unavailable as the error code. # http://developer.android.com/guide/google/gcm/gcm.html#error_codes - raise GCMUnavailableException("Server unavailable. Resent the message") + raise GCMUnavailableException( + "Server unavailable. Resent the message") elif error == 'NotRegistered': - raise GCMNotRegisteredException("Registration id is not valid anymore") + raise GCMNotRegisteredException( + "Registration id is not valid anymore") elif error == 'MismatchSenderId': - raise GCMMismatchSenderIdException("A Registration ID is tied to a certain group of senders") + raise GCMMismatchSenderIdException( + "A Registration ID is tied to a certain group of senders") elif error == 'MessageTooBig': raise GCMMessageTooBigException("Message can't exceed 4096 bytes") @@ -202,7 +238,8 @@ def handle_plaintext_response(self, response): def handle_json_response(self, response, registration_ids): errors = group_response(response, registration_ids, 'error') - canonical = group_response(response, registration_ids, 'registration_id') + canonical = group_response( + response, registration_ids, 'registration_id') info = {} if errors: @@ -218,7 +255,7 @@ def extract_unsent_reg_ids(self, info): return [] def plaintext_request(self, registration_id, data=None, collapse_key=None, - delay_while_idle=False, time_to_live=None, retries=5, dry_run=False): + delay_while_idle=False, time_to_live=None, retries=5, dry_run=False): """ Makes a plaintext request to GCM servers @@ -251,7 +288,7 @@ def plaintext_request(self, registration_id, data=None, collapse_key=None, raise IOError("Could not make request after %d attempts" % attempt) def json_request(self, registration_ids, data=None, collapse_key=None, - delay_while_idle=False, time_to_live=None, retries=5, dry_run=False): + delay_while_idle=False, time_to_live=None, retries=5, dry_run=False): """ Makes a JSON request to GCM servers @@ -265,7 +302,8 @@ def json_request(self, registration_ids, data=None, collapse_key=None, if not registration_ids: raise GCMMissingRegistrationException("Missing registration_ids") if len(registration_ids) > 1000: - raise GCMTooManyRegIdsException("Exceded number of registration_ids") + raise GCMTooManyRegIdsException( + "Exceded number of registration_ids") attempt = 0 backoff = self.BACKOFF_INITIAL_DELAY