We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Deferred server events are not always propagated
Handlers for "launch" and "attach" requests in the adapter don't lock the session correctly. As a result, there's a race condition between the code that replays server events after responding to those requests, and the code that queues up server events for replaying - and it's possible for an event to be deferred after events have already been replayed, which results in it never being propagated.

"process" event is the one this affects most often, and when that happens, it breaks the debug session.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/ptvsd/adapter/ide.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License. See LICENSE in the project root
3 # for license information.
4 
5 from __future__ import absolute_import, print_function, unicode_literals
6 
7 import platform
8 
9 import ptvsd
10 from ptvsd.common import json, log, messaging
11 from ptvsd.common.compat import unicode
12 from ptvsd.adapter import components
13 
14 
15 class IDE(components.Component):
16     """Handles the IDE side of a debug session."""
17 
18     message_handler = components.Component.message_handler
19 
20     class Capabilities(components.Capabilities):
21         PROPERTIES = {
22             "supportsVariableType": False,
23             "supportsVariablePaging": False,
24             "supportsRunInTerminalRequest": False,
25             "supportsMemoryReferences": False,
26         }
27 
28     class Expectations(components.Capabilities):
29         PROPERTIES = {
30             "locale": "en-US",
31             "linesStartAt1": True,
32             "columnsStartAt1": True,
33             "pathFormat": json.enum("path"),  # we don't support "uri"
34         }
35 
36     def __init__(self, session, stream):
37         super(IDE, self).__init__(session, stream)
38 
39         self.client_id = None
40         """ID of the connecting client. This can be 'test' while running tests."""
41 
42         self._initialize_request = None
43         """The "initialize" request as received from the IDE, to propagate to the
44         server later."""
45 
46         self._deferred_events = []
47         """Deferred events from the launcher and the server that must be propagated
48         only if and when the "launch" or "attach" response is sent.
49         """
50 
51         assert not session.ide
52         session.ide = self
53 
54         self.channel.send_event(
55             "output",
56             {
57                 "category": "telemetry",
58                 "output": "ptvsd.adapter",
59                 "data": {"packageVersion": ptvsd.__version__},
60             },
61         )
62 
63     def propagate_after_start(self, event):
64         # pydevd starts sending events as soon as we connect, but the IDE doesn't
65         # expect to see any until it receives the response to "launch" or "attach"
66         # request. If IDE is not ready yet, save the event instead of propagating
67         # it immediately.
68         if self._deferred_events is not None:
69             self._deferred_events.append(event)
70             log.debug("Propagation deferred.")
71         else:
72             self.ide.channel.propagate(event)
73 
74     def _propagate_deferred_events(self):
75         log.debug("Propagating deferred events to {0}...", self.ide)
76         for event in self._deferred_events:
77             log.debug("Propagating deferred {0}", event.describe())
78             self.ide.channel.propagate(event)
79         log.info("All deferred events propagated to {0}.", self.ide)
80         self._deferred_events = None
81 
82     # Generic event handler. There are no specific handlers for IDE events, because
83     # there are no events from the IDE in DAP - but we propagate them if we can, in
84     # case some events appear in future protocol versions.
85     @message_handler
86     def event(self, event):
87         if self.server:
88             self.server.channel.propagate(event)
89 
90     # Generic request handler, used if there's no specific handler below.
91     @message_handler
92     def request(self, request):
93         return self.server.channel.delegate(request)
94 
95     @message_handler
96     def initialize_request(self, request):
97         if self._initialize_request is not None:
98             raise request.isnt_valid("Session is already initialized")
99 
100         self.client_id = request("clientID", "")
101         self.capabilities = self.Capabilities(self, request)
102         self.expectations = self.Expectations(self, request)
103         self._initialize_request = request
104 
105         return {
106             "supportsCompletionsRequest": True,
107             "supportsConditionalBreakpoints": True,
108             "supportsConfigurationDoneRequest": True,
109             "supportsDebuggerProperties": True,
110             "supportsDelayedStackTraceLoading": True,
111             "supportsEvaluateForHovers": True,
112             "supportsExceptionInfoRequest": True,
113             "supportsExceptionOptions": True,
114             "supportsHitConditionalBreakpoints": True,
115             "supportsLogPoints": True,
116             "supportsModulesRequest": True,
117             "supportsSetExpression": True,
118             "supportsSetVariable": True,
119             "supportsValueFormattingOptions": True,
120             "supportsTerminateDebuggee": True,
121             "supportsGotoTargetsRequest": True,
122             "exceptionBreakpointFilters": [
123                 {"filter": "raised", "label": "Raised Exceptions", "default": False},
124                 {"filter": "uncaught", "label": "Uncaught Exceptions", "default": True},
125             ],
126         }
127 
128     # Common code for "launch" and "attach" request handlers.
129     #
130     # See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
131     # for the sequence of request and events necessary to orchestrate the start.
132     def _start_message_handler(f):
133         f = components.Component.message_handler(f)
134 
135         def handle(self, request):
136             assert request.is_request("launch", "attach")
137             if self._initialize_request is None:
138                 raise request.isnt_valid("Session is not initialized yet")
139             if self.launcher:
140                 raise request.isnt_valid("Session is already started")
141 
142             self.session.no_debug = request("noDebug", json.default(False))
143             self.session.debug_options = set(
144                 request("debugOptions", json.array(unicode))
145             )
146 
147             f(self, request)
148 
149             if self.server:
150                 self.server.initialize(self._initialize_request)
151                 self._initialize_request = None
152 
153                 # pydevd doesn't send "initialized", and responds to the start request
154                 # immediately, without waiting for "configurationDone". If it changes
155                 # to conform to the DAP spec, we'll need to defer waiting for response.
156                 self.server.channel.delegate(request)
157 
158             if self.session.no_debug:
159                 request.respond({})
160                 self._propagate_deferred_events()
161                 return
162 
163             if {"WindowsClient", "Windows"} & self.session.debug_options:
164                 client_os_type = "WINDOWS"
165             elif {"UnixClient", "UNIX"} & self.session.debug_options:
166                 client_os_type = "UNIX"
167             else:
168                 client_os_type = "WINDOWS" if platform.system() == "Windows" else "UNIX"
169             self.server.channel.request(
170                 "setDebuggerProperty",
171                 {
172                     "skipSuspendOnBreakpointException": ("BaseException",),
173                     "skipPrintBreakpointException": ("NameError",),
174                     "multiThreadsSingleNotification": True,
175                     "ideOS": client_os_type,
176                 },
177             )
178 
179             # Let the IDE know that it can begin configuring the adapter.
180             self.channel.send_event("initialized")
181 
182             self._start_request = request
183             return messaging.NO_RESPONSE  # will respond on "configurationDone"
184 
185         return handle
186 
187     @_start_message_handler
188     def launch_request(self, request):
189         sudo = request("sudo", json.default("Sudo" in self.session.debug_options))
190         if sudo:
191             if platform.system() == "Windows":
192                 raise request.cant_handle('"sudo":true is not supported on Windows.')
193         else:
194             if "Sudo" in self.session.debug_options:
195                 raise request.isnt_valid(
196                     '"sudo":false and "debugOptions":["Sudo"] are mutually exclusive'
197                 )
198 
199         # Launcher doesn't use the command line at all, but we pass the arguments so
200         # that they show up in the terminal if we're using "runInTerminal".
201         if "program" in request:
202             args = request("program", json.array(unicode, vectorize=True, size=(1,)))
203         elif "module" in request:
204             args = ["-m"] + request(
205                 "module", json.array(unicode, vectorize=True, size=(1,))
206             )
207         elif "code" in request:
208             args = ["-c"] + request(
209                 "code", json.array(unicode, vectorize=True, size=(1,))
210             )
211         else:
212             args = []
213         args += request("args", json.array(unicode))
214 
215         console = request(
216             "console",
217             json.enum(
218                 "internalConsole",
219                 "integratedTerminal",
220                 "externalTerminal",
221                 optional=True,
222             ),
223         )
224         console_title = request("consoleTitle", json.default("Python Debug Console"))
225 
226         self.session.spawn_debuggee(request, sudo, args, console, console_title)
227 
228         if "RedirectOutput" in self.session.debug_options:
229             # The launcher is doing output redirection, so we don't need the server.
230             request.arguments["debugOptions"].remove("RedirectOutput")
231 
232     @_start_message_handler
233     def attach_request(self, request):
234         if self.session.no_debug:
235             raise request.isnt_valid('"noDebug" is not supported for "attach"')
236 
237         pid = request("processId", int, optional=True)
238         if pid == ():
239             # When the adapter is spawned by the debug server, it is connected to the
240             # latter from the get go, and "host" and "port" in the "attach" request
241             # are actually the host and port on which the adapter itself was listening,
242             # so we can ignore those.
243             if self.server:
244                 return
245 
246             host = request("host", "127.0.0.1")
247             port = request("port", int)
248             if request("listen", False):
249                 with self.accept_connection_from_server((host, port)):
250                     pass
251             else:
252                 self.session.connect_to_server((host, port))
253         else:
254             if self.server:
255                 raise request.isnt_valid(
256                     '"attach" with "processId" cannot be serviced by adapter '
257                     "that is already associated with a debug server"
258                 )
259 
260             ptvsd_args = request("ptvsdArgs", json.array(unicode))
261             self.session.inject_server(pid, ptvsd_args)
262 
263     @message_handler
264     def configurationDone_request(self, request):
265         if self._start_request is None:
266             request.cant_handle(
267                 '"configurationDone" is only allowed during handling of a "launch" '
268                 'or an "attach" request'
269             )
270 
271         try:
272             request.respond(self.server.channel.delegate(request))
273         finally:
274             self._start_request.respond({})
275             self._start_request = None
276             self._propagate_deferred_events()
277 
278     @message_handler
279     def pause_request(self, request):
280         request.arguments["threadId"] = "*"
281         return self.server.channel.delegate(request)
282 
283     @message_handler
284     def continue_request(self, request):
285         request.arguments["threadId"] = "*"
286 
287         try:
288             return self.server.channel.delegate(request)
289         except messaging.NoMoreMessages:
290             # pydevd can sometimes allow the debuggee to exit before the queued
291             # "continue" response gets sent. Thus, a failed "continue" response
292             # indicating that the server disconnected should be treated as success.
293             return {"allThreadsContinued": True}
294 
295     @message_handler
296     def ptvsd_systemInfo_request(self, request):
297         result = {"ptvsd": {"version": ptvsd.__version__}}
298         if self.server:
299             try:
300                 pydevd_info = self.server.channel.request("pydevdSystemInfo")
301             except Exception:
302                 # If the server has already disconnected, or couldn't handle it,
303                 # report what we've got.
304                 pass
305             else:
306                 result.update(pydevd_info)
307         return result
308 
309     @message_handler
310     def terminate_request(self, request):
311         self.session.finalize('IDE requested "terminate"', terminate_debuggee=True)
312         return {}
313 
314     @message_handler
315     def disconnect_request(self, request):
316         self.session.finalize(
317             'IDE requested "disconnect"',
318             request("terminateDebuggee", json.default(bool(self.launcher))),
319         )
320         return {}
321
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/ptvsd/adapter/ide.py b/src/ptvsd/adapter/ide.py
--- a/src/ptvsd/adapter/ide.py
+++ b/src/ptvsd/adapter/ide.py
@@ -130,8 +130,7 @@
     # See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
     # for the sequence of request and events necessary to orchestrate the start.
     def _start_message_handler(f):
