Skip to content

Commit 95d8eda

Browse files
chore(ci): drop support for Python 3.7 (#3638)
* Python 3.7 deprecation + cleaning code with unnecessary compats * Ops, missing a file * Ops, missing a file * Reverting a potential breaking change * Refactoring data class to cache some properties * Importing from typing_extensions directly * Reverting * Addressing Heitor's feedback --------- Co-authored-by: Heitor Lessa <[email protected]>
1 parent 190e9a9 commit 95d8eda

39 files changed

+67
-436
lines changed

Diff for: .github/ISSUE_TEMPLATE/bug_report.yml

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ body:
5858
attributes:
5959
label: AWS Lambda function runtime
6060
options:
61-
- "3.7"
6261
- "3.8"
6362
- "3.9"
6463
- "3.10"

Diff for: .github/ISSUE_TEMPLATE/static_typing.yml

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ body:
2525
attributes:
2626
label: AWS Lambda function runtime
2727
options:
28-
- "3.7"
2928
- "3.8"
3029
- "3.9"
3130
- "3.10"

Diff for: .github/workflows/quality_check.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
strategy:
4545
max-parallel: 4
4646
matrix:
47-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
47+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
4848
env:
4949
PYTHON: "${{ matrix.python-version }}"
5050
permissions:

Diff for: .github/workflows/quality_check_pydanticv2.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
strategy:
4444
max-parallel: 4
4545
matrix:
46-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
46+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
4747
env:
4848
PYTHON: "${{ matrix.python-version }}"
4949
permissions:

Diff for: .github/workflows/run-e2e-tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
strategy:
4848
fail-fast: false # needed so if a version fails, the others will still be able to complete and cleanup
4949
matrix:
50-
version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
50+
version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
5151
if: ${{ github.actor != 'dependabot[bot]' && github.repository == 'aws-powertools/powertools-lambda-python' }}
5252
steps:
5353
- name: "Checkout"

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
[![Build](https://github.com/aws-powertools/powertools-lambda-python/actions/workflows/quality_check.yml/badge.svg)](https://github.com/aws-powertools/powertools-lambda-python/actions/workflows/python_build.yml)
55
[![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-python/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-python)
6-
![PythonSupport](https://img.shields.io/static/v1?label=python&message=%203.7|%203.8|%203.9|%203.10|%203.11|%203.12&color=blue?style=flat-square&logo=python) ![PyPI version](https://badge.fury.io/py/aws-lambda-powertools.svg) ![PyPi monthly downloads](https://img.shields.io/pypi/dm/aws-lambda-powertools) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python/badge)](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python) [![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET)](https://discord.gg/B8zZKbbyET)
6+
![PythonSupport](https://img.shields.io/static/v1?label=python&message=%203.8|%203.9|%203.10|%203.11|%203.12&color=blue?style=flat-square&logo=python) ![PyPI version](https://badge.fury.io/py/aws-lambda-powertools.svg) ![PyPi monthly downloads](https://img.shields.io/pypi/dm/aws-lambda-powertools) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python/badge)](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-python) [![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET)](https://discord.gg/B8zZKbbyET)
77

88
Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.powertools.aws.dev/lambda/python/latest/#features).
99

Diff for: aws_lambda_powertools/logging/compat.py

-51
This file was deleted.

Diff for: aws_lambda_powertools/logging/logger.py

-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
overload,
2323
)
2424

25-
from aws_lambda_powertools.logging import compat
2625
from aws_lambda_powertools.shared import constants
2726
from aws_lambda_powertools.shared.functions import (
2827
extract_event_from_common_models,
@@ -302,9 +301,6 @@ def _init_logger(
302301
self.addHandler(self.logger_handler)
303302
self.structure_logs(formatter_options=formatter_options, **kwargs)
304303

305-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
306-
self._logger.findCaller = compat.findCaller # type: ignore[method-assign]
307-
308304
# Pytest Live Log feature duplicates log records for colored output
309305
# but we explicitly add a filter for log deduplication.
310306
# This flag disables this protection when you explicit want logs to be duplicated (#262)
@@ -467,9 +463,6 @@ def info(
467463
extra = extra or {}
468464
extra = {**extra, **kwargs}
469465

470-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
471-
if sys.version_info < (3, 8): # pragma: no cover
472-
return self._logger.info(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
473466
return self._logger.info(
474467
msg,
475468
*args,
@@ -492,9 +485,6 @@ def error(
492485
extra = extra or {}
493486
extra = {**extra, **kwargs}
494487

495-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
496-
if sys.version_info < (3, 8): # pragma: no cover
497-
return self._logger.error(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
498488
return self._logger.error(
499489
msg,
500490
*args,
@@ -517,9 +507,6 @@ def exception(
517507
extra = extra or {}
518508
extra = {**extra, **kwargs}
519509

520-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
521-
if sys.version_info < (3, 8): # pragma: no cover
522-
return self._logger.exception(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
523510
return self._logger.exception(
524511
msg,
525512
*args,
@@ -542,9 +529,6 @@ def critical(
542529
extra = extra or {}
543530
extra = {**extra, **kwargs}
544531

545-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
546-
if sys.version_info < (3, 8): # pragma: no cover
547-
return self._logger.critical(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
548532
return self._logger.critical(
549533
msg,
550534
*args,
@@ -567,9 +551,6 @@ def warning(
567551
extra = extra or {}
568552
extra = {**extra, **kwargs}
569553

570-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
571-
if sys.version_info < (3, 8): # pragma: no cover
572-
return self._logger.warning(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
573554
return self._logger.warning(
574555
msg,
575556
*args,
@@ -592,9 +573,6 @@ def debug(
592573
extra = extra or {}
593574
extra = {**extra, **kwargs}
594575

595-
# Maintenance: We can drop this upon Py3.7 EOL. It's a backport for "location" key to work
596-
if sys.version_info < (3, 8): # pragma: no cover
597-
return self._logger.debug(msg, *args, exc_info=exc_info, stack_info=stack_info, extra=extra)
598576
return self._logger.debug(
599577
msg,
600578
*args,

Diff for: aws_lambda_powertools/shared/types.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import sys
2-
from typing import Any, Callable, Dict, List, TypeVar, Union
3-
4-
if sys.version_info >= (3, 8):
5-
from typing import Literal, Protocol, TypedDict
6-
else:
7-
from typing_extensions import Literal, Protocol, TypedDict
2+
from typing import Any, Callable, Dict, List, Literal, Protocol, TypedDict, TypeVar, Union
83

94
if sys.version_info >= (3, 9):
105
from typing import Annotated
@@ -16,7 +11,6 @@
1611
else:
1712
from typing_extensions import NotRequired
1813

19-
2014
# Even though `get_args` and `get_origin` were added in Python 3.8, they only handle Annotated correctly on 3.10.
2115
# So for python < 3.10 we use the backport from typing_extensions.
2216
if sys.version_info >= (3, 10):

Diff for: aws_lambda_powertools/utilities/data_classes/active_mq_event.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from functools import cached_property
12
from typing import Any, Dict, Iterator, Optional
23

34
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
@@ -23,12 +24,9 @@ def decoded_data(self) -> str:
2324
"""Decodes the data as a str"""
2425
return base64_decode(self.data)
2526

26-
@property
27+
@cached_property
2728
def json_data(self) -> Any:
28-
"""Parses the data as json"""
29-
if self._json_data is None:
30-
self._json_data = self._json_deserializer(self.decoded_data)
31-
return self._json_data
29+
return self._json_deserializer(self.decoded_data)
3230

3331
@property
3432
def connection_id(self) -> str:

Diff for: aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import tempfile
22
import zipfile
3+
from functools import cached_property
34
from typing import Any, Dict, List, Optional
45
from urllib.parse import unquote_plus
56

@@ -17,12 +18,13 @@ def user_parameters(self) -> Optional[str]:
1718
"""User parameters"""
1819
return self.get("UserParameters", None)
1920

20-
@property
21+
@cached_property
2122
def decoded_user_parameters(self) -> Optional[Dict[str, Any]]:
2223
"""Json Decoded user parameters"""
23-
if self._json_data is None and self.user_parameters is not None:
24-
self._json_data = self._json_deserializer(self.user_parameters)
25-
return self._json_data
24+
if self.user_parameters is not None:
25+
return self._json_deserializer(self.user_parameters)
26+
27+
return None
2628

2729

2830
class CodePipelineActionConfiguration(DictWrapper):

Diff for: aws_lambda_powertools/utilities/data_classes/common.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import base64
22
import json
33
from collections.abc import Mapping
4+
from functools import cached_property
45
from typing import Any, Callable, Dict, Iterator, List, Optional, overload
56

67
from aws_lambda_powertools.shared.headers_serializer import BaseHeadersSerializer
@@ -24,7 +25,6 @@ def __init__(self, data: Dict[str, Any], json_deserializer: Optional[Callable] =
2425
by default json.loads
2526
"""
2627
self._data = data
27-
self._json_data: Optional[Any] = None
2828
self._json_deserializer = json_deserializer or json.loads
2929

3030
def __getitem__(self, key: str) -> Any:
@@ -138,14 +138,12 @@ def body(self) -> Optional[str]:
138138
"""Submitted body of the request as a string"""
139139
return self.get("body")
140140

141-
@property
141+
@cached_property
142142
def json_body(self) -> Any:
143143
"""Parses the submitted body as json"""
144-
if self._json_data is None:
145-
self._json_data = self._json_deserializer(self.decoded_body)
146-
return self._json_data
144+
return self._json_deserializer(self.decoded_body)
147145

148-
@property
146+
@cached_property
149147
def decoded_body(self) -> str:
150148
"""Dynamically base64 decode body as a str"""
151149
body: str = self["body"]

Diff for: aws_lambda_powertools/utilities/data_classes/kafka_event.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
from functools import cached_property
23
from typing import Any, Dict, Iterator, List, Optional
34

45
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
@@ -53,12 +54,10 @@ def decoded_value(self) -> bytes:
5354
"""Decodes the base64 encoded value as bytes."""
5455
return base64.b64decode(self.value)
5556

56-
@property
57+
@cached_property
5758
def json_value(self) -> Any:
5859
"""Decodes the text encoded data as JSON."""
59-
if self._json_data is None:
60-
self._json_data = self._json_deserializer(self.decoded_value.decode("utf-8"))
61-
return self._json_data
60+
return self._json_deserializer(self.decoded_value.decode("utf-8"))
6261

6362
@property
6463
def headers(self) -> List[Dict[str, List[int]]]:

Diff for: aws_lambda_powertools/utilities/data_classes/kinesis_firehose_event.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import warnings
44
from dataclasses import dataclass, field
5+
from functools import cached_property
56
from typing import Any, Callable, ClassVar, Dict, Iterator, List, Optional, Tuple
67

78
from typing_extensions import Literal
@@ -70,7 +71,6 @@ class KinesisFirehoseDataTransformationRecord:
7071
metadata: Optional[KinesisFirehoseDataTransformationRecordMetadata] = None
7172
json_serializer: Callable = json.dumps
7273
json_deserializer: Callable = json.loads
73-
_json_data: Optional[Any] = None
7474

7575
def asdict(self) -> Dict:
7676
if self.result not in self._valid_result_types:
@@ -102,14 +102,13 @@ def data_as_text(self) -> str:
102102
return ""
103103
return self.data_as_bytes.decode("utf-8")
104104

105-
@property
105+
@cached_property
106106
def data_as_json(self) -> Dict:
107107
"""Decoded base64-encoded data loaded to json"""
108108
if not self.data:
109109
return {}
110-
if self._json_data is None:
111-
self._json_data = self.json_deserializer(self.data_as_text)
112-
return self._json_data
110+
111+
return self.json_deserializer(self.data_as_text)
113112

114113

115114
@dataclass(repr=False, order=False)
@@ -240,12 +239,10 @@ def data_as_text(self) -> str:
240239
"""Decoded base64-encoded data as text"""
241240
return self.data_as_bytes.decode("utf-8")
242241

243-
@property
242+
@cached_property
244243
def data_as_json(self) -> dict:
245244
"""Decoded base64-encoded data loaded to json"""
246-
if self._json_data is None:
247-
self._json_data = self._json_deserializer(self.data_as_text)
248-
return self._json_data
245+
return self._json_deserializer(self.data_as_text)
249246

250247
def build_data_transformation_response(
251248
self,

Diff for: aws_lambda_powertools/utilities/data_classes/rabbit_mq_event.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from functools import cached_property
12
from typing import Any, Dict, List
23

34
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
@@ -84,12 +85,10 @@ def decoded_data(self) -> str:
8485
"""Decodes the data as a str"""
8586
return base64_decode(self.data)
8687

87-
@property
88+
@cached_property
8889
def json_data(self) -> Any:
8990
"""Parses the data as json"""
90-
if self._json_data is None:
91-
self._json_data = self._json_deserializer(self.decoded_data)
92-
return self._json_data
91+
return self._json_deserializer(self.decoded_data)
9392

9493

9594
class RabbitMQEvent(DictWrapper):

Diff for: aws_lambda_powertools/utilities/data_classes/sqs_event.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from functools import cached_property
12
from typing import Any, Dict, Iterator, Optional, Type, TypeVar
23

34
from aws_lambda_powertools.utilities.data_classes import S3Event
@@ -107,7 +108,7 @@ def body(self) -> str:
107108
"""The message's contents (not URL-encoded)."""
108109
return self["body"]
109110

110-
@property
111+
@cached_property
111112
def json_body(self) -> Any:
112113
"""Deserializes JSON string available in 'body' property
113114
@@ -132,9 +133,7 @@ def json_body(self) -> Any:
132133
data: list = record.json_body # ["telemetry_values"]
133134
```
134135
"""
135-
if self._json_data is None:
136-
self._json_data = self._json_deserializer(self["body"])
137-
return self._json_data
136+
return self._json_deserializer(self["body"])
138137

139138
@property
140139
def attributes(self) -> SQSRecordAttributes:

0 commit comments

Comments
 (0)