Skip to content

drop python<=3.7 support #801

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/test-and-deploy.yml
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ jobs:
timeout-minutes: 20
strategy:
matrix:
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12' ]
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ]
steps:
- name: Checkout twilio-python
uses: actions/checkout@v3
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
6 changes: 3 additions & 3 deletions examples/basic_usage.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ def example():

# Get all messages
all_messages = client.messages.list()
print("There are {} messages in your account.".format(len(all_messages)))
print(f"There are {len(all_messages)} messages in your account.")

# Get only last 10 messages...
some_messages = client.messages.list(limit=10)
@@ -25,7 +25,7 @@ def example():

# Get messages in smaller pages...
all_messages = client.messages.list(page_size=10)
print("There are {} messages in your account.".format(len(all_messages)))
print(f"There are {len(all_messages)} messages in your account.")

print("Sending a message...")
new_message = client.messages.create(to="XXXX", from_="YYYY", body="Twilio rocks!")
@@ -38,7 +38,7 @@ def example():
twiml_response.say("Hello!")
twiml_response.hangup()
twiml_xml = twiml_response.to_xml()
print("Generated twiml: {}".format(twiml_xml))
print(f"Generated twiml: {twiml_xml}")


if __name__ == "__main__":
2 changes: 1 addition & 1 deletion examples/client_validation.py
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ def example():
print("Trying out client validation...")
messages = client.messages.list(limit=10)
for m in messages:
print("Message {}".format(m.sid))
print(f"Message {m.sid}")

print("Client validation works!")

4 changes: 1 addition & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
help_center="https://www.twilio.com/help/contact",
url="https://github.com/twilio/twilio-python/",
keywords=["twilio", "twiml"],
python_requires=">=3.7.0",
python_requires=">=3.8",
install_requires=[
"requests >= 2.0.0",
"PyJWT >= 2.0.0, < 3.0.0",
@@ -33,8 +33,6 @@
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

class IntegrationTestCase(unittest.TestCase):
def setUp(self):
super(IntegrationTestCase, self).setUp()
super().setUp()
self.account_sid = "AC" + "a" * 32
self.auth_token = "AUTHTOKEN"
self.holodeck = Holodeck()
10 changes: 5 additions & 5 deletions tests/holodeck.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
from twilio import __version__


class Hologram(object):
class Hologram:
def __init__(self, request, response):
self.request = request
self.response = response
@@ -32,7 +32,7 @@ def add_standard_headers(self, request):
platform.machine(),
platform.python_version(),
),
"X-Twilio-Client": "python-{}".format(__version__),
"X-Twilio-Client": f"python-{__version__}",
"Accept": "application/json",
"Accept-Charset": "utf-8",
}
@@ -54,7 +54,7 @@ def assert_has_request(self, request):
)
if self._requests:
message += "Requests received:\n"
message += "\n".join(" * {}".format(r) for r in self.requests)
message += "\n".join(f" * {r}" for r in self.requests)
else:
message += "No Requests received"

