mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Raise syntax errors that Python can format nicely to show the location
Summary: Update _create_syntax_error_code to raise an error with similar arguments to the real arguments it already has, with our modifications. Test Plan: Updated python unit tests Reviewers: jarek, dsagal Reviewed By: dsagal Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3040
This commit is contained in:
		
							parent
							
								
									fb583f303a
								
							
						
					
					
						commit
						52fd28815e
					
				| @ -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 { | ||||
|  | ||||
| @ -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) | ||||
| 
 | ||||
| #---------------------------------------------------------------------- | ||||
| 
 | ||||
|  | ||||
| @ -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): | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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), | ||||
|  | ||||
| @ -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')) | ||||
| ====================================================================== | ||||
| """ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user