1919import inspect
2020import sys
2121import traceback
22+ from inspect import FrameInfo
2223from pathlib import Path
2324from typing import (
2425 TYPE_CHECKING ,
2526 Any ,
2627 Callable ,
2728 Dict ,
29+ Generator ,
2830 List ,
2931 Mapping ,
3032 Optional ,
@@ -362,7 +364,7 @@ def _send_message_to_server(
362364 "params" : self ._replace_channels_with_guids (params ),
363365 "metadata" : metadata ,
364366 }
365- if self ._tracing_count > 0 and frames and object ._guid != "localUtils" :
367+ if self .needs_full_stack_trace () and frames and object ._guid != "localUtils" :
366368 self .local_utils .add_stack_to_tracing_no_reply (id , frames )
367369
368370 self ._transport .send (message )
@@ -508,17 +510,44 @@ def _replace_guids_with_channels(self, payload: Any) -> Any:
508510 return result
509511 return payload
510512
513+ def needs_full_stack_trace (self ) -> bool :
514+ return self ._tracing_count > 0
515+
516+ def get_frame_info (self ) -> Generator [FrameInfo ]:
517+ current_frame = inspect .currentframe ()
518+
519+ if current_frame is None :
520+ return
521+
522+ # Don't include the current method ("get_frame_info()") in the callstack
523+ current_frame = current_frame .f_back
524+
525+ while current_frame :
526+ traceback_info = inspect .getframeinfo (current_frame , 0 )
527+
528+ yield FrameInfo (
529+ current_frame ,
530+ traceback_info .filename ,
531+ traceback_info .lineno ,
532+ traceback_info .function ,
533+ traceback_info .code_context ,
534+ traceback_info .index ,
535+ )
536+
537+ current_frame = current_frame .f_back
538+
511539 async def wrap_api_call (
512540 self , cb : Callable [[], Any ], is_internal : bool = False
513541 ) -> Any :
514542 if self ._api_zone .get ():
515543 return await cb ()
516544 task = asyncio .current_task (self ._loop )
517- st : List [inspect .FrameInfo ] = getattr (
518- task , "__pw_stack__" , None
519- ) or inspect .stack (0 )
520-
521- parsed_st = _extract_stack_trace_information_from_stack (st , is_internal )
545+ st : Union [List [FrameInfo ], Generator [FrameInfo ]] = (
546+ getattr (task , "__pw_stack__" , None ) or self .get_frame_info ()
547+ )
548+ parsed_st = _extract_stack_trace_information_from_stack (
549+ st , is_internal , self .needs_full_stack_trace ()
550+ )
522551 self ._api_zone .set (parsed_st )
523552 try :
524553 return await cb ()
@@ -533,10 +562,12 @@ def wrap_api_call_sync(
533562 if self ._api_zone .get ():
534563 return cb ()
535564 task = asyncio .current_task (self ._loop )
536- st : List [inspect .FrameInfo ] = getattr (
537- task , "__pw_stack__" , None
538- ) or inspect .stack (0 )
539- parsed_st = _extract_stack_trace_information_from_stack (st , is_internal )
565+ st : Union [List [FrameInfo ], Generator [FrameInfo ]] = (
566+ getattr (task , "__pw_stack__" , None ) or self .get_frame_info ()
567+ )
568+ parsed_st = _extract_stack_trace_information_from_stack (
569+ st , is_internal , self .needs_full_stack_trace ()
570+ )
540571 self ._api_zone .set (parsed_st )
541572 try :
542573 return cb ()
@@ -567,7 +598,9 @@ class ParsedStackTrace(TypedDict):
567598
568599
569600def _extract_stack_trace_information_from_stack (
570- st : List [inspect .FrameInfo ], is_internal : bool
601+ st : Union [List [FrameInfo ], Generator [FrameInfo ]],
602+ is_internal : bool ,
603+ needs_full_stack : bool ,
571604) -> ParsedStackTrace :
572605 playwright_module_path = str (Path (playwright .__file__ ).parents [0 ])
573606 last_internal_api_name = ""
@@ -596,6 +629,9 @@ def _extract_stack_trace_information_from_stack(
596629 "function" : method_name ,
597630 }
598631 )
632+
633+ if not needs_full_stack :
634+ break
599635 if is_playwright_internal :
600636 last_internal_api_name = method_name
601637 elif last_internal_api_name :
0 commit comments