Skip to content

Commit

Permalink
Merge pull request #17 from urbanairship/testpy3
Browse files Browse the repository at this point in the history
Ticket CE-713 - Add support for Python 3.3 and 3.4
  • Loading branch information
bugzPDX committed Dec 31, 2014
2 parents c850142 + 96c7791 commit 5a1d702
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 37 deletions.
15 changes: 7 additions & 8 deletions README.rst
Expand Up @@ -8,13 +8,12 @@ app pages.
Requirements
============

As of version 0.6, Python 2.6 or 2.7 is required. Python 3.3 support will
follow.
As of version 0.7, Python 2.6, 2.7, 3.3 or 3.4 is required.

Functionality
=============

Version 0.6 is a major upgrade, focusing on support for the new version 3 push
Version 0.7 is a major upgrade, focusing on support for the new version 3 push
API. There has also been a major reorganization of the codebase.

* device token registration
Expand Down Expand Up @@ -79,9 +78,9 @@ http://support.urbanairship.com/
History
=======

* 0.1 Initial release
* 0.2 Added tags, broadcast, feedback
* 0.3 Added deregister, device token list, other minor improvements
* 0.4 Added batch push
* 0.5 Added Android, Blackberry, Rich Push, and scheduled notifications
* 0.6 Major refactoring, support for push api v3
* 0.5 Added Android, Blackberry, Rich Push, and scheduled notifications
* 0.4 Added batch push
* 0.3 Added deregister, device token list, other minor improvements
* 0.2 Added tags, broadcast, feedback
* 0.1 Initial release
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -7,7 +7,7 @@
__about__ = {}

with open("urbanairship/__about__.py") as fp:
exec(fp, None, __about__)
exec(fp.read(), None, __about__)

