1
- from urllib .parse import urlparse
1
+ from collections .abc import Mapping
2
+ from typing import Any
3
+ from urllib .parse import ParseResult , ParseResultBytes , urlparse
2
4
3
5
from django .utils .encoding import force_bytes , force_str
4
6
from packaging .version import Version
5
7
from rest_framework .exceptions import NotFound , ParseError
6
8
7
9
from sentry import eventstore
8
10
from sentry .api .endpoints .project_release_files import ArtifactSource
11
+ from sentry .eventstore .models import BaseEvent
12
+ from sentry .interfaces .exception import Exception as ExceptionInterface
13
+ from sentry .interfaces .stacktrace import Frame
9
14
from sentry .models .distribution import Distribution
15
+ from sentry .models .project import Project
10
16
from sentry .models .release import Release
11
17
from sentry .models .releasefile import ReleaseFile , read_artifact_index
12
18
from sentry .models .sourcemapprocessingissue import SourceMapProcessingIssue
23
29
}
24
30
25
31
26
- def source_map_debug (project , event_id , exception_idx , frame_idx ):
32
+ class SourceMapDebug :
33
+ def __init__ (self , issue : str | None = None , data : Mapping [str , Any ] | None = None ) -> None :
34
+ self .issue = issue
35
+ self .data = data
36
+
37
+
38
+ class SourceMapException (Exception ):
39
+ def __init__ (self , issue : str , data : Mapping [str , Any ] | None = None ) -> None :
40
+ super ().__init__ (issue , data )
41
+ self .issue = issue
42
+ self .data = data
43
+
44
+
45
+ def source_map_debug (
46
+ project : Project ,
47
+ event_id : int ,
48
+ exception_idx : int ,
49
+ frame_idx : int ,
50
+ ) -> SourceMapDebug :
27
51
event = eventstore .backend .get_event_by_id (project .id , event_id )
28
52
if event is None :
29
53
raise NotFound (detail = "Event not found" )
@@ -126,7 +150,9 @@ def source_map_debug(project, event_id, exception_idx, frame_idx):
126
150
return SourceMapDebug ()
127
151
128
152
129
- def _get_frame_filename_and_path (exception , frame_idx ):
153
+ def _get_frame_filename_and_path (
154
+ exception : ExceptionInterface , frame_idx : int
155
+ ) -> tuple [Frame , str , str ]:
130
156
frame_list = exception .stacktrace .frames
131
157
try :
132
158
frame = frame_list [frame_idx ]
@@ -137,7 +163,14 @@ def _get_frame_filename_and_path(exception, frame_idx):
137
163
return frame , filename , abs_path
138
164
139
165
140
- def _find_matches (release_artifacts , abs_path , unified_path , filename , release , event ):
166
+ def _find_matches (
167
+ release_artifacts : list [ReleaseFile ],
168
+ abs_path : str | None ,
169
+ unified_path : str ,
170
+ filename : str ,
171
+ release : Release ,
172
+ event : BaseEvent ,
173
+ ) -> tuple [list [ReleaseFile ], list [ReleaseFile ]]:
141
174
full_matches = [
142
175
artifact
143
176
for artifact in release_artifacts
@@ -148,7 +181,7 @@ def _find_matches(release_artifacts, abs_path, unified_path, filename, release,
148
181
return full_matches , partial_matches
149
182
150
183
151
- def _find_partial_matches (unified_path , artifacts ) :
184
+ def _find_partial_matches (unified_path : str , artifacts : list [ ReleaseFile ]) -> list [ ReleaseFile ] :
152
185
filename = unified_path .split ("/" )[- 1 ]
153
186
filename_matches = [
154
187
artifact for artifact in artifacts if artifact .name .split ("/" )[- 1 ] == filename
@@ -162,15 +195,17 @@ def _find_partial_matches(unified_path, artifacts):
162
195
return []
163
196
164
197
165
- def _extract_release (event , project ) :
198
+ def _extract_release (event : BaseEvent , project : Project ) -> Release :
166
199
release_version = event .get_tag ("sentry:release" )
167
200
168
201
if not release_version :
169
202
raise SourceMapException (SourceMapProcessingIssue .MISSING_RELEASE )
170
203
return Release .objects .get (organization = project .organization , version = release_version )
171
204
172
205
173
- def _verify_dist_matches (release , event , artifact , filename ):
206
+ def _verify_dist_matches (
207
+ release : Release , event : BaseEvent , artifact : ReleaseFile , filename : str
208
+ ) -> bool :
174
209
try :
175
210
if event .dist is None and artifact .dist_id is None :
176
211
return True
@@ -188,7 +223,14 @@ def _verify_dist_matches(release, event, artifact, filename):
188
223
return True
189
224
190
225
191
- def _find_matching_artifact (release_artifacts , urlparts , abs_path , filename , release , event ):
226
+ def _find_matching_artifact (
227
+ release_artifacts : list [ReleaseFile ],
228
+ urlparts : ParseResult | ParseResultBytes ,
229
+ abs_path : str | None ,
230
+ filename : str ,
231
+ release : Release ,
232
+ event : BaseEvent ,
233
+ ) -> ReleaseFile :
192
234
unified_path = _unify_url (urlparts )
193
235
full_matches , partial_matches = _find_matches (
194
236
release_artifacts , abs_path , unified_path , filename , release , event
@@ -225,7 +267,7 @@ def _find_matching_artifact(release_artifacts, urlparts, abs_path, filename, rel
225
267
return full_matches [0 ]
226
268
227
269
228
- def _discover_sourcemap_url (artifact , filename ) :
270
+ def _discover_sourcemap_url (artifact : ReleaseFile , filename : str ) -> str | None :
229
271
file = artifact .file
230
272
# Code adapted from sentry/lang/javascript/processor.py
231
273
sourcemap_header = file .headers .get ("Sourcemap" , file .headers .get ("X-SourceMap" ))
@@ -240,11 +282,11 @@ def _discover_sourcemap_url(artifact, filename):
240
282
return force_str (sourcemap ) if sourcemap is not None else None
241
283
242
284
243
- def _unify_url (urlparts ) :
244
- return "~" + urlparts .path
285
+ def _unify_url (urlparts : ParseResult | ParseResultBytes ) -> str :
286
+ return "~" + str ( urlparts .path )
245
287
246
288
247
- def _get_releasefiles (release , organization_id ) :
289
+ def _get_releasefiles (release : Release , organization_id : int ) -> list [ ReleaseFile ] :
248
290
data_sources = []
249
291
250
292
file_list = ReleaseFile .public_objects .filter (release_id = release .id ).exclude (artifact_count = 0 )
@@ -267,21 +309,21 @@ def _get_releasefiles(release, organization_id):
267
309
return data_sources
268
310
269
311
270
- def _find_url_prefix (filepath , artifact_name ) :
312
+ def _find_url_prefix (filepath_str : str , artifact_name_str : str ) -> str | None :
271
313
# Right now, we only support 3 cases for finding the url prefix:
272
314
# 1. If the file name is a suffix of the artifact name, return the missing prefix
273
315
# Example : "/static/app.js" and "~/dist/static/app/js"
274
316
# 2. If there is only 1 substitution needed to make the file name and artifact name match
275
317
# Example : "~/dist/static/header/app.js" and "~/dist/static/footer/app.js"
276
318
# 3. If there is only 1 difference that needs to be added to make the file name and artifact name match
277
319
# Example : "~/dist/app.js" and "~/dist/static/header/app.js"
278
- idx = artifact_name .find (filepath )
320
+ idx = artifact_name_str .find (filepath_str )
279
321
# If file name is suffix of artifact name, return the missing prefix
280
322
if idx != - 1 :
281
- return artifact_name [:idx ]
323
+ return artifact_name_str [:idx ]
282
324
283
- filepath = filepath .split ("/" )
284
- artifact_name = artifact_name .split ("/" )
325
+ filepath = filepath_str .split ("/" )
326
+ artifact_name = artifact_name_str .split ("/" )
285
327
if len (filepath ) == len (artifact_name ):
286
328
matches = [filepath [i ] != artifact_name [i ] for i in range (len (filepath ))]
287
329
if sum (matches ) == 1 :
@@ -290,22 +332,11 @@ def _find_url_prefix(filepath, artifact_name):
290
332
291
333
if len (filepath ) + 1 == len (artifact_name ):
292
334
# If not suffix, find the missing parts and return them
293
- filepath = set (filepath )
294
- artifact_name = set (artifact_name )
335
+ filepath_set = set (filepath )
336
+ artifact_name_set = set (artifact_name )
295
337
296
- differences = list (filepath .symmetric_difference (artifact_name ))
338
+ differences = list (filepath_set .symmetric_difference (artifact_name_set ))
297
339
if len (differences ) == 1 :
298
340
return "/" .join (differences + ["" ])
299
341
300
-
301
- class SourceMapException (Exception ):
302
- def __init__ (self , issue , data = None ):
303
- super ().__init__ (issue , data )
304
- self .issue = issue
305
- self .data = data
306
-
307
-
308
- class SourceMapDebug :
309
- def __init__ (self , issue = None , data = None ):
310
- self .issue = issue
311
- self .data = data
342
+ return None
0 commit comments