Skip to content

Commit b6ba2a5

Browse files
authored
Allow more control over exceptions (#86)
1 parent cc90b26 commit b6ba2a5

File tree

6 files changed

+40
-16
lines changed

6 files changed

+40
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3535
- The authentication success and failure displayed in the browser were revamped to be more user-friendly. `requests_auth.testing` was modified to accommodate this change:
3636
- `tab.assert_success` `expected_message` parameter was removed.
3737
- `tab.assert_failure` `expected_message` parameter should not be prefixed with `Unable to properly perform authentication: ` anymore and `\n` in the message should be replaced with `<br>`.
38+
- Exceptions issued by `requests_auth` are now inheriting from `requests_auth.RequestsAuthException`, itself inheriting from `requests.RequestException`, instead of `Exception`.
3839

3940
### Fixed
4041
- Type information is now provided following [PEP 561](https://www.python.org/dev/peps/pep-0561/).

requests_auth/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
InvalidToken,
4141
TokenExpiryNotProvided,
4242
InvalidGrantRequest,
43+
RequestsAuthException,
4344
)
4445
from requests_auth.version import __version__
4546

@@ -67,6 +68,7 @@
6768
"SupportMultiAuth",
6869
"JsonTokenFileCache",
6970
"TokenMemoryCache",
71+
"RequestsAuthException",
7072
"GrantNotProvided",
7173
"TimeoutOccurred",
7274
"AuthenticationFailed",

requests_auth/_errors.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,45 @@
11
from json import JSONDecodeError
22
from typing import Union
33

4-
from requests import Response
4+
from requests import Response, RequestException
55

66

7-
class AuthenticationFailed(Exception):
7+
class RequestsAuthException(RequestException): ...
8+
9+
10+
class AuthenticationFailed(RequestsAuthException):
811
"""User was not authenticated."""
912

1013
def __init__(self):
11-
Exception.__init__(self, "User was not authenticated.")
14+
RequestsAuthException.__init__(self, "User was not authenticated.")
1215

1316

14-
class TimeoutOccurred(Exception):
17+
class TimeoutOccurred(RequestsAuthException):
1518
"""No response within timeout interval."""
1619

1720
def __init__(self, timeout: float):
18-
Exception.__init__(
21+
RequestsAuthException.__init__(
1922
self, f"User authentication was not received within {timeout} seconds."
2023
)
2124

2225

23-
class InvalidToken(Exception):
26+
class InvalidToken(RequestsAuthException):
2427
"""Token is invalid."""
2528

2629
def __init__(self, token_name: str):
27-
Exception.__init__(self, f"{token_name} is invalid.")
30+
RequestsAuthException.__init__(self, f"{token_name} is invalid.")
2831

2932

30-
class GrantNotProvided(Exception):
33+
class GrantNotProvided(RequestsAuthException):
3134
"""Grant was not provided."""
3235

3336
def __init__(self, grant_name: str, dictionary_without_grant: dict):
34-
Exception.__init__(
37+
RequestsAuthException.__init__(
3538
self, f"{grant_name} not provided within {dictionary_without_grant}."
3639
)
3740

3841

39-
class InvalidGrantRequest(Exception):
42+
class InvalidGrantRequest(RequestsAuthException):
4043
"""
4144
If the request failed client authentication or is invalid, the authorization server returns an error response as described in https://tools.ietf.org/html/rfc6749#section-5.2
4245
"""
@@ -64,7 +67,7 @@ class InvalidGrantRequest(Exception):
6467
}
6568

6669
def __init__(self, response: Union[Response, dict]):
67-
Exception.__init__(self, InvalidGrantRequest.to_message(response))
70+
RequestsAuthException.__init__(self, InvalidGrantRequest.to_message(response))
6871

6972
@staticmethod
7073
def to_message(response: Union[Response, dict]) -> str:
@@ -114,17 +117,19 @@ def _pop(key: str) -> str:
114117
return message
115118

116119

117-
class StateNotProvided(Exception):
120+
class StateNotProvided(RequestsAuthException):
118121
"""State was not provided."""
119122

120123
def __init__(self, dictionary_without_state: dict):
121-
Exception.__init__(
124+
RequestsAuthException.__init__(
122125
self, f"state not provided within {dictionary_without_state}."
123126
)
124127

125128

126-
class TokenExpiryNotProvided(Exception):
129+
class TokenExpiryNotProvided(RequestsAuthException):
127130
"""Token expiry was not provided."""
128131

129132
def __init__(self, token_body: dict):
130-
Exception.__init__(self, f"Expiry (exp) is not provided in {token_body}.")
133+
RequestsAuthException.__init__(
134+
self, f"Expiry (exp) is not provided in {token_body}."
135+
)

tests/features/token_cache/test_json_token_file_cache.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66
import jwt
7+
import requests
78

89
import requests_auth
910
import requests_auth._oauth2.tokens
@@ -78,6 +79,8 @@ def failing_dump(*args):
7879
with pytest.raises(requests_auth.AuthenticationFailed) as exception_info:
7980
same_cache.get_token("key1")
8081
assert str(exception_info.value) == "User was not authenticated."
82+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
83+
assert isinstance(exception_info.value, requests.RequestException)
8184

8285
assert caplog.messages == [
8386
"Cannot save tokens.",

tests/oauth2/authorization_code/test_oauth2_authorization_code.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,12 @@ def test_oauth2_authorization_code_flow_uses_custom_failure(
151151
displayed_html="FAILURE: {display_time}\n{information}",
152152
)
153153

154-
with pytest.raises(requests_auth.InvalidGrantRequest):
154+
with pytest.raises(requests_auth.InvalidGrantRequest) as exception_info:
155155
requests.get("http://authorized_only", auth=auth)
156156

157+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
158+
assert isinstance(exception_info.value, requests.RequestException)
159+
157160
tab.assert_failure(
158161
"invalid_request: The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed."
159162
)
@@ -478,6 +481,8 @@ def test_empty_token_is_invalid(
478481
str(exception_info.value)
479482
== "access_token not provided within {'access_token': '', 'token_type': 'example', 'expires_in': 3600, 'refresh_token': 'tGzv3JOkF0XG5Qx2TlKWIA', 'example_parameter': 'example_value'}."
480483
)
484+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
485+
assert isinstance(exception_info.value, requests.RequestException)
481486
tab.assert_success()
482487

483488

tests/oauth2/implicit/test_oauth2_implicit.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ def open(self, url, new):
308308
str(exception_info.value)
309309
== "User authentication was not received within 0.1 seconds."
310310
)
311+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
312+
assert isinstance(exception_info.value, requests.RequestException)
311313

312314

313315
def test_browser_error(token_cache, responses: RequestsMock, monkeypatch):
@@ -371,6 +373,8 @@ def test_empty_token_is_invalid(token_cache, browser_mock: BrowserMock):
371373
auth=requests_auth.OAuth2Implicit("http://provide_token"),
372374
)
373375
assert str(exception_info.value) == " is invalid."
376+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
377+
assert isinstance(exception_info.value, requests.RequestException)
374378
tab.assert_success()
375379

376380

@@ -386,6 +390,8 @@ def test_token_without_expiry_is_invalid(token_cache, browser_mock: BrowserMock)
386390
auth=requests_auth.OAuth2Implicit("http://provide_token"),
387391
)
388392
assert str(exception_info.value) == "Expiry (exp) is not provided in None."
393+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
394+
assert isinstance(exception_info.value, requests.RequestException)
389395
tab.assert_success()
390396

391397

@@ -612,6 +618,8 @@ def test_oauth2_implicit_flow_post_failure_if_state_is_not_provided(
612618
str(exception_info.value)
613619
== f"state not provided within {{'access_token': ['{token}']}}."
614620
)
621+
assert isinstance(exception_info.value, requests_auth.RequestsAuthException)
622+
assert isinstance(exception_info.value, requests.RequestException)
615623
tab.assert_failure(f"state not provided within {{'access_token': ['{token}']}}.")
616624

617625

0 commit comments

Comments
 (0)