setup(
name="urbanairship",
Expand Down
10 changes: 5 additions & 5 deletions tests/devices/test_device_info.py
Expand Up @@ -10,7 +10,7 @@ class TestDeviceInfo(unittest.TestCase):
def test_channel_list(self):
with mock.patch.object(ua.Airship, "_request") as mock_request:
response = requests.Response()
response._content = ('''{"channels":[
response._content = (b'''{"channels":[
{"channel_id": "0492662a-1b52-4343-a1f9-c6b0c72931c0"},
{"channel_id": "d95ceae2-85cb-41b7-a87d-09c9b3ce4051"},
{"channel_id": "f10cf38c-3fbd-47e8-a4aa-43cf91d80ba1"}]}''')
Expand Down Expand Up @@ -56,7 +56,7 @@ def test_channel_lookup(self):
}
}
}
)
).encode('utf-8')

response.status_code = 200
mock_request.return_value = response
Expand All @@ -81,7 +81,7 @@ def test_device_token_feedback(self):
with mock.patch.object(ua.Airship, "_request") as mock_request:
response = requests.Response()
response._content = (
'''[{
b'''[{
"alias" : null,
"device_token" : "4D95CB349F95ADEBE05F0A71B5F5B62D0F696667454DA60CD55282F67321710C",
"marked_inactive_on" : "2014-12-16 20:21:42"
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_apid_feedback(self):
with mock.patch.object(ua.Airship, "_request") as mock_request:
response = requests.Response()
response._content = (
'''[{
b'''[{
"alias" : null,
"apid" : "80242a4c-d870-4fce-b0c3-724c865cfbfe",
"gcm_registration_id": "null",
Expand Down Expand Up @@ -183,7 +183,7 @@ def test_device_pin_info(self):
"created": "2013-03-11 17:49:36",
"last_registration": "2014-05-01 18:00:27"
}
)
).encode('utf-8')
response.status_code = 200
mock_request.return_value = response

Expand Down
39 changes: 39 additions & 0 deletions tests/push/test_message.py
Expand Up @@ -51,6 +51,23 @@ def test_ios(self):
ua.notification(ios=ua.ios(content_available=True)),
{'ios': { 'content-available': True}})

def test_ios_unicode(self):
self.assertEqual(
ua.notification(ios=ua.ios(
alert=u'Hello',
badge=u'+1',
expiry=u'time',
)),
{'ios': {
'alert': 'Hello',
'badge': '+1',
'expiry': 'time'
}})

self.assertEqual(
ua.notification(ios=ua.ios(content_available=True)),
{'ios': { 'content-available': True}})

def test_android(self):
self.assertEqual(
ua.notification(android=ua.android(
Expand All @@ -70,6 +87,17 @@ def test_android(self):
}
}})

def test_android_unicode(self):
self.assertEqual(
ua.notification(android=ua.android(
alert=u'Hello',
time_to_live=u'100',
)),
{'android': {
'alert': 'Hello',
'time_to_live': '100',
}})

def test_amazon(self):
self.assertEqual(
ua.notification(amazon=ua.amazon(
Expand All @@ -91,6 +119,17 @@ def test_amazon(self):
}
}})

def test_amazon_unicode(self):
self.assertEqual(
ua.notification(amazon=ua.amazon(
alert=u'Amazon test',
expires_after=u'100',
)),
{'amazon': {
'alert': 'Amazon test',
'expires_after':'100',
}})

def test_blackberry(self):
self.assertEqual(
ua.notification(blackberry=ua.blackberry(
Expand Down
11 changes: 5 additions & 6 deletions tests/push/test_push.py
Expand Up @@ -129,7 +129,7 @@ def test_push_success(self):
with mock.patch.object(ua.Airship, '_request') as mock_request:
response = requests.Response()
response._content = (
'''{"push_ids": ["0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
b'''{"push_ids": ["0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
response.status_code = 202
mock_request.return_value = response

Expand All @@ -147,7 +147,7 @@ def test_schedule_success(self):
with mock.patch.object(ua.Airship, '_request') as mock_request:
response = requests.Response()
response._content = (
'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
b'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
response.status_code = 202
mock_request.return_value = response

Expand All @@ -168,7 +168,7 @@ def test_local_schedule_success(self):
with mock.patch.object(ua.Airship, '_request') as mock_request:
response = requests.Response()
response._content = (
'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
b'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
response.status_code = 202
mock_request.return_value = response

Expand Down Expand Up @@ -204,7 +204,7 @@ def test_schedule_from_url(self):
"content_encoding": "utf8",
},
},
})
}).encode('utf-8')

response.status_code = 200
mock_request.return_value = response
Expand Down Expand Up @@ -246,7 +246,7 @@ def test_update_schedule(self):
response = requests.Response()
response.status_code = 202
response._content = (
'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')
b'''{"schedule_urls": ["https://go.urbanairship.com/api/schedules/0492662a-1b52-4343-a1f9-c6b0c72931c0"]}''')

mock_request.return_value = response

Expand All @@ -267,4 +267,3 @@ def options(self):
push.notification = ua.notification(alert="Hello Expiry")
push.options = ua.options(expiry=10080)
push.device_types = ua.all_

2 changes: 1 addition & 1 deletion urbanairship/devices/__init__.py
@@ -1,4 +1,4 @@
from devicelist import (
from .devicelist import (
ChannelList,
ChannelInfo,
DevicePINInfo,
Expand Down
8 changes: 4 additions & 4 deletions urbanairship/devices/devicelist.py
Expand Up @@ -110,10 +110,10 @@ def __iter__(self):

def next(self):
try:
return DeviceInfo.from_payload(self._token_iter.next(), self.id_key)
return DeviceInfo.from_payload(next(self._token_iter), self.id_key)
except StopIteration:
self._fetch_next_page()
return DeviceInfo.from_payload(self._token_iter.next(), self.id_key)
return DeviceInfo.from_payload(next(self._token_iter), self.id_key)

def _fetch_next_page(self):
if not self.next_url:
Expand Down Expand Up @@ -153,10 +153,10 @@ class ChannelList(DeviceList):

def next(self):
try:
return ChannelInfo.from_payload(self._token_iter.next(), self.id_key)
return ChannelInfo.from_payload(next(self._token_iter), self.id_key)
except StopIteration:
self._fetch_next_page()
return ChannelInfo.from_payload(self._token_iter.next(), self.id_key)
return ChannelInfo.from_payload(next(self._token_iter), self.id_key)


class APIDList(DeviceList):
Expand Down
4 changes: 2 additions & 2 deletions urbanairship/push/audience.py
Expand Up @@ -163,8 +163,8 @@ def recent_date(**kwargs):
"""
if not len(kwargs) == 1:
raise ValueError("Must specificy a single date resolution")
resolution = kwargs.keys()[0]
value = kwargs.values()[0]
resolution = list(kwargs.keys())[0]
value = list(kwargs.values())[0]

if resolution not in ('minutes' 'hours' 'days' 'weeks' 'months' 'years'):
raise ValueError("Invalid date resolution: %s" % resolution)
Expand Down
31 changes: 21 additions & 10 deletions urbanairship/push/payload.py
@@ -1,4 +1,15 @@
import re
import sys

# Python coarse version differentiation
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3

# Set version string type
if PY3:
string_type = str
elif PY2:
string_type = basestring

# Valid autobadge values: auto, +N, -N
VALID_AUTOBADGE = re.compile(r'^(auto|[+-][\d]+)$')
Expand Down Expand Up @@ -56,13 +67,13 @@ def ios(alert=None, badge=None, sound=None, content_available=False,
"""
payload = {}
if alert is not None:
if not (isinstance(alert, basestring) or isinstance(alert, dict)):
if not (isinstance(alert, (string_type, dict))):
raise ValueError("iOS alert must be a string or dictionary")
payload['alert'] = alert
if badge is not None:
if not (isinstance(badge, basestring) or isinstance(badge, int)):
if not (isinstance(badge, (string_type, int))):
raise ValueError("iOS badge must be an integer or string")
if isinstance(badge, basestring) and not VALID_AUTOBADGE.match(badge):
if isinstance(badge, string_type) and not VALID_AUTOBADGE.match(badge):
raise ValueError("Invalid iOS autobadge value")
payload['badge'] = badge
if sound is not None:
Expand All @@ -72,7 +83,7 @@ def ios(alert=None, badge=None, sound=None, content_available=False,
if extra is not None:
payload['extra'] = extra
if expiry is not None:
if not (isinstance(expiry, basestring) or isinstance(expiry, int)):
if not (isinstance(expiry, (string_type, int))):
raise ValueError("iOS expiry must be an integer or string")
payload['expiry'] = expiry
return payload
Expand Down Expand Up @@ -107,7 +118,7 @@ def android(alert=None, collapse_key=None, time_to_live=None,
payload['collapse_key'] = collapse_key
if time_to_live is not None:
payload['time_to_live'] = time_to_live
if not (isinstance(time_to_live, basestring) or isinstance(time_to_live, int)):
if not (isinstance(time_to_live, (string_type, int))):
raise ValueError("Android time_to_live value must be an integer or time set in UTC as a string")
if delay_while_idle:
payload['delay_while_idle'] = True
Expand Down Expand Up @@ -141,7 +152,7 @@ def amazon(alert=None, consolidation_key=None, expires_after=None, extra=None,
payload['consolidation_key'] = consolidation_key
if expires_after is not None:
payload['expires_after'] = expires_after
if not (isinstance(expires_after, basestring) or isinstance(expires_after, int)):
if not (isinstance(expires_after, (string_type, int))):
raise ValueError("Amazon time_to_live value must be an integer or time set in UTC as a string")
if extra is not None:
payload['extra'] = extra
Expand Down Expand Up @@ -181,7 +192,7 @@ def wns_payload(alert=None, toast=None, tile=None, badge=None):
Must include exactly one of ``alert``, ``toast``, ``tile``, or ``badge``.
"""
if len(filter(None, (alert, toast, tile, badge))) != 1:
if sum(1 for x in (alert, toast, tile, badge) if x) != 1:
raise ValueError("WNS payload must have one notification type.")
payload = {}
if alert is not None:
Expand All @@ -201,7 +212,7 @@ def mpns_payload(alert=None, toast=None, tile=None):
Must include exactly one of ``alert``, ``toast``, or ``tile``.
"""
if len(filter(None, (alert, toast, tile))) != 1:
if sum(1 for x in (alert, toast, tile) if x) != 1:
raise ValueError("MPNS payload must have one notification type.")
payload = {}
if alert is not None:
Expand Down Expand Up @@ -237,7 +248,7 @@ def message(title, body, content_type=None, content_encoding=None, extra=None, e
payload['extra'] = extra
if expiry is not None:
payload['expiry'] = expiry
if not (isinstance(expiry, basestring) or isinstance(expiry, int)):
if not (isinstance(expiry, (string_type, int))):
raise ValueError("Expiry value must be an integer or time set in UTC as a string")
return payload

Expand Down Expand Up @@ -269,6 +280,6 @@ def options(expiry=None):
payload = {}
if expiry is not None:
payload['expiry'] = expiry
if not (isinstance(expiry, basestring) or isinstance(expiry, int)):
if not (isinstance(expiry, (string_type, int))):
raise ValueError("Expiry value must be an integer or time set in UTC as a string")
return payload

0 comments on commit 5a1d702

Please sign in to comment.