88import weakref
99from collections import deque
1010from enum import Enum
11- from pathlib import Path
11+ from pathlib import Path , PurePath
1212from typing import (
1313 Any ,
1414 Deque ,
@@ -182,7 +182,7 @@ def global_id(self) -> int:
182182
183183
184184class HitCountEntry (NamedTuple ):
185- source : str
185+ source : pathlib . PurePath
186186 line : int
187187 type : str
188188
@@ -408,7 +408,7 @@ def set_breakpoints(
408408 ) -> List [Breakpoint ]:
409409
410410 if self .is_windows_path (source .path or "" ):
411- path = pathlib .PureWindowsPath (source .path or "" )
411+ path : pathlib . PurePath = pathlib .PureWindowsPath (source .path or "" )
412412 else :
413413 path = pathlib .PurePath (source .path or "" )
414414
@@ -486,9 +486,9 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
486486 self .requested_state = RequestedState .Nothing
487487
488488 if source is not None :
489- source = self .map_path_to_client (str (Path (source ).absolute ()))
490- if source in self .breakpoints :
491- breakpoints = [v for v in self .breakpoints [source ].breakpoints if v .line == line_no ]
489+ source_path = self .map_path_to_client (str (Path (source ).absolute ()))
490+ if source_path in self .breakpoints :
491+ breakpoints = [v for v in self .breakpoints [source_path ].breakpoints if v .line == line_no ]
492492 if len (breakpoints ) > 0 :
493493 for point in breakpoints :
494494 if point .condition is not None :
@@ -505,7 +505,7 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
505505 return
506506 if point .hit_condition is not None :
507507 hit = False
508- entry = HitCountEntry (source , line_no , type )
508+ entry = HitCountEntry (source_path , line_no , type )
509509 if entry not in self .hit_counts :
510510 self .hit_counts [entry ] = 0
511511 self .hit_counts [entry ] += 1
@@ -531,7 +531,7 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
531531 body = OutputEventBody (
532532 output = message + os .linesep ,
533533 category = OutputCategory .CONSOLE ,
534- source = Source (path = str (source )) if source else None ,
534+ source = Source (path = str (source_path )) ,
535535 line = line_no ,
536536 )
537537 ),
@@ -878,24 +878,35 @@ def get_threads(self) -> List[Thread]:
878878 WINDOW_PATH_REGEX = re .compile (r"^(([a-z]:[\\/])|(\\\\)).*$" , re .RegexFlag .IGNORECASE )
879879
880880 @classmethod
881- def is_windows_path (cls , path : os .PathLike [str ]) -> bool :
881+ def is_windows_path (cls , path : Union [ os .PathLike [str ], str ]) -> bool :
882882 return bool (cls .WINDOW_PATH_REGEX .fullmatch (str (path )))
883883
884- def map_path_to_client (self , path : os .PathLike [str ]) -> pathlib .PurePath :
884+ @staticmethod
885+ def relative_to (path : pathlib .PurePath , * other : pathlib .PurePath ) -> Optional [pathlib .PurePath ]:
886+ try :
887+ return path .relative_to (* other )
888+ except ValueError :
889+ return None
890+
891+ def map_path_to_client (self , path : Union [os .PathLike [str ], str ]) -> pathlib .PurePath :
892+ if not isinstance (path , PurePath ):
893+ path = PurePath (path )
894+
885895 if not self .path_mappings :
886- return pathlib . PurePath ( path )
896+ return path
887897
888898 for mapping in self .path_mappings :
889899
890900 remote_root_path = Path (mapping .remote_root or "." ).absolute ()
891901
892- if Path (path ).is_relative_to (remote_root_path ):
902+ if (
903+ mapping .local_root is not None
904+ and (relative_path := self .relative_to (Path (path ), remote_root_path )) is not None
905+ ):
893906 if self .is_windows_path (mapping .local_root ):
894- local_root_path = str (pathlib .PureWindowsPath (mapping .local_root ))
895- return pathlib .PureWindowsPath (path .replace (str (remote_root_path ), local_root_path or "" ))
907+ return pathlib .PureWindowsPath (mapping .local_root , relative_path )
896908 else :
897- local_root_path = str (pathlib .PurePath (mapping .local_root ))
898- return pathlib .PurePath (path .replace (str (remote_root_path ), local_root_path or "" ))
909+ return pathlib .PurePath (mapping .local_root , relative_path )
899910
900911 return path
901912
@@ -962,7 +973,11 @@ def log_message(self, message: Dict[str, Any]) -> None:
962973 self .last_fail_message = msg
963974
964975 current_frame = self .full_stack_frames [0 ] if self .full_stack_frames else None
965- source = Source (path = str (self .map_path_to_client (current_frame .source ))) if current_frame else None
976+ source = (
977+ Source (path = str (self .map_path_to_client (current_frame .source )))
978+ if current_frame and current_frame .source
979+ else None
980+ )
966981 line = current_frame .line if current_frame else None
967982
968983 if self .output_log :
0 commit comments