def test_serialize_returns_frames(self): interface = Stacktrace(frames=[{ 'lineno': 1, 'filename': 'foo.py', }]) result = interface.serialize() assert 'frames' in result
def test_serialize_returns_frames(self): interface = Stacktrace(frames=[{ 'lineno': 1, 'filename': 'foo.py', }]) result = interface.serialize() self.assertTrue('frames' in result)
def test_serialize_returns_frames(self): interface = Stacktrace(frames=[{"lineno": 1, "filename": "foo.py"}]) result = interface.serialize() assert "frames" in result
def expand_javascript_source(data, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty Mutates the input ``data`` with expanded context if available. """ from sentry.interfaces import Stacktrace try: stacktrace = Stacktrace(**data['sentry.interfaces.Stacktrace']) except KeyError: logger.debug('No stacktrace for event %r', data['event_id']) return # build list of frames that we can actually grab source for frames = [ f for f in stacktrace.frames if f.lineno is not None and f.is_url() ] if not frames: logger.debug('Event %r has no frames with enough context to fetch remote source', data['event_id']) return data file_list = set() sourcemap_capable = set() source_code = {} sourcemaps = {} for f in frames: file_list.add(f.abs_path) if f.colno is not None: sourcemap_capable.add(f.abs_path) while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent logger.debug('Fetching remote source %r', filename) result = fetch_url(filename) if result == BAD_SOURCE: continue # If we didn't have a colno, a sourcemap wont do us any good if filename not in sourcemap_capable: source_code[filename] = (result.body.splitlines(), None) continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.debug('Found sourcemap %r for minified script %r', sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: index = fetch_sourcemap(sourcemap, logger=logger) if not index: continue sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) has_changes = False for frame in frames: try: source, sourcemap = source_code[frame.abs_path] except KeyError: # we must've failed pulling down the source continue # may have had a failure pulling down the sourcemap previously if sourcemap in sourcemaps and frame.colno is not None: state = find_source(sourcemaps[sourcemap], frame.lineno, frame.colno) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) logger.debug('Mapping compressed source %r to mapping in %r', frame.abs_path, abs_path) try: source, _ = source_code[abs_path] except KeyError: pass else: # Store original data in annotation frame.data = { 'orig_lineno': frame['lineno'], 'orig_colno': frame['colno'], 'orig_function': frame['function'], 'orig_abs_path': frame['abs_path'], 'orig_filename': frame['filename'], 'sourcemap': sourcemap, } # SourceMap's return zero-indexed lineno's frame.lineno = state.src_line + 1 frame.colno = state.src_col frame.function = state.name frame.abs_path = abs_path frame.filename = state.src has_changes = True # TODO: theoretically a minified source could point to another mapped, minified source frame.pre_context, frame.context_line, frame.post_context = get_source_context( source=source, lineno=frame.lineno) if has_changes: data['sentry.interfaces.Stacktrace'] = stacktrace.serialize()
def expand_javascript_source(data, **kwargs): """ Attempt to fetch source code for javascript frames. Frames must match the following requirements: - lineno >= 0 - colno >= 0 - abs_path is the HTTP URI to the source - context_line is empty Mutates the input ``data`` with expanded context if available. """ from sentry.interfaces import Stacktrace try: stacktrace = Stacktrace(**data['sentry.interfaces.Stacktrace']) except KeyError: logger.debug('No stacktrace for event %r', data['event_id']) return # build list of frames that we can actually grab source for frames = [ f for f in stacktrace.frames if f.lineno is not None and f.is_url() ] if not frames: logger.debug( 'Event %r has no frames with enough context to fetch remote source', data['event_id']) return data file_list = set() sourcemap_capable = set() source_code = {} sourcemaps = {} for f in frames: file_list.add(f.abs_path) if f.colno is not None: sourcemap_capable.add(f.abs_path) while file_list: filename = file_list.pop() # TODO: respect cache-contro/max-age headers to some extent logger.debug('Fetching remote source %r', filename) result = fetch_url(filename) if result == BAD_SOURCE: continue # If we didn't have a colno, a sourcemap wont do us any good if filename not in sourcemap_capable: source_code[filename] = (result.body.splitlines(), None) continue # TODO: we're currently running splitlines twice sourcemap = discover_sourcemap(result, logger=logger) source_code[filename] = (result.body.splitlines(), sourcemap) if sourcemap: logger.debug('Found sourcemap %r for minified script %r', sourcemap, result.url) # pull down sourcemap if sourcemap and sourcemap not in sourcemaps: index = fetch_sourcemap(sourcemap, logger=logger) if not index: continue sourcemaps[sourcemap] = index # queue up additional source files for download for source in index.sources: if source not in source_code: file_list.add(urljoin(result.url, source)) has_changes = False for frame in frames: try: source, sourcemap = source_code[frame.abs_path] except KeyError: # we must've failed pulling down the source continue # may have had a failure pulling down the sourcemap previously if sourcemap in sourcemaps and frame.colno is not None: state = find_source(sourcemaps[sourcemap], frame.lineno, frame.colno) # TODO: is this urljoin right? (is it relative to the sourcemap or the originating file) abs_path = urljoin(sourcemap, state.src) logger.debug('Mapping compressed source %r to mapping in %r', frame.abs_path, abs_path) try: source, _ = source_code[abs_path] except KeyError: pass else: # Store original data in annotation frame.data = { 'orig_lineno': frame['lineno'], 'orig_colno': frame['colno'], 'orig_function': frame['function'], 'orig_abs_path': frame['abs_path'], 'orig_filename': frame['filename'], 'sourcemap': sourcemap, } # SourceMap's return zero-indexed lineno's frame.lineno = state.src_line + 1 frame.colno = state.src_col frame.function = state.name frame.abs_path = abs_path frame.filename = state.src has_changes = True # TODO: theoretically a minified source could point to another mapped, minified source frame.pre_context, frame.context_line, frame.post_context = get_source_context( source=source, lineno=frame.lineno) if has_changes: data['sentry.interfaces.Stacktrace'] = stacktrace.serialize()