================
@@ -0,0 +1,529 @@
+# FIXME: remove when LLDB_MINIMUM_PYTHON_VERSION > 3.8
+from __future__ import annotations
+
+import contextlib
+import dataclasses
+import functools
+import io
+import itertools
+import json
+import logging
+import os
+import subprocess
+import threading
+from concurrent import futures
+from concurrent.futures import Future
+from dataclasses import fields
+from pathlib import Path
+from typing import Any, Callable, Generic, Optional, Type, TypeVar
+
+from .dap_types import (
+    AnyResponse,
+    ArgsProtocol,
+    Capabilities,
+    CapabilitiesEvent,
+    ContinueArgs,
+    DAPError,
+    DisconnectArgs,
+    ErrorResponse,
+    Event,
+    ExitedEvent,
+    GotoArgs,
+    InitializeArgs,
+    InitializedEvent,
+    Message,
+    MessageType,
+    NextArgs,
+    OutputCategory,
+    OutputEvent,
+    RawMessage,
+    Request,
+    Response,
+    RestartArgs,
+    ReverseResponse,
+    RunInTerminalRequest,
+    RunInTerminalResponse,
+    StepInArgs,
+    StepOutArgs,
+    StoppedEvent,
+    TerminateArgs,
+    dict_to_message,
+)
+from .utils import (
+    DebugAdapter,
+    EventHistory,
+    MessageHandler,
+    SubProcessSpawner,
+    redirect_stream,
+)
+
+R = TypeVar("R")
+
+# Any Request that resumes execution (or terminates the session),
+# invalidates any frameId or variablesReference captured during the current 
stop.
+# Sending one of these Requests advances the session's stop_generation.
+# To prevent using a frameId or variablesReference that is no longer valid once
+# the session continues.
+_RESUMING_COMMANDS = (
+    ContinueArgs,
+    NextArgs,
+    StepInArgs,
+    StepOutArgs,
+    GotoArgs,
+    RestartArgs,
+    TerminateArgs,
+)
+
+
+class PendingResponse(Generic[AnyResponse]):
+    """A Holds the future to the expected request for the sequence id."""
+
+    def __init__(
+        self,
+        seq: int,
+        response_class: Type[AnyResponse],
+        raw_future: Future[RawMessage],
+        timeout: float,
+        command: str,
+        on_resolve: Callable[[Response], None] = lambda _: None,
+    ):
+        assert issubclass(
+            response_class, Response
+        ), f"'{response_class.__name__}' must be a subclass of Response."
+        self.seq = seq
+        self.response_class: Type[AnyResponse] = response_class
+        self._future = raw_future
+        self._timeout = timeout
+        self._command = command
+        self._on_resolve = on_resolve
+
+    def result(self, msg: Optional[str] = None) -> AnyResponse:
+        response = self.result_or_error()
+
+        if isinstance(response, self.response_class):
+            return response
+        detail = f"expected '{self.response_class.__name__}' got {response}."
+        raise DAPError(f"{msg}:\n\t{detail}" if msg else detail)
+
+    def error(self, msg: Optional[str] = None) -> ErrorResponse:
+        response = self.result_or_error()
+
+        if isinstance(response, ErrorResponse):
+            return response
+        detail = f"expected 'ErrorResponse' got {response}."
+        raise DAPError(f"{msg}\n\t{detail}" if msg else detail)
+
+    def result_or_error(self) -> AnyResponse | ErrorResponse:
+        try:
+            raw = self._future.result(timeout=self._timeout)
+        except (TimeoutError, futures.TimeoutError) as e:
+            msg = f"\n\tRequest '{self._command}' (seq={self.seq}) timed out 
after {self._timeout}s"
+            e.args = (f"{e.args[0]}{msg}", *e.args)
+            raise
+        except ConnectionError as e:
+            raise DAPError(
+                f"Session ended before getting response for "
+                f"'{self._command}' (seq={self.seq})"
+            ) from e
+
+        cls = self.response_class if raw["success"] else ErrorResponse
+        response = cls.from_json(raw)
+
+        self._on_resolve(response)
+        return response
+
+
+def _synchronized(method: Callable[..., R]) -> Callable[..., R]:
+    """Class method decorator to acquire and release the lock automatically."""
+
+    @functools.wraps(method)
+    def wrapper(self, *args: Any, **kwargs: Any) -> R:
+        with self._lock:
+            return method(self, *args, **kwargs)
+
+    return wrapper
+
+
+class _DAPSessionState:
+    def __init__(self):
+        self._lock = threading.RLock()
+        self._initialized = False
+        self._capabilities = Capabilities()
+        self.output_streams = {
+            OutputCategory.STDOUT: io.StringIO(),
+            OutputCategory.STDERR: io.StringIO(),
+            OutputCategory.CONSOLE: io.StringIO(),
+            OutputCategory.IMPORTANT: io.StringIO(),
+            OutputCategory.TELEMETRY: io.StringIO(),
+        }
+        self._stopped_thread_id: Optional[int] = None
+        self._last_response: Optional[Response] = None
+        self._stop_generation: int = 0
+
+    @property
+    @_synchronized
+    def is_initialized(self):
+        return self._initialized
+
+    @_synchronized
+    def set_initialized(self, val: bool):
----------------
ashgti wrote:

Should this be a property setter? e.g. `@is_initialized.setter`?

https://github.com/llvm/llvm-project/pull/203978
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to