Fixed an issue with PIO Home's "No JSON object could be decoded" // Resolve #2823

This commit is contained in:
Ivan Kravets
2019-08-15 21:44:07 +03:00
parent 59114bbd86
commit 3edf7e6ca8
2 changed files with 50 additions and 29 deletions

View File

@ -15,6 +15,7 @@ PlatformIO 4.0
* Renamed "enable_ssl" setting to `strict_ssl <http://docs.platformio.org/page/userguide/cmd_settings.html#strict-ssl>`__
* Fixed an issue with incorrect escaping of Windows slashes when using `PIO Unified Debugger <http://docs.platformio.org/page/plus/debugging.html>`__ and "piped" openOCD
* Fixed an issue when "debug", "home", "run", and "test" commands were not shown in "platformio --help" CLI
* Fixed an issue with PIO Home's "No JSON object could be decoded" (`issue #2823 <https://github.com/platformio/platformio-core/issues/2823>`_)
4.0.0 (2019-07-10)
~~~~~~~~~~~~~~~~~~

View File

@ -21,6 +21,7 @@ from io import BytesIO, StringIO
import click
import jsonrpc # pylint: disable=import-error
from twisted.internet import defer # pylint: disable=import-error
from twisted.internet import threads # pylint: disable=import-error
from twisted.internet import utils # pylint: disable=import-error
@ -68,6 +69,10 @@ class MultiThreadingStdStream(object):
class PIOCoreRPC(object):
@staticmethod
def version():
return __version__
@staticmethod
def setup_multithreading_std_streams():
if isinstance(sys.stdout, MultiThreadingStdStream):
@ -79,41 +84,67 @@ class PIOCoreRPC(object):
@staticmethod
def call(args, options=None):
PIOCoreRPC.setup_multithreading_std_streams()
cwd = (options or {}).get("cwd") or os.getcwd()
return defer.maybeDeferred(PIOCoreRPC._call_generator, args, options)
@staticmethod
@defer.inlineCallbacks
def _call_generator(args, options=None):
for i, arg in enumerate(args):
if isinstance(arg, string_types):
args[i] = arg.encode(get_filesystem_encoding()) if PY2 else arg
else:
args[i] = str(arg)
def _call_inline():
to_json = "--json-output" in args
try:
if args and args[0] in ("account", "remote"):
result = yield PIOCoreRPC._call_subprocess(args, options)
defer.returnValue(PIOCoreRPC._process_result(result, to_json))
else:
result = yield PIOCoreRPC._call_inline(args, options)
try:
defer.returnValue(
PIOCoreRPC._process_result(result, to_json))
except ValueError:
# fall-back to subprocess method
result = yield PIOCoreRPC._call_subprocess(args, options)
defer.returnValue(
PIOCoreRPC._process_result(result, to_json))
except Exception as e: # pylint: disable=bare-except
raise jsonrpc.exceptions.JSONRPCDispatchException(
code=4003, message="PIO Core Call Error", data=str(e))
@staticmethod
def _call_inline(args, options):
PIOCoreRPC.setup_multithreading_std_streams()
cwd = (options or {}).get("cwd") or os.getcwd()
def _thread_task():
with fs.cd(cwd):
exit_code = __main__.main(["-c"] + args)
return (PIOCoreRPC.thread_stdout.get_value_and_reset(),
PIOCoreRPC.thread_stderr.get_value_and_reset(), exit_code)
if args and args[0] in ("account", "remote"):
d = utils.getProcessOutputAndValue(
helpers.get_core_fullpath(),
args,
path=cwd,
env={k: v
for k, v in os.environ.items() if "%" not in k})
else:
d = threads.deferToThread(_call_inline)
d.addCallback(PIOCoreRPC._call_callback, "--json-output" in args)
d.addErrback(PIOCoreRPC._call_errback)
return d
return threads.deferToThread(_thread_task)
@staticmethod
def _call_callback(result, json_output=False):
def _call_subprocess(args, options):
cwd = (options or {}).get("cwd") or os.getcwd()
return utils.getProcessOutputAndValue(
helpers.get_core_fullpath(),
args,
path=cwd,
env={k: v
for k, v in os.environ.items() if "%" not in k})
@staticmethod
def _process_result(result, to_json=False):
out, err, code = result
text = ("%s\n\n%s" % (out, err)).strip()
if code != 0:
raise Exception(text)
if not json_output:
if not to_json:
return text
try:
return json.loads(out)
@ -129,14 +160,3 @@ class PIOCoreRPC(object):
except ValueError:
pass
raise e
@staticmethod
def _call_errback(failure):
raise jsonrpc.exceptions.JSONRPCDispatchException(
code=4003,
message="PIO Core Call Error",
data=failure.getErrorMessage())
@staticmethod
def version():
return __version__