Skip to content

Commit c5fa0e1

Browse files
author
Alejandro Casanovas
committed
Added pyproject.toml
Second part of fixing the refresh token Updated requirements Added py3.13 to classifiers changed dependency from requests-oauthlib to msal
1 parent 9d0b2e1 commit c5fa0e1

File tree

5 files changed

+81
-96
lines changed

5 files changed

+81
-96
lines changed

O365/connection.py

+62-91
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ def _internal_request(self, request_obj, url, method, **kwargs):
745745
"""
746746
method = method.lower()
747747
if method not in self._allowed_methods:
748-
raise ValueError('Method must be one of: {}'.format(self._allowed_methods))
748+
raise ValueError(f'Method must be one of: {self._allowed_methods}')
749749

750750
if 'headers' not in kwargs:
751751
kwargs['headers'] = {**self.default_headers}
@@ -769,95 +769,60 @@ def _internal_request(self, request_obj, url, method, **kwargs):
769769
if self.timeout is not None:
770770
kwargs['timeout'] = self.timeout
771771

772-
request_done = False
773-
token_refreshed = False
774-
775-
while not request_done:
776-
self._check_delay() # sleeps if needed
772+
self._check_delay() # sleeps if needed
773+
try:
774+
log.debug(f'Requesting ({method.upper()}) URL: {url}')
775+
log.debug(f'Request parameters: {kwargs}')
776+
# auto_retry will occur inside this function call if enabled
777+
response = request_obj.request(method, url, **kwargs)
778+
779+
response.raise_for_status() # raise 4XX and 5XX error codes.
780+
log.debug(f'Received response ({response.status_code}) from URL {response.url}')
781+
return response
782+
except (ConnectionError, ProxyError, SSLError, Timeout) as e:
783+
# We couldn't connect to the target url, raise error
784+
log.debug(f'Connection Error calling: {url}.{f"Using proxy {self.proxy}" if self.proxy else ""}')
785+
raise e # re-raise exception
786+
except HTTPError as e:
787+
# Server response with 4XX or 5XX error status codes
788+
if e.response.status_code == 401 and self._token_expired_flag is False:
789+
# This could be a token expired error.
790+
if self.token_backend.token_is_expired():
791+
log.debug('Oauth Token is expired')
792+
# Token has expired, try to refresh the token and try again on the next loop
793+
self._token_expired_flag = True
794+
raise TokenExpiredError('Oauth Token is expired')
795+
796+
# try to extract the error message:
777797
try:
778-
log.debug('Requesting ({}) URL: {}'.format(method.upper(), url))
779-
log.debug('Request parameters: {}'.format(kwargs))
780-
# auto_retry will occur inside this function call if enabled
781-
response = request_obj.request(method, url, **kwargs)
782-
response.raise_for_status() # raise 4XX and 5XX error codes.
783-
log.debug('Received response ({}) from URL {}'.format(
784-
response.status_code, response.url))
785-
request_done = True
786-
return response
787-
except (ConnectionError, ProxyError, SSLError, Timeout) as e:
788-
# We couldn't connect to the target url, raise error
789-
log.debug('Connection Error calling: {}.{}'
790-
''.format(url, ('Using proxy: {}'.format(self.proxy)
791-
if self.proxy else '')))
792-
raise e # re-raise exception
793-
except TokenExpiredError as e:
794-
# Token has expired, try to refresh the token and try again on the next loop
795-
log.debug('Oauth Token is expired')
796-
if self.token_backend.token.is_long_lived is False and self.auth_flow_type == 'authorization':
797-
raise e
798-
if token_refreshed:
799-
# Refresh token done but still TokenExpiredError raise
800-
raise RuntimeError('Token Refresh Operation not working')
801-
should_rt = self.token_backend.should_refresh_token(self)
802-
if should_rt is True:
803-
# The backend has checked that we can refresh the token
804-
if self.refresh_token() is False:
805-
raise RuntimeError('Token Refresh Operation not working')
806-
token_refreshed = True
807-
elif should_rt is False:
808-
# the token was refreshed by another instance and updated into
809-
# this instance, so: update the session token and
810-
# go back to the loop and try the request again.
811-
request_obj.token = self.token_backend.token
812-
else:
813-
# the refresh was performed by the tokend backend.
814-
token_refreshed = True
815-
except HTTPError as e:
816-
# Server response with 4XX or 5XX error status codes
817-
818-
if e.response.status_code == 401 and self._token_expired_flag is False:
819-
# This could be a token expired error.
820-
if self.token_backend.token_is_expired():
821-
log.debug('Oauth Token is expired')
822-
# Token has expired, try to refresh the token and try again on the next loop
823-
if self.token_backend.token_is_long_lived is False and self.auth_flow_type == 'authorization':
824-
raise e
825-
826-
# try to extract the error message:
827-
try:
828-
error = response.json()
829-
error_message = error.get('error', {}).get('message', '')
830-
error_code = (
831-
error.get("error", {}).get("innerError", {}).get("code", "")
832-
)
833-
except ValueError:
834-
error_message = ''
835-
error_code = ''
836-
837-
status_code = int(e.response.status_code / 100)
838-
if status_code == 4:
839-
# Client Error
840-
# Logged as error. Could be a library error or Api changes
841-
log.error(
842-
"Client Error: {} | Error Message: {} | Error Code: {}".format(
843-
str(e), error_message, error_code
844-
)
845-
)
846-
else:
847-
# Server Error
848-
log.debug('Server Error: {}'.format(str(e)))
849-
if self.raise_http_errors:
850-
if error_message:
851-
raise HTTPError('{} | Error Message: {}'.format(e.args[0], error_message),
852-
response=response) from None
853-
else:
854-
raise e
798+
error = e.response.json()
799+
error_message = error.get('error', {}).get('message', '')
800+
error_code = (
801+
error.get("error", {}).get("innerError", {}).get("code", "")
802+
)
803+
except ValueError:
804+
error_message = ''
805+
error_code = ''
806+
807+
status_code = int(e.response.status_code / 100)
808+
if status_code == 4:
809+
# Client Error
810+
# Logged as error. Could be a library error or Api changes
811+
log.error(f'Client Error: {e} | Error Message: {error_message} | Error Code: {error_code}')
812+
else:
813+
# Server Error
814+
log.debug(f'Server Error: {e}')
815+
if self.raise_http_errors:
816+
if error_message:
817+
raise HTTPError(f'{e.args[0]} | Error Message: {error_message}', response=e.response) from None
855818
else:
856-
return e.response
857-
except RequestException as e:
858-
# catch any other exception raised by requests
859-
log.debug('Request Exception: {}'.format(str(e)))
860-
raise e
819+
raise e
820+
else:
821+
return e.response
822+
except RequestException as e:
823+
# catch any other exception raised by requests
824+
log.debug(f'Request Exception: {e}')
825+
raise e
861826

862827
def naive_request(self, url, method, **kwargs):
863828
""" Makes a request to url using an without oauth authorization
@@ -872,6 +837,7 @@ def naive_request(self, url, method, **kwargs):
872837
if self.naive_session is None:
873838
# lazy creation of a naive session
874839
self.naive_session = self.get_naive_session()
840+
875841
return self._internal_request(self.naive_session, url, method, **kwargs)
876842

