diff --git a/platformio/commands/home/rpc/handlers/piocore.py b/platformio/commands/home/rpc/handlers/piocore.py index 19d7d370..4370b5a9 100644 --- a/platformio/commands/home/rpc/handlers/piocore.py +++ b/platformio/commands/home/rpc/handlers/piocore.py @@ -16,7 +16,8 @@ from __future__ import absolute_import import json import os -import re +import sys +import thread from io import BytesIO import jsonrpc # pylint: disable=import-error @@ -26,8 +27,46 @@ from platformio import __main__, __version__, util from platformio.compat import string_types +class ThreadSafeStdBuffer(object): + + def __init__(self, parent_stream, parent_thread_id): + self.parent_stream = parent_stream + self.parent_thread_id = parent_thread_id + self._buffer = {} + + def write(self, value): + thread_id = thread.get_ident() + if thread_id == self.parent_thread_id: + return self.parent_stream.write(value) + if thread_id not in self._buffer: + self._buffer[thread_id] = BytesIO() + return self._buffer[thread_id].write(value) + + def flush(self): + return (self.parent_stream.flush() + if thread.get_ident() == self.parent_thread_id else None) + + def getvalue_and_close(self, thread_id=None): + thread_id = thread_id or thread.get_ident() + if thread_id not in self._buffer: + return "" + result = self._buffer.get(thread_id).getvalue() + self._buffer.get(thread_id).close() + del self._buffer[thread_id] + return result + + class PIOCoreRPC(object): + def __init__(self): + cur_thread_id = thread.get_ident() + PIOCoreRPC.thread_stdout = ThreadSafeStdBuffer(sys.stdout, + cur_thread_id) + PIOCoreRPC.thread_stderr = ThreadSafeStdBuffer(sys.stderr, + cur_thread_id) + sys.stdout = PIOCoreRPC.thread_stdout + sys.stderr = PIOCoreRPC.thread_stderr + @staticmethod def call(args, options=None): try: @@ -40,15 +79,10 @@ class PIOCoreRPC(object): code=4002, message="PIO Core: non-ASCII chars in arguments") def _call_cli(): - outbuff = BytesIO() - errbuff = BytesIO() - with util.capture_std_streams(outbuff, errbuff): - with util.cd((options or {}).get("cwd") or os.getcwd()): - exit_code = __main__.main(["-c"] + args) - result = (outbuff.getvalue(), errbuff.getvalue(), exit_code) - outbuff.close() - errbuff.close() - return result + with util.cd((options or {}).get("cwd") or os.getcwd()): + exit_code = __main__.main(["-c"] + args) + return (PIOCoreRPC.thread_stdout.getvalue_and_close(), + PIOCoreRPC.thread_stderr.getvalue_and_close(), exit_code) d = threads.deferToThread(_call_cli) d.addCallback(PIOCoreRPC._call_callback, "--json-output" in args) @@ -61,15 +95,7 @@ class PIOCoreRPC(object): text = ("%s\n\n%s" % (out, err)).strip() if code != 0: raise Exception(text) - if not json_output: - return text - try: - return json.loads(out) - except ValueError as e: - if "sh: " in out: - return json.loads( - re.sub(r"^sh: [^\n]+$", "", out, flags=re.M).strip()) - raise e + return json.loads(out) if json_output else text @staticmethod def _call_errback(failure): diff --git a/platformio/commands/home/rpc/server.py b/platformio/commands/home/rpc/server.py index f99b09b3..b77e1b94 100644 --- a/platformio/commands/home/rpc/server.py +++ b/platformio/commands/home/rpc/server.py @@ -14,6 +14,7 @@ # pylint: disable=import-error +import click import jsonrpc from autobahn.twisted.websocket import (WebSocketServerFactory, WebSocketServerProtocol) @@ -26,7 +27,7 @@ from platformio.compat import PY2, dump_json_to_unicode, is_bytes class JSONRPCServerProtocol(WebSocketServerProtocol): def onMessage(self, payload, isBinary): # pylint: disable=unused-argument - # print("> %s" % payload) + # click.echo("> %s" % payload) response = jsonrpc.JSONRPCResponseManager.handle( payload, self.factory.dispatcher).data # if error @@ -52,11 +53,11 @@ class JSONRPCServerProtocol(WebSocketServerProtocol): message=failure.getErrorMessage()) del response["result"] response['error'] = e.error._data # pylint: disable=protected-access - print(response['error']) + click.secho(str(response['error']), fg="red", err=True) self.sendJSONResponse(response) def sendJSONResponse(self, response): - # print("< %s" % response) + # click.echo("< %s" % response) response = dump_json_to_unicode(response) if not PY2 and not is_bytes(response): response = response.encode("utf-8") diff --git a/platformio/commands/platform.py b/platformio/commands/platform.py index 26f86666..5b35c4df 100644 --- a/platformio/commands/platform.py +++ b/platformio/commands/platform.py @@ -182,7 +182,7 @@ def platform_frameworks(query, json_output): for framework in util.get_api_result("/frameworks", cache_valid="7d"): if query == "all": query = "" - search_data = framework + search_data = dump_json_to_unicode(framework) if query and query.lower() not in search_data.lower(): continue framework['homepage'] = ("https://platformio.org/frameworks/" +