Skip to content

docs(examples): enforce and fix all mypy errors #1393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
53ad2ed
chore(mypy): fix redef _
heitorlessa Jul 28, 2022
ab10966
fix(tracer): correct ctx manager and default provider type
heitorlessa Jul 28, 2022
c55a9c1
chore(mypy): add requests type def as dev dep
heitorlessa Jul 28, 2022
3ba9e8a
chore(mypy): ignore lack of botocore types
heitorlessa Jul 28, 2022
f905451
chore(mypy): ignore lack of orjson types
heitorlessa Jul 28, 2022
6c24df6
chore(example): fix constructor default
heitorlessa Jul 28, 2022
23b5179
chore(mypy): ignore sentinel value assignment; needs issue
heitorlessa Jul 28, 2022
91ead7f
docs(examples): fix incorrect type response and Response class
heitorlessa Jul 28, 2022
15b6351
chore(mypy): fix potential none operator
heitorlessa Jul 28, 2022
ff5ec56
chore(mypy): fix return type on graphql merchant
heitorlessa Jul 28, 2022
6718279
chore(mypy): fix missing return
heitorlessa Jul 28, 2022
3ae1575
chore(mypy): ignore sentinel type, missing return
heitorlessa Jul 28, 2022
fe713c3
chore(mypy): ignore present attr defined in custom model; note for te…
heitorlessa Jul 28, 2022
306bfd3
chore(mypy): fix import and type annotation on async graphql
heitorlessa Jul 28, 2022
f1d1ff3
chore(mypy): fix coroutine response not being ready to subscribe
heitorlessa Jul 28, 2022
a9e7ca5
chore(ci): ensure mypy runs on examples
heitorlessa Jul 28, 2022
3135117
chore(mypy): address leftovers
heitorlessa Jul 28, 2022
bb583f4
chore(mypy): use backward compatible List annotation
heitorlessa Jul 28, 2022
cf29df7
chore(mypy): use backward compatible List annotation
heitorlessa Jul 28, 2022
d0ede58
Merge branch 'docs/examples-mypy' of https://github.com/heitorlessa/a…
heitorlessa Jul 28, 2022
cb791a4
chore(mypy): add typing_extensions, 3.6 3.7 compatible changes
heitorlessa Jul 28, 2022
1cbf580
docs(examples): fix highlighting due to backwards compatible typedict…
heitorlessa Jul 28, 2022
5004034
chore: leftover from rebase
heitorlessa Jul 28, 2022
0fd0820
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-power…
heitorlessa Jul 28, 2022
d3d196e
chore(mypy): add leftover assignment ignore
heitorlessa Jul 28, 2022
4a98d0e
Revert "chore(mypy): add leftover assignment ignore"
heitorlessa Jul 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ changelog:
docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog > CHANGELOG.md

mypy:
poetry run mypy --pretty aws_lambda_powertools
poetry run mypy --pretty aws_lambda_powertools examples
9 changes: 8 additions & 1 deletion aws_lambda_powertools/event_handler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
Event handler decorators for common Lambda events
"""

from .api_gateway import ALBResolver, APIGatewayHttpResolver, ApiGatewayResolver, APIGatewayRestResolver, CORSConfig, Response
from .api_gateway import (
ALBResolver,
APIGatewayHttpResolver,
ApiGatewayResolver,
APIGatewayRestResolver,
CORSConfig,
Response,
)
from .appsync import AppSyncResolver

__all__ = [
Expand Down
1 change: 1 addition & 0 deletions aws_lambda_powertools/event_handler/appsync.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def lambda_handler(event, context):
ValueError
If we could not find a field resolver
"""
# Maintenance: revisit generics/overload to fix [attr-defined] in mypy usage
BaseRouter.current_event = data_model(event)
BaseRouter.lambda_context = context
resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name)
Expand Down
118 changes: 59 additions & 59 deletions aws_lambda_powertools/tracing/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,34 @@
import numbers
import traceback
from contextlib import contextmanager
from typing import Any, AsyncContextManager, ContextManager, List, NoReturn, Optional, Set, Union
from typing import Any, Generator, List, NoReturn, Optional, Sequence, Union


class BaseProvider(abc.ABC):
@abc.abstractmethod # type: ignore
@contextmanager
def in_subsegment(self, name=None, **kwargs) -> ContextManager:
"""Return a subsegment context manger.
class BaseSegment(abc.ABC):
"""Holds common properties and methods on segment and subsegment."""

@abc.abstractmethod
def close(self, end_time: Optional[int] = None):
"""Close the trace entity by setting `end_time`
and flip the in progress flag to False.

Parameters
----------
name: str
Subsegment name
kwargs: Optional[dict]
Optional parameters to be propagated to segment
end_time: int
Time in epoch seconds, by default current time will be used.
"""

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

Parameters
----------
name: str
Subsegment name
kwargs: Optional[dict]
Optional parameters to be propagated to segment
"""
@abc.abstractmethod
def remove_subsegment(self, subsegment: Any):
"""Remove input subsegment from child subsegments."""

@abc.abstractmethod
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> NoReturn:
"""Annotate current active trace entity with a key-value pair.
"""Annotate segment or subsegment with a key-value pair.

