Repository: microsoft/ptvsd

Issue:
Launch a SubModule
## Environment data

- PTVSD version: ms-python.python-2019.3.6139
- OS and version: MacOS
- Python version (& distribution if applicable, e.g. Anaconda): 3.6.5
- Using VS Code or Visual Studio: VS Code (or CLI)

## Actual behavior

Cannot launch a __main__.py in a submodule...
ptvsd complains that the module is not found

## Expected  behavior

Launching a __main__.py method in a submodule works

## Steps to reproduce:
1. Create a __main__.py in a submodule.
2. Create a launch configuration that runs it with "module": "root.sub" or use the CLI:
/projects/pybot/venv/bin/python /Users/jkassis/.vscode/extensions/ms-python.python-2019.3.6139/pythonFiles/ptvsd_launcher.py --default --host 127.0.0.1 --port 51251 -m commands.pybot
3. Run it.
4. Watch the magic.
5. Move it up one level
6. Run...
/projects/pybot/venv/bin/python /Users/jkassis/.vscode/extensions/ms-python.python-2019.3.6139/pythonFiles/ptvsd_launcher.py --default --host 127.0.0.1 --port 51251 -m pybot
7. Watch the magic.


Discussion:

Resolving patch:
diff --git a/src/ptvsd/__main__.py b/src/ptvsd/__main__.py
--- a/src/ptvsd/__main__.py
+++ b/src/ptvsd/__main__.py
@@ -213,6 +213,10 @@ def setup_connection():
     elif opts.target_kind == 'file':
         sys.argv[0] = opts.target
     elif opts.target_kind == 'module':
+        # Add current directory to path, like Python itself does for -m. This must
+        # be in place before trying to use find_spec below to resolve submodules.
+        sys.path.insert(0, '')
+
         # We want to do the same thing that run_module() would do here, without
         # actually invoking it. On Python 3, it's exposed as a public API, but
         # on Python 2, we have to invoke a private function in runpy for this.
@@ -280,9 +284,6 @@ def run_module():
 
     ptvsd.log.info('Running module {0}', target)
 
-    # Add current directory to path, like Python itself does for -m.
-    sys.path.insert(0, '')
-
     # Docs say that runpy.run_module is equivalent to -m, but it's not actually
     # the case for packages - -m sets __name__ to '__main__', but run_module sets
     # it to `pkg.__main__`. This breaks everything that uses the standard pattern


Test patch:
diff --git a/tests/func/test_run.py b/tests/func/test_run.py
--- a/tests/func/test_run.py
+++ b/tests/func/test_run.py
@@ -11,6 +11,7 @@
 import ptvsd
 
 from tests.helpers import print
+from tests.helpers.pathutils import get_test_root
 from tests.helpers.pattern import ANY, Regex
 from tests.helpers.session import DebugSession
 from tests.helpers.timeline import Event
@@ -49,6 +50,20 @@ def code_to_debug():
         session.wait_for_exit()
 
 
+def test_run_submodule():
+    cwd = get_test_root('testpkgs')
+    with DebugSession() as session:
+        session.initialize(
+            target=('module', 'pkg1.sub'),
+            start_method='launch',
+            ignore_unobserved=[Event('continued')],
+            cwd=cwd,
+        )
+        session.start_debugging()
+        session.wait_for_next(Event('output', ANY.dict_with({'category': 'stdout', 'output': 'three'})))
+        session.wait_for_exit()
+
+
 @pytest.mark.parametrize('run_as', ['file', 'module', 'code'])
 def test_nodebug(pyfile, run_as):
     @pyfile
diff --git a/tests/func/testfiles/testpkgs/pkg1/sub/__init__.py b/tests/func/testfiles/testpkgs/pkg1/sub/__init__.py
new file mode 100644
diff --git a/tests/func/testfiles/testpkgs/pkg1/sub/__main__.py b/tests/func/testfiles/testpkgs/pkg1/sub/__main__.py
new file mode 100644
--- /dev/null
+++ b/tests/func/testfiles/testpkgs/pkg1/sub/__main__.py
@@ -0,0 +1,3 @@
+print('one')
+print('two')
+print('three')