diff --git a/CHANGELOG.md b/CHANGELOG.md index b7dd7e8d5..d40b4968d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. +## [0.81.0](https://github.com/robotcodedev/robotcode/compare/v0.80.0..v0.81.0) - 2024-05-03 + +### Bug Fixes + +- **debugger:** Rework async code in debugger and some other little quitrks in debugger, like hiding the debugger thread when debuggin python code ([d7fe611](https://github.com/robotcodedev/robotcode/commit/d7fe611b1a88a8cac3ea31de4cf7de6a33339ea3)) + + +### Features + +- **vscode:** Get debugpy debuggerpackagepath from ms-python.debugpy extension ([9a00d0c](https://github.com/robotcodedev/robotcode/commit/9a00d0c4588bcfe55d5838070dc76410fa060c19)) + + ## [0.80.0](https://github.com/robotcodedev/robotcode/compare/v0.79.0..v0.80.0) - 2024-05-01 ### Features diff --git a/package-lock.json b/package-lock.json index 61a1893a8..05a67680e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "robotcode", - "version": "0.80.0", + "version": "0.81.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "robotcode", - "version": "0.80.0", + "version": "0.81.0", "dependencies": { "@vscode/python-extension": "^1.0.5", "ansi-colors": "^4.1.3", diff --git a/package.json b/package.json index 2a9b06e74..e61ab5438 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Robot Framework IntelliSense, linting, test execution and debugging, code formatting, refactoring, and many more", "icon": "images/icon.png", "publisher": "d-biehl", - "version": "0.80.0", + "version": "0.81.0", "author": { "name": "Daniel Biehl", "url": "https://github.com/robotcodedev/" @@ -71,8 +71,14 @@ "color": "#111111" }, "sponsor": { - "url": "https://github.com/sponsors/d-biehl" + "url": "https://opencollective.com/robotcode" }, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/robotcode" + } + ], "main": "./out/extension.js", "contributes": { "icons": { diff --git a/packages/analyze/pyproject.toml b/packages/analyze/pyproject.toml index 4b7e72b03..7975a1812 100644 --- a/packages/analyze/pyproject.toml +++ b/packages/analyze/pyproject.toml @@ -27,9 +27,9 @@ classifiers = [ ] dependencies = [ "robotframework>=4.1.0", - "robotcode-plugin==0.80.0", - "robotcode-robot==0.80.0", - "robotcode==0.80.0", + "robotcode-plugin==0.81.0", + "robotcode-robot==0.81.0", + "robotcode==0.81.0", ] dynamic = ["version"] @@ -38,7 +38,7 @@ analyze = "robotcode.analyze.hooks" [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/analyze/src/robotcode/analyze/__version__.py b/packages/analyze/src/robotcode/analyze/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/analyze/src/robotcode/analyze/__version__.py +++ b/packages/analyze/src/robotcode/analyze/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/core/pyproject.toml b/packages/core/pyproject.toml index cfa2b676f..db8d00efe 100644 --- a/packages/core/pyproject.toml +++ b/packages/core/pyproject.toml @@ -30,7 +30,7 @@ dynamic = ["version"] [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/core/src/robotcode/core/__version__.py b/packages/core/src/robotcode/core/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/core/src/robotcode/core/__version__.py +++ b/packages/core/src/robotcode/core/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/core/src/robotcode/core/concurrent.py b/packages/core/src/robotcode/core/concurrent.py index 8a73dd363..7b421f36c 100644 --- a/packages/core/src/robotcode/core/concurrent.py +++ b/packages/core/src/robotcode/core/concurrent.py @@ -217,7 +217,9 @@ def _remove_future_from_running_tasks(future: Task[Any]) -> None: _P = ParamSpec("_P") -def run_as_task(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.kwargs) -> Task[_TResult]: +def _create_task_in_thread( + callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.kwargs +) -> Tuple[Task[_TResult], threading.Thread]: future: Task[_TResult] = Task() with _running_tasks_lock: thread = threading.Thread( @@ -227,8 +229,26 @@ def run_as_task(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.k ) _running_tasks[future] = thread future.add_done_callback(_remove_future_from_running_tasks) + # TODO: don't set daemon=True because it can be deprecated in future pyhton versions thread.daemon = True + return future, thread + + +def run_as_task(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.kwargs) -> Task[_TResult]: + future, thread = _create_task_in_thread(callable, *args, **kwargs) + + thread.start() + + return future + + +def run_as_debugpy_hidden_task(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.kwargs) -> Task[_TResult]: + future, thread = _create_task_in_thread(callable, *args, **kwargs) + + thread.pydev_do_not_trace = True # type: ignore[attr-defined] + thread.is_pydev_daemon_thread = True # type: ignore[attr-defined] + thread.start() return future diff --git a/packages/core/src/robotcode/core/utils/debugpy.py b/packages/core/src/robotcode/core/utils/debugpy.py index c55584ae8..1f0c4e199 100644 --- a/packages/core/src/robotcode/core/utils/debugpy.py +++ b/packages/core/src/robotcode/core/utils/debugpy.py @@ -1,5 +1,8 @@ +import threading from typing import Optional, Sequence, Tuple, Union +from robotcode.core.concurrent import run_as_debugpy_hidden_task + from .logging import LoggingDescriptor from .net import find_free_port @@ -15,14 +18,25 @@ def is_debugpy_installed() -> bool: return True -def wait_for_debugpy_connected() -> bool: +def wait_for_debugpy_connected(timeout: float = 30) -> bool: if is_debugpy_installed(): import debugpy # noqa: T100 + connected = threading.Event() _logger.info("wait for debugpy client") + + def _wait_for_client() -> bool: + if not connected.wait(timeout=timeout): + debugpy.wait_for_client.cancel() + return False + + return True + + wait_task = run_as_debugpy_hidden_task(_wait_for_client) debugpy.wait_for_client() # noqa: T100 + connected.set() + return wait_task.result() - return True return False diff --git a/packages/debugger/pyproject.toml b/packages/debugger/pyproject.toml index 49e64e72b..64abbc404 100644 --- a/packages/debugger/pyproject.toml +++ b/packages/debugger/pyproject.toml @@ -28,8 +28,8 @@ classifiers = [ dynamic = ["version"] dependencies = [ "robotframework>=4.1.0", - "robotcode-jsonrpc2==0.80.0", - "robotcode-runner==0.80.0", + "robotcode-jsonrpc2==0.81.0", + "robotcode-runner==0.81.0", ] [project.optional-dependencies] @@ -40,7 +40,7 @@ debugger = "robotcode.debugger.hooks" [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/debugger/src/robotcode/debugger/__version__.py b/packages/debugger/src/robotcode/debugger/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/debugger/src/robotcode/debugger/__version__.py +++ b/packages/debugger/src/robotcode/debugger/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/debugger/src/robotcode/debugger/cli.py b/packages/debugger/src/robotcode/debugger/cli.py index 97dc71ea2..f1b2ff6c6 100644 --- a/packages/debugger/src/robotcode/debugger/cli.py +++ b/packages/debugger/src/robotcode/debugger/cli.py @@ -1,4 +1,3 @@ -import asyncio from typing import Optional, Sequence, Tuple import click @@ -22,7 +21,8 @@ add_help_option=True, ) @click.option( - "--debug / --no-debug", + "--debug/--no-debug", + is_flag=True, default=True, help="Enable/disable debug mode", show_default=True, @@ -34,7 +34,7 @@ show_default=True, ) @click.option( - "--wait-for-client / --no-wait-for-client", + "--wait-for-client/--no-wait-for-client", is_flag=True, default=True, help="Waits until a debug client is connected.", @@ -55,14 +55,14 @@ show_default=True, ) @click.option( - "--debugpy / --no-debugpy", + "--debugpy/--no-debugpy", is_flag=True, default=False, help="Enable/disable python debugging.", show_default=True, ) @click.option( - "--debugpy-wait-for-client", + "--debugpy-wait-for-client/--no-debugpy-wait-for-client", is_flag=True, default=True, help="Waits for a debugpy client to connect.", @@ -170,28 +170,26 @@ def debug( try: app.exit( - asyncio.run( - run_debugger( - ctx=ctx, - app=app, - args=list(robot_options_and_args), - mode=mode, - addresses=bind, - port=port if port is not None else DEBUGGER_DEFAULT_PORT, - pipe_name=pipe_name, - debug=debug, - stop_on_entry=stop_on_entry, - wait_for_client=wait_for_client, - wait_for_client_timeout=wait_for_client_timeout, - configuration_done_timeout=configuration_done_timeout, - debugpy=debugpy, - debugpy_wait_for_client=debugpy_wait_for_client, - debugpy_port=debugpy_port, - output_messages=output_messages, - output_log=output_log, - output_timestamps=output_timestamps, - group_output=group_output, - ) + run_debugger( + ctx=ctx, + app=app, + args=list(robot_options_and_args), + mode=mode, + addresses=bind, + port=port if port is not None else DEBUGGER_DEFAULT_PORT, + pipe_name=pipe_name, + debug=debug, + stop_on_entry=stop_on_entry, + wait_for_client=wait_for_client, + wait_for_client_timeout=wait_for_client_timeout, + configuration_done_timeout=configuration_done_timeout, + debugpy=debugpy, + debugpy_wait_for_client=debugpy_wait_for_client, + debugpy_port=debugpy_port, + output_messages=output_messages, + output_log=output_log, + output_timestamps=output_timestamps, + group_output=group_output, ) ) diff --git a/packages/debugger/src/robotcode/debugger/protocol.py b/packages/debugger/src/robotcode/debugger/protocol.py index e64c6e4d9..2310c73d6 100644 --- a/packages/debugger/src/robotcode/debugger/protocol.py +++ b/packages/debugger/src/robotcode/debugger/protocol.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import asyncio import inspect import json @@ -142,7 +140,7 @@ def _handle_body(self, body: bytes, charset: str) -> None: ) def _handle_messages(self, iterator: Iterator[ProtocolMessage]) -> None: - def done(f: asyncio.Future[Any]) -> None: + def done(f: "asyncio.Future[Any]") -> None: ex = f.exception() if ex is not None and not isinstance(ex, asyncio.CancelledError): self._logger.exception(ex, exc_info=ex) @@ -249,7 +247,7 @@ def handle_request(self, message: Request) -> None: self._received_request[message.seq] = result - def done(t: asyncio.Task[Any]) -> None: + def done(t: "asyncio.Task[Any]") -> None: try: self.send_response(message.seq, message.command, t.result()) except asyncio.CancelledError: @@ -257,7 +255,6 @@ def done(t: asyncio.Task[Any]) -> None: except (SystemExit, KeyboardInterrupt): raise except DebugAdapterRPCErrorException as ex: - self._logger.exception(ex) self.send_error( message=ex.message, request_seq=message.seq, @@ -321,7 +318,7 @@ def send_request(self, request: Request, return_type: Optional[Type[TResult]] = @_logger.call def send_request_async( self, request: Request, return_type: Optional[Type[TResult]] = None - ) -> asyncio.Future[TResult]: + ) -> "asyncio.Future[TResult]": return asyncio.wrap_future(self.send_request(request, return_type)) @_logger.call diff --git a/packages/debugger/src/robotcode/debugger/run.py b/packages/debugger/src/robotcode/debugger/run.py index 712b5c8e1..0cf14c207 100644 --- a/packages/debugger/src/robotcode/debugger/run.py +++ b/packages/debugger/src/robotcode/debugger/run.py @@ -2,7 +2,8 @@ import functools import os import threading -import warnings +import time +from concurrent.futures import CancelledError from typing import ( TYPE_CHECKING, Callable, @@ -15,10 +16,7 @@ import click -from robotcode.core.async_tools import ( - run_coroutine_from_thread_async, - run_coroutine_in_thread, -) +from robotcode.core.concurrent import run_as_debugpy_hidden_task from robotcode.core.types import ServerMode, TcpParams from robotcode.core.utils.debugpy import ( enable_debugpy, @@ -53,20 +51,22 @@ def set_server(value: "DebugAdapterServer") -> None: @_logger.call -async def wait_for_server(timeout: float = 5) -> "DebugAdapterServer": - async def wait() -> None: - while get_server() is None: - await asyncio.sleep(0.005) +def wait_for_server(timeout: float = 10) -> "DebugAdapterServer": - await asyncio.wait_for(wait(), timeout) + start_time = time.monotonic() + while get_server() is None and time.monotonic() - start_time < timeout: + time.sleep(0.005) result = get_server() - assert result is not None + + if result is None: + raise RuntimeError("Timeout to get server instance.") + return result @_logger.call -async def _debug_adapter_server_( +async def _debug_adapter_server_async( on_config_done_callback: Optional[Callable[["DebugAdapterServer"], None]], mode: ServerMode, addresses: Union[str, Sequence[str], None], @@ -89,14 +89,26 @@ async def _debug_adapter_server_( await server.serve() +def _debug_adapter_server_( + on_config_done_callback: Optional[Callable[["DebugAdapterServer"], None]], + mode: ServerMode, + addresses: Union[str, Sequence[str], None], + port: int, + pipe_name: Optional[str], +) -> None: + asyncio.run(_debug_adapter_server_async(on_config_done_callback, mode, addresses, port, pipe_name)) + + DEFAULT_TIMEOUT = 10.0 config_done_callback: Optional[Callable[["DebugAdapterServer"], None]] = None +debugpy_connected = threading.Event() @_logger.call -async def start_debugpy_async( +def start_debugpy( + app: Application, debugpy_port: Optional[int] = None, addresses: Union[Sequence[str], str, None] = None, wait_for_debugpy_client: bool = False, @@ -122,17 +134,22 @@ def connect_debugpy(server: "DebugAdapterServer") -> None: ) if wait_for_debugpy_client: - wait_for_debugpy_connected() + app.verbose(f"Wait for debugpy incomming connections listening on {addresses}:{port}") + if not wait_for_debugpy_connected(wait_for_client_timeout): + app.warning("No debugpy client connected") + else: + app.verbose("Debugpy client connected") + debugpy_connected.set() config_done_callback = connect_debugpy @_logger.call -async def run_debugger( +def run_debugger( ctx: click.Context, app: Application, args: List[str], - mode: str, + mode: ServerMode, addresses: Union[str, Sequence[str], None], port: int, pipe_name: Optional[str] = None, @@ -150,19 +167,21 @@ async def run_debugger( group_output: bool = False, ) -> int: if debug and debugpy and not is_debugpy_installed(): - app.warning("Debugpy not installed.") + app.warning("Debugpy not installed") if debug and debugpy: - app.verbose("Try to start debugpy session.") - await start_debugpy_async( + app.verbose("Try to start debugpy session") + start_debugpy( + app, debugpy_port, addresses, debugpy_wait_for_client, wait_for_client_timeout, ) - app.verbose("Start robotcode debugger thread.") - server_future = run_coroutine_in_thread( + app.verbose("Start robotcode debugger thread") + + run_as_debugpy_hidden_task( _debug_adapter_server_, config_done_callback, mode, @@ -171,41 +190,29 @@ async def run_debugger( pipe_name, ) - server = await wait_for_server() + server = wait_for_server() + exit_code = 255 try: if wait_for_client: - app.verbose("Wait for incomming connections.") + app.verbose("Wait for incomming connections") try: - await run_coroutine_from_thread_async( - server.protocol.wait_for_client, - wait_for_client_timeout, - loop=server.loop, - ) - except asyncio.CancelledError: - pass - except asyncio.TimeoutError as e: - raise ConnectionError("No incomming connection from a debugger client.") from e + server.protocol.wait_for_client(wait_for_client_timeout) + except TimeoutError as e: + raise ConnectionError("No incomming connection from a debugger client") from e - await run_coroutine_from_thread_async(server.protocol.wait_for_initialized, loop=server.loop) + server.protocol.wait_for_initialized(wait_for_client_timeout) if wait_for_client: app.verbose("Wait for debug configuration.") try: - await run_coroutine_from_thread_async( - server.protocol.wait_for_configuration_done, - configuration_done_timeout, - loop=server.loop, - ) - except asyncio.CancelledError: - pass - except asyncio.TimeoutError as e: - raise ConnectionError("Timeout to get configuration from client.") from e + server.protocol.wait_for_configuration_done(configuration_done_timeout) + except TimeoutError as e: + raise ConnectionError("Timeout to get configuration from client") from e if debugpy and debugpy_wait_for_client: - app.verbose("Wait for debugpy incomming connections.") - wait_for_debugpy_connected() + debugpy_connected.wait(wait_for_client_timeout) args = [ "--listener", @@ -225,14 +232,14 @@ async def run_debugger( Debugger.instance().set_main_thread(threading.current_thread()) Debugger.instance().server_loop = server.loop - app.verbose("Start the debugger instance.") + app.verbose("Start the debugger instance") Debugger.instance().start() exit_code = 0 try: from robotcode.runner.cli.robot import robot - app.verbose("Start robot.") + app.verbose("Start robot") try: robot_ctx = robot.make_context("robot", args, parent=ctx) robot.invoke(robot_ctx) @@ -240,8 +247,7 @@ async def run_debugger( exit_code = cast(int, e.code) finally: if server.protocol.connected: - await run_coroutine_from_thread_async( - server.protocol.send_event_async, + server.protocol.send_event( Event( event="robotExited", body={ @@ -250,27 +256,19 @@ async def run_debugger( "outputFile": Debugger.instance().robot_output_file, "exitCode": exit_code, }, - ), - loop=server.loop, + ) ) - await run_coroutine_from_thread_async(server.protocol.exit, exit_code, loop=server.loop) - except asyncio.CancelledError: + server.protocol.exit(exit_code) + except CancelledError: pass finally: if server.protocol.connected: - await run_coroutine_from_thread_async(server.protocol.terminate, loop=server.loop) + server.protocol.terminate() - try: - await run_coroutine_from_thread_async(server.protocol.wait_for_disconnected, loop=server.loop) - except asyncio.TimeoutError: - warnings.warn("Timeout at disconnect client occurred.") + if not server.protocol.wait_for_disconnected(): + app.warning("Timeout to get disconnected from client") - server_future.cancel() - - try: - await server_future - except asyncio.CancelledError: - pass + server.loop.stop() return exit_code diff --git a/packages/debugger/src/robotcode/debugger/server.py b/packages/debugger/src/robotcode/debugger/server.py index 1ea7180c9..8d4cf0fd7 100644 --- a/packages/debugger/src/robotcode/debugger/server.py +++ b/packages/debugger/src/robotcode/debugger/server.py @@ -1,8 +1,9 @@ import asyncio import os +import threading from typing import Any, Callable, Dict, List, Literal, Optional, Union -from robotcode.core import async_tools +from robotcode.core import concurrent from robotcode.core.types import ServerMode, TcpParams from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method @@ -59,21 +60,21 @@ class DebugAdapterServerProtocol(DebugAdapterProtocol): def __init__(self) -> None: super().__init__() - self._connected_event = async_tools.Event() - self._disconnected_event = async_tools.Event() + self._connected_event = threading.Event() + self._disconnected_event = threading.Event() self._connected = False self._sigint_signaled = False self._initialized = False - self._initialized_event = async_tools.Event() + self._initialized_event = threading.Event() - self._exited_lock = async_tools.Lock() + self._exited_lock = concurrent.RLock() self._exited = False - self._terminated_lock = async_tools.Lock() + self._terminated_lock = concurrent.RLock() self._terminated = False - self._received_configuration_done_event = async_tools.Event() + self._received_configuration_done_event = threading.Event() self._received_configuration_done = False self.received_configuration_done_callback: Optional[Callable[[], None]] = None @@ -88,13 +89,13 @@ def connected(self) -> bool: return self._connected @property - async def exited(self) -> bool: - async with self._exited_lock: + def exited(self) -> bool: + with self._exited_lock: return self._exited @property - async def terminated(self) -> bool: - async with self._terminated_lock: + def terminated(self) -> bool: + with self._terminated_lock: return self._terminated @_logger.call @@ -116,22 +117,24 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: self._disconnected_event.set() @_logger.call - async def wait_for_client(self, timeout: float = 5) -> bool: - await asyncio.wait_for(self._connected_event.wait(), timeout) + def wait_for_client(self, timeout: float = 5) -> bool: + if not self._connected_event.wait(timeout): + raise TimeoutError("Timeout waiting for client") return self._connected @_logger.call - async def wait_for_initialized(self, timeout: float = 30) -> bool: - await asyncio.wait_for(self._initialized_event.wait(), timeout) + def wait_for_initialized(self, timeout: float = 30) -> bool: + if not self._initialized_event.wait(timeout): + raise TimeoutError("Timeout waiting for client initialization") return self._initialized @_logger.call - async def wait_for_disconnected(self, timeout: float = 30) -> bool: - await asyncio.wait_for(self._disconnected_event.wait(), timeout) + def wait_for_disconnected(self, timeout: float = 5) -> bool: + self._disconnected_event.wait(timeout) - return self._connected + return not self._connected @rpc_method(name="initialize", param_type=InitializeRequestArguments) async def _initialize(self, arguments: InitializeRequestArguments, *args: Any, **kwargs: Any) -> Capabilities: @@ -211,15 +214,15 @@ def initialized(self) -> None: self._initialized_event.set() @_logger.call - async def exit(self, exit_code: int) -> None: - async with self._exited_lock: - await self.send_event_async(ExitedEvent(body=ExitedEventBody(exit_code=exit_code))) + def exit(self, exit_code: int) -> None: + with self._exited_lock: + self.send_event(ExitedEvent(body=ExitedEventBody(exit_code=exit_code))) self._exited = True @_logger.call - async def terminate(self) -> None: - async with self._terminated_lock: - await self.send_event_async(TerminatedEvent()) + def terminate(self) -> None: + with self._terminated_lock: + self.send_event(TerminatedEvent()) self._terminated = True @rpc_method(name="terminate", param_type=TerminateArguments) @@ -251,11 +254,7 @@ async def _disconnect( *args: Any, **kwargs: Any, ) -> None: - if ( - (not (await self.exited) or not (await self.terminated)) - and arguments is not None - and arguments.terminate_debuggee - ): + if (not (self.exited) or not (self.terminated)) and arguments is not None and arguments.terminate_debuggee: os._exit(-1) else: await self.send_event_async(Event("disconnectRequested")) @@ -290,8 +289,9 @@ async def _configuration_done( self.received_configuration_done_callback() @_logger.call - async def wait_for_configuration_done(self, timeout: float = 5) -> bool: - await asyncio.wait_for(self._received_configuration_done_event.wait(), timeout) + def wait_for_configuration_done(self, timeout: float = 5) -> bool: + if not self._received_configuration_done_event.wait(timeout): + raise TimeoutError("Timeout waiting for configuration done event") return self._received_configuration_done diff --git a/packages/jsonrpc2/pyproject.toml b/packages/jsonrpc2/pyproject.toml index 0e96fc72d..ad2fe2568 100644 --- a/packages/jsonrpc2/pyproject.toml +++ b/packages/jsonrpc2/pyproject.toml @@ -25,12 +25,12 @@ classifiers = [ "Framework :: Robot Framework", "Framework :: Robot Framework :: Tool", ] -dependencies = ["robotcode-core==0.80.0"] +dependencies = ["robotcode-core==0.81.0"] dynamic = ["version"] [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/jsonrpc2/src/robotcode/jsonrpc2/__version__.py b/packages/jsonrpc2/src/robotcode/jsonrpc2/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/jsonrpc2/src/robotcode/jsonrpc2/__version__.py +++ b/packages/jsonrpc2/src/robotcode/jsonrpc2/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/language_server/pyproject.toml b/packages/language_server/pyproject.toml index a9a9a1c3e..d1eec56e9 100644 --- a/packages/language_server/pyproject.toml +++ b/packages/language_server/pyproject.toml @@ -27,9 +27,9 @@ classifiers = [ ] dependencies = [ "robotframework>=4.1.0", - "robotcode-jsonrpc2==0.80.0", - "robotcode-robot==0.80.0", - "robotcode==0.80.0", + "robotcode-jsonrpc2==0.81.0", + "robotcode-robot==0.81.0", + "robotcode==0.81.0", ] dynamic = ["version"] @@ -38,7 +38,7 @@ langserver = "robotcode.language_server.hooks" [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/language_server/src/robotcode/language_server/__version__.py b/packages/language_server/src/robotcode/language_server/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/language_server/src/robotcode/language_server/__version__.py +++ b/packages/language_server/src/robotcode/language_server/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/modifiers/pyproject.toml b/packages/modifiers/pyproject.toml index 5e26020b9..5f7ae261e 100644 --- a/packages/modifiers/pyproject.toml +++ b/packages/modifiers/pyproject.toml @@ -30,7 +30,7 @@ dynamic = ["version"] [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/modifiers/src/robotcode/modifiers/__version__.py b/packages/modifiers/src/robotcode/modifiers/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/modifiers/src/robotcode/modifiers/__version__.py +++ b/packages/modifiers/src/robotcode/modifiers/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/plugin/pyproject.toml b/packages/plugin/pyproject.toml index 6085b5f54..a9704ef39 100644 --- a/packages/plugin/pyproject.toml +++ b/packages/plugin/pyproject.toml @@ -29,7 +29,7 @@ dependencies = ["click>=8.0.0", "pluggy>=1.0.0", "tomli_w>=1.0.0"] dynamic = ["version"] [project.urls] -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/plugin/src/robotcode/plugin/__version__.py b/packages/plugin/src/robotcode/plugin/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/plugin/src/robotcode/plugin/__version__.py +++ b/packages/plugin/src/robotcode/plugin/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/robot/pyproject.toml b/packages/robot/pyproject.toml index 86e104dd6..08f32ec4c 100644 --- a/packages/robot/pyproject.toml +++ b/packages/robot/pyproject.toml @@ -29,13 +29,13 @@ dependencies = [ "robotframework>=4.1.0", "tomli>=1.1.0; python_version < '3.11'", "platformdirs>=3.2.0,<4.2.0", - "robotcode-core==0.80.0", + "robotcode-core==0.81.0", ] dynamic = ["version"] [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/robot/src/robotcode/robot/__version__.py b/packages/robot/src/robotcode/robot/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/robot/src/robotcode/robot/__version__.py +++ b/packages/robot/src/robotcode/robot/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/packages/runner/pyproject.toml b/packages/runner/pyproject.toml index e1a07c0f0..6609523f8 100644 --- a/packages/runner/pyproject.toml +++ b/packages/runner/pyproject.toml @@ -28,10 +28,10 @@ classifiers = [ dynamic = ["version"] dependencies = [ "robotframework>=4.1.0", - "robotcode-robot==0.80.0", - "robotcode-modifiers==0.80.0", - "robotcode-plugin==0.80.0", - "robotcode==0.80.0", + "robotcode-robot==0.81.0", + "robotcode-modifiers==0.81.0", + "robotcode-plugin==0.81.0", + "robotcode==0.81.0", ] [project.entry-points.robotcode] @@ -39,7 +39,7 @@ runner = "robotcode.runner.hooks" [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" diff --git a/packages/runner/src/robotcode/runner/__version__.py b/packages/runner/src/robotcode/runner/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/packages/runner/src/robotcode/runner/__version__.py +++ b/packages/runner/src/robotcode/runner/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/pyproject.toml b/pyproject.toml index a76cf03e6..55c91cb49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,16 +50,16 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = [ - "robotcode-core==0.80.0", - "robotcode-plugin==0.80.0", - "robotcode-robot==0.80.0", + "robotcode-core==0.81.0", + "robotcode-plugin==0.81.0", + "robotcode-robot==0.81.0", ] dynamic = ["version"] [project.urls] Homepage = "https://robotcode.io" -Donate = "https://github.com/sponsors/d-biehl" +Donate = "https://opencollective.com/robotcode" Documentation = "https://github.com/robotcodedev/robotcode#readme" Changelog = "https://github.com/robotcodedev/robotcode/blob/main/CHANGELOG.md" Issues = "https://github.com/robotcodedev/robotcode/issues" @@ -70,20 +70,20 @@ robotcode = "robotcode.cli.__main__:main" [project.optional-dependencies] -debugger = ["robotcode-debugger==0.80.0"] -languageserver = ["robotcode-language-server==0.80.0"] -runner = ["robotcode-runner==0.80.0"] -analyze = ["robotcode-analyze==0.80.0"] +debugger = ["robotcode-debugger==0.81.0"] +languageserver = ["robotcode-language-server==0.81.0"] +runner = ["robotcode-runner==0.81.0"] +analyze = ["robotcode-analyze==0.81.0"] yaml = ["PyYAML>=5.4"] lint = ["robotframework-robocop>=2.0.0"] tidy = ["robotframework-tidy>=2.0.0"] rest = ["docutils"] colored = ["rich"] all = [ - "robotcode-debugger==0.80.0", - "robotcode-language-server==0.80.0", - "robotcode-runner==0.80.0", - "robotcode-analyze==0.80.0", + "robotcode-debugger==0.81.0", + "robotcode-language-server==0.81.0", + "robotcode-runner==0.81.0", + "robotcode-analyze==0.81.0", "PyYAML>=5.4", "robotframework-robocop>=2.0.0", "robotframework-tidy>=2.0.0", diff --git a/src/robotcode/cli/__version__.py b/src/robotcode/cli/__version__.py index 47481ca65..b6eaf2e75 100644 --- a/src/robotcode/cli/__version__.py +++ b/src/robotcode/cli/__version__.py @@ -1 +1 @@ -__version__ = "0.80.0" +__version__ = "0.81.0" diff --git a/vscode-client/debugmanager.ts b/vscode-client/debugmanager.ts index 2f240934b..da2410c93 100644 --- a/vscode-client/debugmanager.ts +++ b/vscode-client/debugmanager.ts @@ -192,6 +192,10 @@ class RobotCodeDebugConfigurationProvider implements vscode.DebugConfigurationPr ...(envPythonPath ? envPythonPath.split(path.delimiter) : []), ].join(path.delimiter); debugConfiguration.env = env; + } else { + this.pythonManager.outputChannel.appendLine( + "WARNING: Failed to get debugpy path from extension. You need to manually install the debugpy package in your python environment.", + ); } } } else if (debugConfiguration.request === "attach") { @@ -562,13 +566,7 @@ export class DebugManager { _event: string, options?: { port: number; addresses: undefined | string[] | null; processId?: number | null }, ): Promise { - if ( - session.type === "robotcode" && - !session.configuration.noDebug && - session.configuration.attachPython && - options && - options.port - ) { + if (session.type === "robotcode" && !session.configuration.noDebug && options && options.port) { let pythonConfiguration = (session.configuration.pythonConfiguration as { [Key: string]: unknown }) ?? {}; if (typeof pythonConfiguration === "string" || pythonConfiguration instanceof String) { diff --git a/vscode-client/pythonmanger.ts b/vscode-client/pythonmanger.ts index 06ae1a2eb..0a46fea61 100644 --- a/vscode-client/pythonmanger.ts +++ b/vscode-client/pythonmanger.ts @@ -141,16 +141,16 @@ export class PythonManager { public async getDebuggerPackagePath(): Promise { // TODO: this is not enabled in debugpy extension yet - // const debugpy = vscode.extensions.getExtension("ms-python.debugpy"); - // if (debugpy !== undefined) { - // if (!debugpy.isActive) { - // await debugpy.activate(); - // } - // const path = (debugpy.exports as PythonExtension)?.debug.getDebuggerPackagePath(); - // if (path !== undefined) { - // return path; - // } - // } + const debugpy = vscode.extensions.getExtension("ms-python.debugpy"); + if (debugpy !== undefined) { + if (!debugpy.isActive) { + await debugpy.activate(); + } + const path = (debugpy.exports as PythonExtension)?.debug.getDebuggerPackagePath(); + if (path !== undefined) { + return path; + } + } return (await this.getPythonExtension())?.debug.getDebuggerPackagePath(); } diff --git a/vscode-client/testcontrollermanager.ts b/vscode-client/testcontrollermanager.ts index 7c38f56f5..8097b4c72 100644 --- a/vscode-client/testcontrollermanager.ts +++ b/vscode-client/testcontrollermanager.ts @@ -1448,10 +1448,10 @@ export class TestControllerManager { if (item !== undefined) { switch (event.attributes.status) { case "PASS": - if (!item?.canResolveChildren) run.passed(item, event.attributes.elapsedtime); + run.passed(item, event.attributes.elapsedtime); break; case "SKIP": - if (!item?.canResolveChildren) run.skipped(item); + run.skipped(item); break; default: { @@ -1493,12 +1493,10 @@ export class TestControllerManager { } } - if (!item?.canResolveChildren) { - if (event.attributes.status === "FAIL") { - run.failed(item, messages, event.attributes.elapsedtime); - } else { - run.errored(item, messages, event.attributes.elapsedtime); - } + if (event.attributes.status === "FAIL") { + run.failed(item, messages, event.attributes.elapsedtime); + } else { + run.errored(item, messages, event.attributes.elapsedtime); } } break;