Note: Annotations will be indexed for later search query.

Expand All @@ -48,9 +43,8 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N

@abc.abstractmethod
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoReturn:
"""Add metadata to the current active trace entity.

Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API.
"""Add metadata to segment or subsegment. Metadata is not indexed
but can be later retrieved by BatchGetTraces API.

Parameters
----------
Expand All @@ -63,45 +57,52 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoRe
"""

@abc.abstractmethod
def patch(self, modules: Set[str]) -> NoReturn:
"""Instrument a set of supported libraries
def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False):
"""Add an exception to trace entities.

Parameters
----------
modules: Set[str]
Set of modules to be patched
"""

@abc.abstractmethod
def patch_all(self) -> NoReturn:
"""Instrument all supported libraries"""
exception: Exception
Caught exception
stack: List[traceback.StackSummary]
List of traceback summaries

Output from `traceback.extract_stack()`.
remote: bool
Whether it's a client error (False) or downstream service error (True), by default False
"""

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

class BaseProvider(abc.ABC):
@abc.abstractmethod
def close(self, end_time: Optional[int] = None):
"""Close the trace entity by setting `end_time`
and flip the in progress flag to False.
@contextmanager
def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
"""Return a subsegment context manger.

Parameters
----------
end_time: int
Time in epoch seconds, by default current time will be used.
name: str
Subsegment name
kwargs: Optional[dict]
Optional parameters to be propagated to segment
"""

@abc.abstractmethod
def add_subsegment(self, subsegment: Any):
"""Add input subsegment as a child subsegment."""
@contextmanager
def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
"""Return a subsegment async context manger.

@abc.abstractmethod
def remove_subsegment(self, subsegment: Any):
"""Remove input subsegment from child subsegments."""
Parameters
----------
name: str
Subsegment name
kwargs: Optional[dict]
Optional parameters to be propagated to segment
"""

@abc.abstractmethod
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> NoReturn:
"""Annotate segment or subsegment with a key-value pair.
"""Annotate current active trace entity with a key-value pair.

Note: Annotations will be indexed for later search query.

Expand All @@ -115,8 +116,9 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N

@abc.abstractmethod
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoReturn:
"""Add metadata to segment or subsegment. Metadata is not indexed
but can be later retrieved by BatchGetTraces API.
"""Add metadata to the current active trace entity.

Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API.

Parameters
----------
Expand All @@ -129,17 +131,15 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> NoRe
"""

@abc.abstractmethod
def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False):
"""Add an exception to trace entities.
def patch(self, modules: Sequence[str]) -> NoReturn:
"""Instrument a set of supported libraries

Parameters
----------
exception: Exception
Caught exception
stack: List[traceback.StackSummary]
List of traceback summaries

Output from `traceback.extract_stack()`.
remote: bool
Whether it's a client error (False) or downstream service error (True), by default False
modules: Set[str]
Set of modules to be patched
"""

@abc.abstractmethod
def patch_all(self) -> NoReturn:
"""Instrument all supported libraries"""
2 changes: 1 addition & 1 deletion aws_lambda_powertools/tracing/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def __init__(
self.__build_config(
service=service, disabled=disabled, auto_patch=auto_patch, patch_modules=patch_modules, provider=provider
)
self.provider = self._config["provider"]
self.provider: BaseProvider = self._config["provider"]
self.disabled = self._config["disabled"]
self.service = self._config["service"]
self.auto_patch = self._config["auto_patch"]
Expand Down
2 changes: 1 addition & 1 deletion docs/core/event_handler/api_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ You can use **`not_found`** decorator to override this behavior, and return a cu

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.

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

Expand Down
20 changes: 10 additions & 10 deletions docs/core/event_handler/appsync.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Here's an example where we have two separate functions to resolve `getTodo` and

=== "getting_started_graphql_api_resolver.py"

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

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

=== "nested_mappings.py"

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

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

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

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

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

=== "graphql_transformer_merchant_info.py"

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

=== "graphql_transformer_search_merchant.py"

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

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

=== "custom_models.py.py"

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

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

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

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

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

=== "assert_graphql_response_module.py"

```python hl_lines="10"
```python hl_lines="17"
--8<-- "examples/event_handler_graphql/src/assert_graphql_response_module.py"
```

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

=== "assert_async_graphql_response.py"

```python hl_lines="27"
```python hl_lines="28"
--8<-- "examples/event_handler_graphql/src/assert_async_graphql_response.py"
```

=== "assert_async_graphql_response_module.py"

```python hl_lines="14"
```python hl_lines="21"
--8<-- "examples/event_handler_graphql/src/assert_async_graphql_response_module.py"
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import json
from dataclasses import dataclass
from pathlib import Path
from typing import List

import pytest
from assert_async_graphql_response_module import Location, app # instance of AppSyncResolver
from assert_async_graphql_response_module import Todo, app # instance of AppSyncResolver


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

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import sys

if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict

import asyncio
from typing import TypedDict
from typing import List

import aiohttp

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


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


@logger.inject_lambda_context(correlation_id_path=correlation_paths.APPSYNC_RESOLVER)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
from typing import TypedDict
import sys

if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict

from typing import List

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


Expand Down
Loading