8000 Port the twilio-python library to use Requests. · randy3465/twilio-python@b95beae · GitHub
[go: up one dir, main page]

Skip to content

Commit b95beae

Browse files
author
Kevin Burke
committed
Port the twilio-python library to use Requests.
Major surgery on classes and objects. - We don't need to do Unicode encoding anymore; requests takes care of this for us. Added a test that proves this is the case. - We don't need to do proxying anymore; requests handles this for us. - The signature for Resources is no longer `uri, auth, timeout`. That's stupid. Instead it passes around `uri, client`. That's it. - TwilioRestClient doesn't take a random grab bag of parameters anymore. It takes an account sid, a token, and a `Transport` class which encodes connection details (proxies, retries, timeouts, host). - There's no reason for Resources to know HTTP implementation details. Those have been moved to the client. - Moves TwilioRestException into the rest folder, cleans up parameter naming. - Deletes a lot of mocks and `assert_called_with` assertions from the tests.
1 parent 8145d53 commit b95beae

38 files changed

+522
-761
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ analysis:
1616
test: analysis
1717
. venv/bin/activate; nosetests
1818

19+
# This makes the unit tests run about 100ms slower, which is why it's
20+
# a separate target.
1921
cover:
2022
. venv/bin/activate; nosetests --with-coverage --cover-package=twilio
2123

docs/api/rest/index.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@
1818
:members:
1919
:inherited-members:
2020

21+
.. autoclass:: Transport
22+
:members:
23+
:inherited-members:

docs/usage/basics.rst

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,18 @@ directly to the the constructor.
4141
Proxies
4242
-------
4343

44-
:class:`TwilioRestClient` supports HTTP and SOCKS4/5 proxies. You can change
45-
the proxy configuration at any time with the :class:`Connection` class:
44+
:class:`TwilioRestClient` supports HTTP and SOCKS4/5 proxies, using the same
45+
format as the Python Requests library. Here's an example:
4646

4747
.. code-block:: python
4848
49-
from twilio.rest.resources import Connection
50-
from twilio.rest.resources.connection import PROXY_TYPE_SOCKS5
51-
52-
Connection.set_proxy_info(
53-
'example.com',
54-
5000,
55-
proxy_type=PROXY_TYPE_SOCKS5,
56-
proxy_user='username',
57-
proxy_pass='password',
58-
)
49+
from twilio.rest import Transport, TwilioRestClient
50+
proxies = {
51+
"http": "http://10.10.1.10:3128",
52+
"https": "http://10.10.1.10:1080",
53+
}
54+
transport = Transport(proxies=proxies)
55+
client = TwilioRestClient(account="AC123", token="456", transport=transport)
5956
6057
The :class:`TwilioRestClient` will retrieve and use the current proxy
6158
information for each request.
@@ -65,8 +62,8 @@ Listing Resources
6562
-------------------
6663

6764
The :class:`TwilioRestClient` gives you access to various list resources.
68-
:meth:`ListResource.list <twilio.rest.resources.ListResource.list>`, by default,
69-
returns the most recent 50 instance resources.
65+
:meth:`ListResource.list <twilio.rest.resources.base.ListResource.list>`, by
66+
default, returns the most recent 50 instance resources.
7067

7168
.. code-block:: python
7269
@@ -97,11 +94,10 @@ The following will return page 3 with page size of 25.
9794
Listing All Resources
9895
^^^^^^^^^^^^^^^^^^^^^^^
9996

100-
Sometimes you'd like to retrieve all records from a list resource.
101-
Instead of manually paging over the resource,
102-
the :class:`resources.ListResource.iter` method returns a generator.
103-
After exhausting the current page,
104-
the generator will request the next page of results.
97+
Sometimes you'd like to retrieve all records from a list resource. Instead of
98+
manually paging over the resource, the :class:`resources.ListResource.iter`
99+
method returns a generator. After exhausting the current page, the generator
100+
will request the next page of results.
105101

106102
.. warning:: Accessing all your records can be slow. We suggest only doing so when you absolutely need all the records.
107103

@@ -121,8 +117,7 @@ the generator will request the next page of results.
121117
Get an Individual Resource
122118
-----------------------------
123119

124-
To get an individual instance resource, use
125-
:meth:`resources.ListResource.get`.
120+
To get an individual instance resource, use :meth:`resources.ListResource.get`.
126121
Provide the :attr:`sid` of the resource you'd like to get.
127122

128123
.. code-block:: python

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
#
1010
# You need to have the setuptools module installed. Try reading the setuptools
1111
# documentation: http://pypi.python.org/pypi/setuptools
12-
REQUIRES = ["httplib2 >= 0.7", "six"]
12+
REQUIRES = [
13+
"requests >= 2.2.1",
14+
"six",
15+
]
1316

1417
if sys.version_info < (2, 6):
1518
REQUIRES.append('simplejson')

tests/test_accounts.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
import unittest
22

3-
from mock import Mock, patch
3+
from mock import patch
44

55
from tools import create_mock_json
6-
from twilio.rest.resources import Account
6+
from twilio.rest import TwilioRestClient
7+
from twilio.rest.resources import Account, Accounts
8+
9+
client = TwilioRestClient('AC123', 'token')
10+
accounts = Accounts('/Accounts', client)
711

812

913
class AccountTest(unittest.TestCase):
1014

11-
@patch("twilio.rest.resources.base.make_twilio_request")
15+
@patch("twilio.rest.TwilioRestClient.make_twilio_request")
1216
def test_usage_records_subresource(self, request):
1317
resp = create_mock_json("tests/resources/usage_records_list.json")
1418
request.return_value = resp
1519

16-
mock = Mock()
17-
mock.uri = "/base"
18-
account = Account(mock, 'AC123')
20+
account = Account(accounts, 'AC123')
1921
account.load_subresources()
2022
records = account.usage_records.list()
2123
self.assertEquals(len(records), 2)
2224

23-
@patch("twilio.rest.resources.base.make_twilio_request")
25+
@patch("twilio.rest.TwilioRestClient.make_twilio_request")
2426
def test_usage_triggers_subresource(self, request):
2527
resp = create_mock_json("tests/resources/usage_triggers_list.json")
2628
request.return_value = resp
2729

28-
mock = Mock()
29-
mock.uri = "/base"
30-
account = Account(mock, 'AC123')
30+
account = Account(accounts, 'AC123')
3131
account.load_subresources()
3232
triggers = account.usage_triggers.list()
3333
self.assertEquals(len(triggers), 2)

tests/test_applications.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
from mock import Mock, patch
44

5+
from twilio.rest import TwilioRestClient
56
from twilio.rest.resources import Applications, Application
67

78

89
class ApplicationsTest(unittest.TestCase):
910
def setUp(self):
1011
self.parent = Mock()
11-
self.resource = Applications("http://api.twilio.com", ("user", "pass"))
12+
client = TwilioRestClient("user", "pass")
13+
self.resource = Applications("http://api.twilio.com", client)
1214

1315
def test_create_application_sms_url_method(self):
1416
self.resource.create_instance = Mock()

tests/test_authorized_connect_apps.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,33 @@
44
from mock import Mock, patch
55
from nose.tools import assert_equal
66

7+
from twilio.rest import TwilioRestClient
78
from twilio.rest.resources import AuthorizedConnectApps
89
from twilio.rest.resources import AuthorizedConnectApp
910

1011

1112
class AuthorizedConnectAppTest(unittest.TestCase):
1213

1314
def setUp(self):
14-
self.parent = Mock()
1515
self.uri = "/base"
16-
self.auth = ("AC123", "token")
17-
self.resource = AuthorizedConnectApps(self.uri, self.auth)
16+
self.client = TwilioRestClient("AC123", "token")
17+
self.resource = AuthorizedConnectApps(self.uri, self.client)
1818

