fix(middleware_factory): ret type annotation for handler dec#1066
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #1066 +/- ##
========================================
Coverage 99.96% 99.96%
========================================
Files 119 119
Lines 5365 5365
Branches 612 612
========================================
Hits 5363 5363
Partials 2 2
Continue to review full report at Codecov.
|
|
Thanks a lot @huonw !! Question: Do we lose type signature with the current solution? e.g., create a decorator with a given type annotation on a key-value param, call it using an incorrect type and let mypy detect it. The way I solved this for Tracer (similar, not the same) on preserving the function signature types is using AnyCallableT - This Instead of using overload, could you try with |
|
I think this is an strict improvement over the current code (i.e. nothing is being lost): the middlewares used to be completely untyped, while now they're at least Are you proposing something like |
|
If your current Callable type is helping preserve the type signature (not being done today), then we should stop here and merge \o/! The AnyCallableT suggestion was for the first layer (like Tracer, Logger, Metrics decorators), it wouldn't work for the second one for reasons you called out - hence the thought to experiment as that wasn't the final solution. Let me know the answer to the types as I'm not entirely sure I understood. If types aren't being preserved with the latest commit, we can experiment other ways (typing from my phone again, need to think of other ways still) |
|
Ah, I see. I think that may be tricky and/or require manual casts at each use site of the decorator: from typing import Any, Callable, cast
AnyCallable = Callable[..., Any]
def base_decorator(f: AnyCallable) -> AnyCallable:
return f
@base_decorator
def my_decorator(g: AnyCallable) -> AnyCallable:
return g
my_decorator = cast(Callable[[AnyCallable], AnyCallable], my_decorator)There's quite a few downsides of this:
On balance, and especially because these decorators are designed for handlers (i.e. functions that typically aren't used elsewhere in library code), I feel the minimal |
|
Looking into this tomorrow ;-) Finished the other bits |
|
Just ran |
|
Nah, spent the last two hours and while I made headwinds towards identifying kwargs in a non-strict mode, I hit a dead end with Otherwise, I'll take this opportunity to fix a doc typo as part of this PR and then merge. Sample code used from typing import Any, Callable, Dict, List, Optional
from aws_lambda_powertools.middleware_factory import lambda_handler_decorator
@lambda_handler_decorator
def obfuscate_sensitive_data(
handler: Callable[..., Any], event: Dict[str, Any], context: Any, fields: Optional[List[str]] = None
) -> Any:
# Obfuscate email before calling Lambda handler
if fields:
for field in fields:
if field in event:
event[field] = "REDACTED"
return handler(event, context)
@obfuscate_sensitive_data(fields=10)
def lambda_handler(event: Dict[str, Any], context: Any) -> Any:
...Mypy strict output against sample code blah.py:19:2: error: Unexpected keyword argument "fields" for "obfuscate_sensitive_data" [call-arg]
blah.py:19:2: error: Too few arguments for "obfuscate_sensitive_data" [call-arg]
Found 2 errors in 1 file (checked 1 source file) |
|
I have no further insight on that patch 😄 |
heitorlessa
left a comment
There was a problem hiding this comment.
suggesting a reword on return type comment, accepting, and merging.
|
Thanks again @huonw for taking the time, truly appreciate that. Hopefully, we will be able to solve this and other similar situations with typing_extensions in the future |
|
Woohoo, thanks for the tweak and then merging 👍 |
Issue #, if available: #1060
Description of changes:
This is two versions of typing
lambda_handler_decoratormore accurately for #1060, to ensure that middleware using it have a type. They end up with typeCallable[..., Any]but that's better than nothing.The first commit 937e6fe tries to be more accurate via overloads and protocols, but it doesn't work as written, and I cannot quite work out why. The errors below demonstrate the issues. It's potentially resolvable with 3.10's
ParamSpec(I'm not suretyping_extensionsdepended upon for 3.8 and 3.8?):Errors
Checklist
Breaking change checklist
RFC issue #:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.