11import xmlrpclib
22import threading
33import httplib
4+ import functools
45from sys import hexversion
56
67OPTIONS = {'CONFIGURED' : False , 'TIMEOUT' : 20 }
@@ -40,32 +41,76 @@ def configure(opts):
4041class UnknownAppID (Exception ): pass
4142class APNSNotConfigured (Exception ): pass
4243
44+ def reprovision_and_retry (func ):
45+ """
46+ Wraps the `errback` callback of the API functions, automatically trying to
47+ re-provision if the app ID can not be found during the operation. If that's
48+ unsuccessful, it will raise the UnknownAppID error.
49+ """
50+ @functools .wraps (func )
51+ def wrapper (* a , ** kw ):
52+ errback = kw .get ('errback' , None )
53+ if errback is None :
54+ def errback (e ):
55+ raise e
56+ def errback_wrapper (e ):
57+ if isinstance (e , UnknownAppID ) and 'INITIAL' in OPTIONS :
58+ try :
59+ for initial in OPTIONS ['INITIAL' ]:
60+ provision (* initial ) # retry provisioning the initial setup
61+ func (* a , ** kw ) # and try the function once more
62+ except Exception , new_exc :
63+ errback (new_exc ) # throwing the new exception
64+ else :
65+ errback (e ) # not an instance of UnknownAppID - nothing we can do here
66+ kw ['errback' ] = errback_wrapper
67+ return func (* a , ** kw )
68+ return wrapper
69+
70+ def default_callback (func ):
71+ @functools .wraps (func )
72+ def wrapper (* a , ** kw ):
73+ if 'callback' not in kw :
74+ kw ['callback' ] = lambda c : c
75+ return func (* a , ** kw )
76+ return wrapper
4377
44- def provision (app_id , path_to_cert , environment , timeout = 15 , callback = None ):
78+ @default_callback
79+ @reprovision_and_retry
80+ def provision (app_id , path_to_cert , environment , timeout = 15 , async = False ,
81+ callback = None , errback = None ):
4582 args = [app_id , path_to_cert , environment , timeout ]
46- if callback is None :
47- return _xmlrpc_thread ('provision' , args , lambda r : r )
48- t = threading .Thread (target = _xmlrpc_thread , args = ['provision' , args , callback ])
83+ f_args = ['provision' , args , callback , errback ]
84+ if not async :
85+ return _xmlrpc_thread (* f_args )
86+ t = threading .Thread (target = _xmlrpc_thread , args = f_args )
4987 t .daemon = True
5088 t .start ()
5189
52- def notify (app_id , tokens , notifications , callback = None ):
90+ @default_callback
91+ @reprovision_and_retry
92+ def notify (app_id , tokens , notifications , async = False , callback = None ,
93+ errback = None ):
5394 args = [app_id , tokens , notifications ]
54- if callback is None :
55- return _xmlrpc_thread ('notify' , args , lambda r : r )
56- t = threading .Thread (target = _xmlrpc_thread , args = ['notify' , args , callback ])
95+ f_args = ['notify' , args , callback , errback ]
96+ if not async :
97+ return _xmlrpc_thread (* f_args )
98+ t = threading .Thread (target = _xmlrpc_thread , args = f_args )
5799 t .daemon = True
58100 t .start ()
59101
60- def feedback (app_id , callback = None ):
102+ @default_callback
103+ @reprovision_and_retry
104+ def feedback (app_id , async = False , callback = None , errback = None ):
61105 args = [app_id ]
62- if callback is None :
63- return _xmlrpc_thread ('feedback' , args , lambda r : r )
64- t = threading .Thread (target = _xmlrpc_thread , args = ['feedback' , args , callback ])
106+ f_args = ['feedback' , args , callback , errback ]
107+ if not async :
108+ return _xmlrpc_thread (* f_args )
109+ t = threading .Thread (target = _xmlrpc_thread , args = f_args )
65110 t .daemon = True
66111 t .start ()
67112
68- def _xmlrpc_thread (method , args , callback ):
113+ def _xmlrpc_thread (method , args , callback , errback = None ):
69114 if not configure ({}):
70115 raise APNSNotConfigured ('APNS Has not been configured.' )
71116 proxy = ServerProxy (OPTIONS ['HOST' ], allow_none = True , use_datetime = True ,
@@ -77,8 +122,11 @@ def _xmlrpc_thread(method, args, callback):
77122 return callback (proxy (* args ))
78123 except xmlrpclib .Fault , e :
79124 if e .faultCode == 404 :
80- raise UnknownAppID
81- raise
125+ e = UnknownAppID ()
126+ if errback is not None :
127+ errback (e )
128+ else :
129+ raise e
82130
83131
84132## --------------------------------------------------------------
0 commit comments