Skip to content
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

Make Requester.__createException robust against missing message and body #2159

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 8 additions & 8 deletions github/Requester.py
Expand Up @@ -422,26 +422,26 @@ def __customConnection(self, url):
return cnx

def __createException(self, status, headers, output):
if status == 401 and output.get("message") == "Bad credentials":
message = output.get("message", "").lower() if output is not None else ""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea making this case insensitive! 👍


if status == 401 and message == "bad credentials":
cls = GithubException.BadCredentialsException
elif (
status == 401
and Consts.headerOTP in headers
and re.match(r".*required.*", headers[Consts.headerOTP])
):
cls = GithubException.TwoFactorException
elif status == 403 and output.get("message").startswith(
"Missing or invalid User Agent string"
elif status == 403 and message.startswith(
"missing or invalid user agent string"
):
cls = GithubException.BadUserAgentException
elif status == 403 and (
output.get("message").lower().startswith("api rate limit exceeded")
or output.get("message")
.lower()
.endswith("please wait a few minutes before you try again.")
message.startswith("api rate limit exceeded")
or message.endswith("please wait a few minutes before you try again.")
):
cls = GithubException.RateLimitExceededException
elif status == 404 and output.get("message") == "Not Found":
elif status == 404 and message == "not found":
cls = GithubException.UnknownObjectException
else:
cls = GithubException.GithubException
Expand Down
124 changes: 123 additions & 1 deletion tests/Requester.py
@@ -1,6 +1,6 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Enrico Minack <github@enrico.minack.dev> #
# Copyright 2022 Enrico Minack <github@enrico.minack.dev> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
Expand Down Expand Up @@ -91,3 +91,125 @@ def testBaseUrlPrefixRedirection(self):
self.logger.info.assert_called_once_with(
"Following Github server redirection from /api/v3/repos/PyGithub/PyGithub to /repos/PyGithub/PyGithub"
)

def assertException(self, exception, exception_type, status, data, headers, string):
self.assertIsInstance(exception, exception_type)
self.assertEqual(exception.status, status)
if data is None:
self.assertIsNone(exception.data)
else:
self.assertEqual(exception.data, data)
self.assertEqual(exception.headers, headers)
self.assertEqual(str(exception), string)

def testShouldCreateBadCredentialsException(self):
exc = self.g._Github__requester.__createException(
401, {"header": "value"}, {"message": "Bad credentials"}
)
self.assertException(
exc,
github.BadCredentialsException,
401,
{"message": "Bad credentials"},
{"header": "value"},
'401 {"message": "Bad credentials"}',
)

def testShouldCreateTwoFactorException(self):
exc = self.g._Github__requester.__createException(
401,
{"x-github-otp": "required; app"},
{
"message": "Must specify two-factor authentication OTP code.",
"documentation_url": "https://developer.github.com/v3/auth#working-with-two-factor-authentication",
},
)
self.assertException(
exc,
github.TwoFactorException,
401,
{
"message": "Must specify two-factor authentication OTP code.",
"documentation_url": "https://developer.github.com/v3/auth#working-with-two-factor-authentication",
},
{"x-github-otp": "required; app"},
'401 {"message": "Must specify two-factor authentication OTP code.", "documentation_url": "https://developer.github.com/v3/auth#working-with-two-factor-authentication"}',
)

def testShouldCreateBadUserAgentException(self):
exc = self.g._Github__requester.__createException(
403,
{"header": "value"},
{"message": "Missing or invalid User Agent string"},
)
self.assertException(
exc,
github.BadUserAgentException,
403,
{"message": "Missing or invalid User Agent string"},
{"header": "value"},
'403 {"message": "Missing or invalid User Agent string"}',
)

def testShouldCreateRateLimitExceededException(self):
for message in [
"API Rate Limit Exceeded for 92.104.200.119",
"You have triggered an abuse detection mechanism. Please wait a few minutes before you try again.",
"You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
]:
with self.subTest(message=message):
exc = self.g._Github__requester.__createException(
403, {"header": "value"}, {"message": message}
)
self.assertException(
exc,
github.RateLimitExceededException,
403,
{"message": message},
{"header": "value"},
f'403 {{"message": "{message}"}}',
)

def testShouldCreateUnknownObjectException(self):
exc = self.g._Github__requester.__createException(
404, {"header": "value"}, {"message": "Not Found"}
)
self.assertException(
exc,
github.UnknownObjectException,
404,
{"message": "Not Found"},
{"header": "value"},
'404 {"message": "Not Found"}',
)

def testShouldCreateGithubException(self):
for status in range(400, 600):
with self.subTest(status=status):
exc = self.g._Github__requester.__createException(
status, {"header": "value"}, {"message": "Something unknown"}
)
self.assertException(
exc,
github.GithubException,
status,
{"message": "Something unknown"},
{"header": "value"},
f'{status} {{"message": "Something unknown"}}',
)

def testShouldCreateExceptionWithoutMessage(self):
for status in range(400, 600):
with self.subTest(status=status):
exc = self.g._Github__requester.__createException(status, {}, {})
self.assertException(
exc, github.GithubException, status, {}, {}, f"{status} {{}}"
)

def testShouldCreateExceptionWithoutOutput(self):
for status in range(400, 600):
with self.subTest(status=status):
exc = self.g._Github__requester.__createException(status, {}, None)
self.assertException(
exc, github.GithubException, status, None, {}, f"{status} null"
)