Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added version checking for class definition #74

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyapns/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .client import notify, provision, feedback, configure__version__ = "0.4.0"__author__ = "Samuel Sutch"__license__ = "MIT"__copyright__ = "Copyrighit 2012 Samuel Sutch"
from sys import hexversionif hexversion < 0x02070000: from .client import notify, provision, feedback, configureelse: from .client3 import notify, provision, feedback, configure__version__ = "0.4.0"__author__ = "Samuel Sutch"__license__ = "MIT"__copyright__ = "Copyrighit 2012 Samuel Sutch"
Expand Down
11 changes: 6 additions & 5 deletions pyapns/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ class TimeoutHTTPConnection(httplib.HTTPConnection):
def connect(self):
httplib.HTTPConnection.connect(self)
self.sock.settimeout(self.timeout)

if hexversion < 0x02070000:
class TimeoutHTTP(httplib.HTTP):
_connection_class = TimeoutHTTPConnection

class TimeoutHTTP(httplib.HTTP):
_connection_class = TimeoutHTTPConnection
def set_timeout(self, timeout):
self._conn.timeout = timeout

def set_timeout(self, timeout):
self._conn.timeout = timeout

164 changes: 164 additions & 0 deletions pyapns/client3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import xmlrpc.client
import threading
import http.client
import functools
from sys import hexversion

OPTIONS = {'CONFIGURED': False, 'TIMEOUT': 20}

def configure(opts):
if not OPTIONS['CONFIGURED']:
try: # support for django
import django.conf
OPTIONS.update(django.conf.settings.PYAPNS_CONFIG)
OPTIONS['CONFIGURED'] = True
except:
pass
if not OPTIONS['CONFIGURED']:
try: # support for programatic configuration
OPTIONS.update(opts)
OPTIONS['CONFIGURED'] = True
except:
pass
if not OPTIONS['CONFIGURED']:
try: # pylons support
import pylons.config
OPTIONS.update({'HOST': pylons.config.get('pyapns_host')})
try:
OPTIONS.update({'TIMEOUT': int(pylons.config.get('pyapns_timeout'))})
except:
pass # ignore, an optional value
OPTIONS['CONFIGURED'] = True
except:
pass
# provision initial app_ids
if 'INITIAL' in OPTIONS:
for args in OPTIONS['INITIAL']:
provision(*args)
return OPTIONS['CONFIGURED']


class UnknownAppID(Exception): pass
class APNSNotConfigured(Exception): pass

def reprovision_and_retry(func):
"""
Wraps the `errback` callback of the API functions, automatically trying to
re-provision if the app ID can not be found during the operation. If that's
unsuccessful, it will raise the UnknownAppID error.
"""
@functools.wraps(func)
def wrapper(*a, **kw):
errback = kw.get('errback', None)
if errback is None:
def errback(e):
raise e
def errback_wrapper(e):
if isinstance(e, UnknownAppID) and 'INITIAL' in OPTIONS:
try:
for initial in OPTIONS['INITIAL']:
provision(*initial) # retry provisioning the initial setup
func(*a, **kw) # and try the function once more
except Exception as new_exc:
errback(new_exc) # throwing the new exception
else:
errback(e) # not an instance of UnknownAppID - nothing we can do here
kw['errback'] = errback_wrapper
return func(*a, **kw)
return wrapper

def default_callback(func):
@functools.wraps(func)
def wrapper(*a, **kw):
if 'callback' not in kw:
kw['callback'] = lambda c: c
return func(*a, **kw)
return wrapper

@default_callback
@reprovision_and_retry
def provision(app_id, path_to_cert, environment, timeout=15, async=False,
callback=None, errback=None):
args = [app_id, path_to_cert, environment, timeout]
f_args = ['provision', args, callback, errback]
if not async:
return _xmlrpc_thread(*f_args)
t = threading.Thread(target=_xmlrpc_thread, args=f_args)
t.daemon = True
t.start()

@default_callback
@reprovision_and_retry
def notify(app_id, tokens, notifications, async=False, callback=None,
errback=None):
args = [app_id, tokens, notifications]
f_args = ['notify', args, callback, errback]
if not async:
return _xmlrpc_thread(*f_args)
t = threading.Thread(target=_xmlrpc_thread, args=f_args)
t.daemon = True
t.start()

@default_callback
@reprovision_and_retry
def feedback(app_id, async=False, callback=None, errback=None):
args = [app_id]
f_args = ['feedback', args, callback, errback]
if not async:
return _xmlrpc_thread(*f_args)
t = threading.Thread(target=_xmlrpc_thread, args=f_args)
t.daemon = True
t.start()

def _xmlrpc_thread(method, args, callback, errback=None):
if not configure({}):
raise APNSNotConfigured('APNS Has not been configured.')
proxy = ServerProxy(OPTIONS['HOST'], allow_none=True, use_datetime=True,
timeout=OPTIONS['TIMEOUT'])
try:
parts = method.strip().split('.')
for part in parts:
proxy = getattr(proxy, part)
return callback(proxy(*args))
except xmlrpc.client.Fault as e:
if e.faultCode == 404:
e = UnknownAppID()
if errback is not None:
errback(e)
else:
raise e


## --------------------------------------------------------------
## Thank you Volodymyr Orlenko:
## http://blog.bjola.ca/2007/08/using-timeout-with-xmlrpclib.html
## --------------------------------------------------------------

def ServerProxy(url, *args, **kwargs):
t = TimeoutTransport()
t.timeout = kwargs.pop('timeout', 20)
kwargs['transport'] = t
return xmlrpc.client.ServerProxy(url, *args, **kwargs)

class TimeoutTransport(xmlrpc.client.Transport):
def make_connection(self, host):
if hexversion < 0x02070000:
conn = TimeoutHTTP(host)
conn.set_timeout(self.timeout)
else:
conn = TimeoutHTTPConnection(host)
conn.timeout = self.timeout
return conn

class TimeoutHTTPConnection(http.client.HTTPConnection):
def connect(self):
http.client.HTTPConnection.connect(self)
self.sock.settimeout(self.timeout)

if hexversion < 0x02070000:
class TimeoutHTTP(http.client.HTTP):
_connection_class = TimeoutHTTPConnection

def set_timeout(self, timeout):
self._conn.timeout = timeout