Support "force_ansi" option for core.exec and allow to raise exception on cmd error

This commit is contained in:
Ivan Kravets
2023-07-25 12:25:42 +03:00
parent e3557760df
commit d2fd0f242e
2 changed files with 31 additions and 12 deletions

View File

@ -14,6 +14,7 @@
import asyncio import asyncio
import functools import functools
import os
from platformio import __main__, __version__, app, proc, util from platformio import __main__, __version__, app, proc, util
from platformio.compat import ( from platformio.compat import (
@ -21,10 +22,16 @@ from platformio.compat import (
aio_create_task, aio_create_task,
aio_get_running_loop, aio_get_running_loop,
get_locale_encoding, get_locale_encoding,
shlex_join,
) )
from platformio.exception import UserSideException
from platformio.home.rpc.handlers.base import BaseRPCHandler 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): class PIOCoreProtocol(asyncio.SubprocessProtocol):
def __init__(self, exit_future, on_data_callback=None): def __init__(self, exit_future, on_data_callback=None):
self.exit_future = exit_future self.exit_future = exit_future
@ -66,27 +73,41 @@ class CoreRPC(BaseRPCHandler):
def version(): def version():
return __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() loop = aio_get_running_loop()
exit_future = loop.create_future() exit_future = loop.create_future()
data_callback = functools.partial( data_callback = functools.partial(
self._on_exec_data_received, exec_options=options self._on_exec_data_received, exec_options=options
) )
if args[0] != "--caller" and app.get_session_var("caller_id"): if args[0] != "--caller" and app.get_session_var("caller_id"):
args = ["--caller", app.get_session_var("caller_id")] + args 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( transport, protocol = await loop.subprocess_exec(
lambda: PIOCoreProtocol(exit_future, data_callback), lambda: PIOCoreProtocol(exit_future, data_callback),
get_core_fullpath(), get_core_fullpath(),
*args, *args,
stdin=None, stdin=None,
**options.get("spawn", {}), **kwargs,
) )
await exit_future await exit_future
transport.close() 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 { return {
"stdout": protocol.stdout, "stdout": protocol.stdout,
"stderr": protocol.stderr, "stderr": protocol.stderr,
"returncode": transport.get_returncode(), "returncode": return_code,
} }
def _on_exec_data_received(self, exec_options, pipe, data): def _on_exec_data_received(self, exec_options, pipe, data):

View File

@ -48,19 +48,15 @@ class ProjectRPC(BaseRPCHandler):
if not os.path.isdir(project_dir): if not os.path.isdir(project_dir):
os.makedirs(project_dir) os.makedirs(project_dir)
envclone = os.environ.copy() args = ["project", "init", "-d", project_dir]
envclone["PLATFORMIO_FORCE_ANSI"] = "true"
options = options or {}
options["spawn"] = {"env": envclone, "cwd": project_dir}
args = ["project", "init"]
ide = app.get_session_var("caller_id") ide = app.get_session_var("caller_id")
if ide in ProjectGenerator.get_supported_ides(): if ide in ProjectGenerator.get_supported_ides():
args.extend(["--ide", ide]) args.extend(["--ide", ide])
exec_options = options.get("exec", {})
if configuration.get("example"): if configuration.get("example"):
await self.factory.notify_clients( await self.factory.notify_clients(
method=options.get("stdoutNotificationMethod"), method=exec_options.get("stdoutNotificationMethod"),
params=["Copying example files...\n"], params=["Copying example files...\n"],
actor="frontend", actor="frontend",
) )
@ -68,7 +64,9 @@ class ProjectRPC(BaseRPCHandler):
else: else:
args.extend(self._pre_init_empty(configuration)) 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 @staticmethod
def _pre_init_empty(configuration): def _pre_init_empty(configuration):
@ -115,9 +113,9 @@ class ProjectRPC(BaseRPCHandler):
def configuration(project_dir, env): def configuration(project_dir, env):
assert is_platformio_project(project_dir) assert is_platformio_project(project_dir)
with fs.cd(project_dir): with fs.cd(project_dir):
config = ProjectConfig(os.path.join(project_dir, "platformio.ini"))
platform = PlatformFactory.from_env(env, autoinstall=True) platform = PlatformFactory.from_env(env, autoinstall=True)
platform_pkg = PlatformPackageManager().get_package(platform.get_dir()) platform_pkg = PlatformPackageManager().get_package(platform.get_dir())
config = ProjectConfig.get_instance()
board_id = config.get(f"env:{env}", "board", None) board_id = config.get(f"env:{env}", "board", None)
# frameworks # frameworks