Skip to content

Commit 723effc

Browse files
authored
docs(examples): enforce and fix all mypy errors (#1393)
1 parent a0ddd46 commit 723effc

26 files changed

+230
-158
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ changelog:
9999
docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog > CHANGELOG.md
100100

101101
mypy:
102-
poetry run mypy --pretty aws_lambda_powertools
102+
poetry run mypy --pretty aws_lambda_powertools examples

aws_lambda_powertools/event_handler/__init__.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22
Event handler decorators for common Lambda events
33
"""
44

5-
from .api_gateway import ALBResolver, APIGatewayHttpResolver, ApiGatewayResolver, APIGatewayRestResolver, CORSConfig, Response
5+
from .api_gateway import (
6+
ALBResolver,
7+
APIGatewayHttpResolver,
8+
ApiGatewayResolver,
9+
APIGatewayRestResolver,
10+
CORSConfig,
11+
Response,
12+
)
613
from .appsync import AppSyncResolver
714

815
__all__ = [

aws_lambda_powertools/event_handler/appsync.py

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def lambda_handler(event, context):
142142
ValueError
143143
If we could not find a field resolver
144144
"""
145+
# Maintenance: revisit generics/overload to fix [attr-defined] in mypy usage
145146
BaseRouter.current_event = data_model(event)
146147
BaseRouter.lambda_context = context
147148
resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name)

aws_lambda_powertools/tracing/base.py

+59-59
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,34 @@
22
import numbers
33
import traceback
44
from contextlib import contextmanager
5-
from typing import Any, AsyncContextManager, ContextManager, List, NoReturn, Optional, Set, Union
5+
from typing import Any, Generator, List, NoReturn, Optional, Sequence, Union
66

77

8-
class BaseProvider(abc.ABC):
9-
@abc.abstractmethod # type: ignore
10-
@contextmanager
11-
def in_subsegment(self, name=None, **kwargs) -> ContextManager:
12-
"""Return a subsegment context manger.
8+
class BaseSegment(abc.ABC):
9+
"""Holds common properties and methods on segment and subsegment."""
10+
11+
@abc.abstractmethod
12+
def close(self, end_time: Optional[int] = None):
13+
"""Close the trace entity by setting `end_time`
14+
and flip the in progress flag to False.
1315
1416
Parameters
1517
----------
16-
name: str
17-
Subsegment name
18-
kwargs: Optional[dict]
19-
Optional parameters to be propagated to segment
18+
end_time: int
19+
Time in epoch seconds, by default current time will be used.
2020
"""
2121

22-
@abc.abstractmethod # type: ignore
23-
@contextmanager
24-
def in_subsegment_async(self, name=None, **kwargs) -> AsyncContextManager:
25-
"""Return a subsegment async context manger.
22+
@abc.abstractmethod
23+
def add_subsegment(self, subsegment: Any):
24+
"""Add input subsegment as a child subsegment."""
2625

27-
Parameters
28-
----------
29-
name: str
30-
Subsegment name
31-
kwargs: Optional[dict]
32-
Optional parameters to be propagated to segment
33-
"""
26+
@abc.abstractmethod
27+
def remove_subsegment(self, subsegment: Any):
28+
"""Remove input subsegment from child subsegments."""
3429

3530
@abc.abstractmethod
3631
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> NoReturn:
37-
"""Annotate current active trace entity with a key-value pair.
32+
"""Annotate segment or subsegment with a key-value pair.
3833
3934
Note: Annotations will be indexed for later search query.
4035
@@ -48,9 +43,8 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N
4843

4944
@abc.abstractmethod
5045
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoReturn:
51-
"""Add metadata to the current active trace entity.
52-
53-
Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API.
46+
"""Add metadata to segment or subsegment. Metadata is not indexed
47+
but can be later retrieved by BatchGetTraces API.
5448
5549
Parameters
5650
----------
@@ -63,45 +57,52 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoRe
6357
"""
6458

6559
@abc.abstractmethod
66-
def patch(self, modules: Set[str]) -> NoReturn:
67-
"""Instrument a set of supported libraries
60+
def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False):
61+
"""Add an exception to trace entities.
6862
6963
Parameters
7064
----------
71-
modules: Set[str]
72-
Set of modules to be patched
73-
"""
74-
75-
@abc.abstractmethod
76-
def patch_all(self) -> NoReturn:
77-
"""Instrument all supported libraries"""
65+
exception: Exception
66+
Caught exception
67+
stack: List[traceback.StackSummary]
68+
List of traceback summaries
7869
70+
Output from `traceback.extract_stack()`.
71+
remote: bool
72+
Whether it's a client error (False) or downstream service error (True), by default False
73+
"""
7974

80-
class BaseSegment(abc.ABC):
81-
"""Holds common properties and methods on segment and subsegment."""
8275

