Skip to content

Commit dae267c

Browse files
authored
chore(deps): Bump minimum supported Python version to 3.9 and add 3.13 to CIs (#892)
* chore(deps): Bump minimum supported Python version to 3.9 and add 3.13 to CIs * fix deprecation warnings * fix GHA build status svg * fix: Correctly scope async eventloop * fix: Bump pylint to v2.7.4 and astroid to v2.5.8 to fix lint issues * fix ml tests * fix lint * fix: remove commented code
1 parent b57af18 commit dae267c

35 files changed

+119
-120
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
fail-fast: false
1010
matrix:
11-
python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.9']
11+
python: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy3.9']
1212

1313
steps:
1414
- uses: actions/checkout@v4
@@ -35,10 +35,10 @@ jobs:
3535
runs-on: ubuntu-latest
3636
steps:
3737
- uses: actions/checkout@v4
38-
- name: Set up Python 3.8
38+
- name: Set up Python 3.9
3939
uses: actions/setup-python@v5
4040
with:
41-
python-version: 3.8
41+
python-version: 3.9
4242
- name: Install dependencies
4343
run: |
4444
python -m pip install --upgrade pip

.github/workflows/nightly.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Set up Python
3737
uses: actions/setup-python@v5
3838
with:
39-
python-version: 3.8
39+
python-version: 3.9
4040

4141
- name: Install dependencies
4242
run: |
@@ -45,6 +45,7 @@ jobs:
4545
pip install setuptools wheel
4646
pip install tensorflow
4747
pip install keras
48+
pip install build
4849
4950
- name: Run unit tests
5051
run: pytest
@@ -57,7 +58,7 @@ jobs:
5758

5859
# Build the Python Wheel and the source distribution.
5960
- name: Package release artifacts
60-
run: python setup.py bdist_wheel sdist
61+
run: python -m build
6162

6263
# Attach the packaged artifacts to the workflow output. These can be manually
6364
# downloaded for later inspection if necessary.

.github/workflows/release.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
- name: Set up Python
4848
uses: actions/setup-python@v5
4949
with:
50-
python-version: 3.8
50+
python-version: 3.9
5151

5252
- name: Install dependencies
5353
run: |
@@ -56,6 +56,7 @@ jobs:
5656
pip install setuptools wheel
5757
pip install tensorflow
5858
pip install keras
59+
pip install build
5960
6061
- name: Run unit tests
6162
run: pytest
@@ -68,7 +69,7 @@ jobs:
6869

6970
# Build the Python Wheel and the source distribution.
7071
- name: Package release artifacts
71-
run: python setup.py bdist_wheel sdist
72+
run: python -m build
7273

7374
# Attach the packaged artifacts to the workflow output. These can be manually
7475
# downloaded for later inspection if necessary.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ information on using pull requests.
8585

8686
### Initial Setup
8787

88-
You need Python 3.8+ to build and test the code in this repo.
88+
You need Python 3.9+ to build and test the code in this repo.
8989

9090
We recommend using [pip](https://pypi.python.org/pypi/pip) for installing the necessary tools and
9191
project dependencies. Most recent versions of Python ship with pip. If your development environment

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Build Status](https://travis-ci.org/firebase/firebase-admin-python.svg?branch=master)](https://travis-ci.org/firebase/firebase-admin-python)
1+
[![Nightly Builds](https://github.com/firebase/firebase-admin-python/actions/workflows/nightly.yml/badge.svg)](https://github.com/firebase/firebase-admin-python/actions/workflows/nightly.yml)
22
[![Python](https://img.shields.io/pypi/pyversions/firebase-admin.svg)](https://pypi.org/project/firebase-admin/)
33
[![Version](https://img.shields.io/pypi/v/firebase-admin.svg)](https://pypi.org/project/firebase-admin/)
44

@@ -43,8 +43,8 @@ requests, code review feedback, and also pull requests.
4343

4444
## Supported Python Versions
4545

46-
We currently support Python 3.7+. However, Python 3.7 and Python 3.8 support is deprecated,
47-
and developers are strongly advised to use Python 3.9 or higher. Firebase
46+
We currently support Python 3.9+. However, Python 3.9 support is deprecated,
47+
and developers are strongly advised to use Python 3.10 or higher. Firebase
4848
Admin Python SDK is also tested on PyPy and
4949
[Google App Engine](https://cloud.google.com/appengine/) environments.
5050

firebase_admin/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,12 @@ def _load_from_environment(self):
178178
with open(config_file, 'r') as json_file:
179179
json_str = json_file.read()
180180
except Exception as err:
181-
raise ValueError('Unable to read file {}. {}'.format(config_file, err))
181+
raise ValueError('Unable to read file {}. {}'.format(config_file, err)) from err
182182
try:
183183
json_data = json.loads(json_str)
184184
except Exception as err:
185-
raise ValueError('JSON string "{0}" is not valid json. {1}'.format(json_str, err))
185+
raise ValueError(
186+
'JSON string "{0}" is not valid json. {1}'.format(json_str, err)) from err
186187
return {k: v for k, v in json_data.items() if k in _CONFIG_VALID_KEYS}
187188

188189

firebase_admin/_auth_providers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,13 +422,13 @@ def _validate_url(url, label):
422422
if not parsed.netloc:
423423
raise ValueError('Malformed {0}: "{1}".'.format(label, url))
424424
return url
425-
except Exception:
426-
raise ValueError('Malformed {0}: "{1}".'.format(label, url))
425+
except Exception as exception:
426+
raise ValueError('Malformed {0}: "{1}".'.format(label, url)) from exception
427427

428428

429429
def _validate_x509_certificates(x509_certificates):
430430
if not isinstance(x509_certificates, list) or not x509_certificates:
431431
raise ValueError('x509_certificates must be a non-empty list.')
432-
if not all([isinstance(cert, str) and cert for cert in x509_certificates]):
432+
if not all(isinstance(cert, str) and cert for cert in x509_certificates):
433433
raise ValueError('x509_certificates must only contain non-empty strings.')
434434
return [{'x509Certificate': cert} for cert in x509_certificates]

firebase_admin/_auth_utils.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ def validate_photo_url(photo_url, required=False):
175175
if not parsed.netloc:
176176
raise ValueError('Malformed photo URL: "{0}".'.format(photo_url))
177177
return photo_url
178-
except Exception:
179-
raise ValueError('Malformed photo URL: "{0}".'.format(photo_url))
178+
except Exception as err:
179+
raise ValueError('Malformed photo URL: "{0}".'.format(photo_url)) from err
180180

181181
def validate_timestamp(timestamp, label, required=False):
182182
"""Validates the given timestamp value. Timestamps must be positive integers."""
@@ -186,8 +186,8 @@ def validate_timestamp(timestamp, label, required=False):
186186
raise ValueError('Boolean value specified as timestamp.')
187187
try:
188188
timestamp_int = int(timestamp)
189-
except TypeError:
190-
raise ValueError('Invalid type for timestamp value: {0}.'.format(timestamp))
189+
except TypeError as err:
190+
raise ValueError('Invalid type for timestamp value: {0}.'.format(timestamp)) from err
191191
else:
192192
if timestamp_int != timestamp:
193193
raise ValueError('{0} must be a numeric value and a whole number.'.format(label))
@@ -207,8 +207,8 @@ def validate_int(value, label, low=None, high=None):
207207
raise ValueError('Invalid type for integer value: {0}.'.format(value))
208208
try:
209209
val_int = int(value)
210-
except TypeError:
211-
raise ValueError('Invalid type for integer value: {0}.'.format(value))
210+
except TypeError as err:
211+
raise ValueError('Invalid type for integer value: {0}.'.format(value)) from err
212212
else:
213213
if val_int != value:
214214
# This will be True for non-numeric values like '2' and non-whole numbers like 2.5.
@@ -246,8 +246,8 @@ def validate_custom_claims(custom_claims, required=False):
246246
MAX_CLAIMS_PAYLOAD_SIZE))
247247
try:
248248
parsed = json.loads(claims_str)
249-
except Exception:
250-
raise ValueError('Failed to parse custom claims string as JSON.')
249+
except Exception as err:
250+
raise ValueError('Failed to parse custom claims string as JSON.') from err
251251

252252
if not isinstance(parsed, dict):
253253
raise ValueError('Custom claims must be parseable as a JSON object.')

firebase_admin/_sseclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class KeepAuthSession(transport.requests.AuthorizedSession):
3434
"""A session that does not drop authentication on redirects between domains."""
3535

3636
def __init__(self, credential):
37-
super(KeepAuthSession, self).__init__(credential)
37+
super().__init__(credential)
3838

3939
def rebuild_auth(self, prepared_request, response):
4040
pass

firebase_admin/_token_gen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def signing_provider(self):
158158
'Failed to determine service account: {0}. Make sure to initialize the SDK '
159159
'with service account credentials or specify a service account ID with '
160160
'iam.serviceAccounts.signBlob permission. Please refer to {1} for more '
161-
'details on creating custom tokens.'.format(error, url))
161+
'details on creating custom tokens.'.format(error, url)) from error
162162
return self._signing_provider
163163

164164
def create_custom_token(self, uid, developer_claims=None, tenant_id=None):
@@ -203,7 +203,7 @@ def create_custom_token(self, uid, developer_claims=None, tenant_id=None):
203203
return jwt.encode(signing_provider.signer, payload, header=header)
204204
except google.auth.exceptions.TransportError as error:
205205
msg = 'Failed to sign custom token. {0}'.format(error)
206-
raise TokenSignError(msg, error)
206+
raise TokenSignError(msg, error) from error
207207

208208

209209
def create_session_cookie(self, id_token, expires_in):
@@ -403,7 +403,7 @@ def verify(self, token, request, clock_skew_seconds=0):
403403
verified_claims['uid'] = verified_claims['sub']
404404
return verified_claims
405405
except google.auth.exceptions.TransportError as error:
406-
raise CertificateFetchError(str(error), cause=error)
406+
raise CertificateFetchError(str(error), cause=error) from error
407407
except ValueError as error:
408408
if 'Token expired' in str(error):
409409
raise self._expired_token_error(str(error), cause=error)

firebase_admin/_user_import.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,10 @@ def provider_data(self):
216216
def provider_data(self, provider_data):
217217
if provider_data is not None:
218218
try:
219-
if any([not isinstance(p, UserProvider) for p in provider_data]):
219+
if any(not isinstance(p, UserProvider) for p in provider_data):
220220
raise ValueError('One or more provider data instances are invalid.')
221-
except TypeError:
222-
raise ValueError('provider_data must be iterable.')
221+
except TypeError as err:
222+
raise ValueError('provider_data must be iterable.') from err
223223
self._provider_data = provider_data
224224

225225
@property

firebase_admin/_user_mgt.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class UserRecord(UserInfo):
128128
"""Contains metadata associated with a Firebase user account."""
129129

130130
def __init__(self, data):
131-
super(UserRecord, self).__init__()
131+
super().__init__()
132132
if not isinstance(data, dict):
133133
raise ValueError('Invalid data argument: {0}. Must be a dictionary.'.format(data))
134134
if not data.get('localId'):
@@ -452,7 +452,7 @@ class ProviderUserInfo(UserInfo):
452452
"""Contains metadata regarding how a user is known by a particular identity provider."""
453453

454454
def __init__(self, data):
455-
super(ProviderUserInfo, self).__init__()
455+
super().__init__()
456456
if not isinstance(data, dict):
457457
raise ValueError('Invalid data argument: {0}. Must be a dictionary.'.format(data))
458458
if not data.get('rawId'):
@@ -518,8 +518,8 @@ def encode_action_code_settings(settings):
518518
if not parsed.netloc:
519519
raise ValueError('Malformed dynamic action links url: "{0}".'.format(settings.url))
520520
parameters['continueUrl'] = settings.url
521-
except Exception:
522-
raise ValueError('Malformed dynamic action links url: "{0}".'.format(settings.url))
521+
except Exception as err:
522+
raise ValueError('Malformed dynamic action links url: "{0}".'.format(settings.url)) from err
523523

524524
# handle_code_in_app
525525
if settings.handle_code_in_app is not None:
@@ -788,13 +788,13 @@ def import_users(self, users, hash_alg=None):
788788
raise ValueError(
789789
'Users must be a non-empty list with no more than {0} elements.'.format(
790790
MAX_IMPORT_USERS_SIZE))
791-
if any([not isinstance(u, _user_import.ImportUserRecord) for u in users]):
791+
if any(not isinstance(u, _user_import.ImportUserRecord) for u in users):
792792
raise ValueError('One or more user objects are invalid.')
793-
except TypeError:
794-
raise ValueError('users must be iterable')
793+
except TypeError as err:
794+
raise ValueError('users must be iterable') from err
795795

796796
payload = {'users': [u.to_dict() for u in users]}
797-
if any(['passwordHash' in u for u in payload['users']]):
797+
if any('passwordHash' in u for u in payload['users']):
798798
if not isinstance(hash_alg, _user_import.UserImportHash):
799799
raise ValueError('A UserImportHash is required to import users with passwords.')
800800
payload.update(hash_alg.to_dict())

firebase_admin/app_check.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def verify_token(self, token: str) -> Dict[str, Any]:
8484
except (InvalidTokenError, DecodeError) as exception:
8585
raise ValueError(
8686
f'Verifying App Check token failed. Error: {exception}'
87-
)
87+
) from exception
8888

8989
verified_claims['app_id'] = verified_claims.get('sub')
9090
return verified_claims
@@ -112,28 +112,28 @@ def _decode_and_verify(self, token: str, signing_key: str):
112112
algorithms=["RS256"],
113113
audience=self._scoped_project_id
114114
)
115-
except InvalidSignatureError:
115+
except InvalidSignatureError as exception:
116116
raise ValueError(
117117
'The provided App Check token has an invalid signature.'
118-
)
119-
except InvalidAudienceError:
118+
) from exception
119+
except InvalidAudienceError as exception:
120120
raise ValueError(
121121
'The provided App Check token has an incorrect "aud" (audience) claim. '
122122
f'Expected payload to include {self._scoped_project_id}.'
123-
)
124-
except InvalidIssuerError:
123+
) from exception
124+
except InvalidIssuerError as exception:
125125
raise ValueError(
126126
'The provided App Check token has an incorrect "iss" (issuer) claim. '
127127
f'Expected claim to include {self._APP_CHECK_ISSUER}'
128-
)
129-
except ExpiredSignatureError:
128+
) from exception
129+
except ExpiredSignatureError as exception:
130130
raise ValueError(
131131
'The provided App Check token has expired.'
132-
)
132+
) from exception
133133
except InvalidTokenError as exception:
134134
raise ValueError(
135135
f'Decoding App Check token failed. Error: {exception}'
136-
)
136+
) from exception
137137

