Skip to content

Commit 1ab38cd

Browse files
authored
Merge branch 'develop' into pydantic
2 parents 7da981f + 330c76a commit 1ab38cd

File tree

18 files changed

+3003
-41
lines changed

18 files changed

+3003
-41
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [1.3.1] - 2020-08-22
10+
### Fixed
11+
- **Tracer**: capture_method decorator did not properly handle nested context managers
12+
13+
## [1.3.0] - 2020-08-21
14+
### Added
15+
- **Utilities**: Add new `parameters` utility to retrieve a single or multiple parameters from SSM Parameter Store, Secrets Manager, DynamoDB, or your very own
16+
17+
## [1.2.0] - 2020-08-20
18+
### Added
19+
- **Tracer**: capture_method decorator now supports generator functions (including context managers)
20+
921
## [1.1.3] - 2020-08-18
1022
### Fixed
1123
- **Logger**: Logs emitted twice, structured and unstructured, due to Lambda configuring the root handler

aws_lambda_powertools/tracing/tracer.py

+115-32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import copy
23
import functools
34
import inspect
@@ -320,6 +321,39 @@ def lambda_handler(event: dict, context: Any) -> Dict:
320321
booking_id = event.get("booking_id")
321322
asyncio.run(confirm_booking(booking_id=booking_id))
322323
324+
**Custom generator function using capture_method decorator**
325+
326+
from aws_lambda_powertools import Tracer
327+
tracer = Tracer(service="booking")
328+
329+
@tracer.capture_method
330+
def bookings_generator(booking_id):
331+
resp = call_to_booking_service()
332+
yield resp[0]
333+
yield resp[1]
334+
335+
def lambda_handler(event: dict, context: Any) -> Dict:
336+
gen = bookings_generator(booking_id=booking_id)
337+
result = list(gen)
338+
339+
**Custom generator context manager using capture_method decorator**
340+
341+
from aws_lambda_powertools import Tracer
342+
tracer = Tracer(service="booking")
343+
344+
@tracer.capture_method
345+
@contextlib.contextmanager
346+
def booking_actions(booking_id):
347+
resp = call_to_booking_service()
348+
yield "example result"
349+
cleanup_stuff()
350+
351+
def lambda_handler(event: dict, context: Any) -> Dict:
352+
booking_id = event.get("booking_id")
353+
354+
with booking_actions(booking_id=booking_id) as booking:
355+
result = booking
356+
323357
**Tracing nested async calls**
324358
325359
from aws_lambda_powertools import Tracer
@@ -392,43 +426,92 @@ async def async_tasks():
392426
err
393427
Exception raised by method
394428
"""
395-
method_name = f"{method.__name__}"
396429

397430
if inspect.iscoroutinefunction(method):
431+
decorate = self._decorate_async_function(method=method)
432+
elif inspect.isgeneratorfunction(method):
433+
decorate = self._decorate_generator_function(method=method)
434+
elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__):
435+
decorate = self._decorate_generator_function_with_context_manager(method=method)
436+
else:
437+
decorate = self._decorate_sync_function(method=method)
398438

399-
@functools.wraps(method)
400-
async def decorate(*args, **kwargs):
401-
async with self.provider.in_subsegment_async(name=f"## {method_name}") as subsegment:
402-
try:
403-
logger.debug(f"Calling method: {method_name}")
404-
response = await method(*args, **kwargs)
405-
self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment)
406-
except Exception as err:
407-
logger.exception(f"Exception received from '{method_name}' method")
408-
self._add_full_exception_as_metadata(
409-
function_name=method_name, error=err, subsegment=subsegment
410-
)
411-
raise
412-
413-
return response
439+
return decorate
414440

415-
else:
441+
def _decorate_async_function(self, method: Callable = None):
442+
method_name = f"{method.__name__}"
443+
444+
@functools.wraps(method)
445+
async def decorate(*args, **kwargs):
446+
async with self.provider.in_subsegment_async(name=f"## {method_name}") as subsegment:
447+
try:
448+
logger.debug(f"Calling method: {method_name}")
449+
response = await method(*args, **kwargs)
450+
self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment)
451+
except Exception as err:
452+
logger.exception(f"Exception received from '{method_name}' method")
453+
self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment)
454+
raise
416455

417-
@functools.wraps(method)
418-
def decorate(*args, **kwargs):
419-
with self.provider.in_subsegment(name=f"## {method_name}") as subsegment:
420-
try:
421-
logger.debug(f"Calling method: {method_name}")
422-
response = method(*args, **kwargs)
423-
self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment)
424-
except Exception as err:
425-
logger.exception(f"Exception received from '{method_name}' method")
426-
self._add_full_exception_as_metadata(
427-
function_name=method_name, error=err, subsegment=subsegment
428-
)
429-
raise
430-
431-
return response
456+
return response
457+
458+
return decorate
459+
460+
def _decorate_generator_function(self, method: Callable = None):
461+
method_name = f"{method.__name__}"
462+
463+
@functools.wraps(method)
464+
def decorate(*args, **kwargs):
465+
with self.provider.in_subsegment(name=f"## {method_name}") as subsegment:
466+
try:
467+
logger.debug(f"Calling method: {method_name}")
468+
result = yield from method(*args, **kwargs)
469+
self._add_response_as_metadata(function_name=method_name, data=result, subsegment=subsegment)
470+
except Exception as err:
471+
logger.exception(f"Exception received from '{method_name}' method")
472+
self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment)
473+
raise
474+
475+
return result
476+
477+
return decorate
478+
479+
def _decorate_generator_function_with_context_manager(self, method: Callable = None):
480+
method_name = f"{method.__name__}"
481+
482+
@functools.wraps(method)
483+
@contextlib.contextmanager
484+
def decorate(*args, **kwargs):
485+
with self.provider.in_subsegment(name=f"## {method_name}") as subsegment:
486+
try:
487+
logger.debug(f"Calling method: {method_name}")
488+
with method(*args, **kwargs) as return_val:
489+
result = return_val
490+
yield result
491+
self._add_response_as_metadata(function_name=method_name, data=result, subsegment=subsegment)
492+
except Exception as err:
493+
logger.exception(f"Exception received from '{method_name}' method")
494+
self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment)
495+
raise
496+
497+
return decorate
498+
499+
def _decorate_sync_function(self, method: Callable = None):
500+
method_name = f"{method.__name__}"
501+
502+
@functools.wraps(method)
503+
def decorate(*args, **kwargs):
504+
with self.provider.in_subsegment(name=f"## {method_name}") as subsegment:
505+
try:
506+
logger.debug(f"Calling method: {method_name}")
507+
response = method(*args, **kwargs)
508+
self._add_response_as_metadata(function_name=method_name, data=response, subsegment=subsegment)
509+
except Exception as err:
510+
logger.exception(f"Exception received from '{method_name}' method")
511+
self._add_full_exception_as_metadata(function_name=method_name, error=err, subsegment=subsegment)
512+
raise
513+
514+
return response
432515

433516
return decorate
434517

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""General utilities for Powertools"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
Parameter retrieval and caching utility
5+
"""
6+
7+
from .base import BaseProvider
8+
from .dynamodb import DynamoDBProvider
9+
from .exceptions import GetParameterError, TransformParameterError
10+
from .secrets import SecretsProvider, get_secret
11+
from .ssm import SSMProvider, get_parameter, get_parameters
12+
13+
__all__ = [
14+
"BaseProvider",
15+
"GetParameterError",
16+
"DynamoDBProvider",
17+
"SecretsProvider",
18+
"SSMProvider",
19+
"TransformParameterError",
20+
"get_parameter",
21+
"get_parameters",
22+
"get_secret",
23+
]

0 commit comments

Comments
 (0)