Repository: microsoft/ptvsd

Issue:
Getting syntax error instead of actual message of a thrown exception in Python
@wmpauli commented on [Thu Oct 18 2018](https://github.com/Microsoft/vscode-python/issues/2949)

Issue Type: <b>Bug</b>

I keep on getting very strange behavior when I execute lines of code in the the DEBUG CONSOLE: I get a syntax error, instead of the actual message of the exception. Here is a very simple example of a code snippet, that intentionally has an error in it:
```python
import pandas as pd
# this is incorrect, as it should be "data = {'values': [1]}"
data = {'values': 1}
df = pd.DataFrame(data=data)
```
If I run this with the debugger, I get the expected `ValueError` in line 4 ("If using all scalar values, you must pass an index"). However, if I run this in the DEBUG CONSOLE, I get a syntax error instead of the message from the exception:
```
SyntaxError('invalid syntax', ('<string>', 1, 4, 'df = pd.DataFrame(data=data)'))
```
Any ideas? I get this all the time, not just for pandas. This behavior makes is really hard to debug code, because of the actual error message, I just get this `SyntaxError`.

Note, I also posted a question here, but to no avail: https://stackoverflow.com/questions/52861388/debug-console-throws-syntax-error-instead-of-showing-exception-message


Extension version: 2018.9.0
VS Code version: Code - Insiders 1.29.0-insider (9e83209ed59a22df3d593735d04cc963396bb89e, 2018-10-18T05:17:36.900Z)
OS version: Windows_NT x64 10.0.17763

<details>
<summary>System Info</summary>

|Item|Value|
|---|---|
|CPUs|Intel(R) Xeon(R) CPU E5-1620 v4 @ 3.50GHz (8 x 3492)|
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off<br>flash_3d: enabled<br>flash_stage3d: enabled<br>flash_stage3d_baseline: enabled<br>gpu_compositing: enabled<br>multiple_raster_threads: enabled_on<br>native_gpu_memory_buffers: disabled_software<br>rasterization: unavailable_software<br>video_decode: enabled<br>video_encode: enabled<br>webgl: enabled<br>webgl2: enabled|
|Memory (System)|31.92GB (21.97GB free)|
|Process Argv||
|Screen Reader|no|
|VM|0%|

</details>
<!-- generated by issue reporter -->

---

@d3r3kk commented on [Thu Oct 25 2018](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-433286674)

Thanks for raising the issue @wmpauli, I've been able to reproduce your issue and have some extra information for the team.

Note that there is a workaround: do not assign the result of the created DataFrame to a variable.

This does what you expect and shows `ValueError` in the debug console:
```python
pd.DataFrame(data=data)
```

This doesn't, in the way you describe:
```python
pf = pd.DataFrame(data=data)
```

----
## Environment

My environment:
OS: Windows 10
VSCode Version: 1.29.0-insider
vscode-python: 2018.10.0-alpha (ptvsd-4.1.3)
Python: 3.7
Virtual Environment: venv
Affected packages: pandas 0.23.4

## Sample project
The issue arises as described for the attached sample project.
[2949_error_message_incorrect.zip](https://github.com/Microsoft/vscode-python/files/2517722/2949_error_message_incorrect.zip)

## Repro video
Here's a video of the issue in action:
![2949_error_incorrect_in_debug_console](https://user-images.githubusercontent.com/11466735/47545247-0da3b680-d8a0-11e8-9889-c1a7416481c7.gif)

***Note:*** The problem manifests only when you capture the output of `DataFrame(data=data)` into a variable. 

## Relevant output (?)

In the interactive terminal, the following output can be seen:
```
(.venv) C:\dev\github\test\2949_error_message_incorrect> cd 'c:\dev\github\test\2949_error_message_incorrect'; ${env:PYTHONIOENCODING}='UTF-8'; ${env:PYTHONUNBUFFERED}='1'; & 'c:\dev\github\test\2949_error_message_incorrect\.venv\Scripts\python.exe' 'c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd_launcher.py' '53049' 'c:\dev\github\test\2949_error_message_incorrect\error_msg_repro.py'
Traceback (most recent call last):
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\wrapper.py", line 1292, in done
    fut.result()
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\futures.py", line 40, in result
    reraise(self._exc_info)
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\reraise3.py", line 8, in reraise
    raise exc_info[1].with_traceback(exc_info[2])
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\futures.py", line 155, in callback
    x = it.send(fut.result())
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\wrapper.py", line 2274, in on_completions
    for item in list(xml.comp):
  File "c:\Users\dekeeler\.vscode-insiders\extensions\vscode-python\pythonFiles\experimental\ptvsd\ptvsd\untangle.py", line 87, in __getattr__
    "'%s' has no attribute '%s'" % (self._name, key)
AttributeError: 'xml' has no attribute 'comp'
```



---

@brettcannon commented on [Fri Oct 26 2018](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-433563738)

This is expected behaviour as the debug console is using `eval()` for evaluating your commands, not `exec()`. In that instance you will get a syntax error as `eval()` only accepts expressions not statements like assignment.

---

@wmpauli commented on [Fri Oct 26 2018](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-433577901)

Also, please note that this SyntaxError doesn't occur in previous versions, in which the error message from pandas (or other modules) is correctly printed on the debug console. 

E.g. 
Issue Type: <b>Bug</b>

same steps

VS Code version: Code 1.23.1 (d0182c3417d225529c6d5ad24b7572815d0de9ac, 2018-05-10T17:11:17.614Z)
OS version: Windows_NT x64 10.0.17134

<details>
<summary>System Info</summary>

|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz (4 x 2808)|
|GPU Status|2d_canvas: enabled<br>flash_3d: enabled<br>flash_stage3d: enabled<br>flash_stage3d_baseline: enabled<br>gpu_compositing: enabled<br>multiple_raster_threads: enabled_on<br>native_gpu_memory_buffers: disabled_software<br>rasterization: disabled_software<br>video_decode: enabled<br>video_encode: enabled<br>vpx_decode: unavailable_software<br>webgl: enabled<br>webgl2: enabled|
|Memory (System)|15.93GB (6.98GB free)|
|Process Argv|C:\Program Files\Microsoft VS Code\Code.exe|
|Screen Reader|no|
|VM|0%|

</details><details><summary>Extensions (1)</summary>

Extension|Author (truncated)|Version
---|---|---
python|ms-|2018.6.0


</details>
<!-- generated by issue reporter -->

---

@jreynolds01 commented on [Fri Oct 26 2018](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-433587773)

@brettcannon Are you saying that the debug console will (and should) **always** return a SyntaxError when an assignment is used and another error is raised? The repro video showed successful assignment to 'my_data', so it's allowed when it's correct. Overall, the syntax error seems very limited, and it seems similar to saying 'when using the debug console, you can use assignment as long as it's correct, but you can't use assignment to find out anything useful, because the actual error that occurs will be hidden by a syntax error'. Is that documented anywhere else?

---

@brettcannon commented on [Mon Oct 29 2018](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-434074021)

@jreynolds01 good catch, I'll re-open the issue.

---

@bersbersbers commented on [Wed Mar 13 2019](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-472363479)

I am surprised that this issue has not attracted more attention, as I find it super annoying and have been waiting for it to be fixed for months (without even digging up this issue as I was certain there must be enough other people  annoyed by this that it would be fixed shortly).

Anyway, to summarize:

On the debug console, `a=1` works fine, just as `a` and `1`. `1+` returns a correct SyntaxError ("unexpected EOF while parsing"), while `a=1+` returns a less detailed SyntaxError ("invalid syntax"). `xyz()` returns a NameError. `a=xyz()` returns a SyntaxError ("invalid syntax") - I guess this is the issue here.

In a Python terminal, `a=1` works fine, as do `a` and `1`. Both `1+` and `a=1+` return "SyntaxError: invalid syntax". `xyz()` and `a=xyz()`: NameError.

In a Python terminal, `eval('a')` and `eval('1')` work fine. `eval('1+')` : "SyntaxError: unexpected EOF while parsing". All this is consistent with the behavior of the debug console.

`eval('a=1')`, `eval('a=1+')`, `eval('a=xyz()')`: "SyntaxError: invalid syntax", while `eval('xyz()')`: NameError. Comparing these to the behavior of the debug console, all except `eval('a=1')` behave the same - just `a=1` seems be go some other route than `eval(...)` - obviously, as that is working.

I have however been unable to find the piece of code that makes `a=1` work, and which fails to send `a=xyz()` the same way. I begin to think that might be `evaluate_expression` (defined at https://github.com/Microsoft/ptvsd/blob/60c3480b26e42fc7500c5b4a4f9438a3e1ad0209/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py#L243 and used at https://github.com/Microsoft/ptvsd/blob/11ffd749be56fd9c523b0ea0bda792abacc0d7ac/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py#L889), is that an indication this issue could be better filed at `ptvsd`?

---

@brettcannon commented on [Wed Mar 13 2019](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-472559967)

@DonJayamanne is this on ptvsd or PVSC side of things?

---

@DonJayamanne commented on [Wed Mar 13 2019](https://github.com/Microsoft/vscode-python/issues/2949#issuecomment-472570970)

Ptvsd




Discussion:
I can repro this with 4.2.4. Looks like with current master, evaluations are broken. In master with the above repro i don't see either `ValueError` or 'SyntaxError`.

/cc @fabioz 


The new JSON-based implementation of evaluate seems to be ignoring exceptions for exec but not eval. When expression is coming from debug console (`context='repl'`), it tries eval first, and falls back to exec if it fails for any reason, so effectively all errors get swallowed. The success path works fine though. 

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
@@ -897,8 +897,10 @@ def internal_evaluate_expression_json(py_db, request, thread_id):
         elif context == 'repl':
             try:
                 pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=True)
-            except:
-                pass
+            except Exception as ex:
+                err = ''.join(traceback.format_exception_only(type(ex), ex))
+                _evaluate_response(py_db, request, result='', error_message=err)
+                return
             # No result on exec.
             _evaluate_response(py_db, request, result='')
             return
diff --git a/src/ptvsd/wrapper.py b/src/ptvsd/wrapper.py
--- a/src/ptvsd/wrapper.py
+++ b/src/ptvsd/wrapper.py
@@ -1648,6 +1648,11 @@ def on_evaluate(self, request, args):
             is_json=True)
 
         body = resp_args['body']
+
+        # Currently there is an issue in VSC where returning success=false for a eval request,
+        # in repl context. VSC does not show the error response in the debug console.
+        if args.get('context', None) == 'repl' and not resp_args['success']:
+            body['result'] = resp_args['message']
         self.send_response(request, **body)
 
     @async_handler


Test patch:
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_evaluate.py b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_evaluate.py
new file mode 100644
--- /dev/null
+++ b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_evaluate.py
@@ -0,0 +1,8 @@
+def Call():
+    var_1 = 5
+
+    var_all = 1  # Break here
+
+if __name__ == '__main__':
+    Call()
+    print('TEST SUCEEDED!')
\ No newline at end of file
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
@@ -934,6 +934,49 @@ def test_stepping(case_setup):
 
         writer.finished_ok = True
 
+def test_evaluate(case_setup):
+    with case_setup.test_file('_debugger_case_evaluate.py') as writer:
+        json_facade = JsonFacade(writer)
+
+        writer.write_set_protocol('http_json')
+        writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
+
+        json_facade.write_make_initial_run()
+
+        hit = writer.wait_for_breakpoint_hit()
+
+        stack_trace_request = json_facade.write_request(
+            pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(threadId=hit.thread_id)))
+        stack_trace_response = json_facade.wait_for_response(stack_trace_request)
+        stack_frame = next(iter(stack_trace_response.body.stackFrames))
+        stack_frame_id = stack_frame['id']
+
+        # Test evaluate request that results in 'eval'
+        eval_request = json_facade.write_request(
+            pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1', frameId=stack_frame_id, context='repl')))
+        eval_response = json_facade.wait_for_response(eval_request)
+        assert eval_response.body.result == '5'
+        assert eval_response.body.type == 'int'
+
+        # Test evaluate request that results in 'exec'
+        exec_request = json_facade.write_request(
+            pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1 = 6', frameId=stack_frame_id, context='repl')))
+        exec_response = json_facade.wait_for_response(exec_request)
+        assert exec_response.body.result == ''
+
+        # Test evaluate request that results in 'exec' but fails
+        exec_request = json_facade.write_request(
+            pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1 = "abc"/6', frameId=stack_frame_id, context='repl')))
+        exec_response = json_facade.wait_for_response(exec_request)
+        assert exec_response.success == False
+        assert exec_response.body.result == ''
+        assert exec_response.message.find('TypeError') > -1
+
+        writer.write_run_thread(hit.thread_id)
+
+        writer.finished_ok = True
+
+
 
 @pytest.mark.skipif(IS_JYTHON, reason='Flaky on Jython.')
 def test_path_translation_and_source_reference(case_setup):