{
  "version": "1.8.2",
  "script": "class ManagedProcess:\n    @staticmethod\n    def _stream_logs(logger, is_stderr, stream):\n        # iter() reads until '' or EOF\n        for line in iter(stream.readline, \"\"):\n            line = line.rstrip()\n            if is_stderr:\n                logger.error(line)\n            else:\n                logger.info(line)\n\n    @staticmethod\n    def run(js7Step, cmd):\n        if not cmd:\n            raise ValueError(\"No command specified: 'cmd' must be a non-empty list\")\n\n        process = subprocess.Popen(\n            cmd,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            text=True,\n            bufsize=1\n        )\n\n        js7Step.setCancelableResource(process)\n\n        # Defines the thread for live stdout\n        stdout_thread = threading.Thread(\n            target=ManagedProcess._stream_logs,\n            args=(js7Step.getLogger(), False, process.stdout),\n            daemon=False\n        )\n\n        # Defines the thread for live stderr\n        stderr_thread = threading.Thread(\n            target=ManagedProcess._stream_logs,\n            args=(js7Step.getLogger(), True, process.stderr),\n            daemon=False\n        )\n\n        stdout_thread.start()\n        stderr_thread.start()      \n\n        # Waits until the process is finish\n        rc = process.wait()\n        \n        # Ensures threads have finished reading\n        stdout_thread.join()\n        stderr_thread.join()\n\n        return rc\n\n    @staticmethod\n    def cancel(js7Step):\n        if js7Step is None:\n            return\n            \n        process = js7Step.getCancelableResource()\n        if process is None:\n            return\n        \n        try:\n            if process.poll() is None:\n                js7Step.getLogger().info(f\"[ManagedProcess.cancel][kill]{process}...\")\n                process.kill()\n        except Exception as e:\n            js7Step.getLogger().error(f\"[ManagedProcess.cancel][{process}]{e}\")\n\n        return process.wait()"
}