@@ -383,6 +383,72 @@ def handler(event, context):
383383 assert "test result" in result
384384
385385
386+ def test_tracer_yield_from_context_manager_exception_metadata (mocker , provider_stub , in_subsegment_mock ):
387+ # GIVEN tracer is initialized
388+ provider = provider_stub (in_subsegment = in_subsegment_mock .in_subsegment )
389+ tracer = Tracer (provider = provider , service = "booking" )
390+
391+ # WHEN capture_method decorator is used on a context manager
392+ # and the method raises an exception
393+ @tracer .capture_method
394+ @contextlib .contextmanager
395+ def yield_with_capture ():
396+ yield "partial"
397+ raise ValueError ("test" )
398+
399+ with pytest .raises (ValueError ):
400+ with yield_with_capture () as partial_val :
401+ assert partial_val == "partial"
402+
403+ # THEN we should add the exception using method name as key plus error
404+ # and their service name as the namespace
405+ put_metadata_mock_args = in_subsegment_mock .put_metadata .call_args [1 ]
406+ assert put_metadata_mock_args ["key" ] == "yield_with_capture error"
407+ assert isinstance (put_metadata_mock_args ["value" ], ValueError )
408+ assert put_metadata_mock_args ["namespace" ] == "booking"
409+
410+
411+ def test_tracer_yield_from_nested_context_manager (mocker , provider_stub , in_subsegment_mock ):
412+ # GIVEN tracer is initialized
413+ provider = provider_stub (in_subsegment = in_subsegment_mock .in_subsegment )
414+ tracer = Tracer (provider = provider , service = "booking" )
415+
416+ # WHEN capture_method decorator is used on a context manager nesting another context manager
417+ class NestedContextManager (object ):
418+ def __enter__ (self ):
419+ self ._value = {"result" : "test result" }
420+ return self ._value
421+
422+ def __exit__ (self , exc_type , exc_val , exc_tb ):
423+ self ._value ["result" ] = "exit was called before yielding"
424+
425+ @tracer .capture_method
426+ @contextlib .contextmanager
427+ def yield_with_capture ():
428+ with NestedContextManager () as nested_context :
429+ yield nested_context
430+
431+ @tracer .capture_lambda_handler
432+ def handler (event , context ):
433+ response = []
434+ with yield_with_capture () as yielded_value :
435+ response .append (yielded_value ["result" ])
436+
437+ return response
438+
439+ result = handler ({}, {})
440+
441+ # THEN we should have a subsegment named after the method name
442+ # and add its response as trace metadata
443+ handler_trace , yield_function_trace = in_subsegment_mock .in_subsegment .call_args_list
444+
445+ assert "test result" in in_subsegment_mock .put_metadata .call_args [1 ]["value" ]
446+ assert in_subsegment_mock .in_subsegment .call_count == 2
447+ assert handler_trace == mocker .call (name = "## handler" )
448+ assert yield_function_trace == mocker .call (name = "## yield_with_capture" )
449+ assert "test result" in result
450+
451+
386452def test_tracer_yield_from_generator (mocker , provider_stub , in_subsegment_mock ):
387453 # GIVEN tracer is initialized
388454 provider = provider_stub (in_subsegment = in_subsegment_mock .in_subsegment )
@@ -411,3 +477,28 @@ def handler(event, context):
411477 assert handler_trace == mocker .call (name = "## handler" )
412478 assert generator_fn_trace == mocker .call (name = "## generator_fn" )
413479 assert "test result" in result
480+
481+
482+ def test_tracer_yield_from_generator_exception_metadata (mocker , provider_stub , in_subsegment_mock ):
483+ # GIVEN tracer is initialized
484+ provider = provider_stub (in_subsegment = in_subsegment_mock .in_subsegment )
485+ tracer = Tracer (provider = provider , service = "booking" )
486+
487+ # WHEN capture_method decorator is used on a generator function
488+ # and the method raises an exception
489+ @tracer .capture_method
490+ def generator_fn ():
491+ yield "partial"
492+ raise ValueError ("test" )
493+
494+ with pytest .raises (ValueError ):
495+ gen = generator_fn ()
496+ list (gen )
497+
498+ # THEN we should add the exception using method name as key plus error
499+ # and their service name as the namespace
500+ put_metadata_mock_args = in_subsegment_mock .put_metadata .call_args [1 ]
501+ assert put_metadata_mock_args ["key" ] == "generator_fn error"
502+ assert put_metadata_mock_args ["namespace" ] == "booking"
503+ assert isinstance (put_metadata_mock_args ["value" ], ValueError )
504+ assert str (put_metadata_mock_args ["value" ]) == "test"
0 commit comments