Skip to content

Commit 2208047

Browse files
authored
feat!: Drop support for python 3.5 (#654)
Closes: SDK-1889
1 parent e010bcd commit 2208047

File tree

79 files changed

+376
-481
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+376
-481
lines changed

.travis.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ before_cache:
1515

1616
matrix:
1717
include:
18-
- python: 3.5
18+
- python: 3.6
1919
env: TOX_ENV=pycodestyle
20-
- python: 3.5
20+
- python: 3.6
2121
env: TOX_ENV=pylint
22-
- python: 3.5
23-
env: TOX_ENV=py35
2422
- python: 3.6
2523
env: TOX_ENV=py36
2624
- python: 3.7
@@ -30,7 +28,7 @@ matrix:
3028
sudo: true
3129
- python: pypy
3230
env: TOX_ENV=pypy PYPY_VERSION='3.6-7.3.0'
33-
- python: 3.5
31+
- python: 3.6
3432
env: TOX_ENV=coverage
3533

3634
# commands to install dependencies

.travis/install.sh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
2121
fi
2222

2323
case "${TOX_ENV}" in
24-
py35)
25-
pyenv install 3.5.0
26-
pyenv global 3.5.0
27-
;;
2824
py36)
2925
pyenv install 3.6.0
3026
pyenv global 3.6.0

boxsdk/auth/jwt_auth.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ def _normalize_user_id(cls, user: Any) -> Optional[str]:
317317
return user.object_id
318318
if isinstance(user, str):
319319
return str(user)
320-
raise TypeError("Got unsupported type {0!r} for user.".format(user.__class__.__name__))
320+
raise TypeError(f"Got unsupported type {user.__class__.__name__!r} for user.")
321321

322322
def authenticate_instance(self, enterprise: Optional[str] = None) -> str:
323323
"""
@@ -340,8 +340,8 @@ def authenticate_instance(self, enterprise: Optional[str] = None) -> str:
340340
raise ValueError("authenticate_instance: Requires the enterprise ID, but it was not provided.")
341341
if all(enterprises) and (enterprise != self._enterprise_id):
342342
raise ValueError(
343-
"authenticate_instance: Given enterprise ID {given_enterprise!r}, but {auth} already has ID {existing_enterprise!r}"
344-
.format(auth=self, given_enterprise=enterprise, existing_enterprise=self._enterprise_id)
343+
f"authenticate_instance: Given enterprise ID {enterprise!r}, "
344+
f"but {self} already has ID {self._enterprise_id!r}"
345345
)
346346
if not self._enterprise_id:
347347
self._enterprise_id = enterprise
@@ -396,8 +396,7 @@ def _normalize_rsa_private_key(
396396
'rsa_private_key_data must be binary data (bytes/str), '
397397
'a file-like object with a read() method, '
398398
'or an instance of RSAPrivateKey, '
399-
'but got {0!r}'
400-
.format(data.__class__.__name__)
399+
f'but got {data.__class__.__name__!r}'
401400
)
402401

403402
@staticmethod
@@ -412,8 +411,8 @@ def _normalize_rsa_private_key_passphrase(passphrase: Any):
412411

413412
if not isinstance(passphrase, (bytes, type(None))):
414413
raise TypeError(
415-
"rsa_private_key_passphrase must contain binary data (bytes/str), got {0!r}"
416-
.format(passphrase.__class__.__name__)
414+
f"rsa_private_key_passphrase must contain binary data (bytes/str), "
415+
f"got {passphrase.__class__.__name__!r}"
417416
)
418417
return passphrase
419418

@@ -448,6 +447,6 @@ def from_settings_file(cls, settings_file_sys_path: str, **kwargs: Any) -> 'JWTA
448447
:param settings_file_sys_path: Path to the JSON file containing the configuration.
449448
:return: Auth instance configured as specified by the JSON file.
450449
"""
451-
with open(settings_file_sys_path) as config_file:
450+
with open(settings_file_sys_path, encoding='utf-8') as config_file:
452451
config_dictionary = json.load(config_file)
453452
return cls.from_settings_dictionary(config_dictionary, **kwargs)

