From 14393a91d00484ef52639e3ed784900f16d03c23 Mon Sep 17 00:00:00 2001 From: Steffen Leistner Date: Thu, 1 Aug 2019 15:48:24 +0200 Subject: [PATCH 1/2] chore: change docstyle related settings --- .editorconfig | 10 ++++------ setup.cfg | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0fd8c7ba..1f542d29 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,19 +7,17 @@ trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 -max_line_length = 80 +max_line_length = 120 [*.md] trim_trailing_whitespace = false -[*.{yml,yaml,json}] +[*.{yml, yaml, json}] indent_size = 2 [Makefile] indent_style = tab [LICENSE] -insert_final_newline = none -trim_trailing_whitespace = none -indent_style = none -indent_size = none +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/setup.cfg b/setup.cfg index db995835..40bac318 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ classifiers = [options] python_requires = >=3.7 -packages=find: +packages = find: include_package_data = True test_suite = tests setup_requires = @@ -72,7 +72,7 @@ include_trailing_comma = True use_parentheses = True force_single_line = False indent = 4 -line_length = 79 +line_length = 120 wrap_length = 60 not_skip = __init__.py length_sort = True @@ -127,7 +127,7 @@ commands = lint: flake8 lambda_handlers isort: isort -c -rc lambda_handlers mypy: mypy lambda_handlers - doclint: pydocstyle --convention=numpy + doclint: pydocstyle --convention=numpy -v lambda_handlers [testenv:tests] deps = From 29f71c37a78b29812860c374490355be3d8d72d1 Mon Sep 17 00:00:00 2001 From: Steffen Leistner Date: Thu, 1 Aug 2019 15:51:15 +0200 Subject: [PATCH 2/2] fix(LambdaHandler): remove context option The LambdaContext paramater for Validator#validate_event calls have bben removed in order to avoid any tampering with the context object. BREAKING CHANGE: The context option has been removed. --- lambda_handlers/__init__.py | 6 +--- lambda_handlers/handlers/event_handler.py | 2 +- lambda_handlers/handlers/http_handler.py | 11 ++----- lambda_handlers/handlers/lambda_handler.py | 17 ++++------ .../handlers/mixins/validation_mixin.py | 8 ++--- .../handlers/tests/test_lambda_handler.py | 4 +-- lambda_handlers/response/response_builder.py | 8 +---- .../http/http_marshmallow_validator.py | 4 +-- .../tests/test_http_jsonschema_validator.py | 4 +-- .../tests/test_http_marshmallow_validator.py | 15 +++------ .../http/tests/test_http_validator.py | 31 +++++++------------ .../tests/test_marshmallow_validator.py | 4 +-- .../validators/tests/test_validator.py | 21 +++++-------- lambda_handlers/validators/validator.py | 13 +++----- 14 files changed, 47 insertions(+), 101 deletions(-) diff --git a/lambda_handlers/__init__.py b/lambda_handlers/__init__.py index 9d4b8828..5275a91c 100644 --- a/lambda_handlers/__init__.py +++ b/lambda_handlers/__init__.py @@ -9,11 +9,7 @@ InternalServerError, ) from lambda_handlers.version import __version__ # noqa -from lambda_handlers.handlers import ( # noqa - HTTPHandler, - EventHandler, - LambdaHandler, -) +from lambda_handlers.handlers import HTTPHandler, EventHandler, LambdaHandler # noqa from lambda_handlers.response import cors_headers, response_builder # noqa from lambda_handlers.formatters import input_format, output_format # noqa from lambda_handlers.validators import jsonschema, marshmallow # noqa diff --git a/lambda_handlers/handlers/event_handler.py b/lambda_handlers/handlers/event_handler.py index 4cbe51b5..2ec78054 100644 --- a/lambda_handlers/handlers/event_handler.py +++ b/lambda_handlers/handlers/event_handler.py @@ -42,7 +42,7 @@ def validator(self) -> Validator: def before(self, event, context): """Event hook called before the handler. It formats and validates `event`.""" - return self.validate_event(self.format_input(event), context) + return self.validate_event(self.format_input(event)), context def after(self, result): """Event method called after the handler. It formats and validates `result`.""" diff --git a/lambda_handlers/handlers/http_handler.py b/lambda_handlers/handlers/http_handler.py index 7a68b630..9aff1b08 100644 --- a/lambda_handlers/handlers/http_handler.py +++ b/lambda_handlers/handlers/http_handler.py @@ -4,13 +4,7 @@ from typing import Any, Dict from lambda_handlers.types import Headers, APIGatewayProxyResult -from lambda_handlers.errors import ( - FormatError, - NotFoundError, - BadRequestError, - ValidationError, - ResultValidationError, -) +from lambda_handlers.errors import FormatError, NotFoundError, BadRequestError, ValidationError, ResultValidationError from lambda_handlers.response import CORSHeaders from lambda_handlers.handlers.event_handler import EventHandler from lambda_handlers.response.response_builder import ( @@ -96,7 +90,8 @@ def _create_headers(self, headers: Headers) -> Headers: return headers or None - def _handle_error(self, error) -> APIGatewayProxyResult: + @staticmethod + def _handle_error(error) -> APIGatewayProxyResult: if isinstance(error, NotFoundError): return not_found(error.description) if isinstance(error, ResultValidationError): diff --git a/lambda_handlers/handlers/lambda_handler.py b/lambda_handlers/handlers/lambda_handler.py index 92477257..f4dc15d7 100644 --- a/lambda_handlers/handlers/lambda_handler.py +++ b/lambda_handlers/handlers/lambda_handler.py @@ -1,11 +1,11 @@ """A base class for AWS Lambda handlers.""" from abc import ABC -from typing import Any, Dict, Tuple, Callable, Optional +from typing import Any, Dict, Tuple, NewType, Callable, Optional from functools import wraps Event = Dict[str, Any] -Context = Dict[str, Any] +LambdaContext = NewType('LambdaContext', object) class LambdaHandler(ABC): @@ -17,25 +17,20 @@ class LambdaHandler(ABC): def __init__(self, handler: Optional[Callable] = None): self._handler = handler - def __call__(self, handler: Callable): - """Decorate `handler`.""" + def __call__(self, handler: Callable): # noqa: D102 @wraps(handler) def wrapper(event, context): return self._call_handler(handler, event, context) + return wrapper - def _call_handler( - self, - handler: Callable, - event: Event, - context: Context, - ) -> Any: + def _call_handler(self, handler: Callable, event: Event, context: LambdaContext) -> Any: try: return self.after(handler(*self.before(event, context))) except Exception as exception: return self.on_exception(exception) - def before(self, event: Event, context: Context) -> Tuple[Event, Context]: + def before(self, event: Event, context: LambdaContext) -> Tuple[Event, LambdaContext]: """Event method to be called just before the handler is executed.""" return event, context diff --git a/lambda_handlers/handlers/mixins/validation_mixin.py b/lambda_handlers/handlers/mixins/validation_mixin.py index 0a6820ba..7c3008a4 100644 --- a/lambda_handlers/handlers/mixins/validation_mixin.py +++ b/lambda_handlers/handlers/mixins/validation_mixin.py @@ -13,14 +13,12 @@ def validator(self): """Return the validator for events and results.""" pass - def validate_event(self, event, context): + def validate_event(self, event): """Validate the event with `self.validator`, return event and context.""" if self.validator: - transformed_event, transformed_context = self.validator.validate_event(event, context) + transformed_event = self.validator.validate_event(event) event.update(transformed_event) - if context is not None: - context.update(transformed_context) - return event, context + return event def validate_result(self, result: Dict[str, Any]) -> Dict[str, Any]: """Validate and return the result with `self.validator`.""" diff --git a/lambda_handlers/handlers/tests/test_lambda_handler.py b/lambda_handlers/handlers/tests/test_lambda_handler.py index 7eba8168..eab06071 100644 --- a/lambda_handlers/handlers/tests/test_lambda_handler.py +++ b/lambda_handlers/handlers/tests/test_lambda_handler.py @@ -2,7 +2,7 @@ import pytest -from lambda_handlers.handlers.lambda_handler import Context, LambdaHandler +from lambda_handlers.handlers.lambda_handler import LambdaContext, LambdaHandler Event = Dict[str, Any] @@ -14,7 +14,7 @@ def __init__(self, message: str, event: Event): class CallOrderAwareHandler(LambdaHandler): - def before(self, event: Event, context: Context) -> Tuple[Event, Context]: + def before(self, event: Event, context: LambdaContext) -> Tuple[Event, LambdaContext]: event['route'].append('before') return super().before(event, context) diff --git a/lambda_handlers/response/response_builder.py b/lambda_handlers/response/response_builder.py index 01f483d2..22765396 100644 --- a/lambda_handlers/response/response_builder.py +++ b/lambda_handlers/response/response_builder.py @@ -4,13 +4,7 @@ from typing import Any, Union from lambda_handlers.types import APIGatewayProxyResult -from lambda_handlers.errors import ( - LambdaError, - NotFoundError, - ForbiddenError, - BadRequestError, - InternalServerError, -) +from lambda_handlers.errors import LambdaError, NotFoundError, ForbiddenError, BadRequestError, InternalServerError def ok(result: Any) -> APIGatewayProxyResult: diff --git a/lambda_handlers/validators/http/http_marshmallow_validator.py b/lambda_handlers/validators/http/http_marshmallow_validator.py index fe96069c..299c1ecd 100644 --- a/lambda_handlers/validators/http/http_marshmallow_validator.py +++ b/lambda_handlers/validators/http/http_marshmallow_validator.py @@ -1,9 +1,7 @@ """A AWS HTTP event validator that uses Marshmallow.""" from lambda_handlers.validators.http.http_validator import HttpValidator -from lambda_handlers.validators.marshmallow_validator import ( - MarshmallowValidator, -) +from lambda_handlers.validators.marshmallow_validator import MarshmallowValidator class HttpMarshmallowValidator(MarshmallowValidator, HttpValidator): diff --git a/lambda_handlers/validators/http/tests/test_http_jsonschema_validator.py b/lambda_handlers/validators/http/tests/test_http_jsonschema_validator.py index 11f755ac..1aecd60a 100644 --- a/lambda_handlers/validators/http/tests/test_http_jsonschema_validator.py +++ b/lambda_handlers/validators/http/tests/test_http_jsonschema_validator.py @@ -2,9 +2,7 @@ from lambda_handlers.validators.http.http_validator import HttpValidator from lambda_handlers.validators.jsonschema_validator import JSONSchemaValidator -from lambda_handlers.validators.http.http_jsonschema_validator import ( - HttpJSONSchemaValidator, -) +from lambda_handlers.validators.http.http_jsonschema_validator import HttpJSONSchemaValidator @pytest.fixture(scope='session') diff --git a/lambda_handlers/validators/http/tests/test_http_marshmallow_validator.py b/lambda_handlers/validators/http/tests/test_http_marshmallow_validator.py index aee3ae11..01f636c3 100644 --- a/lambda_handlers/validators/http/tests/test_http_marshmallow_validator.py +++ b/lambda_handlers/validators/http/tests/test_http_marshmallow_validator.py @@ -3,12 +3,8 @@ from lambda_handlers.errors import EventValidationError from lambda_handlers.validators.http.http_validator import HttpValidator -from lambda_handlers.validators.marshmallow_validator import ( - MarshmallowValidator, -) -from lambda_handlers.validators.http.http_marshmallow_validator import ( - HttpMarshmallowValidator, -) +from lambda_handlers.validators.marshmallow_validator import MarshmallowValidator +from lambda_handlers.validators.http.http_marshmallow_validator import HttpMarshmallowValidator class TestHttpMarshmallowSchemaValidator: @@ -39,8 +35,7 @@ def test_validate_valid_request(self, subject): 'accountable': True, }, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { @@ -48,10 +43,8 @@ def test_validate_invalid_request(self, subject): 'user_name': True, }, } - context = {} - with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) nested_errors = error.value.description assert isinstance(nested_errors, list) diff --git a/lambda_handlers/validators/http/tests/test_http_validator.py b/lambda_handlers/validators/http/tests/test_http_validator.py index b26ca78c..8c3ead80 100644 --- a/lambda_handlers/validators/http/tests/test_http_validator.py +++ b/lambda_handlers/validators/http/tests/test_http_validator.py @@ -17,9 +17,8 @@ def subject(self, simple_schema_validator): def test_validate_event(self, subject, mocker): event = {} - context = {} validate_spy = mocker.spy(subject, 'validate') - assert (event, context) == subject.validate_event(event, context) + assert event == subject.validate_event(event) assert validate_spy.call_count == 0 def test_validate_result(self, subject, mocker): @@ -46,21 +45,19 @@ def test_validate_valid_request(self, subject): 'accountable': True, 'comment': 'some comment', } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { - 'price': '12', + 'price': '13', } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) assert isinstance(error.value.description, list) assert len(error.value.description) == 3 - assert {'price': ['12 is not of type ']} in error.value.description + assert {'price': ['13 is not of type ']} in error.value.description assert {'accountable': ['missing']} in error.value.description assert {'comment': ['missing']} in error.value.description @@ -113,8 +110,7 @@ def test_validate_valid_request(self, subject): 'accountable': True, }, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { @@ -122,10 +118,9 @@ def test_validate_invalid_request(self, subject): 'user_name': True, }, } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) nested_errors = error.value.description assert isinstance(nested_errors, list) @@ -162,8 +157,7 @@ def test_validate_valid_request(self, subject): 'filter': 'a,b,c', }, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { @@ -174,10 +168,9 @@ def test_validate_invalid_request(self, subject): 'filter': 100, }, } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) nested_errors = error.value.description assert isinstance(nested_errors, list) @@ -230,8 +223,7 @@ def test_validate_valid_request(self, subject): 'content': 'some long text', }, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { @@ -242,10 +234,9 @@ def test_validate_invalid_request(self, subject): 'filter': 100, }, } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) nested_errors = error.value.description assert isinstance(nested_errors, list) diff --git a/lambda_handlers/validators/tests/test_marshmallow_validator.py b/lambda_handlers/validators/tests/test_marshmallow_validator.py index 85089a16..5b3ad750 100644 --- a/lambda_handlers/validators/tests/test_marshmallow_validator.py +++ b/lambda_handlers/validators/tests/test_marshmallow_validator.py @@ -5,9 +5,7 @@ from marshmallow.validate import Range from lambda_handlers.validators.validator import Validator -from lambda_handlers.validators.marshmallow_validator import ( - MarshmallowValidator, -) +from lambda_handlers.validators.marshmallow_validator import MarshmallowValidator class EventSchema(Schema): diff --git a/lambda_handlers/validators/tests/test_validator.py b/lambda_handlers/validators/tests/test_validator.py index 2d5567ee..e372184d 100644 --- a/lambda_handlers/validators/tests/test_validator.py +++ b/lambda_handlers/validators/tests/test_validator.py @@ -29,9 +29,8 @@ def subject(self, simple_schema_validator): def test_validate_event(self, subject, mocker): event = {} - context = {} validate_spy = mocker.spy(subject, 'validate') - assert (event, context) == subject.validate_event(event, context) + assert event == subject.validate_event(event) assert validate_spy.call_count == 0 def test_validate_result(self, subject, mocker): @@ -58,17 +57,15 @@ def test_validate_valid_request(self, subject): 'accountable': True, 'comment': 'some comment', } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { 'price': '12', } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) assert isinstance(error.value.description, list) assert len(error.value.description) == 3 @@ -125,8 +122,7 @@ def test_validate_valid(self, subject): 'accountable': True, }, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_request(self, subject): event = { @@ -134,10 +130,9 @@ def test_validate_invalid_request(self, subject): 'user_name': True, }, } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) nested_errors = error.value.description assert isinstance(nested_errors, list) @@ -167,17 +162,15 @@ def test_validate_input_schema_takes_precedence(self, subject): 'price': 12, 'accountable': True, } - context = {} - assert subject.validate_event(event, context) + assert subject.validate_event(event) def test_validate_invalid_input(self, subject): event = { 'price': '12', } - context = {} with pytest.raises(EventValidationError) as error: - subject.validate_event(event, context) + subject.validate_event(event) assert isinstance(error.value.description, list) assert len(error.value.description) == 2 diff --git a/lambda_handlers/validators/validator.py b/lambda_handlers/validators/validator.py index 0584b281..86035d5c 100644 --- a/lambda_handlers/validators/validator.py +++ b/lambda_handlers/validators/validator.py @@ -32,7 +32,7 @@ def schemas(self) -> Dict[str, Any]: """The schemas for each section of context.""" return {} - def validate_event(self, event: Any, context: Any) -> Tuple[Any, Any]: + def validate_event(self, event: Any) -> Any: """Validate `event` against the input_schema. Parameters @@ -40,9 +40,6 @@ def validate_event(self, event: Any, context: Any) -> Tuple[Any, Any]: event: The event data object. - context: - The context data object. - Returns ------- data: Any @@ -64,9 +61,9 @@ def validate_event(self, event: Any, context: Any) -> Tuple[Any, Any]: description = self.format_errors(errors) raise EventValidationError(description) - return data, context + return data - return self._validate_event_contexts(event, context) + return self._validate_event_contexts(event) def validate_result(self, result: Dict[str, Any]) -> Dict[str, Any]: """Validate `result` against the output_schema. @@ -97,7 +94,7 @@ def validate_result(self, result: Dict[str, Any]) -> Dict[str, Any]: return data - def _validate_event_contexts(self, event, context) -> Tuple[Dict[str, Any], List[Any]]: + def _validate_event_contexts(self, event) -> Union[Dict[str, Any], List[Any]]: transformed_data, errors = self._validate_many( event, @@ -109,7 +106,7 @@ def _validate_event_contexts(self, event, context) -> Tuple[Dict[str, Any], List [{key: self.format_errors(error)} for key, error in errors.items()], ) - return transformed_data, context + return transformed_data def _validate_many( self,