Repository: microsoft/debugpy

Issue:
"subProcess" is not respected
debugpy always behaves as if it was set to true. Somewhere along the line, we lost the bit in launcher that translates it to a CLI switch. 

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25.png' alt='33.3%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list) [33.3% Coverage](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25.png' alt='33.3%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list) [33.3% Coverage](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=97&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25.png' alt='33.3%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list) [33.3% Coverage](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=97&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/src/debugpy/__init__.py b/src/debugpy/__init__.py
index dfd809ba..baa5a7c5 100644
--- a/src/debugpy/__init__.py
+++ b/src/debugpy/__init__.py
@@ -84,7 +84,10 @@ def configure(properties=None, **kwargs):
 
         debugpy.configure({"subProcess": False})
     """
-    pass
+
+    from debugpy.server import api
+
+    return api.configure(properties, **kwargs)
 
 
 def listen(address):
diff --git a/src/debugpy/launcher/handlers.py b/src/debugpy/launcher/handlers.py
index 0665a256..93b6c4b3 100644
--- a/src/debugpy/launcher/handlers.py
+++ b/src/debugpy/launcher/handlers.py
@@ -67,6 +67,8 @@ def launch_request(request):
             "--connect",
             str(port),
         ]
+        if not request("subProcess", True):
+            cmdline += ["--configure-subProcess", "False"]
         adapter_access_token = request("adapterAccessToken", unicode, optional=True)
         if adapter_access_token != ():
             cmdline += ["--adapter-access-token", compat.filename(adapter_access_token)]
diff --git a/src/debugpy/server/api.py b/src/debugpy/server/api.py
index 7696319d..3615d523 100644
--- a/src/debugpy/server/api.py
+++ b/src/debugpy/server/api.py
@@ -107,6 +107,7 @@ def _starts_debugging(func):
 
         ensure_logging()
         log.debug("{0}({1!r}, **{2!r})", func.__name__, address, kwargs)
+        log.info("Initial debug configuration: {0!j}", _config)
 
         settrace_kwargs = {
             "suspend": False,


Test patch:
diff --git a/tests/debug/runners.py b/tests/debug/runners.py
index 6014f0ae..5d0a7e2f 100644
--- a/tests/debug/runners.py
+++ b/tests/debug/runners.py
@@ -229,24 +229,38 @@ def attach_connect(session, target, method, cwd=None, wait=True, log_dir=None):
             args += ["--wait-for-client"]
         if log_dir is not None:
             args += ["--log-to", log_dir]
+        if "subProcess" in config:
+            args += ["--configure-subProcess", str(config["subProcess"])]
         debuggee_setup = None
     elif method == "api":
         args = []
+        api_config = {k: v for k, v in config.items() if k in {"subProcess"}}
         debuggee_setup = """
 import debugpy
 if {log_dir!r}:
     debugpy.log_to({log_dir!r})
+debugpy.configure({api_config!r})
 debugpy.listen(({host!r}, {port!r}))
 if {wait!r}:
     debugpy.wait_for_client()
 """
         debuggee_setup = fmt(
-            debuggee_setup, host=host, port=port, wait=wait, log_dir=log_dir
+            debuggee_setup,
+            host=host,
+            port=port,
+            wait=wait,
+            log_dir=log_dir,
+            api_config=api_config,
         )
     else:
         raise ValueError
     args += target.cli(session.spawn_debuggee.env)
 
+    try:
+        del config["subProcess"]
+    except KeyError:
+        pass
+
     session.spawn_debuggee(args, cwd=cwd, setup=debuggee_setup)
     if wait:
         session.wait_for_adapter_socket()
@@ -280,20 +294,31 @@ def attach_listen(session, target, method, cwd=None, log_dir=None):
         ]
         if log_dir is not None:
             args += ["--log-to", log_dir]
+        if "subProcess" in config:
+            args += ["--configure-subProcess", str(config["subProcess"])]
         debuggee_setup = None
     elif method == "api":
         args = []
+        api_config = {k: v for k, v in config.items() if k in {"subProcess"}}
         debuggee_setup = """
 import debugpy
 if {log_dir!r}:
     debugpy.log_to({log_dir!r})
+debugpy.configure({api_config!r})
 debugpy.connect({address!r})
 """
-        debuggee_setup = fmt(debuggee_setup, address=(host, port), log_dir=log_dir)
+        debuggee_setup = fmt(
+            debuggee_setup, address=(host, port), log_dir=log_dir, api_config=api_config
+        )
     else:
         raise ValueError
     args += target.cli(session.spawn_debuggee.env)
 
+    try:
+        del config["subProcess"]
+    except KeyError:
+        pass
+
     def spawn_debuggee(occ):
         assert occ.body == some.dict.containing({"host": host, "port": port})
         session.spawn_debuggee(args, cwd=cwd, setup=debuggee_setup)
@@ -313,15 +338,9 @@ all_launch = [
     launch["externalTerminal"],
 ]
 
-all_attach_listen = [
-    attach_listen["api"],
-    attach_listen["cli"],
-]
+all_attach_listen = [attach_listen["api"], attach_listen["cli"]]
 
-all_attach_connect = [
-    attach_connect["api"],
-    attach_connect["cli"],
-]
+all_attach_connect = [attach_connect["api"], attach_connect["cli"]]
 
 all_attach_socket = all_attach_listen + all_attach_connect
 
diff --git a/tests/debugpy/test_multiproc.py b/tests/debugpy/test_multiproc.py
index 7dbb66ee..0a0f86bb 100644
--- a/tests/debugpy/test_multiproc.py
+++ b/tests/debugpy/test_multiproc.py
@@ -159,7 +159,8 @@ def test_multiprocessing(pyfile, target, run, start_method):
                 parent_backchannel.send("continue")
 
 
-def test_subprocess(pyfile, target, run):
+@pytest.mark.parametrize("subProcess", [True, False, None])
+def test_subprocess(pyfile, target, run, subProcess):
     @pyfile
     def child():
         import os
@@ -195,6 +196,8 @@ def test_subprocess(pyfile, target, run):
 
     with debug.Session() as parent_session:
         backchannel = parent_session.open_backchannel()
+        if subProcess is not None:
+            parent_session.config["subProcess"] = subProcess
 
         with run(parent_session, target(parent, args=[child])):
             pass
@@ -213,6 +216,9 @@ def test_subprocess(pyfile, target, run):
             }
         )
 
+        if subProcess is False:
+            return
+
         child_config = parent_session.wait_for_next_event("debugpyAttach")
         assert child_config == expected_child_config
         parent_session.proceed()