Repository: microsoft/ptvsd

Issue:
SyntaxError for breakpoints with empty condition
@xiaofeng0123 commented on [Mon Apr 01 2019](https://github.com/Microsoft/vscode-python/issues/5071)

Visual Studio Code - Insiders: 1.33.0-insider
python-insider: 2019.4.6304-alpha

Repro step:
1. Input below code:
     for i in range(1,500,20):
           print(i)
2. Set "Expression" condition breakpoint such as "i>400"
3. Start to debug make sure it works well
4. Right-click on the breakpoint to select "Edit Breakpoint"
5. Remove the expression in the dialog and then press "Enter"
6. Right-click on the breakpoint again to select "Edit Breakpoint"
7. Set "Hit Count" condition breakpoint such as "20"
8. Start to debug

Actual Result:
Error displays on "TERMINAL" panel
![image](https://user-images.githubusercontent.com/41426540/55371412-bbdb1880-5530-11e9-985d-85830aff69bb.png)

Expect Result:
No error displays

**Note:**
If remove the "Expression" breakpoint. Then add "Hit Count" breakpoint, it will no error occurs.
Repro gif: ‪\\bvtsrv2\team\v-xiaofz\PVSC\HitCountConditionBreakpoint.gif




Discussion:
It repros even without setting hit count, all you need is to set an expression, and then clear it. It looks like this doesn't change the breakpoint mode, so it ends up having an empty string as expression, which isn't valid Python.

This is reflected in the logs - here's what VSC sends after step 5 when you launch:
```json
            {
                "command": "setBreakpoints",
                "arguments": {
                    "source": {
                        "name": "test.py",
                        "path": "C:\\Temp\\test\\test.py"
                    },
                    "lines": [
                        2
                    ],
                    "breakpoints": [
                        {
                            "line": 2,
                            "condition": ""
                        }
                    ],
                    "sourceModified": false
                },
                "type": "request",
                "seq": 4
            }
```

Note `"condition": ""`. From ptvsd end, the presence or absence of this attribute is what determines whether the breakpoint is conditional or not.

The DAP spec doesn't clearly define the intended behavior, but it does mark `condition` as optional, which implies that it should be missing for non-conditional breakpoints, and VSC is doing the wrong thing here. I suspect other adapters might not run into it because they're relying on whether the value is truthy/falsy to check presence, and empty string is usually false (it is in JS). It might be worth filing a bug about.

In the meantime, we can work around by treating "" in condition as if it were missing.

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py
@@ -59,10 +59,10 @@ def __init__(self, line, condition, func_name, expression, suspend_policy="NONE"
 
     @property
     def has_condition(self):
-        return self.condition is not None or self.hit_condition is not None
+        return bool(self.condition) or bool(self.hit_condition)
 
     def handle_hit_condition(self, frame):
-        if self.hit_condition is None:
+        if not self.hit_condition:
             return False
         ret = False
         with self._hit_condition_lock:
diff --git a/src/ptvsd/_vendored/pydevd/pydevd.py b/src/ptvsd/_vendored/pydevd/pydevd.py
--- a/src/ptvsd/_vendored/pydevd/pydevd.py
+++ b/src/ptvsd/_vendored/pydevd/pydevd.py
@@ -500,7 +500,7 @@ def handle_breakpoint_condition(self, info, pybreakpoint, new_frame):
             if pybreakpoint.handle_hit_condition(new_frame):
                 return True
 
-            if condition is None:
+            if not condition:
                 return False
 
             return eval(condition, new_frame.f_globals, new_frame.f_locals)


Test patch: