Skip to content

Commit

Permalink
show variable in stacktrace
Browse files Browse the repository at this point in the history
  • Loading branch information
Czaki committed Apr 12, 2022
1 parent e71d3c2 commit 0845e4c
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 5 deletions.
227 changes: 227 additions & 0 deletions package/PartSeg/common_backend/python_syntax_highlight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# pragma: no cover
# syntax.py code copied from https://wiki.python.org/moin/PyQt/Python%20syntax%20highlighting

from qtpy import QtCore, QtGui


def format(color, style=""):
"""Return a QTextCharFormat with the given attributes."""
_color = QtGui.QColor()
_color.setNamedColor(color)

_format = QtGui.QTextCharFormat()
_format.setForeground(_color)
if "bold" in style:
_format.setFontWeight(QtGui.QFont.Bold)
if "italic" in style:
_format.setFontItalic(True)

return _format


# Syntax styles that can be shared by all languages
STYLES = {
"keyword": format("blue"),
"operator": format("red"),
"brace": format("darkGray"),
"defclass": format("black", "bold"),
"string": format("magenta"),
"string2": format("darkMagenta"),
"comment": format("darkGreen", "italic"),
"self": format("black", "italic"),
"numbers": format("brown"),
}


class PythonHighlighter(QtGui.QSyntaxHighlighter):
"""Syntax highlighter for the Python language."""

# Python keywords
keywords = [
"and",
"assert",
"break",
"class",
"continue",
"def",
"del",
"elif",
"else",
"except",
"exec",
"finally",
"for",
"from",
"global",
"if",
"import",
"in",
"is",
"lambda",
"not",
"or",
"pass",
"print",
"raise",
"return",
"try",
"while",
"yield",
"None",
"True",
"False",
]

# Python operators
operators = [
"=",
# Comparison
"==",
"!=",
"<",
"<=",
">",
">=",
# Arithmetic
r"\+",
"-",
r"\*",
"/",
"//",
r"\%",
r"\*\*",
# In-place
r"\+=",
"-=",
r"\*=",
"/=",
r"\%=",
# Bitwise
r"\^",
r"\|",
r"\&",
r"\~",
">>",
"<<",
]

# Python braces
braces = [
r"\{",
r"\}",
r"\(",
r"\)",
r"\[",
r"\]",
]

def __init__(self, parent: QtGui.QTextDocument) -> None:
super().__init__(parent)

# Multi-line strings (expression, flag, style)
self.tri_single = (QtCore.QRegExp("'''"), 1, STYLES["string2"])
self.tri_double = (QtCore.QRegExp('"""'), 2, STYLES["string2"])

rules = []

# Keyword, operator, and brace rules
rules += [(r"\b%s\b" % w, 0, STYLES["keyword"]) for w in PythonHighlighter.keywords]
rules += [(f"{o}", 0, STYLES["operator"]) for o in PythonHighlighter.operators]
rules += [(f"{b}", 0, STYLES["brace"]) for b in PythonHighlighter.braces]

# All other rules
rules += [
# 'self'
(r"\bself\b", 0, STYLES["self"]),
# 'def' followed by an identifier
(r"\bdef\b\s*(\w+)", 1, STYLES["defclass"]),
# 'class' followed by an identifier
(r"\bclass\b\s*(\w+)", 1, STYLES["defclass"]),
# Numeric literals
(r"\b[+-]?[0-9]+[lL]?\b", 0, STYLES["numbers"]),
(r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", 0, STYLES["numbers"]),
(r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b", 0, STYLES["numbers"]),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES["string"]),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES["string"]),
# From '#' until a newline
(r"#[^\n]*", 0, STYLES["comment"]),
]

# Build a QRegExp for each pattern
self.rules = [(QtCore.QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]

def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text."""
self.tripleQuoutesWithinStrings = []
# Do other syntax formatting
for expression, nth, format in self.rules:
index = expression.indexIn(text, 0)
if index >= 0 and expression.pattern() in [r'"[^"\\]*(\\.[^"\\]*)*"', r"'[^'\\]*(\\.[^'\\]*)*'"]:
inner_index = self.tri_single[0].indexIn(text, index + 1)
if inner_index == -1:
inner_index = self.tri_double[0].indexIn(text, index + 1)

if inner_index != -1:
self.tripleQuoutesWithinStrings.extend(range(inner_index, inner_index + 3))

while index >= 0:
# skipping triple quotes within strings
if index in self.tripleQuoutesWithinStrings:
index += 1
expression.indexIn(text, index)
continue

# We actually want the index of the nth match
index = expression.pos(nth)
length = len(expression.cap(nth))
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)

self.setCurrentBlockState(0)

# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)

def match_multiline(self, text, delimiter, in_state, style):
"""Do highlighting of multi-line strings. ``delimiter`` should be a
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
start = delimiter.indexIn(text)
# skipping triple quotes within strings
if start in self.tripleQuoutesWithinStrings:
return False
# Move past this match
add = delimiter.matchedLength()

# As long as there's a delimiter match on this line...
while start >= 0:
# Look for the ending delimiter
end = delimiter.indexIn(text, start + add)
# Ending delimiter on this line?
if end >= add:
length = end - start + add + delimiter.matchedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, style)
# Look for the next match
start = delimiter.indexIn(text, start + length)

# Return True if still inside a multi-line string, False otherwise
return self.currentBlockState() == in_state
13 changes: 8 additions & 5 deletions package/PartSeg/common_gui/error_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
.. _sentry: https://sentry.io
"""
import getpass
import io
import pprint
import re
import sys
import traceback
import typing

Expand All @@ -29,8 +29,10 @@
QWidget,
)
from sentry_sdk.utils import event_from_exception, exc_info_from_error
from traceback_with_variables import print_exc

from PartSeg import __version__
from PartSeg.common_backend.python_syntax_highlight import PythonHighlighter
from PartSegCore import state_store
from PartSegCore.io_utils import find_problematic_leafs
from PartSegCore.segmentation.algorithm_base import SegmentationLimitException
Expand All @@ -54,11 +56,12 @@ def __init__(self, exception: Exception, description: str, additional_notes: str
self.send_report_btn.setDisabled(not state_store.report_errors)
self.cancel_btn = QPushButton("Cancel")
self.error_description = QTextEdit()
self._highlight = PythonHighlighter(self.error_description.document())
self.traceback_summary = additional_info
if additional_info is None:
self.error_description.setText(
"".join(traceback.format_exception(type(exception), exception, exception.__traceback__))
)
stream = io.StringIO()
print_exc(exception, file_=stream)
self.error_description.setText(stream.getvalue())
elif isinstance(additional_info, traceback.StackSummary):
self.error_description.setText("".join(additional_info.format()))
elif isinstance(additional_info[1], traceback.StackSummary):
Expand Down Expand Up @@ -117,7 +120,7 @@ def exec_(self):
"""
# TODO check if this check is needed
if not state_store.show_error_dialog:
sys.__excepthook__(type(self.exception), self.exception, self.exception.__traceback__)
print_exc(self.exception)
return False
super().exec_()

Expand Down
1 change: 1 addition & 0 deletions requirements/requirements_pyinstaller.in
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ xlsxwriter
sphinx!=3.5.0
pydantic!=1.8
vispy
traceback-with-variables
2 changes: 2 additions & 0 deletions requirements/requirements_pyinstaller.txt
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ tornado==6.1
# jupyter-client
tqdm==4.64.0
# via napari
traceback-with-variables==2.0.4
# via -r requirements_pyinstaller.in
traitlets==5.1.1
# via
# ipykernel
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ install_requires =
superqt>=0.2.4
sympy>=1.1.1
tifffile>=2020.9.30
traceback-with-variables>=2.0.4
vispy>=0.6.4
xlrd>=1.1.0
xlsxwriter
Expand Down

0 comments on commit 0845e4c

Please sign in to comment.