19-
@patch("twilio.rest.resources.base.make_twilio_request")
19+
@patch("twilio.rest.TwilioRestClient.make_twilio_request")
2020
def test_get(self, mock):
2121
mock.return_value = Mock()
2222
mock.return_value.content = '{"connect_app_sid": "SID"}'
2323

2424
self.resource.get("SID")
25-
mock.assert_called_with("GET", "/base/AuthorizedConnectApps/SID",
26-
auth=self.auth)
25+
mock.assert_called_with("GET", "/base/AuthorizedConnectApps/SID")
2726

28-
@patch("twilio.rest.resources.base.make_twilio_request")
27+
@patch("twilio.rest.TwilioRestClient.make_twilio_request")
2928
def test_list(self, mock):
3029
mock.return_value = Mock()
3130
mock.return_value.content = '{"authorized_connect_apps": []}'
3231

3332
self.resource.list()
34-
mock.assert_called_with("GET", "/base/AuthorizedConnectApps",
35-
params={}, auth=self.auth)
33+
mock.assert_called_with("GET", "/base/AuthorizedConnectApps", params={})
3634

3735
def test_load(self):
3836
instance = AuthorizedConnectApp(Mock(), "sid")

tests/test_available_phonenumber.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from nose.tools import assert_equal, assert_true
55

66
from twilio import TwilioException
7+
from twilio.rest import TwilioRestClient
78
from twilio.rest.resources import AvailablePhoneNumber
89
from twilio.rest.resources import AvailablePhoneNumbers
910
from twilio.rest.resources import PhoneNumbers
10-
from twilio.rest.resources import UNSET_TIMEOUT
11+
12+
BASE = "https://api.twilio.com"
1113

1214

1315
class AvailablePhoneNumberTest(unittest.TestCase):
@@ -31,8 +33,8 @@ def test_purchase(self):
3133
class AvailablePhoneNumbersTest(unittest.TestCase):
3234

3335
def setUp(self):
34-
self.resource = AvailablePhoneNumbers("http://api.twilio.com",
35-
("user", "pass"), UNSET_TIMEOUT, Mock())
36+
client = TwilioRestClient("user", "pass")
37+
self.resource = AvailablePhoneNumbers(BASE, client, Mock())
3638

3739
def test_get(self):
3840
self.assertRaises(TwilioException, self.resource.get, "PN123")
@@ -44,7 +46,7 @@ def test_list(self):
4446

4547
self.resource.list()
4648

47-
uri = "http://api.twilio.com/AvailablePhoneNumbers/US/Local"
49+
uri = BASE + "/AvailablePhoneNumbers/US/Local"
4850
request.assert_called_with("GET", uri, params={})
4951

5052
def test_load_instance(self):
@@ -59,7 +61,7 @@ def test_purchase_status_callback(self):
5961

6062
self.resource.list()
6163

62-
uri = "http://api.twilio.com/AvailablePhoneNumbers/US/Local"
64+
uri = BASE + "/AvailablePhoneNumbers/US/Local"
6365
request.assert_called_with("GET", uri, params={})
6466

6567
def test_mobile(self):
@@ -69,15 +71,15 @@ def test_mobile(self):
6971

7072
self.resource.list(type='mobile', country='GB')
7173

72-
uri = "http://api.twilio.com/AvailablePhoneNumbers/GB/Mobile"
74+
uri = BASE + "/AvailablePhoneNumbers/GB/Mobile"
7375
request.assert_called_with("GET", uri, params={})
7476

7577

7678
class PhoneNumbersTest(unittest.TestCase):
7779

7880
def setUp(self):
79-
self.resource = PhoneNumbers("http://api.twilio.com",
80-
("user", "pass"))
81+
client = TwilioRestClient("user", "pass")
82+
self.resource = PhoneNumbers(BASE, client)
8183

