Repository: google/yapf
Pull request #112 (2015-04-18 03:12:43)

Raise a SyntaxError when passed bad syntax
If you pass a file which has bad syntax atm you see a ParseError, which is slightly confusing #110.

I think it makes more sense for this to raise a SyntaxError, and show this error to the user. Then it's much more obvious what's going on...

STDIN
```
 % echo "    a = 'hi'" | yapf  # python 2
Traceback (most recent call last):
  File "/Users/andy/anaconda/bin/yapf", line 9, in <module>
    load_entry_point('yapf==0.1.5', 'console_scripts', 'yapf')()
  File "/Users/andy/anaconda/lib/python2.7/site-packages/yapf-0.1.5-py2.7.egg/yapf/__init__.py", line 209, in run_main
    sys.exit(main(sys.argv))
  File "/Users/andy/anaconda/lib/python2.7/site-packages/yapf-0.1.5-py2.7.egg/yapf/__init__.py", line 135, in main
    verify=args.verify))
  File "/Users/andy/anaconda/lib/python2.7/site-packages/yapf-0.1.5-py2.7.egg/yapf/yapflib/yapf_api.py", line 101, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source)
  File "/Users/andy/anaconda/lib/python2.7/site-packages/yapf-0.1.5-py2.7.egg/yapf/yapflib/pytree_utils.py", line 113, in ParseCodeToTree
    raise(e)
  File "<unknown>", line 1
    a = 'hi'
    ^
IndentationError: unexpected indent
```

A file (note the SyntaxError shows the offending filename, which is nice):
```
% python yapf/__init__.py bad.py # python 3, you see more traceback
Traceback (most recent call last):
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/pytree_utils.py", line 101, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=5, value='    ', context=('', (1, 0))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/pytree_utils.py", line 107, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=5, value='    ', context=('', (1, 0))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "yapf/__init__.py", line 213, in <module>
    run_main()
  File "yapf/__init__.py", line 209, in run_main
    sys.exit(main(sys.argv))
  File "yapf/__init__.py", line 145, in main
    verify=args.verify)
  File "yapf/__init__.py", line 175, in FormatFiles
    verify=verify)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/yapf_api.py", line 76, in FormatFile
    verify=verify)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/yapf_api.py", line 101, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/pytree_utils.py", line 113, in ParseCodeToTree
    raise(e)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/site-packages/yapf-0.1.5-py3.4.egg/yapf/yapflib/pytree_utils.py", line 111, in ParseCodeToTree
    ast.parse(code)
  File "/Users/andy/anaconda/envs/py34/lib/python3.4/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "bad.py", line 1
    a = 42
    ^
IndentationError: unexpected indent
```

Note: autopep8 just returns the file as is (without performing any reformatting). IMO this is never what you want, you *always* want to be told **loudly** that there's a SyntaxError.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/2366804/badge)](https://coveralls.io/builds/2366804)

Coverage decreased (-0.22%) to 91.47% when pulling **26e7a59ab9ff075068088e9fdd62587bccac3b8f on hayd:syntaxerror** into **605704e90f95dcbffbbae01985e064b47ad8dd64 on google:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/2366804/badge)](https://coveralls.io/builds/2366804)

Coverage decreased (-0.22%) to 91.47% when pulling **26e7a59ab9ff075068088e9fdd62587bccac3b8f on hayd:syntaxerror** into **605704e90f95dcbffbbae01985e064b47ad8dd64 on google:master**.


Patch:
diff --git a/yapf/__init__.py b/yapf/__init__.py
index 2298c4b..fb97d5e 100644
--- a/yapf/__init__.py
+++ b/yapf/__init__.py
@@ -165,11 +165,15 @@ def FormatFiles(filenames, lines,
   """
   for filename in filenames:
     logging.info('Reformatting %s', filename)
-    reformatted_code = yapf_api.FormatFile(filename,
-                                           style_config=style_config,
-                                           lines=lines,
-                                           print_diff=print_diff,
-                                           verify=verify)
+    try:
+      reformatted_code = yapf_api.FormatFile(filename,
+                                             style_config=style_config,
+                                             lines=lines,
+                                             print_diff=print_diff,
+                                             verify=verify)
+    except SyntaxError as e:
+      e.filename = filename
+      raise
     if reformatted_code is not None:
       file_resources.WriteReformattedCode(filename, reformatted_code, in_place)
 
diff --git a/yapf/yapflib/pytree_utils.py b/yapf/yapflib/pytree_utils.py
index 929d927..d5885db 100644
--- a/yapf/yapflib/pytree_utils.py
+++ b/yapf/yapflib/pytree_utils.py
@@ -24,6 +24,7 @@ the lib2to3 library.
   {Get,Set}NodeAnnotation(): manage custom annotations on pytree nodes.
 """
 
+import ast
 from lib2to3 import pygram
 from lib2to3 import pytree
 from lib2to3.pgen2 import driver
@@ -84,6 +85,10 @@ def ParseCodeToTree(code):
   Arguments:
     code: a string with the code to parse.
 
+  Raises:
+    SyntaxError if the code is invalid syntax.
+    parse.ParseError if some other parsing failure.
+
   Returns:
     The root node of the parsed tree.
   """
@@ -97,8 +102,17 @@ def ParseCodeToTree(code):
   except parse.ParseError:
     # Now try to parse using a Python 2 grammar; If this fails, then
     # there's something else wrong with the code.
-    parser_driver = driver.Driver(_GRAMMAR_FOR_PY2, convert=pytree.convert)
-    tree = parser_driver.parse_string(code, debug=False)
+    try:
+      parser_driver = driver.Driver(_GRAMMAR_FOR_PY2, convert=pytree.convert)
+      tree = parser_driver.parse_string(code, debug=False)
+    except parse.ParseError:
+      # Raise a syntax error if the code is invalid python syntax.
+      try:
+        ast.parse(code)
+      except SyntaxError as e:
+        raise(e)
+      else:
+        raise
   return _WrapEndMarker(tree)
 
 


Test patch:
diff --git a/yapftests/yapf_test.py b/yapftests/yapf_test.py
index 8d56fbd..10bb4ed 100644
--- a/yapftests/yapf_test.py
+++ b/yapftests/yapf_test.py
@@ -627,5 +627,14 @@ class CommandLineTest(unittest.TestCase):
     self.assertIsNone(stderrdata)
     self.assertEqual(reformatted_code.decode('utf-8'), expected_formatted_code)
 
+
+class BadInputTest(unittest.TestCase):
+  """Test yapf's behaviour when passed bad input."""
+
+  def testBadSyntax(self):
+    code = "  a = 1\n"
+    self.assertRaises(SyntaxError, yapf_api.FormatCode, code)
+
+
 if __name__ == '__main__':
   unittest.main()