diff --git a/README.md b/README.md index 29303fd4..8d2ebe39 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,10 @@ requests, code review feedback, and also pull requests. ## Supported Python Versions -We currently support Python 3.9+. However, Python 3.9 support is deprecated, -and developers are strongly advised to use Python 3.10 or higher. Firebase -Admin Python SDK is also tested on PyPy and +We currently support Python 3.9+. However, Python 3.9 and 3.10 support is +deprecated and will be dropped in an upcoming minor release. Developers are +strongly advised to use Python 3.11 or higher. Firebase Admin Python SDK is +also tested on PyPy and [Google App Engine](https://cloud.google.com/appengine/) environments. diff --git a/firebase_admin/__init__.py b/firebase_admin/__init__.py index 8c9f628e..1b93f3c3 100644 --- a/firebase_admin/__init__.py +++ b/firebase_admin/__init__.py @@ -21,9 +21,14 @@ from google.auth.credentials import Credentials as GoogleAuthCredentials from google.auth.exceptions import DefaultCredentialsError from firebase_admin import credentials +from firebase_admin import _python_version_support from firebase_admin.__about__ import __version__ +# Perform Python version check and emit a support warning if needed. +_python_version_support.check_python_version() + + _apps = {} _apps_lock = threading.RLock() _clock = datetime.datetime.utcnow diff --git a/firebase_admin/_python_version_support.py b/firebase_admin/_python_version_support.py new file mode 100644 index 00000000..cfeae5c7 --- /dev/null +++ b/firebase_admin/_python_version_support.py @@ -0,0 +1,40 @@ +# Copyright 2026 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Runtime Python version deprecation check for the Firebase Admin SDK.""" + +import sys +import warnings + +# Minimum Python (major, minor) version that does not emit a deprecation warning. +# Bump this tuple in a minor release to deprecate an older Python version ahead +# of dropping it in a future release. +_MIN_NON_DEPRECATED_VERSION = (3, 11) + + +def check_python_version() -> None: + """Emits a FutureWarning if the running Python version is deprecated.""" + current = (sys.version_info.major, sys.version_info.minor) + if current < _MIN_NON_DEPRECATED_VERSION: + current_str = f'{current[0]}.{current[1]}' + recommended_str = ( + f'{_MIN_NON_DEPRECATED_VERSION[0]}.{_MIN_NON_DEPRECATED_VERSION[1]}' + ) + warnings.warn( + f'Support for Python {current_str} in firebase-admin is deprecated ' + 'and will be dropped in a future minor release. Please upgrade to ' + f'Python {recommended_str} or higher to continue receiving updates.', + FutureWarning, + stacklevel=2, + ) diff --git a/tests/test_python_version_support.py b/tests/test_python_version_support.py new file mode 100644 index 00000000..05ed4c04 --- /dev/null +++ b/tests/test_python_version_support.py @@ -0,0 +1,56 @@ +# Copyright 2026 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for firebase_admin._python_version_support.""" + +from collections import namedtuple +from unittest.mock import patch +import warnings + +import pytest + +from firebase_admin._python_version_support import check_python_version + +_VersionInfo = namedtuple('_VersionInfo', ['major', 'minor']) + + +@pytest.mark.parametrize('version_tuple', [(3, 9), (3, 10)]) +def test_deprecated_python_versions_emit_future_warning(version_tuple): + mock_version = _VersionInfo(major=version_tuple[0], minor=version_tuple[1]) + version_str = f'{version_tuple[0]}.{version_tuple[1]}' + + with patch('firebase_admin._python_version_support.sys.version_info', mock_version): + with pytest.warns(FutureWarning) as record: + check_python_version() + + assert len(record) == 1 + assert record[0].filename == __file__ + expected = ( + f'Support for Python {version_str} in firebase-admin is deprecated ' + 'and will be dropped in a future minor release. Please upgrade to ' + 'Python 3.11 or higher to continue receiving updates.' + ) + assert str(record[0].message) == expected + + +@pytest.mark.parametrize('version_tuple', [(3, 11), (3, 12), (3, 13), (3, 14), (3, 15)]) +def test_supported_python_versions_do_not_warn(version_tuple): + mock_version = _VersionInfo(major=version_tuple[0], minor=version_tuple[1]) + + with patch('firebase_admin._python_version_support.sys.version_info', mock_version): + with warnings.catch_warnings(record=True) as recorded: + warnings.simplefilter('always') + check_python_version() + + assert not recorded