-        f = components.Component.message_handler(f)
-
+        @components.Component.message_handler
         def handle(self, request):
             assert request.is_request("launch", "attach")
             if self._initialize_request is None:

{"golden_diff": "diff --git a/src/ptvsd/adapter/ide.py b/src/ptvsd/adapter/ide.py\n--- a/src/ptvsd/adapter/ide.py\n+++ b/src/ptvsd/adapter/ide.py\n@@ -130,8 +130,7 @@\n     # See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522\n     # for the sequence of request and events necessary to orchestrate the start.\n     def _start_message_handler(f):\n-        f = components.Component.message_handler(f)\n-\n+        @components.Component.message_handler\n         def handle(self, request):\n             assert request.is_request(\"launch\", \"attach\")\n             if self._initialize_request is None:\n", "issue": "Deferred server events are not always propagated\nHandlers for \"launch\" and \"attach\" requests in the adapter don't lock the session correctly. As a result, there's a race condition between the code that replays server events after responding to those requests, and the code that queues up server events for replaying - and it's possible for an event to be deferred after events have already been replayed, which results in it never being propagated.\r\n\r\n\"process\" event is the one this affects most often, and when that happens, it breaks the debug session.\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License. See LICENSE in the project root\n# for license information.\n\nfrom __future__ import absolute_import, print_function, unicode_literals\n\nimport platform\n\nimport ptvsd\nfrom ptvsd.common import json, log, messaging\nfrom ptvsd.common.compat import unicode\nfrom ptvsd.adapter import components\n\n\nclass IDE(components.Component):\n    \"\"\"Handles the IDE side of a debug session.\"\"\"\n\n    message_handler = components.Component.message_handler\n\n    class Capabilities(components.Capabilities):\n        PROPERTIES = {\n            \"supportsVariableType\": False,\n            \"supportsVariablePaging\": False,\n            \"supportsRunInTerminalRequest\": False,\n            \"supportsMemoryReferences\": False,\n        }\n\n    class Expectations(components.Capabilities):\n        PROPERTIES = {\n            \"locale\": \"en-US\",\n            \"linesStartAt1\": True,\n            \"columnsStartAt1\": True,\n            \"pathFormat\": json.enum(\"path\"),  # we don't support \"uri\"\n        }\n\n    def __init__(self, session, stream):\n        super(IDE, self).__init__(session, stream)\n\n        self.client_id = None\n        \"\"\"ID of the connecting client. This can be 'test' while running tests.\"\"\"\n\n        self._initialize_request = None\n        \"\"\"The \"initialize\" request as received from the IDE, to propagate to the\n        server later.\"\"\"\n\n        self._deferred_events = []\n        \"\"\"Deferred events from the launcher and the server that must be propagated\n        only if and when the \"launch\" or \"attach\" response is sent.\n        \"\"\"\n\n        assert not session.ide\n        session.ide = self\n\n        self.channel.send_event(\n            \"output\",\n            {\n                \"category\": \"telemetry\",\n                \"output\": \"ptvsd.adapter\",\n                \"data\": {\"packageVersion\": ptvsd.__version__},\n            },\n        )\n\n    def propagate_after_start(self, event):\n        # pydevd starts sending events as soon as we connect, but the IDE doesn't\n        # expect to see any until it receives the response to \"launch\" or \"attach\"\n        # request. If IDE is not ready yet, save the event instead of propagating\n        # it immediately.\n        if self._deferred_events is not None:\n            self._deferred_events.append(event)\n            log.debug(\"Propagation deferred.\")\n        else:\n            self.ide.channel.propagate(event)\n\n    def _propagate_deferred_events(self):\n        log.debug(\"Propagating deferred events to {0}...\", self.ide)\n        for event in self._deferred_events:\n            log.debug(\"Propagating deferred {0}\", event.describe())\n            self.ide.channel.propagate(event)\n        log.info(\"All deferred events propagated to {0}.\", self.ide)\n        self._deferred_events = None\n\n    # Generic event handler. There are no specific handlers for IDE events, because\n    # there are no events from the IDE in DAP - but we propagate them if we can, in\n    # case some events appear in future protocol versions.\n    @message_handler\n    def event(self, event):\n        if self.server:\n            self.server.channel.propagate(event)\n\n    # Generic request handler, used if there's no specific handler below.\n    @message_handler\n    def request(self, request):\n        return self.server.channel.delegate(request)\n\n    @message_handler\n    def initialize_request(self, request):\n        if self._initialize_request is not None:\n            raise request.isnt_valid(\"Session is already initialized\")\n\n        self.client_id = request(\"clientID\", \"\")\n        self.capabilities = self.Capabilities(self, request)\n        self.expectations = self.Expectations(self, request)\n        self._initialize_request = request\n\n        return {\n            \"supportsCompletionsRequest\": True,\n            \"supportsConditionalBreakpoints\": True,\n            \"supportsConfigurationDoneRequest\": True,\n            \"supportsDebuggerProperties\": True,\n            \"supportsDelayedStackTraceLoading\": True,\n            \"supportsEvaluateForHovers\": True,\n            \"supportsExceptionInfoRequest\": True,\n            \"supportsExceptionOptions\": True,\n            \"supportsHitConditionalBreakpoints\": True,\n            \"supportsLogPoints\": True,\n            \"supportsModulesRequest\": True,\n            \"supportsSetExpression\": True,\n            \"supportsSetVariable\": True,\n            \"supportsValueFormattingOptions\": True,\n            \"supportsTerminateDebuggee\": True,\n            \"supportsGotoTargetsRequest\": True,\n            \"exceptionBreakpointFilters\": [\n                {\"filter\": \"raised\", \"label\": \"Raised Exceptions\", \"default\": False},\n                {\"filter\": \"uncaught\", \"label\": \"Uncaught Exceptions\", \"default\": True},\n            ],\n        }\n\n    # Common code for \"launch\" and \"attach\" request handlers.\n    #\n    # See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522\n    # for the sequence of request and events necessary to orchestrate the start.\n    def _start_message_handler(f):\n        f = components.Component.message_handler(f)\n\n        def handle(self, request):\n            assert request.is_request(\"launch\", \"attach\")\n            if self._initialize_request is None:\n                raise request.isnt_valid(\"Session is not initialized yet\")\n            if self.launcher:\n                raise request.isnt_valid(\"Session is already started\")\n\n            self.session.no_debug = request(\"noDebug\", json.default(False))\n            self.session.debug_options = set(\n                request(\"debugOptions\", json.array(unicode))\n            )\n\n            f(self, request)\n\n            if self.server:\n                self.server.initialize(self._initialize_request)\n                self._initialize_request = None\n\n                # pydevd doesn't send \"initialized\", and responds to the start request\n                # immediately, without waiting for \"configurationDone\". If it changes\n                # to conform to the DAP spec, we'll need to defer waiting for response.\n                self.server.channel.delegate(request)\n\n            if self.session.no_debug:\n                request.respond({})\n                self._propagate_deferred_events()\n                return\n\n            if {\"WindowsClient\", \"Windows\"} & self.session.debug_options:\n                client_os_type = \"WINDOWS\"\n            elif {\"UnixClient\", \"UNIX\"} & self.session.debug_options:\n                client_os_type = \"UNIX\"\n            else:\n                client_os_type = \"WINDOWS\" if platform.system() == \"Windows\" else \"UNIX\"\n            self.server.channel.request(\n                \"setDebuggerProperty\",\n                {\n                    \"skipSuspendOnBreakpointException\": (\"BaseException\",),\n                    \"skipPrintBreakpointException\": (\"NameError\",),\n                    \"multiThreadsSingleNotification\": True,\n                    \"ideOS\": client_os_type,\n                },\n            )\n\n            # Let the IDE know that it can begin configuring the adapter.\n            self.channel.send_event(\"initialized\")\n\n            self._start_request = request\n            return messaging.NO_RESPONSE  # will respond on \"configurationDone\"\n\n        return handle\n\n    @_start_message_handler\n    def launch_request(self, request):\n        sudo = request(\"sudo\", json.default(\"Sudo\" in self.session.debug_options))\n        if sudo:\n            if platform.system() == \"Windows\":\n                raise request.cant_handle('\"sudo\":true is not supported on Windows.')\n        else:\n            if \"Sudo\" in self.session.debug_options:\n                raise request.isnt_valid(\n                    '\"sudo\":false and \"debugOptions\":[\"Sudo\"] are mutually exclusive'\n                )\n\n        # Launcher doesn't use the command line at all, but we pass the arguments so\n        # that they show up in the terminal if we're using \"runInTerminal\".\n        if \"program\" in request:\n            args = request(\"program\", json.array(unicode, vectorize=True, size=(1,)))\n        elif \"module\" in request:\n            args = [\"-m\"] + request(\n                \"module\", json.array(unicode, vectorize=True, size=(1,))\n            )\n        elif \"code\" in request:\n            args = [\"-c\"] + request(\n                \"code\", json.array(unicode, vectorize=True, size=(1,))\n            )\n        else:\n            args = []\n        args += request(\"args\", json.array(unicode))\n\n        console = request(\n            \"console\",\n            json.enum(\n                \"internalConsole\",\n                \"integratedTerminal\",\n                \"externalTerminal\",\n                optional=True,\n            ),\n        )\n        console_title = request(\"consoleTitle\", json.default(\"Python Debug Console\"))\n\n        self.session.spawn_debuggee(request, sudo, args, console, console_title)\n\n        if \"RedirectOutput\" in self.session.debug_options:\n            # The launcher is doing output redirection, so we don't need the server.\n            request.arguments[\"debugOptions\"].remove(\"RedirectOutput\")\n\n    @_start_message_handler\n    def attach_request(self, request):\n        if self.session.no_debug:\n            raise request.isnt_valid('\"noDebug\" is not supported for \"attach\"')\n\n        pid = request(\"processId\", int, optional=True)\n        if pid == ():\n            # When the adapter is spawned by the debug server, it is connected to the\n            # latter from the get go, and \"host\" and \"port\" in the \"attach\" request\n            # are actually the host and port on which the adapter itself was listening,\n            # so we can ignore those.\n            if self.server:\n                return\n\n            host = request(\"host\", \"127.0.0.1\")\n            port = request(\"port\", int)\n            if request(\"listen\", False):\n                with self.accept_connection_from_server((host, port)):\n                    pass\n            else:\n                self.session.connect_to_server((host, port))\n        else:\n            if self.server:\n                raise request.isnt_valid(\n                    '\"attach\" with \"processId\" cannot be serviced by adapter '\n                    \"that is already associated with a debug server\"\n                )\n\n            ptvsd_args = request(\"ptvsdArgs\", json.array(unicode))\n            self.session.inject_server(pid, ptvsd_args)\n\n    @message_handler\n    def configurationDone_request(self, request):\n        if self._start_request is None:\n            request.cant_handle(\n                '\"configurationDone\" is only allowed during handling of a \"launch\" '\n                'or an \"attach\" request'\n            )\n\n        try:\n            request.respond(self.server.channel.delegate(request))\n        finally:\n            self._start_request.respond({})\n            self._start_request = None\n            self._propagate_deferred_events()\n\n    @message_handler\n    def pause_request(self, request):\n        request.arguments[\"threadId\"] = \"*\"\n        return self.server.channel.delegate(request)\n\n    @message_handler\n    def continue_request(self, request):\n        request.arguments[\"threadId\"] = \"*\"\n\n        try:\n            return self.server.channel.delegate(request)\n        except messaging.NoMoreMessages:\n            # pydevd can sometimes allow the debuggee to exit before the queued\n            # \"continue\" response gets sent. Thus, a failed \"continue\" response\n            # indicating that the server disconnected should be treated as success.\n            return {\"allThreadsContinued\": True}\n\n    @message_handler\n    def ptvsd_systemInfo_request(self, request):\n        result = {\"ptvsd\": {\"version\": ptvsd.__version__}}\n        if self.server:\n            try:\n                pydevd_info = self.server.channel.request(\"pydevdSystemInfo\")\n            except Exception:\n                # If the server has already disconnected, or couldn't handle it,\n                # report what we've got.\n                pass\n            else:\n                result.update(pydevd_info)\n        return result\n\n    @message_handler\n    def terminate_request(self, request):\n        self.session.finalize('IDE requested \"terminate\"', terminate_debuggee=True)\n        return {}\n\n    @message_handler\n    def disconnect_request(self, request):\n        self.session.finalize(\n            'IDE requested \"disconnect\"',\n            request(\"terminateDebuggee\", json.default(bool(self.launcher))),\n        )\n        return {}\n", "path": "src/ptvsd/adapter/ide.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License. See LICENSE in the project root\n# for license information.\n\nfrom __future__ import absolute_import, print_function, unicode_literals\n\nimport platform\n\nimport ptvsd\nfrom ptvsd.common import json, log, messaging\nfrom ptvsd.common.compat import unicode\nfrom ptvsd.adapter import components\n\n\nclass IDE(components.Component):\n    \"\"\"Handles the IDE side of a debug session.\"\"\"\n\n    message_handler = components.Component.message_handler\n\n    class Capabilities(components.Capabilities):\n        PROPERTIES = {\n            \"supportsVariableType\": False,\n            \"supportsVariablePaging\": False,\n            \"supportsRunInTerminalRequest\": False,\n            \"supportsMemoryReferences\": False,\n        }\n\n    class Expectations(components.Capabilities):\n        PROPERTIES = {\n            \"locale\": \"en-US\",\n            \"linesStartAt1\": True,\n            \"columnsStartAt1\": True,\n            \"pathFormat\": json.enum(\"path\"),  # we don't support \"uri\"\n        }\n\n    def __init__(self, session, stream):\n        super(IDE, self).__init__(session, stream)\n\n        self.client_id = None\n        \"\"\"ID of the connecting client. This can be 'test' while running tests.\"\"\"\n\n        self._initialize_request = None\n        \"\"\"The \"initialize\" request as received from the IDE, to propagate to the\n        server later.\"\"\"\n\n        self._deferred_events = []\n        \"\"\"Deferred events from the launcher and the server that must be propagated\n        only if and when the \"launch\" or \"attach\" response is sent.\n        \"\"\"\n\n        assert not session.ide\n        session.ide = self\n\n        self.channel.send_event(\n            \"output\",\n            {\n                \"category\": \"telemetry\",\n                \"output\": \"ptvsd.adapter\",\n                \"data\": {\"packageVersion\": ptvsd.__version__},\n            },\n        )\n\n    def propagate_after_start(self, event):\n        # pydevd starts sending events as soon as we connect, but the IDE doesn't\n        # expect to see any until it receives the response to \"launch\" or \"attach\"\n        # request. If IDE is not ready yet, save the event instead of propagating\n        # it immediately.\n        if self._deferred_events is not None:\n            self._deferred_events.append(event)\n            log.debug(\"Propagation deferred.\")\n        else:\n            self.ide.channel.propagate(event)\n\n    def _propagate_deferred_events(self):\n        log.debug(\"Propagating deferred events to {0}...\", self.ide)\n        for event in self._deferred_events:\n            log.debug(\"Propagating deferred {0}\", event.describe())\n            self.ide.channel.propagate(event)\n        log.info(\"All deferred events propagated to {0}.\", self.ide)\n        self._deferred_events = None\n\n    # Generic event handler. There are no specific handlers for IDE events, because\n    # there are no events from the IDE in DAP - but we propagate them if we can, in\n    # case some events appear in future protocol versions.\n    @message_handler\n    def event(self, event):\n        if self.server:\n            self.server.channel.propagate(event)\n\n    # Generic request handler, used if there's no specific handler below.\n    @message_handler\n    def request(self, request):\n        return self.server.channel.delegate(request)\n\n    @message_handler\n    def initialize_request(self, request):\n        if self._initialize_request is not None:\n            raise request.isnt_valid(\"Session is already initialized\")\n\n        self.client_id = request(\"clientID\", \"\")\n        self.capabilities = self.Capabilities(self, request)\n        self.expectations = self.Expectations(self, request)\n        self._initialize_request = request\n\n        return {\n            \"supportsCompletionsRequest\": True,\n            \"supportsConditionalBreakpoints\": True,\n            \"supportsConfigurationDoneRequest\": True,\n            \"supportsDebuggerProperties\": True,\n            \"supportsDelayedStackTraceLoading\": True,\n            \"supportsEvaluateForHovers\": True,\n            \"supportsExceptionInfoRequest\": True,\n            \"supportsExceptionOptions\": True,\n            \"supportsHitConditionalBreakpoints\": True,\n            \"supportsLogPoints\": True,\n            \"supportsModulesRequest\": True,\n            \"supportsSetExpression\": True,\n            \"supportsSetVariable\": True,\n            \"supportsValueFormattingOptions\": True,\n            \"supportsTerminateDebuggee\": True,\n            \"supportsGotoTargetsRequest\": True,\n            \"exceptionBreakpointFilters\": [\n                {\"filter\": \"raised\", \"label\": \"Raised Exceptions\", \"default\": False},\n                {\"filter\": \"uncaught\", \"label\": \"Uncaught Exceptions\", \"default\": True},\n            ],\n        }\n\n    # Common code for \"launch\" and \"attach\" request handlers.\n    #\n    # See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522\n    # for the sequence of request and events necessary to orchestrate the start.\n    def _start_message_handler(f):\n        @components.Component.message_handler\n        def handle(self, request):\n            assert request.is_request(\"launch\", \"attach\")\n            if self._initialize_request is None:\n                raise request.isnt_valid(\"Session is not initialized yet\")\n            if self.launcher:\n                raise request.isnt_valid(\"Session is already started\")\n\n            self.session.no_debug = request(\"noDebug\", json.default(False))\n            self.session.debug_options = set(\n                request(\"debugOptions\", json.array(unicode))\n            )\n\n            f(self, request)\n\n            if self.server:\n                self.server.initialize(self._initialize_request)\n                self._initialize_request = None\n\n                # pydevd doesn't send \"initialized\", and responds to the start request\n                # immediately, without waiting for \"configurationDone\". If it changes\n                # to conform to the DAP spec, we'll need to defer waiting for response.\n                self.server.channel.delegate(request)\n\n            if self.session.no_debug:\n                request.respond({})\n                self._propagate_deferred_events()\n                return\n\n            if {\"WindowsClient\", \"Windows\"} & self.session.debug_options:\n                client_os_type = \"WINDOWS\"\n            elif {\"UnixClient\", \"UNIX\"} & self.session.debug_options:\n                client_os_type = \"UNIX\"\n            else:\n                client_os_type = \"WINDOWS\" if platform.system() == \"Windows\" else \"UNIX\"\n            self.server.channel.request(\n                \"setDebuggerProperty\",\n                {\n                    \"skipSuspendOnBreakpointException\": (\"BaseException\",),\n                    \"skipPrintBreakpointException\": (\"NameError\",),\n                    \"multiThreadsSingleNotification\": True,\n                    \"ideOS\": client_os_type,\n                },\n            )\n\n            # Let the IDE know that it can begin configuring the adapter.\n            self.channel.send_event(\"initialized\")\n\n            self._start_request = request\n            return messaging.NO_RESPONSE  # will respond on \"configurationDone\"\n\n        return handle\n\n    @_start_message_handler\n    def launch_request(self, request):\n        sudo = request(\"sudo\", json.default(\"Sudo\" in self.session.debug_options))\n        if sudo:\n            if platform.system() == \"Windows\":\n                raise request.cant_handle('\"sudo\":true is not supported on Windows.')\n        else:\n            if \"Sudo\" in self.session.debug_options:\n                raise request.isnt_valid(\n                    '\"sudo\":false and \"debugOptions\":[\"Sudo\"] are mutually exclusive'\n                )\n\n        # Launcher doesn't use the command line at all, but we pass the arguments so\n        # that they show up in the terminal if we're using \"runInTerminal\".\n        if \"program\" in request:\n            args = request(\"program\", json.array(unicode, vectorize=True, size=(1,)))\n        elif \"module\" in request:\n            args = [\"-m\"] + request(\n                \"module\", json.array(unicode, vectorize=True, size=(1,))\n            )\n        elif \"code\" in request:\n            args = [\"-c\"] + request(\n                \"code\", json.array(unicode, vectorize=True, size=(1,))\n            )\n        else:\n            args = []\n        args += request(\"args\", json.array(unicode))\n\n        console = request(\n            \"console\",\n            json.enum(\n                \"internalConsole\",\n                \"integratedTerminal\",\n                \"externalTerminal\",\n                optional=True,\n            ),\n        )\n        console_title = request(\"consoleTitle\", json.default(\"Python Debug Console\"))\n\n        self.session.spawn_debuggee(request, sudo, args, console, console_title)\n\n        if \"RedirectOutput\" in self.session.debug_options:\n            # The launcher is doing output redirection, so we don't need the server.\n            request.arguments[\"debugOptions\"].remove(\"RedirectOutput\")\n\n    @_start_message_handler\n    def attach_request(self, request):\n        if self.session.no_debug:\n            raise request.isnt_valid('\"noDebug\" is not supported for \"attach\"')\n\n        pid = request(\"processId\", int, optional=True)\n        if pid == ():\n            # When the adapter is spawned by the debug server, it is connected to the\n            # latter from the get go, and \"host\" and \"port\" in the \"attach\" request\n            # are actually the host and port on which the adapter itself was listening,\n            # so we can ignore those.\n            if self.server:\n                return\n\n            host = request(\"host\", \"127.0.0.1\")\n            port = request(\"port\", int)\n            if request(\"listen\", False):\n                with self.accept_connection_from_server((host, port)):\n                    pass\n            else:\n                self.session.connect_to_server((host, port))\n        else:\n            if self.server:\n                raise request.isnt_valid(\n                    '\"attach\" with \"processId\" cannot be serviced by adapter '\n                    \"that is already associated with a debug server\"\n                )\n\n            ptvsd_args = request(\"ptvsdArgs\", json.array(unicode))\n            self.session.inject_server(pid, ptvsd_args)\n\n    @message_handler\n    def configurationDone_request(self, request):\n        if self._start_request is None:\n            request.cant_handle(\n                '\"configurationDone\" is only allowed during handling of a \"launch\" '\n                'or an \"attach\" request'\n            )\n\n        try:\n            request.respond(self.server.channel.delegate(request))\n        finally:\n            self._start_request.respond({})\n            self._start_request = None\n            self._propagate_deferred_events()\n\n    @message_handler\n    def pause_request(self, request):\n        request.arguments[\"threadId\"] = \"*\"\n        return self.server.channel.delegate(request)\n\n    @message_handler\n    def continue_request(self, request):\n        request.arguments[\"threadId\"] = \"*\"\n\n        try:\n            return self.server.channel.delegate(request)\n        except messaging.NoMoreMessages:\n            # pydevd can sometimes allow the debuggee to exit before the queued\n            # \"continue\" response gets sent. Thus, a failed \"continue\" response\n            # indicating that the server disconnected should be treated as success.\n            return {\"allThreadsContinued\": True}\n\n    @message_handler\n    def ptvsd_systemInfo_request(self, request):\n        result = {\"ptvsd\": {\"version\": ptvsd.__version__}}\n        if self.server:\n            try:\n                pydevd_info = self.server.channel.request(\"pydevdSystemInfo\")\n            except Exception:\n                # If the server has already disconnected, or couldn't handle it,\n                # report what we've got.\n                pass\n            else:\n                result.update(pydevd_info)\n        return result\n\n    @message_handler\n    def terminate_request(self, request):\n        self.session.finalize('IDE requested \"terminate\"', terminate_debuggee=True)\n        return {}\n\n    @message_handler\n    def disconnect_request(self, request):\n        self.session.finalize(\n            'IDE requested \"disconnect\"',\n            request(\"terminateDebuggee\", json.default(bool(self.launcher))),\n        )\n        return {}\n", "path": "src/ptvsd/adapter/ide.py"}]}