Repository: microsoft/ptvsd

Issue:
Debugger may crash if module __version__ or __package__ is not json-serializable
Stacktrace:

```
  File “.../ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command_factory_json.py”, line 62, in track_module
    module_events.append(NetCommand(CMD_MODULE_EVENT, 0, module_event, is_json=True))
  File “.../ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command.py”, line 57, in __init__
    text = json.dumps(as_dict)
  File “/usr/lib/python3.5/json/__init__.py”, line 230, in dumps
    return _default_encoder.encode(obj)
  File “/usr/lib/python3.5/json/encoder.py”, line 198, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File “/usr/lib/python3.5/json/encoder.py”, line 256, in iterencode
    return _iterencode(o, 0)
  File “/usr/lib/python3.5/json/encoder.py”, line 179, in default
    raise TypeError(repr(o) + “ is not JSON serializable”)
```


Discussion:

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command_factory_json.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command_factory_json.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command_factory_json.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_net_command_factory_json.py
@@ -46,8 +46,16 @@ def track_module(self, filename_in_utf8, module_name, frame):
             if filename_in_utf8 in self._modules:
                 return
 
-            version = frame.f_globals.get('__version__', '')
-            package_name = frame.f_globals.get('__package__', '')
+            try:
+                version = str(frame.f_globals.get('__version__', ''))
+            except:
+                version = '<unknown>'
+
+            try:
+                package_name = str(frame.f_globals.get('__package__', ''))
+            except:
+                package_name = '<unknown>'
+
             module_id = self._next_id()
 
             module = Module(module_id, module_name, filename_in_utf8)
@@ -204,7 +212,10 @@ def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fm
                         py_db, frames_list
                     ):
 
-                    module_name = frame.f_globals.get('__name__', '')
+                    try:
+                        module_name = str(frame.f_globals.get('__name__', ''))
+                    except:
+                        module_name = '<unknown>'
 
                     module_events.extend(self.modules_manager.track_module(filename_in_utf8, module_name, frame))
 


Test patch:
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py b/src/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py
@@ -501,7 +501,11 @@ def run_process(self, args, writer):
 
             if not writer.finished_ok:
                 self.fail_with_message(
-                    "The thread that was doing the tests didn't finish successfully.", stdout, stderr, writer)
+                    "The thread that was doing the tests didn't finish successfully (writer.finished_ok = True not set).",
+                    stdout,
+                    stderr,
+                    writer
+                )
 
             while True:
                 if process.poll() is not None:
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_module.py b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_module.py
new file mode 100644
--- /dev/null
+++ b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_module.py
@@ -0,0 +1,17 @@
+class MyVersion(object):
+    pass
+
+
+class MyPackage(object):
+    pass
+
+
+class MyName(object):
+    pass
+
+
+__version__ = MyVersion()
+__package__ = MyPackage()
+__name__ = MyName()
+
+print('TEST SUCEEDED')  # Break here
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
@@ -2972,6 +2972,30 @@ def run(self):
         writer.finished_ok = True
 
 
+def test_module_crash(case_setup):
+    with case_setup.test_file('_debugger_case_module.py') as writer:
+        json_facade = JsonFacade(writer)
+
+        writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
+
+        json_facade.write_make_initial_run()
+
+        stopped_event = json_facade.wait_for_json_message(StoppedEvent)
+        thread_id = stopped_event.body.threadId
+
+        json_facade.write_request(
+            pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(threadId=thread_id)))
+
+        module_event = json_facade.wait_for_json_message(ModuleEvent)  # : :type module_event: ModuleEvent
+        assert 'MyName' in module_event.body.module.name
+        assert 'MyVersion' in module_event.body.module.version
+        assert 'MyPackage' in module_event.body.module.kwargs['package']
+
+        json_facade.write_continue()
+
+        writer.finished_ok = True
+
+
 def test_pydevd_systeminfo(case_setup):
     with case_setup.test_file('_debugger_case_print.py') as writer:
         json_facade = JsonFacade(writer)