From f9f47ec65b8e8bddccb4bef50329e1376bd64f93 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Mon, 9 Dec 2024 07:13:37 -0800 Subject: [PATCH] Drop support for Python 3.7 PiperOrigin-RevId: 704275105 --- .github/workflows/test.yml | 8 +--- absl/flags/_defines.py | 39 ++++++++-------- absl/logging/tests/logging_functional_test.py | 13 +----- absl/testing/absltest.py | 4 +- absl/testing/tests/absltest_filtering_test.py | 37 ++------------- absl/testing/tests/absltest_test.py | 2 - absl/testing/tests/parameterized_test.py | 45 ++++++++----------- setup.py | 7 ++- smoke_tests/smoke_test.sh | 7 +-- 9 files changed, 49 insertions(+), 113 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0005d0da..41bab2e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,14 +19,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest, macos-latest, windows-latest] - exclude: - - python-version: "3.7" - os: macos-latest # arm64 - include: - - python-version: "3.7" - os: macos-13 # x64 steps: - uses: actions/checkout@v4 diff --git a/absl/flags/_defines.py b/absl/flags/_defines.py index e9faa8e4..7e4e4e7a 100644 --- a/absl/flags/_defines.py +++ b/absl/flags/_defines.py @@ -20,8 +20,7 @@ import enum import sys import types -import typing -from typing import Any, Iterable, List, Optional, Type, TypeVar, Union, overload +from typing import Any, Iterable, List, Literal, Optional, Type, TypeVar, Union, overload from absl.flags import _argument_parser from absl.flags import _exceptions @@ -65,7 +64,7 @@ def DEFINE( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., serializer: Optional[_argument_parser.ArgumentSerializer[_T]] = ..., module_name: Optional[str] = ..., - required: 'typing.Literal[True]' = ..., + required: Literal[True] = ..., **args: Any ) -> _flagvalues.FlagHolder[_T]: ... @@ -134,7 +133,7 @@ def DEFINE_flag( # pylint: disable=invalid-name flag: _flag.Flag[_T], flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., - required: 'typing.Literal[True]' = ..., + required: Literal[True] = ..., ) -> _flagvalues.FlagHolder[_T]: ... @@ -374,7 +373,7 @@ def DEFINE_string( # pylint: disable=invalid-name help: Optional[str], # pylint: disable=redefined-builtin flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[str]: ... @@ -435,7 +434,7 @@ def DEFINE_boolean( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[bool]: ... @@ -518,7 +517,7 @@ def DEFINE_float( # pylint: disable=invalid-name upper_bound: Optional[float] = ..., flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[float]: ... @@ -607,7 +606,7 @@ def DEFINE_integer( # pylint: disable=invalid-name upper_bound: Optional[int] = ..., flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[int]: ... @@ -696,7 +695,7 @@ def DEFINE_enum( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[str]: ... @@ -781,7 +780,7 @@ def DEFINE_enum_class( # pylint: disable=invalid-name module_name: Optional[str] = ..., case_sensitive: bool = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[_ET]: ... @@ -867,7 +866,7 @@ def DEFINE_list( # pylint: disable=invalid-name help: str, # pylint: disable=redefined-builtin flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[str]]: ... @@ -945,7 +944,7 @@ def DEFINE_spaceseplist( # pylint: disable=invalid-name comma_compat: bool = ..., flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[str]]: ... @@ -1032,7 +1031,7 @@ def DEFINE_multi( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[_T]]: ... @@ -1048,7 +1047,7 @@ def DEFINE_multi( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[_T]]: ... @@ -1156,7 +1155,7 @@ def DEFINE_multi_string( # pylint: disable=invalid-name help: str, # pylint: disable=redefined-builtin flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[str]]: ... @@ -1240,7 +1239,7 @@ def DEFINE_multi_integer( # pylint: disable=invalid-name upper_bound: Optional[int] = ..., flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[int]]: ... @@ -1331,7 +1330,7 @@ def DEFINE_multi_float( # pylint: disable=invalid-name upper_bound: Optional[float] = ..., flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[float]]: ... @@ -1421,7 +1420,7 @@ def DEFINE_multi_enum( # pylint: disable=invalid-name help: str, # pylint: disable=redefined-builtin flag_values: _flagvalues.FlagValues = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[str]]: ... @@ -1515,7 +1514,7 @@ def DEFINE_multi_enum_class( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[_ET]]: ... @@ -1530,7 +1529,7 @@ def DEFINE_multi_enum_class( # pylint: disable=invalid-name flag_values: _flagvalues.FlagValues = ..., module_name: Optional[str] = ..., *, - required: 'typing.Literal[True]', + required: Literal[True], **args: Any ) -> _flagvalues.FlagHolder[List[_ET]]: ... diff --git a/absl/logging/tests/logging_functional_test.py b/absl/logging/tests/logging_functional_test.py index 76fc898e..816cc1fe 100644 --- a/absl/logging/tests/logging_functional_test.py +++ b/absl/logging/tests/logging_functional_test.py @@ -113,15 +113,6 @@ W0000 23:59:59.000000 12345 logging_functional_test_helper.py:82] Warn 4 (every 3) """ -if sys.version_info[0:2] == (3, 4): - _FAKE_ERROR_EXTRA_MESSAGE = """\ -Traceback (most recent call last): - File "logging_functional_test_helper.py", line 456, in _test_do_logging - raise OSError('Fake Error') -""" -else: - _FAKE_ERROR_EXTRA_MESSAGE = '' - _PY_ERROR_LOG_MESSAGE = """\ E1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is VLOG level -2 E1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is log level -2 @@ -147,13 +138,13 @@ raise OSError('Fake Error') OSError: Fake Error E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] No traceback -{fake_error_extra}OSError: Fake Error +OSError: Fake Error E1231 23:59:59.000000 12345 logging_functional_test_helper.py:90] Alarming Stuff E0000 23:59:59.000000 12345 logging_functional_test_helper.py:92] Error first 1 of 2 E0000 23:59:59.000000 12345 logging_functional_test_helper.py:93] Error 1 (every 3) E0000 23:59:59.000000 12345 logging_functional_test_helper.py:92] Error first 2 of 2 E0000 23:59:59.000000 12345 logging_functional_test_helper.py:93] Error 4 (every 3) -""".format(fake_error_extra=_FAKE_ERROR_EXTRA_MESSAGE) +""" _CRITICAL_DOWNGRADE_TO_ERROR_MESSAGE = """\ diff --git a/absl/testing/absltest.py b/absl/testing/absltest.py index c8cf9c54..26a033bf 100644 --- a/absl/testing/absltest.py +++ b/absl/testing/absltest.py @@ -2432,9 +2432,7 @@ def _setup_filtering(argv: MutableSequence[str]) -> bool: if argv is None or not test_filter: return False - filters = shlex.split(test_filter) - if sys.version_info[:2] >= (3, 7): - filters = ['-k=' + test_filter for test_filter in filters] + filters = ['-k=' + test_filter for test_filter in shlex.split(test_filter)] argv[1:1] = filters return True diff --git a/absl/testing/tests/absltest_filtering_test.py b/absl/testing/tests/absltest_filtering_test.py index c4e0ea6c..a0987454 100644 --- a/absl/testing/tests/absltest_filtering_test.py +++ b/absl/testing/tests/absltest_filtering_test.py @@ -60,12 +60,9 @@ def _run_filtered(self, test_filter, use_env_variable, use_app_run): if use_env_variable: env['TESTBRIDGE_TEST_ONLY'] = test_filter elif test_filter: - if sys.version_info[:2] >= (3, 7): - # The -k flags are passed as positional arguments to absl.flags. - additional_args.append('--') - additional_args.extend(['-k=' + f for f in test_filter.split(' ')]) - else: - additional_args.extend(test_filter.split(' ')) + # The -k flags are passed as positional arguments to absl.flags. + additional_args.append('--') + additional_args.extend(['-k=' + f for f in test_filter.split(' ')]) proc = subprocess.Popen( args=([_bazelize_command.get_executable_path(self._test_name)] + @@ -117,9 +114,6 @@ def test_multiple_class_and_method_filter(self, use_env_variable, self.assertIn('class B test C', out) self.assertNotIn('class B test A', out) - @absltest.skipIf( - sys.version_info[:2] < (3, 7), - 'Only Python 3.7+ does glob and substring matching.') def test_substring(self, use_env_variable, use_app_run): out, exit_code = self._run_filtered( 'testA', use_env_variable, use_app_run) @@ -128,9 +122,6 @@ def test_substring(self, use_env_variable, use_app_run): self.assertIn('ClassA.testA', out) self.assertIn('ClassB.testA', out) - @absltest.skipIf( - sys.version_info[:2] < (3, 7), - 'Only Python 3.7+ does glob and substring matching.') def test_glob_pattern(self, use_env_variable, use_app_run): out, exit_code = self._run_filtered( '__main__.Class*.testA', use_env_variable, use_app_run) @@ -139,20 +130,6 @@ def test_glob_pattern(self, use_env_variable, use_app_run): self.assertIn('ClassA.testA', out) self.assertIn('ClassB.testA', out) - @absltest.skipIf( - sys.version_info[:2] >= (3, 7), - "Python 3.7+ uses unittest's -k flag and doesn't fail if no tests match.") - def test_not_found_filters_py36(self, use_env_variable, use_app_run): - out, exit_code = self._run_filtered('NotExistedClass.not_existed_method', - use_env_variable, use_app_run) - self.assertEqual(1, exit_code) - self.assertIn("has no attribute 'NotExistedClass'", out) - - @absltest.skipIf( - sys.version_info[:2] < (3, 7), - 'Python 3.6 passes the filter as positional arguments and fails if no ' - 'tests match.' - ) def test_not_found_filters_py37(self, use_env_variable, use_app_run): out, exit_code = self._run_filtered('NotExistedClass.not_existed_method', use_env_variable, use_app_run) @@ -164,10 +141,6 @@ def test_not_found_filters_py37(self, use_env_variable, use_app_run): self.assertEqual(0, exit_code) self.assertIn('Ran 0 tests', out) - @absltest.skipIf( - sys.version_info[:2] < (3, 7), - 'Python 3.6 passes the filter as positional arguments and matches by name' - ) def test_parameterized_unnamed(self, use_env_variable, use_app_run): out, exit_code = self._run_filtered('ParameterizedTest.test_unnamed', use_env_variable, use_app_run) @@ -176,10 +149,6 @@ def test_parameterized_unnamed(self, use_env_variable, use_app_run): self.assertIn('parameterized unnamed 1', out) self.assertIn('parameterized unnamed 2', out) - @absltest.skipIf( - sys.version_info[:2] < (3, 7), - 'Python 3.6 passes the filter as positional arguments and matches by name' - ) def test_parameterized_named(self, use_env_variable, use_app_run): out, exit_code = self._run_filtered('ParameterizedTest.test_named', use_env_variable, use_app_run) diff --git a/absl/testing/tests/absltest_test.py b/absl/testing/tests/absltest_test.py index 20cb54b7..dbaa62a7 100644 --- a/absl/testing/tests/absltest_test.py +++ b/absl/testing/tests/absltest_test.py @@ -1649,8 +1649,6 @@ def test_enter_context(self): self.assertEqual(self.cm_state, 'yielded') -@absltest.skipIf(not hasattr(absltest.TestCase, 'addClassCleanup'), - 'Python 3.8 required for class-level enter_context') class EnterContextClassmethodTest(absltest.TestCase): cm_state = 'unset' diff --git a/absl/testing/tests/parameterized_test.py b/absl/testing/tests/parameterized_test.py index 211f7f6a..609c5571 100644 --- a/absl/testing/tests/parameterized_test.py +++ b/absl/testing/tests/parameterized_test.py @@ -372,11 +372,6 @@ def test_name(self, name): class SubclassTestCase(SuperclassTestCase): pass - @unittest.skipIf( - (sys.version_info[:2] == (3, 7) and sys.version_info[2] in {0, 1, 2}), - 'Python 3.7.0 to 3.7.2 have a bug that breaks this test, see ' - 'https://bugs.python.org/issue35767', - ) def test_missing_inheritance(self): ts = unittest.defaultTestLoader.loadTestsFromTestCase( self.BadAdditionParams @@ -1054,33 +1049,31 @@ def test_subclass_inherits_superclass_test_params_reprs(self): ) -# IsolatedAsyncioTestCase is only available in Python 3.8+. -if sys.version_info[:2] >= (3, 8): +async def mult(x: float, y: float) -> float: + return x * y - async def mult(x: float, y: float) -> float: - return x * y - class AsyncTest(unittest.IsolatedAsyncioTestCase, parameterized.TestCase): +class AsyncTest(unittest.IsolatedAsyncioTestCase, parameterized.TestCase): - def setUp(self): - super().setUp() - self.verify_ran = False + def setUp(self): + super().setUp() + self.verify_ran = False - def tearDown(self): - super().tearDown() + def tearDown(self): + super().tearDown() - # We need the additional check here because originally the test function - # would run, but a coroutine results from the execution and is never - # awaited, so it looked like a successful test run when in fact the - # internal test logic never executed. If you remove the check for - # coroutine and run_until_complete, then set the parameters to fail they - # never will. - self.assertTrue(self.verify_ran) + # We need the additional check here because originally the test function + # would run, but a coroutine results from the execution and is never + # awaited, so it looked like a successful test run when in fact the + # internal test logic never executed. If you remove the check for + # coroutine and run_until_complete, then set the parameters to fail they + # never will. + self.assertTrue(self.verify_ran) - @parameterized.parameters((1, 2, 2), (2, 2, 4), (3, 2, 6)) - async def test_multiply_expected_matches_actual(self, x, y, expected): - self.assertEqual(await mult(x, y), expected) - self.verify_ran = True + @parameterized.parameters((1, 2, 2), (2, 2, 4), (3, 2, 6)) + async def test_multiply_expected_matches_actual(self, x, y, expected): + self.assertEqual(await mult(x, y), expected) + self.verify_ran = True def _decorate_with_side_effects(func, self): diff --git a/setup.py b/setup.py index ec5258aa..e899e83d 100644 --- a/setup.py +++ b/setup.py @@ -26,8 +26,8 @@ import setuptools # pylint: enable=g-import-not-at-top -if sys.version_info < (3, 7): - raise RuntimeError('Python version 3.7+ is required.') +if sys.version_info < (3, 8): + raise RuntimeError('Python version 3.8+ is required.') setuptools_version = tuple( int(x) for x in setuptools.__version__.split('.')[:2]) @@ -36,7 +36,7 @@ if setuptools_version >= (24, 2): # `python_requires` was added in 24.2, see # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires - additional_kwargs['python_requires'] = '>=3.7' + additional_kwargs['python_requires'] = '>=3.8' _README_PATH = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'README.md') @@ -67,7 +67,6 @@ classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', diff --git a/smoke_tests/smoke_test.sh b/smoke_tests/smoke_test.sh index 99307a40..247b5dbd 100755 --- a/smoke_tests/smoke_test.sh +++ b/smoke_tests/smoke_test.sh @@ -56,12 +56,7 @@ trap 'deactivate' EXIT # When running macOS <= 10.12, pip 9.0.3 is required to connect to PyPI. # So we need to manually use the latest pip to install absl-py. See: # https://mail.python.org/pipermail/distutils-sig/2018-April/032114.html -if [[ "$(python -c "import sys; print(sys.version_info.major, sys.version_info.minor)")" == "3 6" ]]; then - # Latest get-pip.py no longer supports Python 3.6. - curl https://bootstrap.pypa.io/pip/3.6/get-pip.py | python -else - curl https://bootstrap.pypa.io/get-pip.py | python -fi +curl https://bootstrap.pypa.io/get-pip.py | python pip --version python --version