Repository: microsoft/ptvsd

Issue:
Allow non-string values in "env"
## Environment data
- VS Code version: 1.40.1
- Extension version (available under the Extensions sidebar): 2019.11.50794
- OS and version: Ubuntu 19.10
- Python version (& distribution if applicable, e.g. Anaconda): 3.7.5 64-bit
- Type of virtual environment used (N/A | venv | virtualenv | conda | ...): N/A
- Relevant/affected Python packages and their versions: None
- Jedi or Language Server? (i.e. what is `"python.jediEnabled"` set to; more info microsoft/vscode-python#3977): None

## Expected behaviour
Integer environment values are parsed OK as they are valid json value.
## Actual behaviour
Launch failes with message `Invalid message: 'env' Unknown conversion specifier j` with following traceback:
```
/usr/bin/python3 /home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher /home/lain/Documents/some/some.py 
E+00000.026: /handling microsoft/vscode-python#1 request "launch" from Adapter-1/
             Traceback (most recent call last):
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/json.py", line 260, in validate
                 value[k] = validate_value(v)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/json.py", line 108, in validate
                 raise TypeError("must be " + " or ".join(t.__name__ for t in classinfo))
             TypeError: must be str
             
             During handling of the above exception, another exception occurred:
             
             Traceback (most recent call last):
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 413, in __call__
                 value = validate(value)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/json.py", line 262, in validate
                 raise type(exc)("[{0!j}] {1}".format(k, exc))
             ValueError: Unknown conversion specifier j
             
             During handling of the above exception, another exception occurred:
             
             Traceback (most recent call last):
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 1078, in __init__
                 raise self
             ptvsd.common.messaging.InvalidMessageError: Invalid message: 'env' Unknown conversion specifier j
             
             Stack where logged:
               File "/usr/lib/python3.7/threading.py", line 890, in _bootstrap
                 self._bootstrap_inner()
               File "/usr/lib/python3.7/threading.py", line 926, in _bootstrap_inner
                 self.run()
               File "/usr/lib/python3.7/threading.py", line 870, in run
                 self._target(*self._args, **self._kwargs)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 1520, in _run_handlers
                 handler()
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 763, in _handle
                 result = handler(self)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/launcher/adapter.py", line 123, in launch_request
                 env.update(request("env", json.object(unicode)))
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 500, in __call__
                 return self.payload(*args, **kwargs)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 416, in __call__
                 raise message.isnt_valid("{0!r} {1}", key, exc)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 564, in isnt_valid
                 return args[0].error(InvalidMessageError, *args[1:], **kwargs)
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 552, in error
                 exc = exc_type(reason, cause, silent)  # will log it
               File "/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/common/messaging.py", line 1080, in __init__
                 log.exception()
             

E+00000.038: /handling microsoft/vscode-python#1 request "launch" from Adapter-1/
             Handler 'Handlers.launch_request' (file '/home/lain/.vscode/extensions/ms-python.python-2019.11.50794/pythonFiles/lib/python/new_ptvsd/wheels/ptvsd/launcher/../../ptvsd/launcher/adapter.py', line 35)
             couldn't handle microsoft/vscode-python#1 request "launch" from Adapter-1:
             Invalid message: 'env' Unknown conversion specifier j
```
## Steps to reproduce:
1. Create workspace with file "hello.py" with any content.
2. Create debug configuration "Python: Current File"
```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "env": {
                "Hello": 10
            },
            "console": "integratedTerminal"
        }
    ]
}
```



Discussion:
Can report similar behaviour with bool values as well.
The behavior in general is by design - environment variables have string values, so using something else is an error. However, the error message is incorrect - it should clearly spell out what the expected type was. 

(And it tries to do exactly that, but there's a bug in the code generating that error message, which ends up producing the error message that you actually see...)

Resolving patch:
diff --git a/src/ptvsd/common/json.py b/src/ptvsd/common/json.py
--- a/src/ptvsd/common/json.py
+++ b/src/ptvsd/common/json.py
@@ -225,7 +225,7 @@ def validate(value):
             try:
                 value[i] = validate_item(item)
             except (TypeError, ValueError) as exc:
-                raise type(exc)("[{0!j}] {1}".format(i, exc))
+                raise type(exc)(fmt("[{0!j}] {1}", i, exc))
         return value
 
     return validate
@@ -259,7 +259,15 @@ def validate(value):
                 try:
                     value[k] = validate_value(v)
                 except (TypeError, ValueError) as exc:
-                    raise type(exc)("[{0!j}] {1}".format(k, exc))
+                    raise type(exc)(fmt("[{0!j}] {1}", k, exc))
         return value
 
     return validate
+
+
+# A helper to resolve the circular dependency between common.fmt and common.json
+# on Python 2.
+def fmt(*args, **kwargs):
+    from ptvsd.common import fmt
+
+    return fmt(*args, **kwargs)
diff --git a/src/ptvsd/common/messaging.py b/src/ptvsd/common/messaging.py
--- a/src/ptvsd/common/messaging.py
+++ b/src/ptvsd/common/messaging.py
@@ -413,7 +413,10 @@ def __call__(self, key, validate, optional=False):
             value = validate(value)
         except (TypeError, ValueError) as exc:
             message = Message if self.message is None else self.message
-            raise message.isnt_valid("{0!r} {1}", key, exc)
+            err = fmt("{0}", exc)
+            if not err.startswith("["):
+                err = " " + err
+            raise message.isnt_valid("{0!j}{1}", key, err)
         return value
 
     def _invalid_if_no_key(func):


Test patch: