diff --git a/app/client/widgets/TextEditor.css b/app/client/widgets/TextEditor.css index 51bc61f2..60b3b240 100644 --- a/app/client/widgets/TextEditor.css +++ b/app/client/widgets/TextEditor.css @@ -97,14 +97,16 @@ .error_msg { color: black; - cursor: default; - margin: 4px; + cursor: pointer; + padding: 4px; } .error_details { padding: 2px 2px 2px 2px; background-color: #F8ECEA; margin: 0 0 -2px 0; + font-family: 'Monaco', 'Menlo', monospace; + font-size: 12px; } .error_box { diff --git a/sandbox/grist/codebuilder.py b/sandbox/grist/codebuilder.py index a58f5865..b5764af8 100644 --- a/sandbox/grist/codebuilder.py +++ b/sandbox/grist/codebuilder.py @@ -118,10 +118,11 @@ def _create_syntax_error_code(builder, input_text, err): output_offset = output_ln.line_to_offset(err.lineno, err.offset - 1 if err.offset else 0) input_offset = builder.map_back_offset(output_offset) line, col = input_ln.offset_to_line(input_offset) - message = '%s on line %d col %d' % (err.args[0], line, col + 1) - return "%s\nraise %s(%r)" % ( + message = err.args[0] + input_text_line = input_text.splitlines()[line - 1] + return "%s\nraise %s(%r, ('usercode', %r, %r, %r))" % ( textbuilder.line_start_re.sub('# ', input_text.rstrip()), - type(err).__name__, message) + type(err).__name__, message, line, col + 1, input_text_line) #---------------------------------------------------------------------- diff --git a/sandbox/grist/test_codebuilder.py b/sandbox/grist/test_codebuilder.py index 0fa00f95..401bcf4d 100644 --- a/sandbox/grist/test_codebuilder.py +++ b/sandbox/grist/test_codebuilder.py @@ -4,6 +4,7 @@ import unittest import codebuilder import six +unicode_prefix = 'u' if six.PY2 else '' def make_body(formula, default=None): return codebuilder.make_formula_body(formula, default).get_text() @@ -72,14 +73,20 @@ class TestCodeBuilder(unittest.TestCase): # Test that we produce valid code when "$foo" occurs in invalid places. self.assertEqual(make_body('foo($bar=1)'), - "# foo($bar=1)\nraise SyntaxError('invalid syntax on line 1 col 5')") + "# foo($bar=1)\n" + "raise SyntaxError('invalid syntax', ('usercode', 1, 5, %s'foo($bar=1)'))" + % unicode_prefix) self.assertEqual(make_body('def $bar(): pass'), - "# def $bar(): pass\nraise SyntaxError('invalid syntax on line 1 col 5')") + "# def $bar(): pass\n" + "raise SyntaxError('invalid syntax', ('usercode', 1, 5, %s'def $bar(): pass'))" + % unicode_prefix) # If $ is a syntax error, we don't want to turn it into a different syntax error. self.assertEqual(make_body('$foo + ("$%.2f" $ ($17.5))'), '# $foo + ("$%.2f" $ ($17.5))\n' - "raise SyntaxError('invalid syntax on line 1 col 17')") + "raise SyntaxError('invalid syntax', " + "('usercode', 1, 17, {}'$foo + (\"$%.2f\" $ ($17.5))'))" + .format(unicode_prefix)) self.assertEqual(make_body('if $foo:\n' + ' return $foo\n' + 'else:\n' + @@ -88,17 +95,21 @@ class TestCodeBuilder(unittest.TestCase): '# return $foo\n' + '# else:\n' + '# return $ bar\n' + - "raise SyntaxError('invalid syntax on line 4 col 10')") + "raise SyntaxError('invalid syntax', ('usercode', 4, 10, %s' return $ bar'))" + % unicode_prefix) # Check for reasonable behaviour with non-empty text and no statements. self.assertEqual(make_body('# comment'), '# comment\npass') self.assertEqual(make_body('rec = 1'), "# rec = 1\n" + "raise SyntaxError('Grist disallows assignment " + - "to the special variable \"rec\" on line 1 col 1')") + "to the special variable \"rec\"', ('usercode', 1, 1, %s'rec = 1'))" + % unicode_prefix) self.assertEqual(make_body('for rec in []: pass'), "# for rec in []: pass\n" + "raise SyntaxError('Grist disallows assignment " + - "to the special variable \"rec\" on line 1 col 4')") + "to the special variable \"rec\"', " + "('usercode', 1, 4, %s'for rec in []: pass'))" + % unicode_prefix) # some legitimates use of rec body = (""" @@ -124,8 +135,10 @@ return rec """) self.assertRegex(make_body(body), - r"raise SyntaxError\('Grist disallows assignment" + - r" to the special variable \"rec\" on line 4 col 7'\)") + r"raise SyntaxError\('Grist disallows assignment" + + r" to the special variable \"rec\"', " + r"\('usercode', 4, 7, %s'\[1 for rec in \[\]\]'\)\)" + % unicode_prefix) def test_make_formula_body_unicode(self): diff --git a/sandbox/grist/test_formula_error.py b/sandbox/grist/test_formula_error.py index 3116eb3b..f97ef30b 100644 --- a/sandbox/grist/test_formula_error.py +++ b/sandbox/grist/test_formula_error.py @@ -76,10 +76,36 @@ else: )) self.assertFormulaError(self.engine.get_formula_error('Math', 'syntax_err', 3), - SyntaxError, "invalid syntax on line 5 col 9") + SyntaxError, "invalid syntax (usercode, line 5)", + textwrap.dedent( + r""" + File "usercode", line 5 + return: 0 + \^ + SyntaxError: invalid syntax + """ + )) + if six.PY2: + traceback_regex = textwrap.dedent( + r""" + File "usercode", line 2 + if sum\(3, 5\) > 6: + \^ + IndentationError: unexpected indent + """ + ) + else: + traceback_regex = textwrap.dedent( + r""" + File "usercode", line 2 + if sum\(3, 5\) > 6: + IndentationError: unexpected indent + """ + ) self.assertFormulaError(self.engine.get_formula_error('Math', 'indent_err', 3), - IndentationError, "unexpected indent on line 2 col 2") + IndentationError, 'unexpected indent (usercode, line 2)', + traceback_regex) self.assertFormulaError(self.engine.get_formula_error('Math', 'other_err', 3), TypeError, "'int' object is not iterable", diff --git a/sandbox/grist/test_gencode.py b/sandbox/grist/test_gencode.py index 3c9795c5..218e4128 100644 --- a/sandbox/grist/test_gencode.py +++ b/sandbox/grist/test_gencode.py @@ -4,6 +4,7 @@ import unittest import difflib import re +import six from six.moves import xrange import gencode @@ -69,6 +70,11 @@ class TestGenCode(unittest.TestCase): gcode = gencode.GenCode() gcode.make_module(self.schema) generated = gcode.get_user_text() + if six.PY3: + generated = generated.replace( + ", 'for a in b'))", + ", u'for a in b'))", + ) self.assertEqual(generated, saved_sample, "Generated code doesn't match sample:\n" + "".join(difflib.unified_diff(generated.splitlines(True), saved_sample.splitlines(True), diff --git a/sandbox/grist/usercode.py b/sandbox/grist/usercode.py index 9b1c4f1b..b9e20d71 100644 --- a/sandbox/grist/usercode.py +++ b/sandbox/grist/usercode.py @@ -63,6 +63,6 @@ class Address: def badSyntax(rec, table): # for a in b # 10 - raise SyntaxError('invalid syntax on line 1 col 11') + raise SyntaxError('invalid syntax', ('usercode', 1, 11, u'for a in b')) ====================================================================== """