76+
class BaseProvider(abc.ABC):
8377
@abc.abstractmethod
84-
def close(self, end_time: Optional[int] = None):
85-
"""Close the trace entity by setting `end_time`
86-
and flip the in progress flag to False.
78+
@contextmanager
79+
def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
80+
"""Return a subsegment context manger.
8781
8882
Parameters
8983
----------
90-
end_time: int
91-
Time in epoch seconds, by default current time will be used.
84+
name: str
85+
Subsegment name
86+
kwargs: Optional[dict]
87+
Optional parameters to be propagated to segment
9288
"""
9389

9490
@abc.abstractmethod
95-
def add_subsegment(self, subsegment: Any):
96-
"""Add input subsegment as a child subsegment."""
91+
@contextmanager
92+
def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
93+
"""Return a subsegment async context manger.
9794
98-
@abc.abstractmethod
99-
def remove_subsegment(self, subsegment: Any):
100-
"""Remove input subsegment from child subsegments."""
95+
Parameters
96+
----------
97+
name: str
98+
Subsegment name
99+
kwargs: Optional[dict]
100+
Optional parameters to be propagated to segment
101+
"""
101102

102103
@abc.abstractmethod
103104
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> NoReturn:
104-
"""Annotate segment or subsegment with a key-value pair.
105+
"""Annotate current active trace entity with a key-value pair.
105106
106107
Note: Annotations will be indexed for later search query.
107108
@@ -115,8 +116,9 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N
115116

116117
@abc.abstractmethod
117118
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoReturn:
118-
"""Add metadata to segment or subsegment. Metadata is not indexed
119-
but can be later retrieved by BatchGetTraces API.
119+
"""Add metadata to the current active trace entity.
120+
121+
Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API.
120122
121123
Parameters
122124
----------
@@ -129,17 +131,15 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoRe
129131
"""
130132

131133
@abc.abstractmethod
132-
def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False):
133-
"""Add an exception to trace entities.
134+
def patch(self, modules: Sequence[str]) -> NoReturn:
135+
"""Instrument a set of supported libraries
134136
135137
Parameters
136138
----------
137-
exception: Exception
138-
Caught exception
139-
stack: List[traceback.StackSummary]
140-
List of traceback summaries
141-
142-
Output from `traceback.extract_stack()`.
143-
remote: bool
144-
Whether it's a client error (False) or downstream service error (True), by default False
139+
modules: Set[str]
140+
Set of modules to be patched
145141
"""
142+
143+
@abc.abstractmethod
144+
def patch_all(self) -> NoReturn:
145+
"""Instrument all supported libraries"""

aws_lambda_powertools/tracing/tracer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def __init__(
155155
self.__build_config(
156156
service=service, disabled=disabled, auto_patch=auto_patch, patch_modules=patch_modules, provider=provider
157157
)
158-
self.provider = self._config["provider"]
158+
self.provider: BaseProvider = self._config["provider"]
159159
self.disabled = self._config["disabled"]
160160
self.service = self._config["service"]
161161
self.auto_patch = self._config["auto_patch"]

docs/core/event_handler/api_gateway.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ You can use **`not_found`** decorator to override this behavior, and return a cu
201201

202202
You can use **`exception_handler`** decorator with any Python exception. This allows you to handle a common exception outside your route, for example validation errors.
203203

204-
```python hl_lines="14 15" title="Exception handling"
204+
```python hl_lines="13-14" title="Exception handling"
205205
--8<-- "examples/event_handler_rest/src/exception_handling.py"
206206
```
207207

docs/core/event_handler/appsync.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Here's an example where we have two separate functions to resolve `getTodo` and
5858

5959
=== "getting_started_graphql_api_resolver.py"
6060

61-
```python hl_lines="7 13 23 25-26 35 37 48"
61+
```python hl_lines="14 20 30 32-33 42 44 55"
6262
--8<-- "examples/event_handler_graphql/src/getting_started_graphql_api_resolver.py"
6363
```
6464

@@ -112,7 +112,7 @@ You can nest `app.resolver()` decorator multiple times when resolving fields wit
112112

113113
=== "nested_mappings.py"
114114

115-
```python hl_lines="4 10 20-21 23 30"
115+
```python hl_lines="11 17 27-28 28 30 37"
116116
--8<-- "examples/event_handler_graphql/src/nested_mappings.py"
117117
```
118118

@@ -126,7 +126,7 @@ You can nest `app.resolver()` decorator multiple times when resolving fields wit
126126

127127
For Lambda Python3.8+ runtime, this utility supports async functions when you use in conjunction with `asyncio.run`.
128128

129-
```python hl_lines="7 14 24-25 34 36" title="Resolving GraphQL resolvers async"
129+
```python hl_lines="14 21 31-32 41 43" title="Resolving GraphQL resolvers async"
130130
--8<-- "examples/event_handler_graphql/src/async_resolvers.py"
131131
```
132132

@@ -151,13 +151,13 @@ Use the following code for `merchantInfo` and `searchMerchant` functions respect
151151

152152
=== "graphql_transformer_merchant_info.py"
153153

154-
```python hl_lines="4 6 22-23 27-28 36"
154+
```python hl_lines="11 13 29-30 34-35 43"
155155
--8<-- "examples/event_handler_graphql/src/graphql_transformer_merchant_info.py"
156156
```
157157

158158
=== "graphql_transformer_search_merchant.py"
159159

160-
```python hl_lines="4 6 21-22 36 42"
160+
```python hl_lines="11 13 28-29 43 49"
161161
--8<-- "examples/event_handler_graphql/src/graphql_transformer_search_merchant.py"
162162
```
163163

@@ -185,7 +185,7 @@ You can subclass [AppSyncResolverEvent](../../utilities/data_classes.md#appsync-
185185

186186
=== "custom_models.py.py"
187187

188-
```python hl_lines="4 7 23-25 28-29 36 43"
188+
```python hl_lines="11 14 30-32 35-36 43 50"
189189
--8<-- "examples/event_handler_graphql/src/custom_models.py"
190190
```
191191

@@ -214,7 +214,7 @@ Let's assume you have `split_operation.py` as your Lambda function entrypoint an
214214

215215
We import **Router** instead of **AppSyncResolver**; syntax wise is exactly the same.
216216

217-
```python hl_lines="4 8 18-19"
217+
```python hl_lines="11 15 25-26"
218218
--8<-- "examples/event_handler_graphql/src/split_operation_module.py"
219219
```
220220

@@ -242,7 +242,7 @@ Here's an example of how you can test your synchronous resolvers:
242242

243243
=== "assert_graphql_response_module.py"
244244

245-
```python hl_lines="10"
245+
```python hl_lines="17"
246246
--8<-- "examples/event_handler_graphql/src/assert_graphql_response_module.py"
247247
```
248248

@@ -259,13 +259,13 @@ And an example for testing asynchronous resolvers. Note that this requires the `
259259

260260
=== "assert_async_graphql_response.py"
261261

262-
```python hl_lines="27"
262+
```python hl_lines="28"
263263
--8<-- "examples/event_handler_graphql/src/assert_async_graphql_response.py"
264264
```
265265

266266
=== "assert_async_graphql_response_module.py"
267267

268-
```python hl_lines="14"
268+
```python hl_lines="21"
269269
--8<-- "examples/event_handler_graphql/src/assert_async_graphql_response_module.py"
270270
```
271271

examples/event_handler_graphql/src/assert_async_graphql_response.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import json
22
from dataclasses import dataclass
33
from pathlib import Path
4+
from typing import List
45

56
import pytest
6-
from assert_async_graphql_response_module import Location, app # instance of AppSyncResolver
7+
from assert_async_graphql_response_module import Todo, app # instance of AppSyncResolver
78

89

910
@pytest.fixture
@@ -24,7 +25,7 @@ async def test_async_direct_resolver(lambda_context):
2425
fake_event = json.loads(Path("assert_async_graphql_response.json").read_text())
2526

2627
# WHEN
27-
result: list[Location] = await app(fake_event, lambda_context)
28+
result: List[Todo] = await app(fake_event, lambda_context)
2829
# alternatively, you can also run a sync test against `lambda_handler`
2930
# since `lambda_handler` awaits the coroutine to complete
3031

examples/event_handler_graphql/src/assert_async_graphql_response_module.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import sys
2+
3+
if sys.version_info >= (3, 8):
4+
from typing import TypedDict
5+
else:
6+
from typing_extensions import TypedDict
7+
18
import asyncio
2-
from typing import TypedDict
9+
from typing import List
310

411
import aiohttp
512

@@ -22,11 +29,11 @@ class Todo(TypedDict, total=False):
2229

2330

2431
@app.resolver(type_name="Query", field_name="listTodos")
25-
async def list_todos() -> list[Todo]:
32+
async def list_todos() -> List[Todo]:
2633
async with aiohttp.ClientSession(trace_configs=[aiohttp_trace_config()]) as session:
2734
async with session.get("https://jsonplaceholder.typicode.com/todos") as resp:
28-
# first two results to demo assertion
29-
return await resp.json()[:2]
35+
result: List[Todo] = await resp.json()
36+
return result[:2] # first two results to demo assertion
3037

3138

3239
@logger.inject_lambda_context(correlation_id_path=correlation_paths.APPSYNC_RESOLVER)

examples/event_handler_graphql/src/assert_graphql_response_module.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from typing import TypedDict
1+
import sys
2+
3+
if sys.version_info >= (3, 8):
4+
from typing import TypedDict
5+
else:
6+
from typing_extensions import TypedDict
7+
8+
from typing import List
29

310
from aws_lambda_powertools import Logger, Tracer
411
from aws_lambda_powertools.event_handler import AppSyncResolver
@@ -20,7 +27,7 @@ class Location(TypedDict, total=False):
2027
@app.resolver(field_name="listLocations")
2128
@app.resolver(field_name="locations")
2229
@tracer.capture_method
23-
def get_locations(name: str, description: str = "") -> list[Location]: # match GraphQL Query arguments
30+
def get_locations(name: str, description: str = "") -> List[Location]: # match GraphQL Query arguments
2431
return [{"name": name, "description": description}]
2532

2633

0 commit comments

Comments
 (0)