138138
audience = payload.get('aud')
139139
if not isinstance(audience, list) or self._scoped_project_id not in audience:

firebase_admin/credentials.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class _ExternalCredentials(Base):
6363
"""A wrapper for google.auth.credentials.Credentials typed credential instances"""
6464

6565
def __init__(self, credential: GoogleAuthCredentials):
66-
super(_ExternalCredentials, self).__init__()
66+
super().__init__()
6767
self._g_credential = credential
6868

6969
def get_credential(self):
@@ -92,7 +92,7 @@ def __init__(self, cert):
9292
IOError: If the specified certificate file doesn't exist or cannot be read.
9393
ValueError: If the specified certificate is invalid.
9494
"""
95-
super(Certificate, self).__init__()
95+
super().__init__()
9696
if _is_file_path(cert):
9797
with open(cert) as json_file:
9898
json_data = json.load(json_file)
@@ -111,7 +111,7 @@ def __init__(self, cert):
111111
json_data, scopes=_scopes)
112112
except ValueError as error:
113113
raise ValueError('Failed to initialize a certificate credential. '
114-
'Caused by: "{0}"'.format(error))
114+
'Caused by: "{0}"'.format(error)) from error
115115

116116
@property
117117
def project_id(self):
@@ -142,7 +142,7 @@ def __init__(self):
142142
The credentials will be lazily initialized when get_credential() or
143143
project_id() is called. See those methods for possible errors raised.
144144
"""
145-
super(ApplicationDefault, self).__init__()
145+
super().__init__()
146146
self._g_credential = None # Will be lazily-loaded via _load_credential().
147147

148148
def get_credential(self):
@@ -193,7 +193,7 @@ def __init__(self, refresh_token):
193193
IOError: If the specified file doesn't exist or cannot be read.
194194
ValueError: If the refresh token configuration is invalid.
195195
"""
196-
super(RefreshToken, self).__init__()
196+
super().__init__()
197197
if _is_file_path(refresh_token):
198198
with open(refresh_token) as json_file:
199199
json_data = json.load(json_file)

firebase_admin/db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ def request(self, method, url, **kwargs):
926926
kwargs['params'] = query
927927

928928
try:
929-
return super(_Client, self).request(method, url, **kwargs)
929+
return super().request(method, url, **kwargs)
930930
except requests.exceptions.RequestException as error:
931931
raise _Client.handle_rtdb_error(error)
932932

0 commit comments

Comments
 (0)