@@ -79,10 +79,10 @@ def request(
if hologram.request == request:
return hologram.response

message = "\nHolodeck has no hologram for: {}\n".format(request)
message = f"\nHolodeck has no hologram for: {request}\n"
if self._holograms:
message += "Holograms loaded:\n"
message += "\n".join(" - {}".format(h.request) for h in self._holograms)
message += "\n".join(f" - {h.request}" for h in self._holograms)
else:
message += "No Holograms loaded"

1 change: 0 additions & 1 deletion tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Sphinx>=1.8.0
mock
pytest
pytest-cov
aiounittest
1 change: 0 additions & 1 deletion tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

1 change: 0 additions & 1 deletion tests/unit/base/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

2 changes: 1 addition & 1 deletion tests/unit/base/test_version.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ def get_instance(self, payload):

class StreamTestCase(IntegrationTestCase):
def setUp(self):
super(StreamTestCase, self).setUp()
super().setUp()

self.holodeck.mock(
Response(
4 changes: 2 additions & 2 deletions tests/unit/http/test_async_http_client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import aiounittest

from aiohttp import ClientSession
from mock import patch, AsyncMock
from unittest.mock import patch, AsyncMock
from twilio.http.async_http_client import AsyncTwilioHttpClient


class MockResponse(object):
class MockResponse:
"""
A mock of the aiohttp.ClientResponse class
"""
3 changes: 1 addition & 2 deletions tests/unit/http/test_http_client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
import os
import unittest
from collections import OrderedDict

from mock import Mock, patch
from unittest.mock import Mock, patch
from requests import Session

from twilio.base.exceptions import TwilioRestException
4 changes: 1 addition & 3 deletions tests/unit/http/test_validation_client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-

import unittest

from mock import patch, Mock
from unittest.mock import patch, Mock
from requests import Request
from requests import Session

1 change: 0 additions & 1 deletion tests/unit/jwt/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

4 changes: 2 additions & 2 deletions tests/unit/jwt/test_access_token.py
Original file line number Diff line number Diff line change
@@ -22,11 +22,11 @@ def assert_is_not_none(obj):


def assert_in(obj1, obj2):
assert obj1 in obj2, "%r is not in %r" % (obj1, obj2)
assert obj1 in obj2, "{!r} is not in {!r}".format(obj1, obj2)


def assert_greater_equal(obj1, obj2):
assert obj1 > obj2, "%r is not greater than or equal to %r" % (obj1, obj2)
assert obj1 > obj2, "{!r} is not greater than or equal to {!r}".format(obj1, obj2)


class AccessTokenTest(unittest.TestCase):
2 changes: 1 addition & 1 deletion tests/unit/jwt/test_client.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
class ClientCapabilityTokenTest(unittest.TestCase):
def assertIn(self, foo, bar, msg=None):
"""backport for 2.6"""
assert foo in bar, msg or "%s not found in %s" % (foo, bar)
assert foo in bar, msg or "{} not found in {}".format(foo, bar)

def now(self):
return int(time.time())
6 changes: 3 additions & 3 deletions tests/unit/jwt/test_jwt.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
import unittest

import jwt as jwt_lib
from mock import patch
from unittest.mock import patch

from twilio.jwt import Jwt, JwtDecodeError

@@ -24,7 +24,7 @@ def __init__(
headers=None,
payload=None,
):
super(DummyJwt, self).__init__(
super().__init__(
secret_key=secret_key,
issuer=issuer,
subject=subject,
@@ -46,7 +46,7 @@ def _generate_headers(self):
class JwtTest(unittest.TestCase):
def assertIn(self, foo, bar, msg=None):
"""backport for 2.6"""
assert foo in bar, msg or "%s not found in %s" % (foo, bar)
assert foo in bar, msg or "{} not found in {}".format(foo, bar)

def now(self):
return int(real_time.time())
12 changes: 6 additions & 6 deletions tests/unit/jwt/test_task_router.py
Original file line number Diff line number Diff line change
@@ -248,7 +248,7 @@ def test_defaults(self):
decoded = WorkerCapabilityToken.decode(token, self.auth_token)
self.assertNotEqual(None, decoded)

websocket_url = "https://event-bridge.twilio.com/v1/wschannels/{0}/{1}".format(
websocket_url = "https://event-bridge.twilio.com/v1/wschannels/{}/{}".format(
self.account_sid, self.worker_sid
)

@@ -297,7 +297,7 @@ def test_allow_activity_updates(self):
self.assertEqual(len(policies), 7)
policy = policies[6]

url = "https://taskrouter.twilio.com/v1/Workspaces/{0}/Workers/{1}".format(
url = "https://taskrouter.twilio.com/v1/Workspaces/{}/Workers/{}".format(
self.workspace_sid, self.worker_sid
)

@@ -322,13 +322,13 @@ def test_allow_reservation_updates(self):
self.assertEqual(len(policies), 8)

taskPolicy = policies[6]
tasksUrl = "https://taskrouter.twilio.com/v1/Workspaces/{0}/Tasks/**".format(
tasksUrl = "https://taskrouter.twilio.com/v1/Workspaces/{}/Tasks/**".format(
self.workspace_sid
)
self.check_policy("POST", tasksUrl, taskPolicy)

workerReservationsPolicy = policies[7]
reservationsUrl = "https://taskrouter.twilio.com/v1/Workspaces/{0}/Workers/{1}/Reservations/**".format(
reservationsUrl = "https://taskrouter.twilio.com/v1/Workspaces/{}/Workers/{}/Reservations/**".format(
self.workspace_sid, self.worker_sid
)
self.check_policy("POST", reservationsUrl, workerReservationsPolicy)
@@ -353,13 +353,13 @@ def test_pass_policies_in_constructor(self):
self.assertEqual(len(policies), 8)

taskPolicy = policies[6]
tasksUrl = "https://taskrouter.twilio.com/v1/Workspaces/{0}/Tasks/**".format(
tasksUrl = "https://taskrouter.twilio.com/v1/Workspaces/{}/Tasks/**".format(
self.workspace_sid
)
self.check_policy("POST", tasksUrl, taskPolicy)

workerReservationsPolicy = policies[7]
reservationsUrl = "https://taskrouter.twilio.com/v1/Workspaces/{0}/Workers/{1}/Reservations/**".format(
reservationsUrl = "https://taskrouter.twilio.com/v1/Workspaces/{}/Workers/{}/Reservations/**".format(
self.workspace_sid, self.worker_sid
)
self.check_policy("POST", reservationsUrl, workerReservationsPolicy)
2 changes: 1 addition & 1 deletion tests/unit/rest/test_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unittest
import aiounittest

from mock import AsyncMock, Mock
from unittest.mock import AsyncMock, Mock
from twilio.http.response import Response
from twilio.rest import Client

1 change: 0 additions & 1 deletion tests/unit/test_request_validator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import unittest

from django.conf import settings
1 change: 0 additions & 1 deletion tests/unit/twiml/test_voice_response.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from tests.unit.twiml import TwilioTest
from twilio.twiml.voice_response import VoiceResponse, Dial, Enqueue, Gather

8 changes: 4 additions & 4 deletions twilio/base/client_base.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
from twilio.http.response import Response


class ClientBase(object):
class ClientBase:
"""A client for accessing the Twilio API."""

def __init__(
@@ -179,8 +179,8 @@ def get_headers(
)
# Extensions
for extension in self.user_agent_extensions:
headers["User-Agent"] += " {}".format(extension)
headers["X-Twilio-Client"] = "python-{}".format(__version__)
headers["User-Agent"] += f" {extension}"
headers["X-Twilio-Client"] = f"python-{__version__}"

# Types, encodings, etc.
headers["Accept-Charset"] = "utf-8"
@@ -231,4 +231,4 @@ def __repr__(self) -> str:

:returns: Machine friendly representation
"""
return "<Twilio {}>".format(self.account_sid)
return f"<Twilio {self.account_sid}>"
2 changes: 1 addition & 1 deletion twilio/base/domain.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
from twilio.rest import Client


class Domain(object):
class Domain:
"""
This represents at Twilio API subdomain.

7 changes: 3 additions & 4 deletions twilio/base/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import sys
from typing import Optional

@@ -51,7 +50,7 @@ def teal(words: str) -> str:
return "\033[36m\033[49m%s\033[0m" % words

def get_uri(code: int) -> str:
return "https://www.twilio.com/docs/errors/{0}".format(code)
return f"https://www.twilio.com/docs/errors/{code}"

# If it makes sense to print a human readable error message, try to
# do it. The one problem is that someone might catch this error and
@@ -62,7 +61,7 @@ def get_uri(code: int) -> str:
"\n\n{twilio_returned}\n\n{message}\n".format(
red_error=red("HTTP Error"),
request_was=white("Your request was:"),
http_line=teal("%s %s" % (self.method, self.uri)),
http_line=teal("{} {}".format(self.method, self.uri)),
twilio_returned=white("Twilio returned the following information:"),
message=blue(str(self.msg)),
)
@@ -79,4 +78,4 @@ def get_uri(code: int) -> str:
)
return msg
else:
return "HTTP {0} error: {1}".format(self.status, self.msg)
return f"HTTP {self.status} error: {self.msg}"
2 changes: 1 addition & 1 deletion twilio/base/instance_context.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from twilio.base.version import Version


class InstanceContext(object):
class InstanceContext:
def __init__(self, version: Version):
self._version = version
2 changes: 1 addition & 1 deletion twilio/base/instance_resource.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from twilio.base.version import Version


class InstanceResource(object):
class InstanceResource:
def __init__(self, version: Version):
self._version = version
2 changes: 1 addition & 1 deletion twilio/base/list_resource.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from twilio.base.version import Version


class ListResource(object):
class ListResource:
def __init__(self, version: Version):
self._version = version
4 changes: 2 additions & 2 deletions twilio/base/obsolete.py
Original file line number Diff line number Diff line change
@@ -30,9 +30,9 @@ def deprecated_method(new_func=None):
def deprecated_method_wrapper(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
msg = "Function method .{}() is deprecated".format(func.__name__)
msg = f"Function method .{func.__name__}() is deprecated"
msg += (
" in favor of .{}()".format(new_func)
f" in favor of .{new_func}()"
if isinstance(new_func, str)
else ""
)
Loading