877843
def oauth_request(self, url, method, **kwargs):
@@ -889,10 +855,15 @@ def oauth_request(self, url, method, **kwargs):
889855

890856
try:
891857
return self._internal_request(self.session, url, method, **kwargs)
892-
except TokenExpiredError:
858+
except TokenExpiredError as e:
893859
# refresh and try again the request!
894-
self.refresh_token()
895-
return self._internal_request(self.session, url, method, **kwargs)
860+
try:
861+
if self.refresh_token():
862+
return self._internal_request(self.session, url, method, **kwargs)
863+
else:
864+
raise e
865+
finally:
866+
self._token_expired_flag = False
896867

897868
def get(self, url, params=None, **kwargs):
898869
""" Shorthand for self.oauth_request(url, 'get')

pyproject.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[project]
2+
name = "o365"
3+
version = "2.0.38"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.9"
7+
dependencies = [
8+
"beautifulsoup4>=4.12.3",
9+
"msal>=1.31.1",
10+
"python-dateutil>=2.9.0.post0",
11+
"requests>=2.32.3",
12+
"tzdata>=2024.2",
13+
"tzlocal>=5.2",
14+
]

requirements-dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
requests>=2.31.0
2-
requests-oauthlib>=2.0.0
2+
msal>=1.31.1
33
python-dateutil>=2.7
44
tzlocal>=5.0
55
beautifulsoup4>=4.0.0

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
requests-oauthlib>=2.0.0
1+
msal>=1.31.1
22
requests>=2.32.0
33
python-dateutil>=2.7
44
tzlocal>=5.0

setup.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
'Programming Language :: Python :: 3.10',
1919
'Programming Language :: Python :: 3.11',
2020
'Programming Language :: Python :: 3.12',
21+
'Programming Language :: Python :: 3.13',
2122
'Operating System :: OS Independent',
2223
]
2324

@@ -29,17 +30,16 @@ def read(fname):
2930

3031
requires = [
3132
'requests>=2.32.0',
32-
'requests-oauthlib>=2.0.0',
33+
'msal>=1.31.1',
3334
'python-dateutil>=2.7',
3435
'tzlocal>=5.0',
3536
'beautifulsoup4>=4.0.0',
3637
'tzdata>=2023.4'
3738
]
3839

3940
setup(
40-
name='O365',
41+
name='o365',
4142
version=VERSION,
42-
# packages=['O365', 'O365.utils'],
4343
packages=find_packages(),
4444
url='https://github.com/O365/python-o365',
4545
license='Apache License 2.0',

0 commit comments

Comments
 (0)