diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9733ab1d34..a76a0bf826 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ it can be. ## Got an API/Product Question or Problem? If you have questions about how to use `twilio-python`, please see our -[docs][docs-link], and if you don't find the answer there, please contact +[docs](./README.md), and if you don't find the answer there, please contact [help@twilio.com](mailto:help@twilio.com) with any issues you have. ## Found an Issue? @@ -69,10 +69,6 @@ you're working on. For large fixes, please build and test the documentation before submitting the PR to be sure you haven't accidentally introduced layout or formatting issues. -If you want to help improve the docs at -[https://www.twilio.com/docs/libraries/python][docs-link], please contact -[help@twilio.com](mailto:help@twilio.com). - ## Submission Guidelines ### Submitting an Issue @@ -162,6 +158,5 @@ There exists a separate `requirements.txt` document under `tests` that contains make test-install test ``` -[docs-link]: https://www.twilio.com/docs/libraries/python [issue-link]: https://github.com/twilio/twilio-python/issues/new [github]: https://github.com/twilio/twilio-python \ No newline at end of file diff --git a/README.md b/README.md index 7edca27d77..50cc5509e1 100644 --- a/README.md +++ b/README.md @@ -15,68 +15,101 @@ The Python library documentation can be found [here][libdocs]. `twilio-python` uses a modified version of [Semantic Versioning](https://semver.org) for all changes. [See this document](VERSIONS.md) for details. -### Migrating from 5.x - -Please consult the [official migration guide](https://www.twilio.com/docs/libraries/python/migration-guide) for information on upgrading your application using twilio-python 5.x to 6.x - ### Supported Python Versions This library supports the following Python implementations: -* Python 3.7 -* Python 3.8 -* Python 3.9 -* Python 3.10 -* Python 3.11 +- Python 3.7 +- Python 3.8 +- Python 3.9 +- Python 3.10 +- Python 3.11 ## Installation Install from PyPi using [pip](https://pip.pypa.io/en/latest/), a package manager for Python. - pip install twilio +```shell +pip3 install twilio +``` If pip install fails on Windows, check the path length of the directory. If it is greater 260 characters then enable [Long Paths](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) or choose other shorter location. Don't have pip installed? Try installing it, by running this from the command line: - $ curl https://bootstrap.pypa.io/get-pip.py | python +```shell +curl https://bootstrap.pypa.io/get-pip.py | python +``` Or, you can [download the source code -(ZIP)](https://github.com/twilio/twilio-python/zipball/main "twilio-python -source code") for `twilio-python`, and then run: +(ZIP)](https://github.com/twilio/twilio-python/zipball/main 'twilio-python +source code') for `twilio-python`, and then run: + +```shell +python3 setup.py install +``` - python setup.py install +> **Info** +> If the command line gives you an error message that says Permission Denied, try running the above commands with `sudo` (e.g., `sudo pip3 install twilio`). -You may need to run the above commands with `sudo`. +### Test your installation -## Getting Started +Try sending yourself an SMS message. Save the following code sample to your computer with a text editor. Be sure to update the `account_sid`, `auth_token`, and `from_` phone number with values from your [Twilio account](https://console.twilio.com). The `to` phone number will be your own mobile phone. -Getting started with the Twilio API couldn't be easier. Create a -`Client` and you're ready to go. +```python +from twilio.rest import Client + +# Your Account SID and Auth Token from console.twilio.com +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" + +client = Client(account_sid, auth_token) + +message = client.messages.create( + to="+15558675309", + from_="+15017250604", + body="Hello from Python!") + +print(message.sid) +``` + +Save the file as `send_sms.py`. In the terminal, `cd` to the directory containing the file you just saved then run: + +```shell +python3 send_sms.py +``` + +After a brief delay, you will receive the text message on your phone. + +> **Warning** +> It's okay to hardcode your credentials when testing locally, but you should use environment variables to keep them secret before committing any code or deploying to production. Check out [How to Set Environment Variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) for more information. + +## Use the helper library ### API Credentials -The `Twilio` client needs your Twilio credentials. You can either pass these -directly to the constructor (see the code below) or via environment variables. +The `Twilio` client needs your Twilio credentials. You can either pass these directly to the constructor (see the code below) or via environment variables. Authenticating with Account SID and Auth Token: + ```python from twilio.rest import Client -account_sid = "ACXXXXXXXXXXXXXXXXX" -auth_token = "YYYYYYYYYYYYYYYYYY" +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" client = Client(account_sid, auth_token) ``` Authenticating with API Key and API Secret: + ```python from twilio.rest import Client api_key = "XXXXXXXXXXXXXXXXX" api_secret = "YYYYYYYYYYYYYYYYYY" -account_sid = "ACXXXXXXXXXXXXXXXXX" +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" client = Client(api_key, api_secret, account_sid) ``` @@ -88,7 +121,6 @@ We suggest storing your credentials as environment variables. Why? You'll never have to worry about committing your credentials and accidentally posting them somewhere public. - ```python from twilio.rest import Client client = Client() @@ -103,7 +135,8 @@ from twilio.rest import Client client = Client(region='au1', edge='sydney') ``` -A `Client` constructor without these parameters will also look for `TWILIO_REGION` and `TWILIO_EDGE` variables inside the current environment. + +A `Client` constructor without these parameters will also look for `TWILIO_REGION` and `TWILIO_EDGE` variables inside the current environment. Alternatively, you may specify the edge and/or region after constructing the Twilio client: @@ -122,9 +155,9 @@ This will result in the `hostname` transforming from `api.twilio.com` to `api.sy ```python from twilio.rest import Client -username = "ACXXXXXXXXXXXXXXXXX" -password = "YYYYYYYYYYYYYYYYYY" -client = Client(username, password) +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) call = client.calls.create(to="9991231234", from_="9991231234", @@ -132,17 +165,36 @@ call = client.calls.create(to="9991231234", print(call.sid) ``` -### Send an SMS +### Get data about an existing call ```python from twilio.rest import Client -username = "ACXXXXXXXXXXXXXXXXX" -password = "YYYYYYYYYYYYYYYYYY" -client = Client(username, password) +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) -message = client.messages.create(to="+12316851234", from_="+15555555555", - body="Hello there!") +call = client.calls.get("CA42ed11f93dc08b952027ffbc406d0868") +print(call.to) +``` + +### Iterate through records + +The library automatically handles paging for you. Collections, such as `calls` and `messages`, have `list` and `stream` methods that page under the hood. With both `list` and `stream`, you can specify the number of records you want to receive (`limit`) and the maximum size you want each page fetch to be (`page_size`). The library will then handle the task for you. + +`list` eagerly fetches all records and returns them as a list, whereas `stream` returns an iterator and lazily retrieves pages of records as you iterate over the collection. You can also page manually using the `page` method. + +#### Use the `list` method + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +for sms in client.messages.list(): + print(sms.to) ``` ### Asynchronous API Requests @@ -154,10 +206,10 @@ from twilio.http.async_http_client import AsyncTwilioHttpClient from twilio.rest import Client async def main(): - username = "ACXXXXXXXXXXXXXXXXX" - password = "YYYYYYYYYYYYYYYYYY" + account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + auth_token = "your_auth_token" http_client = AsyncTwilioHttpClient() - client = Client(username, password, http_client=http_client) + client = Client(account_sid, auth_token, http_client=http_client) message = await client.messages.create_async(to="+12316851234", from_="+15555555555", body="Hello there!") @@ -172,7 +224,7 @@ Log the API request and response data to the console: ```python import logging -client = Client(username, password) +client = Client(account_sid, auth_token) logging.basicConfig() client.http_client.logger.setLevel(logging.INFO) ``` @@ -182,20 +234,22 @@ Log the API request and response data to a file: ```python import logging -client = Client(username, password) +client = Client(account_sid, auth_token) logging.basicConfig(filename='./log.txt') client.http_client.logger.setLevel(logging.INFO) ``` ### Handling Exceptions +Version 8.x of `twilio-python` exports an exception class to help you handle exceptions that are specific to Twilio methods. To use it, import `TwilioRestException` and catch exceptions as follows: + ```python from twilio.rest import Client from twilio.base.exceptions import TwilioRestException -username = "ACXXXXXXXXXXXXXXXXX" -password = "YYYYYYYYYYYYYYYYYY" -client = Client(username, password) +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) try: message = client.messages.create(to="+12316851234", from_="+15555555555", @@ -204,8 +258,6 @@ except TwilioRestException as e: print(e) ``` -For more descriptive exception types, please see the [Twilio documentation](https://www.twilio.com/docs/libraries/python/usage-guide#exceptions). - ### Generating TwiML To control phone calls, your application needs to output [TwiML][twiml]. @@ -225,9 +277,9 @@ print(str(r)) Welcome to twilio! ``` -### Using a Custom HTTP Client +### Other advanced examples -To use a custom HTTP client with this helper library, please see the [Twilio documentation](https://www.twilio.com/docs/libraries/python/custom-http-clients-python). +- [Learn how to create your own custom HTTP client](./advanced-examples/custom-http-client.md) ### Docker Image diff --git a/advanced-examples/custom-http-client.md b/advanced-examples/custom-http-client.md new file mode 100644 index 0000000000..b3b5a7d20e --- /dev/null +++ b/advanced-examples/custom-http-client.md @@ -0,0 +1,154 @@ +# Custom HTTP Clients for the Twilio Python Helper Library + +If you are working with the Twilio Python Helper Library, and you need to be able to modify the HTTP requests that the library makes to the Twilio servers, you’re in the right place. The most common reason for altering the HTTP request is to connect and authenticate with an enterprise’s proxy server. We’ll provide sample code that you can use in your app to handle this and other use cases. + +## Connect and authenticate with a proxy server + +To connect to a proxy server that's between your app and Twilio, you need a way to modify the HTTP requests that the Twilio helper library makes on your behalf to the Twilio REST API. + +In Python, the Twilio helper library uses the [requests](https://docs.python-requests.org/en/master/) library under the hood to make the HTTP requests, and this allows you to [provide your own `http_client`](/docs/libraries/reference/twilio-python/{{ twilio-python-version }}/docs/source/\_rst/twilio.http.html#module-twilio.http.http_client) for making API requests. + +So the question becomes: how do we apply this to a typical Twilio REST API request, such as? + +```python +client = Client(account_sid, auth_token) + +message = client.messages \ + .create( + to="+15558675310", + body="Hey there!", + from_="+15017122661" + ) + +``` + +To start, you should understand when and where a `http_client` is created and used. + +The helper library creates a default `http_client` for you whenever you call the `Client` constructor with your Twilio credentials. Here, you have the option of creating your own `http_client`, and passing to the constructor, instead of using the default implementation. + +Here’s an example of sending an SMS message with a custom client: + +```python +import os +from twilio.rest import Client +from custom_client import MyRequestClass + +from dotenv import load_dotenv +load_dotenv() + +# Custom HTTP Class +my_request_client = MyRequestClass() + +client = Client(os.getenv("ACCOUNT_SID"), os.getenv("AUTH_TOKEN"), + http_client=my_request_client) + +message = client.messages \ + .create( + to="+15558675310", + body="Hey there!", + from_="+15017122661" + ) + +print('Message SID: {}'.format(message.sid)) +``` + +## Create your custom TwilioRestClient + +When you take a closer look at the constructor for `HttpClient`, you see that the `http_client` parameter is actually of type `twilio.http.HttpClient`. + +`HttpClient` is an abstraction that allows plugging in any implementation of an HTTP client you want (or even creating a mocking layer for unit testing). + +However, within the helper library, there is an implementation of `twilio.http.HttpClient` called `TwilioHttpClient`. This class wraps the `twilio.http.HttpClient` and provides it to the Twilio helper library to make the necessary HTTP requests. + +## Call Twilio through a proxy server + +Now that we understand how all the components fit together, we can create our own `HttpClient` that can connect through a proxy server. To make this reusable, here’s a class that you can use to create this `HttpClient` whenever you need one. + +```python +import os +from requests import Request, Session, hooks +from twilio.http.http_client import TwilioHttpClient +from twilio.http.response import Response + +class MyRequestClass(TwilioHttpClient): + def __init__(self): + self.response = None + + def request(self, method, url, params=None, data=None, headers=None, auth=None, timeout=None, + allow_redirects=False): + # Here you can change the URL, headers and other request parameters + kwargs = { + 'method': method.upper(), + 'url': url, + 'params': params, + 'data': data, + 'headers': headers, + 'auth': auth, + } + + session = Session() + request = Request(**kwargs) + + prepped_request = session.prepare_request(request) + session.proxies.update({ + 'http': os.getenv('HTTP_PROXY'), + 'https': os.getenv('HTTPS_PROXY') + }) + response = session.send( + prepped_request, + allow_redirects=allow_redirects, + timeout=timeout, + ) + + return Response(int(response.status_code), response.text) +``` + +In this example, we are using some environment variables loaded at the program startup to retrieve various configuration settings: + +Your Twilio Account Sid and Auth Token ([found here, in the Twilio console](https://console.twilio.com)) + +A proxy address in the form of http://127.0.0.1:8888 + +These settings are located in an .env file, like so: + +```env +ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +AUTH_TOKEN= your_auth_token + +HTTPS_PROXY=https://127.0.0.1:8888 +HTTP_PROXY=http://127.0.0.1:8888 +``` + +Here’s the full program that sends a text message and shows how it all can work together. + +```python +import os +from twilio.rest import Client +from custom_client import MyRequestClass + +from dotenv import load_dotenv +load_dotenv() + +# Custom HTTP Class +my_request_client = MyRequestClass() + +client = Client(os.getenv("ACCOUNT_SID"), os.getenv("AUTH_TOKEN"), + http_client=my_request_client) + +message = client.messages \ + .create( + to="+15558675310", + body="Hey there!", + from_="+15017122661" + ) + +print('Message SID: {}'.format(message.sid)) +``` + +## What else can this technique be used for? + +Now that you know how to inject your own `http_client` into the Twilio API request pipeline, you could use this technique to add custom HTTP headers and authorization to the requests (perhaps as required by an upstream proxy server). + +You could also implement your own `http_client` to mock the Twilio API responses so your unit and integration tests can run quickly without needing to make a connection to Twilio. In fact, there’s already an example online showing [how to do exactly that with Node.js and Prism](https://www.twilio.com/docs/openapi/mock-api-generation-with-twilio-openapi-spec). + +We can’t wait to see what you build! diff --git a/docs/source/_templates/sidebarintro.html b/docs/source/_templates/sidebarintro.html index d362c5dabb..9acc1e0e23 100644 --- a/docs/source/_templates/sidebarintro.html +++ b/docs/source/_templates/sidebarintro.html @@ -1,11 +1,13 @@ -

About twilio-python

+

About twilio-python

- A Python module for communicating with the Twilio API and generating TwiML. + A Python module for communicating with the Twilio API and generating + TwiML.

Useful Links