From c9f7ea47bc48307ef2b034ce14991da6c1bace05 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 24 Dec 2019 20:55:11 +0200 Subject: [PATCH 1/3] Use stack_data to highlight frames --- heartrate/core.py | 51 ++++++++--------------- heartrate/static/css/monokai.css | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 33 deletions(-) diff --git a/heartrate/core.py b/heartrate/core.py index 1dcbdef..e4351e1 100644 --- a/heartrate/core.py +++ b/heartrate/core.py @@ -5,15 +5,16 @@ import webbrowser from collections import defaultdict, deque, Counter from functools import lru_cache -from itertools import islice, takewhile +from itertools import islice import pygments +import stack_data from executing import Source from flask import Flask, render_template, jsonify, url_for, request # noinspection PyUnresolvedReferences from pygments.formatters import HtmlFormatter # noinspection PyUnresolvedReferences -from pygments.lexers import PythonLexer, Python3Lexer +from pygments.lexers import Python3Lexer from heartrate import files as files_filters @@ -28,7 +29,8 @@ lexer = Python3Lexer() -formatter = HtmlFormatter(nowrap=True) +style = stack_data.style_with_executing_node("monokai", "bg:#acf9ff") +formatter = HtmlFormatter(nowrap=True, style=style) def highlight_python(code): @@ -46,21 +48,6 @@ def highlight_python_and_ranges(code): ) -def highlight_stack_frame(frame): - executing = Source.executing(frame) - node = executing.node - source = executing.source - if node: - source.asttokens() - start = node.first_token.start[0] - end = node.last_token.end[0] - else: - start = end = frame.f_lineno - - highlighted = '\n'.join(highlight_ranges(source, [frame]).splitlines()[start - 1:end]) - return highlight_python_and_ranges(highlighted) - - def trace( files=files_filters.contains_regex(r'#\s*heartrate'), port=9999, @@ -157,26 +144,24 @@ def frames_matching(filename): @app.route('/stacktrace/') def stacktrace(): def gen(): - frame = current_frame() - while frame: - code = frame.f_code - filename = code.co_filename - name = Source.for_frame(frame).code_qualname(code) + options = stack_data.Options(before=0, after=0, pygments_formatter=formatter) + for frame_info in stack_data.FrameInfo.stack_data(current_frame(), options): + filename = frame_info.filename + name = frame_info.executing.code_qualname() + if "heartrate" in filename and name.endswith(trace_func.__name__): + continue yield ( filename, - frame.f_lineno, + frame_info.lineno, name, - highlight_stack_frame(frame), + "\n".join( + line.render(pygmented=True) + for line in frame_info.lines + ), include_file(filename) ) - frame = frame.f_back - - return jsonify(list(takewhile( - lambda entry: not ( - 'heartrate' in entry[0] - and entry[2].endswith(trace_func.__name__)), - list(gen())[::-1] - ))) + + return jsonify(list(gen())) threading.Thread( target=lambda: app.run( diff --git a/heartrate/static/css/monokai.css b/heartrate/static/css/monokai.css index d8777a8..decdb28 100755 --- a/heartrate/static/css/monokai.css +++ b/heartrate/static/css/monokai.css @@ -68,3 +68,74 @@ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ +.highlight .hll-ExecutingNode { background-color: #49483e; background: rgba(172, 249, 255, 0.5) } +.highlight -ExecutingNode { background: #272822; color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } +.highlight .c-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment */ +.highlight .err-ExecutingNode { color: #960050; background-color: #1e0010; background: rgba(172, 249, 255, 0.5) } /* Error */ +.highlight .k-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword */ +.highlight .l-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal */ +.highlight .n-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name */ +.highlight .o-ExecutingNode { color: #f92672; background: rgba(172, 249, 255, 0.5) } /* Operator */ +.highlight .p-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Punctuation */ +.highlight .ch-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.Hashbang */ +.highlight .cm-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.Multiline */ +.highlight .cp-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.Preproc */ +.highlight .cpf-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.PreprocFile */ +.highlight .c1-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.Single */ +.highlight .cs-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Comment.Special */ +.highlight .gd-ExecutingNode { color: #f92672; background: rgba(172, 249, 255, 0.5) } /* Generic.Deleted */ +.highlight .ge-ExecutingNode { font-style: italic; background: rgba(172, 249, 255, 0.5) } /* Generic.Emph */ +.highlight .gi-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Generic.Inserted */ +.highlight .gs-ExecutingNode { font-weight: bold; background: rgba(172, 249, 255, 0.5) } /* Generic.Strong */ +.highlight .gu-ExecutingNode { color: #75715e; background: rgba(172, 249, 255, 0.5) } /* Generic.Subheading */ +.highlight .kc-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword.Constant */ +.highlight .kd-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword.Declaration */ +.highlight .kn-ExecutingNode { color: #f92672; background: rgba(172, 249, 255, 0.5) } /* Keyword.Namespace */ +.highlight .kp-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword.Pseudo */ +.highlight .kr-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword.Reserved */ +.highlight .kt-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Keyword.Type */ +.highlight .ld-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.Date */ +.highlight .m-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number */ +.highlight .s-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String */ +.highlight .na-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Attribute */ +.highlight .nb-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Builtin */ +.highlight .nc-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Class */ +.highlight .no-ExecutingNode { color: #66d9ef; background: rgba(172, 249, 255, 0.5) } /* Name.Constant */ +.highlight .nd-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Decorator */ +.highlight .ni-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Entity */ +.highlight .ne-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Exception */ +.highlight .nf-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Function */ +.highlight .nl-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Label */ +.highlight .nn-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Namespace */ +.highlight .nx-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Other */ +.highlight .py-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Property */ +.highlight .nt-ExecutingNode { color: #f92672; background: rgba(172, 249, 255, 0.5) } /* Name.Tag */ +.highlight .nv-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Variable */ +.highlight .ow-ExecutingNode { color: #f92672; background: rgba(172, 249, 255, 0.5) } /* Operator.Word */ +.highlight .w-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Text.Whitespace */ +.highlight .mb-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Bin */ +.highlight .mf-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Float */ +.highlight .mh-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Hex */ +.highlight .mi-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Integer */ +.highlight .mo-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Oct */ +.highlight .sa-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Affix */ +.highlight .sb-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Backtick */ +.highlight .sc-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Char */ +.highlight .dl-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Delimiter */ +.highlight .sd-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Doc */ +.highlight .s2-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Double */ +.highlight .se-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Escape */ +.highlight .sh-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Heredoc */ +.highlight .si-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Interpol */ +.highlight .sx-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Other */ +.highlight .sr-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Regex */ +.highlight .s1-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Single */ +.highlight .ss-ExecutingNode { color: #e6db74; background: rgba(172, 249, 255, 0.5) } /* Literal.String.Symbol */ +.highlight .bp-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Builtin.Pseudo */ +.highlight .fm-ExecutingNode { color: #a6e22e; background: rgba(172, 249, 255, 0.5) } /* Name.Function.Magic */ +.highlight .vc-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Variable.Class */ +.highlight .vg-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Variable.Global */ +.highlight .vi-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Variable.Instance */ +.highlight .vm-ExecutingNode { color: #f8f8f2; background: rgba(172, 249, 255, 0.5) } /* Name.Variable.Magic */ +.highlight .il-ExecutingNode { color: #ae81ff; background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Integer.Long */ +.highlight .-ExecutingNode { background: rgba(172, 249, 255, 0.5) } /* Literal.Number.Integer.Long */ From ee0b73dbe7f5946f5a5872a6a6a2b2bccb797908 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 24 Dec 2019 21:10:15 +0200 Subject: [PATCH 2/3] Don't collapse repeated frames --- heartrate/core.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/heartrate/core.py b/heartrate/core.py index e4351e1..22440fe 100644 --- a/heartrate/core.py +++ b/heartrate/core.py @@ -145,7 +145,12 @@ def frames_matching(filename): def stacktrace(): def gen(): options = stack_data.Options(before=0, after=0, pygments_formatter=formatter) - for frame_info in stack_data.FrameInfo.stack_data(current_frame(), options): + frame_infos = stack_data.FrameInfo.stack_data( + current_frame(), + options, + collapse_repeated_frames=False, + ) + for frame_info in frame_infos: filename = frame_info.filename name = frame_info.executing.code_qualname() if "heartrate" in filename and name.endswith(trace_func.__name__): From 9d9cd096873e73fa0ea1d17e73f8b8b4bdec7215 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 24 Dec 2019 22:13:34 +0200 Subject: [PATCH 3/3] Use _pygmented_with_ranges from stack_data --- heartrate/core.py | 78 +++++++---------------------------------------- 1 file changed, 11 insertions(+), 67 deletions(-) diff --git a/heartrate/core.py b/heartrate/core.py index 22440fe..2ee1d85 100644 --- a/heartrate/core.py +++ b/heartrate/core.py @@ -7,14 +7,12 @@ from functools import lru_cache from itertools import islice -import pygments import stack_data from executing import Source from flask import Flask, render_template, jsonify, url_for, request # noinspection PyUnresolvedReferences from pygments.formatters import HtmlFormatter -# noinspection PyUnresolvedReferences -from pygments.lexers import Python3Lexer +from stack_data.utils import _pygmented_with_ranges, iter_stack from heartrate import files as files_filters @@ -28,26 +26,10 @@ ] -lexer = Python3Lexer() style = stack_data.style_with_executing_node("monokai", "bg:#acf9ff") formatter = HtmlFormatter(nowrap=True, style=style) -def highlight_python(code): - return pygments.highlight( - code, - lexer, - formatter, - ) - - -def highlight_python_and_ranges(code): - return (highlight_python(code) - .replace(highlight_python(open_sentinel).rstrip('\n'), "") - .replace(highlight_python(close_sentinel).rstrip('\n'), "") - ) - - def trace( files=files_filters.contains_regex(r'#\s*heartrate'), port=9999, @@ -85,9 +67,11 @@ def file_table_context(): source = Source.for_filename(filename) queue = queues[filename] - highlighted = highlight_ranges(source, frames_matching(filename)) - highlighted = highlight_python_and_ranges(highlighted) - highlighted_lines = list(enumerate(highlighted.splitlines())) + highlighted_lines = list(enumerate(_pygmented_with_ranges( + formatter, + source.text, + executing_ranges(filename), + ))) counters = [ queue_counter(queue, 2 ** i) @@ -124,7 +108,6 @@ def file_table_context(): zip=zip, lightnesses=lightnesses, filename=filename, - highlighted=highlighted, ) @app.route('/table/') @@ -134,12 +117,12 @@ def file_table_view(): def current_frame(): return sys._current_frames()[thread_ident] - def frames_matching(filename): - frame = current_frame() - while frame: + def executing_ranges(filename): + for frame in iter_stack(current_frame()): if frame.f_code.co_filename == filename: - yield frame - frame = frame.f_back + executing = Source.executing(frame) + if executing.node: + yield executing.text_range() @app.route('/stacktrace/') def stacktrace(): @@ -202,45 +185,6 @@ def trace_func(frame, event, _arg): webbrowser.open_new_tab(url) -open_sentinel = " $$heartrate_open$$ " -close_sentinel = " $$heartrate_close$$ " - - -def highlight_ranges(source, frames): - text = source.text - ranges = set() - for frame in frames: - executing = Source.executing(frame) - if executing.node: - text_range = executing.text_range() - ranges.add(text_range) - - positions = [] - - for start, end in ranges: - positions.append((start, open_sentinel)) - positions.append((end, close_sentinel)) - while True: - start = text.find('\n', start + 1, end) - if start == -1: - break - positions.append((start, close_sentinel)) - positions.append((start + 1, open_sentinel)) - - # This just makes the loop below simpler - positions.append((len(text), '')) - - positions.sort() - - parts = [] - start = 0 - for position, part in positions: - parts.append(text[start:position]) - parts.append(part) - start = position - return ''.join(parts) - - def queue_counter(queue, n): while True: try: