From d2fd0f242e13aa73b6f35495c53b4421963bf1d7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 25 Jul 2023 12:25:42 +0300 Subject: [PATCH] Support "force_ansi" option for core.exec and allow to raise exception on cmd error --- platformio/home/rpc/handlers/core.py | 27 ++++++++++++++++++++++--- platformio/home/rpc/handlers/project.py | 16 +++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/platformio/home/rpc/handlers/core.py b/platformio/home/rpc/handlers/core.py index 344231ec..11b36da9 100644 --- a/platformio/home/rpc/handlers/core.py +++ b/platformio/home/rpc/handlers/core.py @@ -14,6 +14,7 @@ import asyncio import functools +import os from platformio import __main__, __version__, app, proc, util from platformio.compat import ( @@ -21,10 +22,16 @@ from platformio.compat import ( aio_create_task, aio_get_running_loop, get_locale_encoding, + shlex_join, ) +from platformio.exception import UserSideException from platformio.home.rpc.handlers.base import BaseRPCHandler +class PIOCoreCallError(UserSideException): + MESSAGE = 'An error occured while executing PIO Core command: "{0}"\n\n{1}' + + class PIOCoreProtocol(asyncio.SubprocessProtocol): def __init__(self, exit_future, on_data_callback=None): self.exit_future = exit_future @@ -66,27 +73,41 @@ class CoreRPC(BaseRPCHandler): def version(): return __version__ - async def exec(self, args, options=None): + async def exec(self, args, options=None, raise_exception=False): + options = options or {} loop = aio_get_running_loop() exit_future = loop.create_future() data_callback = functools.partial( self._on_exec_data_received, exec_options=options ) + if args[0] != "--caller" and app.get_session_var("caller_id"): args = ["--caller", app.get_session_var("caller_id")] + args + kwargs = options.get("spawn", {}) + + if "forceANSI" in options: + environ = kwargs.get("env", os.environ.copy()) + environ["PLATFORMIO_FORCE_ANSI"] = "true" + kwargs["env"] = environ + transport, protocol = await loop.subprocess_exec( lambda: PIOCoreProtocol(exit_future, data_callback), get_core_fullpath(), *args, stdin=None, - **options.get("spawn", {}), + **kwargs, ) await exit_future transport.close() + return_code = transport.get_returncode() + if return_code != 0 and raise_exception: + raise PIOCoreCallError( + shlex_join(["pio"] + args), f"{protocol.stdout}\n{protocol.stderr}" + ) return { "stdout": protocol.stdout, "stderr": protocol.stderr, - "returncode": transport.get_returncode(), + "returncode": return_code, } def _on_exec_data_received(self, exec_options, pipe, data): diff --git a/platformio/home/rpc/handlers/project.py b/platformio/home/rpc/handlers/project.py index 0ffd4de6..9033cef2 100644 --- a/platformio/home/rpc/handlers/project.py +++ b/platformio/home/rpc/handlers/project.py @@ -48,19 +48,15 @@ class ProjectRPC(BaseRPCHandler): if not os.path.isdir(project_dir): os.makedirs(project_dir) - envclone = os.environ.copy() - envclone["PLATFORMIO_FORCE_ANSI"] = "true" - options = options or {} - options["spawn"] = {"env": envclone, "cwd": project_dir} - - args = ["project", "init"] + args = ["project", "init", "-d", project_dir] ide = app.get_session_var("caller_id") if ide in ProjectGenerator.get_supported_ides(): args.extend(["--ide", ide]) + exec_options = options.get("exec", {}) if configuration.get("example"): await self.factory.notify_clients( - method=options.get("stdoutNotificationMethod"), + method=exec_options.get("stdoutNotificationMethod"), params=["Copying example files...\n"], actor="frontend", ) @@ -68,7 +64,9 @@ class ProjectRPC(BaseRPCHandler): else: args.extend(self._pre_init_empty(configuration)) - return await self.factory.manager.dispatcher["core.exec"](args, options=options) + return await self.factory.manager.dispatcher["core.exec"]( + args, options=exec_options + ) @staticmethod def _pre_init_empty(configuration): @@ -115,9 +113,9 @@ class ProjectRPC(BaseRPCHandler): def configuration(project_dir, env): assert is_platformio_project(project_dir) with fs.cd(project_dir): - config = ProjectConfig(os.path.join(project_dir, "platformio.ini")) platform = PlatformFactory.from_env(env, autoinstall=True) platform_pkg = PlatformPackageManager().get_package(platform.get_dir()) + config = ProjectConfig.get_instance() board_id = config.get(f"env:{env}", "board", None) # frameworks