boxsdk/auth/oauth2.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import random
66
import string # pylint:disable=deprecated-module
77
from threading import Lock
8-
from typing import Optional, Callable, ContextManager, Tuple, TYPE_CHECKING, Any
8+
from typing import Optional, Callable, ContextManager, Tuple, TYPE_CHECKING, Any, Union
99
from urllib.parse import urlunsplit, urlencode
1010

1111
from ..config import API
@@ -17,6 +17,7 @@
1717

1818
if TYPE_CHECKING:
1919
from boxsdk.session.box_response import BoxResponse
20+
from boxsdk import NetworkResponse
2021

2122

2223
class TokenScope(TextEnum):
@@ -281,7 +282,7 @@ def _execute_token_request(
281282
The response for the token request.
282283
"""
283284
self._check_closed()
284-
url = '{base_auth_url}/token'.format(base_auth_url=self._api_config.OAUTH2_API_URL)
285+
url = f'{self._api_config.OAUTH2_API_URL}/token'
285286
headers = {'content-type': 'application/x-www-form-urlencoded'}
286287
try:
287288
network_response = self._session.request(
@@ -307,7 +308,7 @@ def _execute_token_request(
307308
return token_response
308309

309310
@staticmethod
310-
def _oauth_exception(network_response: 'BoxResponse', url: str) -> BoxOAuthException:
311+
def _oauth_exception(network_response: Union['NetworkResponse', 'BoxResponse'], url: str) -> BoxOAuthException:
311312
"""
312313
Create a BoxOAuthException instance to raise. If the error response is JSON, parse it and include the
313314
code and message in the exception.
@@ -361,7 +362,7 @@ def revoke(self) -> None:
361362
token_to_revoke = access_token or refresh_token
362363
if token_to_revoke is None:
363364
return
364-
url = '{base_auth_url}/revoke'.format(base_auth_url=self._api_config.OAUTH2_API_URL)
365+
url = f'{self._api_config.OAUTH2_API_URL}/revoke'
365366
try:
366367
network_response = self._session.request(
367368
'POST',

boxsdk/auth/redis_managed_oauth2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def __init__(self, unique_id: str = uuid4(), redis_server: Redis = None, *args:
2323
# pylint:disable=keyword-arg-before-vararg
2424
self._unique_id = unique_id
2525
self._redis_server = redis_server or StrictRedis()
26-
refresh_lock = Lock(redis=self._redis_server, name='{0}_lock'.format(self._unique_id))
26+
refresh_lock = Lock(redis=self._redis_server, name=f'{self._unique_id}_lock')
2727
super().__init__(*args, refresh_lock=refresh_lock, **kwargs)
2828
if self._access_token is None:
2929
self._get_and_update_current_tokens()

boxsdk/client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1195,7 +1195,7 @@ def downscope_token(
11951195
:return:
11961196
The response for the downscope token request.
11971197
"""
1198-
url = '{base_auth_url}/token'.format(base_auth_url=self._session.api_config.OAUTH2_API_URL)
1198+
url = f'{self._session.api_config.OAUTH2_API_URL}/token'
11991199
access_token = self.auth.access_token or self.auth.refresh(None)
12001200
data = {
12011201
'subject_token': access_token,

boxsdk/config.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,9 @@ class API:
1717
class Client:
1818
"""Configuration object containing the user agent string."""
1919
VERSION = version.__version__
20-
USER_AGENT_STRING = 'box-python-sdk-{0}'.format(VERSION)
21-
BOX_UA_STRING = 'agent=box-python-sdk/{0}; env=python/{1}.{2}.{3}'.format(
22-
VERSION,
23-
py_version.major,
24-
py_version.minor,
25-
py_version.micro,
26-
)
20+
USER_AGENT_STRING = f'box-python-sdk-{VERSION}'
21+
BOX_UA_STRING = f'agent=box-python-sdk/{VERSION}; ' \
22+
f'env=python/{py_version.major}.{py_version.minor}.{py_version.micro}'
2723

2824

2925
class Proxy:

boxsdk/exception.py

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# coding: utf-8
2+
from typing import Optional
23

34
import attr
45

@@ -11,10 +12,10 @@ class BoxException(Exception):
1112
Base class exception for all errors raised from the SDK.
1213
"""
1314
def __str__(self):
14-
return '{}'.format(self.__class__.__name__)
15+
return self.__class__.__name__
1516

1617
def __repr__(self):
17-
return '<{}>'.format(self.__class__.__name__)
18+
return f'<{self.__class__.__name__}>'
1819

1920

2021
class BoxValueError(ValueError):
@@ -36,50 +37,32 @@ class BoxAPIException(BoxException):
3637
3738
:param status:
3839
HTTP status code of the failed response
39-
:type status:
40-
`int`
4140
:param code:
4241
The 'code' field of the failed response
43-
:type code:
44-
`unicode` or None
4542
:param message:
4643
A message to associate with the exception, e.g. 'message' field of the json in the failed response
47-
:type message:
48-
`unicode` or None
4944
:param request_id:
5045
The 'request_id' field of the json in the failed response
51-
:type request_id:
52-
`unicode` or None
5346
:param headers:
5447
The HTTP headers in the failed response
55-
:type headers:
56-
`dict`
5748
:param url:
5849
The url which raised the exception
59-
:type url:
60-
`unicode`
6150
:param method:
6251
The HTTP verb used to make the request.
63-
:type method:
64-
`unicode`
6552
:param context_info:
6653
The context_info returned in the failed response.
67-
:type context_info:
68-
`dict` or None
6954
:param network_response:
7055
The failed response
71-
:type network_response:
72-
Requests `Response`
7356
"""
74-
status = attr.ib()
75-
code = attr.ib(default=None)
76-
message = attr.ib(default=None)
77-
request_id = attr.ib(default=None)
78-
headers = attr.ib(default=None, hash=False)
79-
url = attr.ib(default=None)
80-
method = attr.ib(default=None)
81-
context_info = attr.ib(default=None)
82-
network_response = attr.ib(default=None, repr=False)
57+
status: int = attr.ib()
58+
code: Optional[str] = attr.ib(default=None)
59+
message: Optional[str] = attr.ib(default=None)
60+
request_id: Optional[str] = attr.ib(default=None)
61+
headers: dict = attr.ib(default=None, hash=False)
62+
url: str = attr.ib(default=None)
63+
method: str = attr.ib(default=None)
64+
context_info: Optional[dict] = attr.ib(default=None)
65+
network_response: 'NetworkResponse' = attr.ib(default=None, repr=False)
8366

8467
def __str__(self):
8568
return '\n'.join((
@@ -101,35 +84,23 @@ class BoxOAuthException(BoxException):
10184
10285
:param status:
10386
HTTP status code of the auth response
104-
:type status:
105-
`int`
10687
:param message:
10788
A message to associate with the exception, e.g. HTTP content of the auth response
108-
:type message:
109-
`unicode`
11089
:param url:
11190
The url which raised the exception
112-
:type url:
113-
`unicode`
11491
:param method:
11592
The HTTP verb used to make the request.
116-
:type method:
117-
`unicode`
11893
:param network_response:
11994
The network response for the request.
120-
:type network_response:
121-
:class:`NetworkResponse`
12295
:param code:
12396
The 'code' field of the failed response
124-
:type code:
125-
`unicode` or None
12697
"""
127-
status = attr.ib()
128-
message = attr.ib(default=None)
129-
url = attr.ib(default=None)
130-
method = attr.ib(default=None)
131-
network_response = attr.ib(default=None, repr=False, type=NetworkResponse)
132-
code = attr.ib(default=None)
98+
status: int = attr.ib()
99+
message: str = attr.ib(default=None)
100+
url: str = attr.ib(default=None)
101+
method: str = attr.ib(default=None)
102+
network_response: NetworkResponse = attr.ib(default=None, repr=False)
103+
code: Optional[str] = attr.ib(default=None)
133104

134105
def __str__(self):
135106
# pylint:disable=no-member
@@ -138,13 +109,8 @@ def __str__(self):
138109
# pylint:enable=no-member
139110
else:
140111
headers = 'N/A'
141-
return '\nMessage: {0}\nStatus: {1}\nURL: {2}\nMethod: {3}\nHeaders: {4}'.format(
142-
self.message,
143-
self.status,
144-
self.url,
145-
self.method,
146-
headers,
147-
)
112+
return f'\nMessage: {self.message}\nStatus: {self.status}\nURL: {self.url}\nMethod: {self.method}' \
113+
f'\nHeaders: {headers}'
148114

149115

150116
__all__ = list(map(str, ['BoxException', 'BoxAPIException', 'BoxOAuthException', 'BoxNetworkException']))

boxsdk/network/default_network.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ class DefaultNetworkResponse(NetworkResponse):
140140
"""
141141

142142
_COMMON_RESPONSE_FORMAT = '"%(method)s %(url)s" %(status_code)s %(content_length)s\n%(headers)s\n%(content)s\n'
143-
SUCCESSFUL_RESPONSE_FORMAT = '\x1b[32m{0}\x1b[0m'.format(_COMMON_RESPONSE_FORMAT)
144-
ERROR_RESPONSE_FORMAT = '\x1b[31m{0}\x1b[0m'.format(_COMMON_RESPONSE_FORMAT)
143+
SUCCESSFUL_RESPONSE_FORMAT = f'\x1b[32m{_COMMON_RESPONSE_FORMAT}\x1b[0m'
144+
ERROR_RESPONSE_FORMAT = f'\x1b[31m{_COMMON_RESPONSE_FORMAT}\x1b[0m'
145145
STREAM_CONTENT_NOT_LOGGED = '<File download contents unavailable for logging>'
146146

147147
def __init__(self, request_response: 'Response', access_token_used: str):
@@ -258,8 +258,5 @@ def log(self, can_safely_log_content: bool = False) -> None:
258258
)
259259

260260
def __repr__(self) -> str:
261-
return '<Box Network Response ({method} {url} {status_code})>'.format(
262-
method=self._request_response.request.method,
263-
url=self._request_response.request.url,
264-
status_code=self.status_code,
265-
)
261+
return f'<Box Network Response ({self._request_response.request.method} {self._request_response.request.url} ' \
262+
f'{self.status_code})>'

boxsdk/object/base_api_json_object.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# coding: utf-8
22

33
import copy
4-
from typing import Any, Iterator, Iterable
4+
from typing import Any, Iterator, Iterable, Optional
55

66
from ..util.translator import Translator
77

@@ -67,12 +67,11 @@ class BaseAPIJSONObject(metaclass=BaseAPIJSONObjectMeta):
6767
# For API object/resource classes this should equal the expected value
6868
# of the 'type' field in API JSON responses. Otherwise, this should be
6969
# `None`.
70-
# :type _item_type: `unicode` or `None`
7170
#
7271
# NOTE: When defining a leaf class with an _item_type in this SDK, it's
7372
# also important to add the module name to __all__ in object/__init__.py,
7473
# so that it will be imported and registered with the default translator.
75-
_item_type = None
74+
_item_type: Optional[str] = None
7675
_untranslated_fields = ()
7776

7877
def __init__(self, response_object: dict = None, **kwargs: Any):
@@ -111,8 +110,8 @@ def __iter__(self) -> Iterator:
111110

112111
def __repr__(self) -> str:
113112
"""Base class override. Return a human-readable representation using the Box ID or name of the object."""
114-
extra_description = ' - {0}'.format(self._description) if self._description else ''
115-
description = '<Box {0}{1}>'.format(self.__class__.__name__, extra_description)
113+
extra_description = f' - {self._description}' if self._description else ''
114+
description = f'<Box {self.__class__.__name__}{extra_description}>'
116115
return description
117116

118117
@property

0 commit comments

Comments
 (0)