5252ENHANCED_NOTIFICATION_COMMAND = 1
5353
5454NOTIFICATION_FORMAT = (
55- '!' # network big-endian
56- 'B' # command
57- 'H' # token length
58- '32s' # token
59- 'H' # payload length
60- '%ds' # payload
61- )
55+ '!' # network big-endian
56+ 'B' # command
57+ 'H' # token length
58+ '32s' # token
59+ 'H' # payload length
60+ '%ds' # payload
61+ )
6262
6363ENHANCED_NOTIFICATION_FORMAT = (
64- '!' # network big-endian
65- 'B' # command
66- 'I' # identifier
67- 'I' # expiry
68- 'H' # token length
69- '32s' # token
70- 'H' # payload length
71- '%ds' # payload
72- )
64+ '!' # network big-endian
65+ 'B' # command
66+ 'I' # identifier
67+ 'I' # expiry
68+ 'H' # token length
69+ '32s' # token
70+ 'H' # payload length
71+ '%ds' # payload
72+ )
7373
7474ERROR_RESPONSE_FORMAT = (
75- '!' # network big-endian
76- 'B' # command
77- 'B' # status
78- 'I' # identifier
79- )
75+ '!' # network big-endian
76+ 'B' # command
77+ 'B' # status
78+ 'I' # identifier
79+ )
8080
8181TOKEN_LENGTH = 32
8282ERROR_RESPONSE_LENGTH = 6
@@ -140,7 +140,7 @@ def unpacked_uint_big_endian(bytes):
140140 Returns an unsigned int from a packed big-endian (network) byte array
141141 """
142142 return unpack ('>I' , bytes )[0 ]
143-
143+
144144 @staticmethod
145145 def unpacked_char_big_endian (bytes ):
146146 """
@@ -230,7 +230,7 @@ def _connect(self):
230230 sys .exc_clear ()
231231 else :
232232 raise
233-
233+
234234 self .connection_alive = True
235235 _logger .debug ("%s APNS connection established" % self .__class__ .__name__ )
236236
@@ -255,14 +255,14 @@ def write(self, string):
255255 if self .enhanced : # nonblocking socket
256256 self ._last_activity_time = time .time ()
257257 _ , wlist , _ = select .select ([], [self ._connection ()], [], WAIT_WRITE_TIMEOUT_SEC )
258-
258+
259259 if len (wlist ) > 0 :
260260 length = self ._connection ().sendall (string )
261261 if length == 0 :
262262 _logger .debug ("sent length: %d" % length ) #DEBUG
263263 else :
264264 _logger .warning ("write socket descriptor is not ready after " + str (WAIT_WRITE_TIMEOUT_SEC ))
265-
265+
266266 else : # blocking socket
267267 return self ._connection ().write (string )
268268
@@ -298,7 +298,7 @@ def __init__(self, payload_size):
298298
299299class Payload (object ):
300300 """A class representing an APNs message payload"""
301- def __init__ (self , alert = None , badge = None , sound = None , category = None , custom = {} , content_available = False ,
301+ def __init__ (self , alert = None , badge = None , sound = None , category = None , custom = None , content_available = False ,
302302 mutable_content = False ):
303303 super (Payload , self ).__init__ ()
304304 self .alert = alert
@@ -334,7 +334,8 @@ def dict(self):
334334 d .update ({'mutable-content' : 1 })
335335
336336 d = { 'aps' : d }
337- d .update (self .custom )
337+ if self .custom :
338+ d .update (self .custom )
338339 return d
339340
340341 def json (self ):
@@ -465,20 +466,20 @@ class GatewayConnection(APNsConnection):
465466 """
466467 A class that represents a connection to the APNs gateway server
467468 """
468-
469+
469470 def __init__ (self , use_sandbox = False , ** kwargs ):
470471 super (GatewayConnection , self ).__init__ (** kwargs )
471472 self .server = (
472473 'gateway.push.apple.com' ,
473474 'gateway.sandbox.push.apple.com' )[use_sandbox ]
474475 self .port = 2195
475- if self .enhanced == True : #start error-response monitoring thread
476+ if self .enhanced == True : #start error-response monitoring thread
476477 self ._last_activity_time = time .time ()
477-
478+
478479 self ._send_lock = threading .RLock ()
479480 self ._error_response_handler_worker = None
480481 self ._response_listener = None
481-
482+
482483 self ._sent_notifications = collections .deque (maxlen = SENT_BUFFER_QTY )
483484
484485 def _init_error_response_handler_worker (self ):
@@ -515,7 +516,7 @@ def _get_enhanced_notification(self, token_hex, payload, identifier, expiry):
515516 notification = pack (fmt , ENHANCED_NOTIFICATION_COMMAND , identifier , expiry ,
516517 TOKEN_LENGTH , token , len (payload ), payload )
517518 return notification
518-
519+
519520 def send_notification (self , token_hex , payload , identifier = 0 , expiry = 0 ):
520521 """
521522 in enhanced mode, send_notification may return error response from APNs if any
@@ -524,7 +525,7 @@ def send_notification(self, token_hex, payload, identifier=0, expiry=0):
524525 self ._last_activity_time = time .time ()
525526 message = self ._get_enhanced_notification (token_hex , payload ,
526527 identifier , expiry )
527-
528+
528529 for i in range (WRITE_RETRY ):
529530 try :
530531 with self ._send_lock :
@@ -534,16 +535,16 @@ def send_notification(self, token_hex, payload, identifier=0, expiry=0):
534535 break
535536 except socket_error as e :
536537 delay = 10 + (i * 2 )
537- _logger .exception ("sending notification with id:" + str (identifier ) +
538- " to APNS failed: " + str (type (e )) + ": " + str (e ) +
538+ _logger .exception ("sending notification with id:" + str (identifier ) +
539+ " to APNS failed: " + str (type (e )) + ": " + str (e ) +
539540 " in " + str (i + 1 ) + "th attempt, will wait " + str (delay ) + " secs for next action" )
540541 time .sleep (delay ) # wait potential error-response to be read
541542
542543 else :
543544 self .write (self ._get_notification (token_hex , payload ))
544-
545+
545546 def _make_sure_error_response_handler_worker_alive (self ):
546- if (not self ._error_response_handler_worker
547+ if (not self ._error_response_handler_worker
547548 or not self ._error_response_handler_worker .is_alive ()):
548549 self ._init_error_response_handler_worker ()
549550 TIMEOUT_SEC = 10
@@ -557,45 +558,45 @@ def _make_sure_error_response_handler_worker_alive(self):
557558 def send_notification_multiple (self , frame ):
558559 self ._sent_notifications += frame .get_notifications (self )
559560 return self .write (frame .get_frame ())
560-
561+
561562 def register_response_listener (self , response_listener ):
562563 self ._response_listener = response_listener
563-
564+
564565 def force_close (self ):
565566 if self ._error_response_handler_worker :
566567 self ._error_response_handler_worker .close ()
567-
568+
568569 def _is_idle_timeout (self ):
569570 TIMEOUT_IDLE = 30
570571 return (time .time () - self ._last_activity_time ) >= TIMEOUT_IDLE
571-
572+
572573 class ErrorResponseHandlerWorker (threading .Thread ):
573574 def __init__ (self , apns_connection ):
574575 threading .Thread .__init__ (self , name = self .__class__ .__name__ )
575576 self ._apns_connection = apns_connection
576577 self ._close_signal = False
577-
578+
578579 def close (self ):
579580 self ._close_signal = True
580-
581+
581582 def run (self ):
582583 while True :
583584 if self ._close_signal :
584585 _logger .debug ("received close thread signal" )
585586 break
586-
587+
587588 if self ._apns_connection ._is_idle_timeout ():
588589 idled_time = (time .time () - self ._apns_connection ._last_activity_time )
589590 _logger .debug ("connection idle after %d secs" % idled_time )
590591 break
591-
592+
592593 if not self ._apns_connection .connection_alive :
593594 time .sleep (1 )
594595 continue
595-
596+
596597 try :
597598 rlist , _ , _ = select .select ([self ._apns_connection ._connection ()], [], [], WAIT_READ_TIMEOUT_SEC )
598-
599+
599600 if len (rlist ) > 0 : # there's some data from APNs
600601 with self ._apns_connection ._send_lock :
601602 buff = self ._apns_connection .read (ERROR_RESPONSE_LENGTH )
@@ -611,23 +612,23 @@ def run(self):
611612 if len (buff ) == 0 :
612613 _logger .warning ("read socket got 0 bytes data" ) #DEBUG
613614 self ._apns_connection ._disconnect ()
614-
615+
615616 except socket_error as e : # APNS close connection arbitrarily
616617 _logger .exception ("exception occur when reading APNS error-response: " + str (type (e )) + ": " + str (e )) #DEBUG
617618 self ._apns_connection ._disconnect ()
618619 continue
619-
620+
620621 time .sleep (0.1 ) #avoid crazy loop if something bad happened. e.g. using invalid certificate
621-
622+
622623 self ._apns_connection ._disconnect ()
623624 _logger .debug ("error-response handler worker closed" ) #DEBUG
624-
625+
625626 def _resend_notifications_by_id (self , failed_identifier ):
626627 fail_idx = Util .getListIndexFromID (self ._apns_connection ._sent_notifications , failed_identifier )
627628 #pop-out success notifications till failed one
628629 self ._resend_notification_by_range (fail_idx + 1 , len (self ._apns_connection ._sent_notifications ))
629630 return
630-
631+
631632 def _resend_notification_by_range (self , start_idx , end_idx ):
632633 self ._apns_connection ._sent_notifications = collections .deque (itertools .islice (self ._apns_connection ._sent_notifications , start_idx , end_idx ))
633634 _logger .info ("resending %s notifications to APNS" % len (self ._apns_connection ._sent_notifications )) #DEBUG
@@ -643,7 +644,7 @@ def _resend_notification_by_range(self, start_idx, end_idx):
643644class Util (object ):
644645 @classmethod
645646 def getListIndexFromID (this_class , the_list , identifier ):
646- return next (index for (index , d ) in enumerate (the_list )
647+ return next (index for (index , d ) in enumerate (the_list )
647648 if d ['id' ] == identifier )
648649 @classmethod
649650 def convert_error_response_to_dict (this_class , error_response_tuple ):
0 commit comments