Remove Python 3.7 support (#7185)

This commit is contained in:
Bert Blommers 2024-01-06 19:20:57 +00:00
parent 79af51c538
commit 2fd5e800e4
17 changed files with 29 additions and 59 deletions

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: 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" ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -46,7 +46,7 @@ jobs:
- name: Start MotoServer - name: Start MotoServer
run: | run: |
python -m build python -m build
docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:3.7-buster /moto/scripts/ci_moto_server.sh & docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:3.9-slim /moto/scripts/ci_moto_server.sh &
python scripts/ci_wait_for_server.py python scripts/ci_wait_for_server.py
- name: Test ServerMode/Coverage - name: Test ServerMode/Coverage
env: env:

View File

@ -35,7 +35,7 @@ jobs:
run: | run: |
pip install PyYAML build pip install PyYAML build
python -m build python -m build
docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e MOTO_PORT=4566 -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock python:3.7-buster /moto/scripts/ci_moto_server.sh & docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e MOTO_PORT=4566 -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock python:3.9-slim /moto/scripts/ci_moto_server.sh &
MOTO_PORT=4566 python scripts/ci_wait_for_server.py MOTO_PORT=4566 python scripts/ci_wait_for_server.py
- name: Get list of tests for this service - name: Get list of tests for this service
id: get-list id: get-list

View File

@ -7,7 +7,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: 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"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -12,7 +12,7 @@ jobs:
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "3.8" python-version: "3.10"
- name: Start MotoServer - name: Start MotoServer
run: | run: |
pip install build pip install build

View File

@ -7,7 +7,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: 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"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -1,7 +1,7 @@
codecov: codecov:
notify: notify:
# Leave a GitHub comment after all builds have passed # Leave a GitHub comment after all builds have passed
after_n_builds: 12 after_n_builds: 10
coverage: coverage:
status: status:
project: project:

View File

@ -1578,10 +1578,6 @@ class APIGatewayBackend(BaseBackend):
validate(api_doc) # type: ignore[arg-type] validate(api_doc) # type: ignore[arg-type]
except OpenAPIValidationError as e: except OpenAPIValidationError as e:
raise InvalidOpenAPIDocumentException(e) raise InvalidOpenAPIDocumentException(e)
except AttributeError:
# Call can fail in Python3.7 due to `typing_extensions 4.6.0` throwing an error
# Easiest to just ignore this for now - Py3.7 is EOL soon anyway
pass
name = api_doc["info"]["title"] name = api_doc["info"]["title"]
description = api_doc["info"]["description"] description = api_doc["info"]["description"]
api = self.create_rest_api(name=name, description=description) api = self.create_rest_api(name=name, description=description)
@ -1653,10 +1649,6 @@ class APIGatewayBackend(BaseBackend):
validate(api_doc) # type: ignore[arg-type] validate(api_doc) # type: ignore[arg-type]
except OpenAPIValidationError as e: except OpenAPIValidationError as e:
raise InvalidOpenAPIDocumentException(e) raise InvalidOpenAPIDocumentException(e)
except AttributeError:
# Call can fail in Python3.7 due to `typing_extensions 4.6.0` throwing an error
# Easiest to just ignore this for now - Py3.7 is EOL soon anyway
pass
if mode == "overwrite": if mode == "overwrite":
api = self.get_rest_api(function_id) api = self.get_rest_api(function_id)

View File

@ -17,7 +17,7 @@ from collections import defaultdict
from datetime import datetime from datetime import datetime
from gzip import GzipFile from gzip import GzipFile
from sys import platform from sys import platform
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Tuple, TypedDict, Union
import requests.exceptions import requests.exceptions
@ -64,6 +64,11 @@ from .utils import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class LayerDataType(TypedDict):
Arn: str
CodeSize: int
def zip2tar(zip_bytes: bytes) -> io.BytesIO: def zip2tar(zip_bytes: bytes) -> io.BytesIO:
tarstream = io.BytesIO() tarstream = io.BytesIO()
timeshift = int((datetime.now() - utcnow()).total_seconds()) timeshift = int((datetime.now() - utcnow()).total_seconds())
@ -176,7 +181,7 @@ class _DockerDataVolumeLayerContext:
def __init__(self, lambda_func: "LambdaFunction"): def __init__(self, lambda_func: "LambdaFunction"):
self._lambda_func = lambda_func self._lambda_func = lambda_func
self._layers: List[Dict[str, str]] = self._lambda_func.layers self._layers: List[LayerDataType] = self._lambda_func.layers
self._vol_ref: Optional[_VolumeRefCount] = None self._vol_ref: Optional[_VolumeRefCount] = None
@property @property
@ -623,9 +628,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
self.package_type = spec.get("PackageType", "Zip") self.package_type = spec.get("PackageType", "Zip")
self.publish = spec.get("Publish", False) # this is ignored currently self.publish = spec.get("Publish", False) # this is ignored currently
self.timeout = spec.get("Timeout", 3) self.timeout = spec.get("Timeout", 3)
self.layers: List[Dict[str, str]] = self._get_layers_data( self.layers: List[LayerDataType] = self._get_layers_data(spec.get("Layers", []))
spec.get("Layers", [])
)
self.signing_profile_version_arn = spec.get("SigningProfileVersionArn") self.signing_profile_version_arn = spec.get("SigningProfileVersionArn")
self.signing_job_arn = spec.get("SigningJobArn") self.signing_job_arn = spec.get("SigningJobArn")
self.code_signing_config_arn = spec.get("CodeSigningConfigArn") self.code_signing_config_arn = spec.get("CodeSigningConfigArn")
@ -720,7 +723,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def __repr__(self) -> str: def __repr__(self) -> str:
return json.dumps(self.get_configuration()) return json.dumps(self.get_configuration())
def _get_layers_data(self, layers_versions_arns: List[str]) -> List[Dict[str, str]]: def _get_layers_data(self, layers_versions_arns: List[str]) -> List[LayerDataType]:
backend = lambda_backends[self.account_id][self.region] backend = lambda_backends[self.account_id][self.region]
layer_versions = [ layer_versions = [
backend.layers_versions_by_arn(layer_version) backend.layers_versions_by_arn(layer_version)
@ -730,9 +733,9 @@ class LambdaFunction(CloudFormationModel, DockerModel):
raise UnknownLayerVersionException(layers_versions_arns) raise UnknownLayerVersionException(layers_versions_arns)
# The `if lv` part is not necessary - we know there are no None's, because of the `all()`-check earlier # The `if lv` part is not necessary - we know there are no None's, because of the `all()`-check earlier
# But MyPy does not seem to understand this # But MyPy does not seem to understand this
# The `type: ignore` is because `code_size` is an int, and we're returning Dict[str, str] return [
# We should convert the return-type into a TypedDict the moment we drop Py3.7 support {"Arn": lv.arn, "CodeSize": lv.code_size} for lv in layer_versions if lv
return [{"Arn": lv.arn, "CodeSize": lv.code_size} for lv in layer_versions if lv] # type: ignore ]
def get_code_signing_config(self) -> Dict[str, Any]: def get_code_signing_config(self) -> Dict[str, Any]:
return { return {

View File

@ -122,12 +122,7 @@ class BaseMockAWS(ContextManager["BaseMockAWS"]):
if self.__class__.nested_count == 0: if self.__class__.nested_count == 0:
if self.__class__.mocks_active: if self.__class__.mocks_active:
try: self.default_session_mock.stop()
self.default_session_mock.stop()
except RuntimeError:
# We only need to check for this exception in Python 3.7
# https://bugs.python.org/issue36366
pass
self.unmock_env_variables() self.unmock_env_variables()
self.__class__.mocks_active = False self.__class__.mocks_active = False
if remove_data: if remove_data:

View File

@ -1,13 +1,8 @@
import sys import sys
from importlib.metadata import version
from moto.utilities.distutils_version import LooseVersion from moto.utilities.distutils_version import LooseVersion
try:
from importlib.metadata import version
except ImportError:
from importlib_metadata import version # type: ignore[no-redef]
PYTHON_VERSION_INFO = sys.version_info PYTHON_VERSION_INFO = sys.version_info
PYTHON_311 = sys.version_info >= (3, 11) PYTHON_311 = sys.version_info >= (3, 11)
RESPONSES_VERSION = version("responses") RESPONSES_VERSION = version("responses")

View File

@ -133,8 +133,6 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
and v in vl and v in vl
) )
# Any in this case means: str | Optional[str]
# When we drop Python 3.7 we should look into replacing this with a TypedDict
def tag_filter(tag_list: List[Dict[str, Any]]) -> bool: def tag_filter(tag_list: List[Dict[str, Any]]) -> bool:
result = [] result = []
if tag_filters: if tag_filters:

View File

@ -6,8 +6,7 @@ click
inflection inflection
lxml lxml
mypy mypy
typing-extensions<=4.5.0; python_version < '3.8' typing-extensions
typing-extensions; python_version >= '3.8'
packaging packaging
build build
prompt_toolkit prompt_toolkit

View File

@ -10,7 +10,6 @@ license = Apache License 2.0
test_suite = tests test_suite = tests
classifiers = classifiers =
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
@ -25,7 +24,7 @@ project_urls =
Changelog = https://github.com/getmoto/moto/blob/master/CHANGELOG.md Changelog = https://github.com/getmoto/moto/blob/master/CHANGELOG.md
[options] [options]
python_requires = >=3.7 python_requires = >=3.8
install_requires = install_requires =
boto3>=1.9.201 boto3>=1.9.201
botocore>=1.12.201 botocore>=1.12.201
@ -36,7 +35,6 @@ install_requires =
python-dateutil<3.0.0,>=2.1 python-dateutil<3.0.0,>=2.1
responses>=0.13.0 responses>=0.13.0
Jinja2>=2.10.1 Jinja2>=2.10.1
importlib_metadata ; python_version < '3.8'
package_dir = package_dir =
moto = moto moto = moto
include_package_data = True include_package_data = True

View File

@ -1,12 +1,10 @@
import os import os
import sys
from unittest import SkipTest
import boto3 import boto3
import pytest import pytest
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from moto import mock_apigateway, settings from moto import mock_apigateway
@mock_apigateway @mock_apigateway
@ -47,9 +45,6 @@ def test_import_rest_api__nested_api():
@mock_apigateway @mock_apigateway
def test_import_rest_api__invalid_api_creates_nothing(): def test_import_rest_api__invalid_api_creates_nothing():
if sys.version_info < (3, 8) or settings.TEST_SERVER_MODE:
raise SkipTest("openapi-module throws an error in Py3.7")
client = boto3.client("apigateway", region_name="us-west-2") client = boto3.client("apigateway", region_name="us-west-2")
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))

View File

@ -1,12 +1,10 @@
import os import os
import sys
from unittest import SkipTest
import boto3 import boto3
import pytest import pytest
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from moto import mock_apigateway, settings from moto import mock_apigateway
@mock_apigateway @mock_apigateway
@ -145,9 +143,6 @@ def test_put_rest_api__existing_methods_still_exist():
@mock_apigateway @mock_apigateway
def test_put_rest_api__fail_on_invalid_spec(): def test_put_rest_api__fail_on_invalid_spec():
if sys.version_info < (3, 8) or settings.TEST_SERVER_MODE:
raise SkipTest("openapi-module throws an error in Py3.7")
client = boto3.client("apigateway", region_name="us-east-2") client = boto3.client("apigateway", region_name="us-east-2")
response = client.create_rest_api(name="my_api", description="this is my api") response = client.create_rest_api(name="my_api", description="this is my api")

View File

@ -85,7 +85,7 @@ def test_lambda_can_be_updated_by_cloudformation():
# Verify function has been created # Verify function has been created
created_fn = lmbda.get_function(FunctionName=created_fn_name) created_fn = lmbda.get_function(FunctionName=created_fn_name)
assert created_fn["Configuration"]["Handler"] == "lambda_function.lambda_handler1" assert created_fn["Configuration"]["Handler"] == "lambda_function.lambda_handler1"
assert created_fn["Configuration"]["Runtime"] == "python3.7" assert created_fn["Configuration"]["Runtime"] == "python3.9"
assert "/test1.zip" in created_fn["Code"]["Location"] assert "/test1.zip" in created_fn["Code"]["Location"]
# Update CF stack # Update CF stack
cf.update_stack(StackName=stack_name, TemplateBody=body2) cf.update_stack(StackName=stack_name, TemplateBody=body2)
@ -97,7 +97,7 @@ def test_lambda_can_be_updated_by_cloudformation():
== created_fn["Configuration"]["FunctionArn"] == created_fn["Configuration"]["FunctionArn"]
) )
assert updated_fn["Configuration"]["Handler"] == "lambda_function.lambda_handler2" assert updated_fn["Configuration"]["Handler"] == "lambda_function.lambda_handler2"
assert updated_fn["Configuration"]["Runtime"] == "python3.8" assert updated_fn["Configuration"]["Runtime"] == "python3.10"
assert "/test2.zip" in updated_fn["Code"]["Location"] assert "/test2.zip" in updated_fn["Code"]["Location"]
@ -310,8 +310,8 @@ def create_stack(cf, s3):
s3.create_bucket(Bucket=bucket_name) s3.create_bucket(Bucket=bucket_name)
s3.put_object(Bucket=bucket_name, Key="test1.zip", Body=get_zip_file()) s3.put_object(Bucket=bucket_name, Key="test1.zip", Body=get_zip_file())
s3.put_object(Bucket=bucket_name, Key="test2.zip", Body=get_zip_file()) s3.put_object(Bucket=bucket_name, Key="test2.zip", Body=get_zip_file())
body1 = get_template(bucket_name, "1", "python3.7") body1 = get_template(bucket_name, "1", "python3.9")
body2 = get_template(bucket_name, "2", "python3.8") body2 = get_template(bucket_name, "2", "python3.10")
stack = cf.create_stack(StackName=stack_name, TemplateBody=body1) stack = cf.create_stack(StackName=stack_name, TemplateBody=body1)
return body2, stack return body2, stack