@@ -61,15 +61,19 @@ class UnsupportedFrameFilename(Exception):
61
61
class FrameFilename :
62
62
def __init__ (self , frame_file_path : str ) -> None :
63
63
self .raw_path = frame_file_path
64
- if frame_file_path [0 ] == "/" :
64
+ is_windows_path = False
65
+ if "\\ " in frame_file_path :
66
+ is_windows_path = True
67
+ frame_file_path = frame_file_path .replace ("\\ " , "/" )
68
+
69
+ if frame_file_path [0 ] == "/" or frame_file_path [0 ] == "\\ " :
65
70
frame_file_path = frame_file_path [1 :]
66
71
67
72
# Using regexes would be better but this is easier to understand
68
73
if (
69
74
not frame_file_path
70
75
or frame_file_path [0 ] in ["[" , "<" ]
71
76
or frame_file_path .find (" " ) > - 1
72
- or frame_file_path .find ("\\ " ) > - 1 # Windows support
73
77
or frame_file_path .find ("/" ) == - 1
74
78
):
75
79
raise UnsupportedFrameFilename ("This path is not supported." )
@@ -79,8 +83,20 @@ def __init__(self, frame_file_path: str) -> None:
79
83
if not self .extension :
80
84
raise UnsupportedFrameFilename ("It needs an extension." )
81
85
86
+ # Remove drive letter if it exists
87
+ if is_windows_path and frame_file_path [1 ] == ":" :
88
+ frame_file_path = frame_file_path [2 :]
89
+ # windows drive letters can be like C:\ or C:
90
+ # so we need to remove the slash if it exists
91
+ if frame_file_path [0 ] == "/" :
92
+ frame_file_path = frame_file_path [1 :]
93
+
82
94
start_at_index = get_straight_path_prefix_end_index (frame_file_path )
83
95
self .straight_path_prefix = frame_file_path [:start_at_index ]
96
+
97
+ # We normalize the path to be as close to what the path would
98
+ # look like in the source code repository, hence why we remove
99
+ # the straight path prefix and drive letter
84
100
self .normalized_path = frame_file_path [start_at_index :]
85
101
if start_at_index == 0 :
86
102
self .root = frame_file_path .split ("/" )[0 ]
@@ -145,7 +161,7 @@ def list_file_matches(self, frame_filename: FrameFilename) -> list[dict[str, str
145
161
)
146
162
continue
147
163
148
- if stack_path .replace (stack_root , source_root , 1 ) != source_path :
164
+ if stack_path .replace (stack_root , source_root , 1 ). replace ( " \\ " , "/" ) != source_path :
149
165
logger .info (
150
166
"Unexpected stack_path/source_path found. A code mapping was not generated." ,
151
167
extra = {
@@ -259,7 +275,7 @@ def _generate_code_mapping_from_tree(
259
275
)
260
276
return []
261
277
262
- if stack_path .replace (stack_root , source_root , 1 ) != source_path :
278
+ if stack_path .replace (stack_root , source_root , 1 ). replace ( " \\ " , "/" ) != source_path :
263
279
logger .info (
264
280
"Unexpected stack_path/source_path found. A code mapping was not generated." ,
265
281
extra = {
@@ -370,6 +386,10 @@ def convert_stacktrace_frame_path_to_source_path(
370
386
If the code mapping does not apply to the frame, returns None.
371
387
"""
372
388
389
+ stack_root = code_mapping .stack_root
390
+ if "\\ " in code_mapping .stack_root :
391
+ stack_root = code_mapping .stack_root .replace ("\\ " , "/" )
392
+
373
393
# In most cases, code mappings get applied to frame.filename, but some platforms such as Java
374
394
# contain folder info in other parts of the frame (e.g. frame.module="com.example.app.MainActivity"
375
395
# gets transformed to "com/example/app/MainActivity.java"), so in those cases we use the
@@ -379,13 +399,13 @@ def convert_stacktrace_frame_path_to_source_path(
379
399
)
380
400
381
401
if stacktrace_path and stacktrace_path .startswith (code_mapping .stack_root ):
382
- return stacktrace_path .replace (code_mapping . stack_root , code_mapping .source_root , 1 )
402
+ return stacktrace_path .replace (stack_root , code_mapping .source_root , 1 )
383
403
384
404
# Some platforms only provide the file's name without folder paths, so we
385
405
# need to use the absolute path instead. If the code mapping has a non-empty
386
406
# stack_root value and it matches the absolute path, we do the mapping on it.
387
407
if frame .abs_path and frame .abs_path .startswith (code_mapping .stack_root ):
388
- return frame .abs_path .replace (code_mapping . stack_root , code_mapping .source_root , 1 )
408
+ return frame .abs_path .replace (stack_root , code_mapping .source_root , 1 )
389
409
390
410
return None
391
411
@@ -505,8 +525,8 @@ def find_roots(stack_path: str, source_path: str) -> tuple[str, str]:
505
525
If there is no overlap, raise an exception since this should not happen
506
526
"""
507
527
stack_root = ""
508
- if stack_path [0 ] == "/" :
509
- stack_root += "/"
528
+ if stack_path [0 ] == "/" or stack_path [ 0 ] == " \\ " :
529
+ stack_root += stack_path [ 0 ]
510
530
stack_path = stack_path [1 :]
511
531
512
532
if stack_path == source_path :
@@ -534,7 +554,7 @@ def find_roots(stack_path: str, source_path: str) -> tuple[str, str]:
534
554
source_root = source_path .rpartition (overlap )[0 ]
535
555
stack_root += stack_path_delim .join (stack_root_items )
536
556
537
- if stack_root and stack_root [- 1 ] != SLASH : # append trailing slash
557
+ if stack_root and stack_root [- 1 ] != stack_path_delim : # append trailing slash
538
558
stack_root = f"{ stack_root } { stack_path_delim } "
539
559
if source_root and source_root [- 1 ] != SLASH :
540
560
source_root = f"{ source_root } { SLASH } "
0 commit comments