8284
def test_reference(self):
8385
assert_equal(self.resource.available_phone_numbers.phone_numbers,
@@ -93,7 +95,7 @@ def test_purchase_status_callback(self):
9395
self.resource.purchase(area_code="530", status_callback_url="http://",
9496
status_callback_method="POST")
9597

96-
uri = "http://api.twilio.com/IncomingPhoneNumbers"
98+
uri = BASE + "/IncomingPhoneNumbers"
9799

98100
data = {
99101
"AreaCode": "530",

tests/test_base_resource.py

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
# -*- coding: utf-8 -*-
22
import unittest
33

4-
from mock import Mock, sentinel, patch, ANY
4+
from mock import Mock
55
from nose.tools import assert_equal, assert_true
66
from six import advance_iterator
77

8-
from twilio.rest.resources.imports import json
8+
from twilio.rest import TwilioRestClient
99
from twilio.rest.resources import Resource
1010
from twilio.rest.resources import ListResource
1111
from twilio.rest.resources import InstanceResource
1212

1313
base_uri = "https://api.twilio.com/2010-04-01"
1414
account_sid = "AC123"
1515
auth = (account_sid, "token")
16+
client = TwilioRestClient(*auth)
1617

1718

1819
def test_resource_init():
19-
r = Resource(base_uri, auth)
20+
r = Resource(base_uri, client)
2021
uri = "%s/%s" % (base_uri, r.name)
2122

2223
assert_equal(r.base_uri, base_uri)
23-
assert_equal(r.auth, auth)
2424
assert_equal(r.uri, uri)
2525

2626

2727
def test_equivalence():
28-
p = ListResource(base_uri, auth)
28+
p = ListResource(base_uri, client)
2929
r1 = p.load_instance({"sid": "AC123"})
3030
r2 = p.load_instance({"sid": "AC123"})
3131
assert_equal(r1, r2)
@@ -34,7 +34,7 @@ def test_equivalence():
3434
class ListResourceTest(unittest.TestCase):
3535

3636
def setUp(self):
37-
self.r = ListResource(base_uri, auth)
37+
self.r = ListResource(base_uri, client)
3838

3939
def testListResourceInit(self):
4040
uri = "%s/%s" % (base_uri, self.r.name)
@@ -102,7 +102,8 @@ def testListResourceCreateResponse201(self):
102102
class testInstanceResourceInit(unittest.TestCase):
103103

104104
def setUp(self):
105-
self.parent = ListResource(base_uri, auth)
105+
client = TwilioRestClient(*auth)
106+
self.parent = ListResource(base_uri, client)
106107
self.r = InstanceResource(self.parent, "123")
107108
self.uri = "%s/%s" % (self.parent.uri, "123")
108109

@@ -139,29 +140,4 @@ def testLoadSubresources(self):
139140
m = Mock()
140141
self.r.subresources = [m]
141142
self.r.load_subresources()
142-
m.assert_called_with(self.r.uri, self.r.auth, self.r.timeout)
143-
144-
145-
class testTimeoutPropagation(unittest.TestCase):
146-
def setUp(self):
147-
self.parent = ListResource(base_uri, auth, timeout=sentinel.timeout)
148-
self.r = InstanceResource(self.parent, "123")
149-
self.uri = "%s/%s" % (self.parent.uri, "123")
150-
151-
@patch('twilio.rest.resources.base.make_request')
152-
def testPassThrough(self, mock_request):
153-
mock_response = Mock()
154-
mock_response.ok = True,
155-
mock_response.content = json.dumps({'key': 'value'})
156-
mock_request.return_value = mock_response
157-
158-
assert_equal(self.r.timeout, sentinel.timeout)
159-
assert_equal((mock_response, {'key': 'value'}), self.r.request('GET', base_uri))
160-
161-
mock_request.assert_called_once_with(
162-
'GET',
163-
base_uri + '.json',
164-
headers=ANY,
165-
timeout=sentinel.timeout,
166-
auth=ANY
167-
)
143+
m.assert_called_with(self.r.uri, self.r.client)

0 commit comments

Comments
 (0)
0