Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…tools-python into complex

* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: (24 commits)
  docs: consistency around admonitions and snippets (aws-powertools#919)
  chore(deps-dev): bump mypy from 0.920 to 0.930 (aws-powertools#925)
  fix(event-sources): handle dynamodb null type as none, not bool (aws-powertools#929)
  fix(apigateway): support @app.not_found() syntax & housekeeping (aws-powertools#926)
  docs: Added GraphQL Sample API to Examples section of README.md (aws-powertools#930)
  feat(idempotency): support dataclasses & pydantic models payloads (aws-powertools#908)
  feat(tracer): ignore tracing for certain hostname(s) or url(s) (aws-powertools#910)
  feat(event-sources): cache parsed json in data class (aws-powertools#909)
  fix(warning): future distutils deprecation (aws-powertools#921)
  docs(batch): remove leftover from legacy
  docs(layer): bump Lambda Layer to version 6
  chore: bump to 1.23.0
  docs(apigateway): add new not_found feature (aws-powertools#915)
  docs: external reference to cloudformation custom resource helper (aws-powertools#914)
  feat(logger): allow handler with custom kwargs signature (aws-powertools#913)
  chore: minor housekeeping before release (aws-powertools#912)
  chore(deps-dev): bump mypy from 0.910 to 0.920 (aws-powertools#903)
  feat(batch): new BatchProcessor for SQS, DynamoDB, Kinesis (aws-powertools#886)
  fix(parser): overload parse when using envelope (aws-powertools#885)
  fix(parser): kinesis sequence number is str, not int (aws-powertools#907)
  ...
  • Loading branch information
heitorlessa committed Dec 31, 2021
2 parents 39c8dd6 + f985c40 commit 9a335f0
Show file tree
Hide file tree
Showing 68 changed files with 4,630 additions and 2,207 deletions.
4 changes: 0 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.5.1
hooks:
- id: python-use-type-annotations
- repo: local
hooks:
- id: black
Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,56 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo

## [Unreleased]


## 1.23.0 - 2021-12-20

### Bug Fixes

* **apigateway:** allow list of HTTP methods in route method ([#838](https://github.com/awslabs/aws-lambda-powertools-python/issues/838))
* **event-sources:** pass authorizer data to APIGatewayEventAuthorizer ([#897](https://github.com/awslabs/aws-lambda-powertools-python/issues/897))
* **event-sources:** handle claimsOverrideDetails set to null ([#878](https://github.com/awslabs/aws-lambda-powertools-python/issues/878))
* **idempotency:** include decorated fn name in hash ([#869](https://github.com/awslabs/aws-lambda-powertools-python/issues/869))
* **metrics:** explicit type to single_metric ctx manager ([#865](https://github.com/awslabs/aws-lambda-powertools-python/issues/865))
* **parameters:** mypy appconfig transform and return types ([#877](https://github.com/awslabs/aws-lambda-powertools-python/issues/877))
* **parser:** mypy overload parse when using envelope ([#885](https://github.com/awslabs/aws-lambda-powertools-python/issues/885))
* **parser:** kinesis sequence number is str, not int ([#907](https://github.com/awslabs/aws-lambda-powertools-python/issues/907))
* **parser:** mypy support for payload type override as models ([#883](https://github.com/awslabs/aws-lambda-powertools-python/issues/883))
* **tracer:** add warm start annotation (ColdStart=False) ([#851](https://github.com/awslabs/aws-lambda-powertools-python/issues/851))

### Documentation

* **nav**: reference cloudformation custom resource helper (CRD) ([#914](https://github.com/awslabs/aws-lambda-powertools-python/issues/914))
* add new public Slack invite
* disable search blur in non-prod env
* update Lambda Layers version
* **apigateway:** add new not_found feature ([#915](https://github.com/awslabs/aws-lambda-powertools-python/issues/915))
* **apigateway:** fix sample layout provided ([#864](https://github.com/awslabs/aws-lambda-powertools-python/issues/864))
* **appsync:** fix users.py typo to locations [#830](https://github.com/awslabs/aws-lambda-powertools-python/issues/830)
* **lambda_layer:** fix CDK layer syntax

### Features

* **apigateway:** add exception_handler support ([#898](https://github.com/awslabs/aws-lambda-powertools-python/issues/898))
* **apigateway:** access parent api resolver from router ([#842](https://github.com/awslabs/aws-lambda-powertools-python/issues/842))
* **batch:** new BatchProcessor for SQS, DynamoDB, Kinesis ([#886](https://github.com/awslabs/aws-lambda-powertools-python/issues/886))
* **logger:** allow handler with custom kwargs signature ([#913](https://github.com/awslabs/aws-lambda-powertools-python/issues/913))
* **tracer:** add service annotation when service is set ([#861](https://github.com/awslabs/aws-lambda-powertools-python/issues/861))

### Maintenance

* minor housekeeping before release ([#912](https://github.com/awslabs/aws-lambda-powertools-python/issues/912))
* correct pr label order
* **ci:** split latest docs workflow
* **deps:** bump fastjsonschema from 2.15.1 to 2.15.2 ([#891](https://github.com/awslabs/aws-lambda-powertools-python/issues/891))
* **deps:** bump actions/setup-python from 2.2.2 to 2.3.0 ([#831](https://github.com/awslabs/aws-lambda-powertools-python/issues/831))
* **deps:** support arm64 when developing locally ([#862](https://github.com/awslabs/aws-lambda-powertools-python/issues/862))
* **deps:** bump actions/setup-python from 2.3.0 to 2.3.1 ([#852](https://github.com/awslabs/aws-lambda-powertools-python/issues/852))
* **deps:** bump aws-xray-sdk from 2.8.0 to 2.9.0 ([#876](https://github.com/awslabs/aws-lambda-powertools-python/issues/876))
* **deps-dev:** bump mypy from 0.910 to 0.920 ([#903](https://github.com/awslabs/aws-lambda-powertools-python/issues/903))
* **deps-dev:** bump flake8 from 3.9.2 to 4.0.1 ([#789](https://github.com/awslabs/aws-lambda-powertools-python/issues/789))
* **deps-dev:** bump black from 21.10b0 to 21.11b1 ([#839](https://github.com/awslabs/aws-lambda-powertools-python/issues/839))
* **deps-dev:** bump black from 21.11b1 to 21.12b0 ([#872](https://github.com/awslabs/aws-lambda-powertools-python/issues/872))

## 1.22.0 - 2021-11-17

Tenet update! We've updated **Idiomatic** tenet to **Progressive** to reflect the new Router feature in Event Handler, and more importantly the new wave of customers coming from SRE, Data Analysis, and Data Science background.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip insta
* [Serverless Shopping cart](https://github.com/aws-samples/aws-serverless-shopping-cart)
* [Serverless Airline](https://github.com/aws-samples/aws-serverless-airline-booking)
* [Serverless E-commerce platform](https://github.com/aws-samples/aws-serverless-ecommerce-platform)
* [Serverless GraphQL Nanny Booking Api](https://github.com/trey-rosius/babysitter_api)

## Credits

Expand Down
67 changes: 52 additions & 15 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from enum import Enum
from functools import partial
from http import HTTPStatus
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union

from aws_lambda_powertools.event_handler import content_types
from aws_lambda_powertools.event_handler.exceptions import ServiceError
from aws_lambda_powertools.event_handler.exceptions import NotFoundError, ServiceError
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.shared.functions import resolve_truthy_env_var_choice
from aws_lambda_powertools.shared.json_encoder import Encoder
Expand All @@ -27,7 +27,6 @@
_SAFE_URI = "-._~()'!*:@,;" # https://www.ietf.org/rfc/rfc3986.txt
# API GW/ALB decode non-safe URI chars; we must support them too
_UNSAFE_URI = "%<>\[\]{}|^" # noqa: W605

_NAMED_GROUP_BOUNDARY_PATTERN = fr"(?P\1[{_SAFE_URI}{_UNSAFE_URI}\\w]+)"


Expand Down Expand Up @@ -435,6 +434,7 @@ def __init__(
self._proxy_type = proxy_type
self._routes: List[Route] = []
self._route_keys: List[str] = []
self._exception_handlers: Dict[Type, Callable] = {}
self._cors = cors
self._cors_enabled: bool = cors is not None
self._cors_methods: Set[str] = {"OPTIONS"}
Expand Down Expand Up @@ -579,7 +579,7 @@ def _remove_prefix(self, path: str) -> str:
@staticmethod
def _path_starts_with(path: str, prefix: str):
"""Returns true if the `path` starts with a prefix plus a `/`"""
if not isinstance(prefix, str) or len(prefix) == 0:
if not isinstance(prefix, str) or prefix == "":
return False

return path.startswith(prefix + "/")
Expand All @@ -596,6 +596,10 @@ def _not_found(self, method: str) -> ResponseBuilder:
headers["Access-Control-Allow-Methods"] = ",".join(sorted(self._cors_methods))
return ResponseBuilder(Response(status_code=204, content_type=None, headers=headers, body=None))

handler = self._lookup_exception_handler(NotFoundError)
if handler:
return ResponseBuilder(handler(NotFoundError()))

return ResponseBuilder(
Response(
status_code=HTTPStatus.NOT_FOUND.value,
Expand All @@ -609,16 +613,11 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
"""Actually call the matching route with any provided keyword arguments."""
try:
return ResponseBuilder(self._to_response(route.func(**args)), route)
except ServiceError as e:
return ResponseBuilder(
Response(
status_code=e.status_code,
content_type=content_types.APPLICATION_JSON,
body=self._json_dump({"statusCode": e.status_code, "message": e.msg}),
),
route,
)
except Exception:
except Exception as exc:
response_builder = self._call_exception_handler(exc, route)
if response_builder:
return response_builder

if self._debug:
# If the user has turned on debug mode,
# we'll let the original exception propagate so
Expand All @@ -628,10 +627,48 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
status_code=500,
content_type=content_types.TEXT_PLAIN,
body="".join(traceback.format_exc()),
)
),
route,
)

raise

def not_found(self, func: Optional[Callable] = None):
if func is None:
return self.exception_handler(NotFoundError)
return self.exception_handler(NotFoundError)(func)

def exception_handler(self, exc_class: Type[Exception]):
def register_exception_handler(func: Callable):
self._exception_handlers[exc_class] = func

return register_exception_handler

def _lookup_exception_handler(self, exp_type: Type) -> Optional[Callable]:
# Use "Method Resolution Order" to allow for matching against a base class
# of an exception
for cls in exp_type.__mro__:
if cls in self._exception_handlers:
return self._exception_handlers[cls]
return None

def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[ResponseBuilder]:
handler = self._lookup_exception_handler(type(exp))
if handler:
return ResponseBuilder(handler(exp), route)

if isinstance(exp, ServiceError):
return ResponseBuilder(
Response(
status_code=exp.status_code,
content_type=content_types.APPLICATION_JSON,
body=self._json_dump({"statusCode": exp.status_code, "message": exp.msg}),
),
route,
)

return None

def _to_response(self, result: Union[Dict, Response]) -> Response:
"""Convert the route's result to a Response
Expand Down
2 changes: 1 addition & 1 deletion aws_lambda_powertools/logging/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def handler(event, context):
)

@functools.wraps(lambda_handler)
def decorate(event, context):
def decorate(event, context, **kwargs):
lambda_context = build_lambda_context_model(context)
cold_start = _is_cold_start()

Expand Down
18 changes: 17 additions & 1 deletion aws_lambda_powertools/shared/functions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
from distutils.util import strtobool
from typing import Any, Optional, Union


def strtobool(value: str) -> bool:
"""Convert a string representation of truth to True or False.
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'value' is anything else.
> note:: Copied from distutils.util.
"""
value = value.lower()
if value in ("y", "yes", "t", "true", "on", "1"):
return True
if value in ("n", "no", "f", "false", "off", "0"):
return False
raise ValueError(f"invalid truth value {value!r}")


def resolve_truthy_env_var_choice(env: str, choice: Optional[bool] = None) -> bool:
"""Pick explicit choice over truthy env value, if available, otherwise return truthy env value
Expand Down
28 changes: 26 additions & 2 deletions aws_lambda_powertools/tracing/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
import numbers
import os
from typing import Any, Callable, Dict, Optional, Sequence, Union, cast, overload
from typing import Any, Callable, Dict, List, Optional, Sequence, Union, cast, overload

from ..shared import constants
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
Expand Down Expand Up @@ -758,7 +758,7 @@ def _patch_xray_provider(self):
# Due to Lazy Import, we need to activate `core` attrib via import
# we also need to include `patch`, `patch_all` methods
# to ensure patch calls are done via the provider
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import xray_recorder # type: ignore

provider = xray_recorder
provider.patch = aws_xray_sdk.core.patch
Expand All @@ -778,3 +778,27 @@ def _disable_xray_trace_batching(self):

def _is_xray_provider(self):
return "aws_xray_sdk" in self.provider.__module__

def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None):
"""If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being
requested.
> NOTE: If the provider is not xray, nothing will be added to ignore list
Documentation
--------------
- https://github.com/aws/aws-xray-sdk-python#ignoring-httplib-requests
Parameters
----------
hostname : Optional, str
The hostname is matched using the Python fnmatch library which does Unix glob style matching.
urls: Optional, List[str]
List of urls to ignore. Example `tracer.ignore_endpoint(urls=["/ignored-url"])`
"""
if not self._is_xray_provider():
return

from aws_xray_sdk.ext.httplib import add_ignored # type: ignore

add_ignored(hostname=hostname, urls=urls)
24 changes: 21 additions & 3 deletions aws_lambda_powertools/utilities/batch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,25 @@
Batch processing utility
"""

from .base import BasePartialProcessor, batch_processor
from .sqs import PartialSQSProcessor, sqs_batch_processor
from aws_lambda_powertools.utilities.batch.base import (
BasePartialProcessor,
BatchProcessor,
EventType,
FailureResponse,
SuccessResponse,
batch_processor,
)
from aws_lambda_powertools.utilities.batch.exceptions import ExceptionInfo
from aws_lambda_powertools.utilities.batch.sqs import PartialSQSProcessor, sqs_batch_processor

__all__ = ("BasePartialProcessor", "PartialSQSProcessor", "batch_processor", "sqs_batch_processor")
__all__ = (
"BatchProcessor",
"BasePartialProcessor",
"ExceptionInfo",
"EventType",
"FailureResponse",
"PartialSQSProcessor",
"SuccessResponse",
"batch_processor",
"sqs_batch_processor",
)
Loading

0 comments on commit 9a335f0

Please sign in to comment.