Skip to content

Commit

Permalink
Merge pull request #52 from suzaku/refactor
Browse files Browse the repository at this point in the history
Random cleanups
  • Loading branch information
geeknam committed Mar 11, 2015
2 parents 1b6aed2 + 8997ed8 commit 0af0b12
Showing 1 changed file with 81 additions and 43 deletions.
124 changes: 81 additions & 43 deletions gcm/gcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,71 @@
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
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):
Expand All @@ -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='/')
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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'

Expand All @@ -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)
Expand All @@ -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")

Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 0af0b12

Please sign in to comment.