Move exceptions to their components

This commit is contained in:
Ivan Kravets
2019-11-28 16:15:54 +02:00
parent 47469e8759
commit 49ceadc6ad
18 changed files with 213 additions and 177 deletions

View File

@ -16,7 +16,6 @@ PlatformIO Core 4.0
* Handle project configuration (monitor, test, and upload options) for PIO Remote commands (`issue #2591 <https://github.com/platformio/platformio-core/issues/2591>`_)
* Warn about broken library manifest when scanning dependencies (`issue #3268 <https://github.com/platformio/platformio-core/issues/3268>`_)
* Fixed an issue with the broken latest news for PIO Home
* Fixed an issue when ``env.BoardConfig()`` does not work for custom boards in extra scripts of libraries (`issue #3264 <https://github.com/platformio/platformio-core/issues/3264>`_)
* Fixed an issue with "start-group/end-group" linker flags on Native development platform (`issue #3282 <https://github.com/platformio/platformio-core/issues/3282>`_)

View File

@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import re
import signal
@ -26,13 +25,13 @@ from twisted.internet import reactor # pylint: disable=import-error
from twisted.internet import stdio # pylint: disable=import-error
from twisted.internet import task # pylint: disable=import-error
from platformio import app, exception, fs, proc, util
from platformio import app, fs, proc, telemetry, util
from platformio.commands.debug import helpers, initcfgs
from platformio.commands.debug.exception import DebugInvalidOptionsError
from platformio.commands.debug.process import BaseProcess
from platformio.commands.debug.server import DebugServer
from platformio.compat import hashlib_encode_data, is_bytes
from platformio.project.helpers import get_project_cache_dir
from platformio.telemetry import MeasurementProtocol
LOG_FILE = None
@ -58,6 +57,7 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
self._target_is_run = False
self._last_server_activity = 0
self._auto_continue_timer = None
self._errors_buffer = b""
def spawn(self, gdb_path, prog_path):
session_hash = gdb_path + prog_path
@ -94,7 +94,7 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
]
args.extend(self.args)
if not gdb_path:
raise exception.DebugInvalidOptions("GDB client is not configured")
raise DebugInvalidOptionsError("GDB client is not configured")
gdb_data_dir = self._get_data_dir(gdb_path)
if gdb_data_dir:
args.extend(["--data-directory", gdb_data_dir])
@ -215,6 +215,9 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
self._handle_error(data)
# go to init break automatically
if self.INIT_COMPLETED_BANNER.encode() in data:
telemetry.send_event(
"Debug", "Started", telemetry.encode_run_environment(self.env_options)
)
self._auto_continue_timer = task.LoopingCall(self._auto_exec_continue)
self._auto_continue_timer.start(0.1)
@ -250,20 +253,19 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
self._target_is_run = True
def _handle_error(self, data):
self._errors_buffer += data
if self.PIO_SRC_NAME.encode() not in data or b"Error in sourced" not in data:
return
configuration = {"debug": self.debug_options, "env": self.env_options}
exd = re.sub(r'\\(?!")', "/", json.dumps(configuration))
exd = re.sub(
r'"(?:[a-z]\:)?((/[^"/]+)+)"',
lambda m: '"%s"' % join(*m.group(1).split("/")[-2:]),
exd,
re.I | re.M,
last_erros = self._errors_buffer.decode()
last_erros = " ".join(reversed(last_erros.split("\n")))
last_erros = re.sub(r'((~|&)"|\\n\"|\\t)', " ", last_erros, flags=re.M)
err = "%s -> %s" % (
telemetry.encode_run_environment(self.env_options),
last_erros,
)
mp = MeasurementProtocol()
mp["exd"] = "DebugGDBPioInitError: %s" % exd
mp["exf"] = 1
mp.send("exception")
telemetry.send_exception("DebugInitError: %s" % err, is_fatal=True)
self.transport.loseConnection()
def _kill_previous_session(self):

View File

@ -23,8 +23,10 @@ import click
from platformio import app, exception, fs, proc, util
from platformio.commands.debug import helpers
from platformio.commands.debug.exception import DebugInvalidOptionsError
from platformio.managers.core import inject_contrib_pysite
from platformio.project.config import ProjectConfig
from platformio.project.exception import ProjectEnvsNotAvailableError
from platformio.project.helpers import is_platformio_project, load_project_ide_data
@ -70,7 +72,7 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
env_name = environment or helpers.get_default_debug_env(config)
env_options = config.items(env=env_name, as_dict=True)
if not set(env_options.keys()) >= set(["platform", "board"]):
raise exception.ProjectEnvsNotAvailable()
raise ProjectEnvsNotAvailableError()
debug_options = helpers.validate_debug_options(ctx, env_options)
assert debug_options
@ -79,7 +81,7 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
configuration = load_project_ide_data(project_dir, env_name)
if not configuration:
raise exception.DebugInvalidOptions("Could not load debug configuration")
raise DebugInvalidOptionsError("Could not load debug configuration")
if "--version" in __unprocessed:
result = proc.exec_command([configuration["gdb_path"], "--version"])
@ -140,7 +142,7 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
helpers.is_prog_obsolete(configuration["prog_path"])
if not isfile(configuration["prog_path"]):
raise exception.DebugInvalidOptions("Program/firmware is missed")
raise DebugInvalidOptionsError("Program/firmware is missed")
# run debugging client
inject_contrib_pysite()

View File

@ -0,0 +1,33 @@
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from platformio.exception import PlatformioException, UserSideException
class DebugError(PlatformioException):
pass
class DebugSupportError(DebugError, UserSideException):
MESSAGE = (
"Currently, PlatformIO does not support debugging for `{0}`.\n"
"Please request support at https://github.com/platformio/"
"platformio-core/issues \nor visit -> https://docs.platformio.org"
"/page/plus/debugging.html"
)
class DebugInvalidOptionsError(DebugError, UserSideException):
pass

View File

@ -22,6 +22,7 @@ from os.path import isfile
from platformio import exception, fs, util
from platformio.commands import PlatformioCLI
from platformio.commands.debug.exception import DebugInvalidOptionsError
from platformio.commands.platform import platform_install as cmd_platform_install
from platformio.commands.run.command import cli as cmd_run
from platformio.compat import is_bytes
@ -301,7 +302,5 @@ def reveal_debug_port(env_debug_port, tool_name, tool_settings):
debug_port = _look_for_serial_port(tool_settings.get("hwids", []))
if not debug_port:
raise exception.DebugInvalidOptions(
"Please specify `debug_port` for environment"
)
raise DebugInvalidOptionsError("Please specify `debug_port` for environment")
return debug_port

View File

@ -17,7 +17,8 @@ from os.path import isdir, isfile, join
from twisted.internet import reactor # pylint: disable=import-error
from platformio import exception, fs, util
from platformio import fs, util
from platformio.commands.debug.exception import DebugInvalidOptionsError
from platformio.commands.debug.helpers import escape_gdbmi_stream, is_gdbmi_mode
from platformio.commands.debug.process import BaseProcess
from platformio.proc import where_is_program
@ -53,7 +54,7 @@ class DebugServer(BaseProcess):
if not isfile(server_executable):
server_executable = where_is_program(server_executable)
if not isfile(server_executable):
raise exception.DebugInvalidOptions(
raise DebugInvalidOptionsError(
"\nCould not launch Debug Server '%s'. Please check that it "
"is installed and is included in a system PATH\n\n"
"See documentation or contact contact@platformio.org:\n"

View File

@ -22,6 +22,7 @@ from serial.tools import miniterm
from platformio import exception, fs, util
from platformio.compat import dump_json_to_unicode
from platformio.project.config import ProjectConfig
from platformio.project.exception import NotPlatformIOProjectError
@click.group(short_help="Monitor device or list existing")
@ -181,7 +182,7 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
with fs.cd(kwargs["project_dir"]):
project_options = get_project_options(kwargs["environment"])
kwargs = apply_project_monitor_options(kwargs, project_options)
except exception.NotPlatformIOProject:
except NotPlatformIOProjectError:
pass
if not kwargs["port"]:

View File

@ -27,6 +27,7 @@ from platformio.compat import PY2, get_filesystem_encoding
from platformio.ide.projectgenerator import ProjectGenerator
from platformio.managers.platform import PlatformManager
from platformio.project.config import ProjectConfig
from platformio.project.exception import ProjectError
from platformio.project.helpers import get_project_dir, is_platformio_project
from platformio.project.options import get_config_options_schema
@ -113,7 +114,7 @@ class ProjectRPC(object):
try:
with fs.cd(project_dir):
data = _get_project_data()
except exception.PlatformIOProjectException:
except ProjectError:
continue
for board_id in data.get("boards", []):
@ -158,7 +159,7 @@ class ProjectRPC(object):
config = ProjectConfig(os.path.join(project_dir, "platformio.ini"))
config.validate(silent=True)
project_description = config.get("platformio", "description")
except exception.PlatformIOProjectException:
except ProjectError:
continue
path_tokens = project_dir.split(os.path.sep)

View File

@ -23,6 +23,7 @@ import click
from platformio import exception, fs
from platformio.commands import device
from platformio.managers.core import pioplus_call
from platformio.project.exception import NotPlatformIOProjectError
# pylint: disable=unused-argument
@ -198,7 +199,7 @@ def device_monitor(ctx, **kwargs):
with fs.cd(kwargs["project_dir"]):
project_options = device.get_project_options(kwargs["environment"])
kwargs = device.apply_project_monitor_options(kwargs, project_options)
except exception.NotPlatformIOProject:
except NotPlatformIOProjectError:
pass
kwargs["baud"] = kwargs["baud"] or 9600

View File

@ -16,6 +16,7 @@ from platformio import exception, telemetry
from platformio.commands.platform import platform_install as cmd_platform_install
from platformio.commands.test.processor import CTX_META_TEST_RUNNING_NAME
from platformio.managers.platform import PlatformFactory
from platformio.project.exception import UndefinedEnvPlatformError
# pylint: disable=too-many-instance-attributes
@ -56,12 +57,12 @@ class EnvironmentProcessor(object):
def process(self):
if "platform" not in self.options:
raise exception.UndefinedEnvPlatform(self.name)
raise UndefinedEnvPlatformError(self.name)
build_vars = self.get_build_variables()
build_targets = list(self.get_build_targets())
telemetry.on_run_environment(self.options, build_targets)
telemetry.send_run_environment(self.options, build_targets)
# skip monitor target, we call it above
if "monitor" in build_targets:

View File

@ -152,49 +152,6 @@ class FDSHASumMismatch(PlatformIOPackageException):
)
#
# Project
#
class PlatformIOProjectException(PlatformioException):
pass
class NotPlatformIOProject(PlatformIOProjectException):
MESSAGE = (
"Not a PlatformIO project. `platformio.ini` file has not been "
"found in current working directory ({0}). To initialize new project "
"please use `platformio init` command"
)
class InvalidProjectConf(PlatformIOProjectException):
MESSAGE = "Invalid '{0}' (project configuration file): '{1}'"
class UndefinedEnvPlatform(PlatformIOProjectException):
MESSAGE = "Please specify platform for '{0}' environment"
class ProjectEnvsNotAvailable(PlatformIOProjectException):
MESSAGE = "Please setup environments in `platformio.ini` file"
class UnknownEnvNames(PlatformIOProjectException):
MESSAGE = "Unknown environment names '{0}'. Valid names are '{1}'"
class ProjectOptionValueError(PlatformIOProjectException):
MESSAGE = "{0} for option `{1}` in section [{2}]"
#
# Library
#
@ -319,7 +276,7 @@ class UpgradeError(PlatformioException):
"""
class HomeDirPermissionsError(PlatformioException):
class HomeDirPermissionsError(UserSideException):
MESSAGE = (
"The directory `{0}` or its parent directory is not owned by the "
@ -338,20 +295,6 @@ class CygwinEnvDetected(PlatformioException):
)
class DebugSupportError(PlatformioException):
MESSAGE = (
"Currently, PlatformIO does not support debugging for `{0}`.\n"
"Please request support at https://github.com/platformio/"
"platformio-core/issues \nor visit -> https://docs.platformio.org"
"/page/plus/debugging.html"
)
class DebugInvalidOptions(PlatformioException):
pass
class TestDirNotExists(PlatformioException):
MESSAGE = (

View File

@ -151,7 +151,7 @@ def after_upgrade(ctx):
"PlatformIO has been successfully upgraded to %s!\n" % __version__,
fg="green",
)
telemetry.on_event(
telemetry.send_event(
category="Auto",
action="Upgrade",
label="%s > %s" % (last_version, __version__),
@ -315,7 +315,7 @@ def check_internal_updates(ctx, what):
ctx.invoke(cmd_lib_update, libraries=outdated_items)
click.echo()
telemetry.on_event(category="Auto", action="Update", label=what.title())
telemetry.send_event(category="Auto", action="Update", label=what.title())
click.echo("*" * terminal_width)
click.echo("")

View File

@ -26,7 +26,7 @@ from platformio.project.config import ProjectConfig
CORE_PACKAGES = {
"contrib-piohome": ">=3.1.0-beta.3,<3.2.0",
"contrib-pysite": "~2.%d%d.0" % (sys.version_info[0], sys.version_info[1]),
"tool-pioplus": "^2.6.0",
"tool-pioplus": "^2.6.1",
"tool-unity": "~1.20403.0",
"tool-scons": "~2.20501.7" if PY2 else "~3.30101.0",
"tool-cppcheck": "~1.189.0",

View File

@ -24,7 +24,7 @@ import click
import requests
import semantic_version
from platformio import __version__, app, exception, fs, telemetry, util
from platformio import __version__, app, exception, fs, util
from platformio.compat import hashlib_encode_data
from platformio.downloader import FileDownloader
from platformio.lockfile import LockFile
@ -660,7 +660,7 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
def install(
self, name, requirements=None, silent=False, after_update=False, force=False
):
): # pylint: disable=unused-argument
pkg_dir = None
# interprocess lock
with LockFile(self.package_dir):
@ -709,13 +709,6 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
manifest = self.load_manifest(pkg_dir)
assert manifest
if not after_update:
telemetry.on_event(
category=self.__class__.__name__,
action="Install",
label=manifest["name"],
)
click.secho(
"{name} @ {version} has been successfully installed!".format(
**manifest
@ -725,7 +718,9 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
return pkg_dir
def uninstall(self, package, requirements=None, after_update=False):
def uninstall(
self, package, requirements=None, after_update=False
): # pylint: disable=unused-argument
# interprocess lock
with LockFile(self.package_dir):
self.cache_reset()
@ -764,13 +759,6 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
click.echo("[%s]" % click.style("OK", fg="green"))
if not after_update:
telemetry.on_event(
category=self.__class__.__name__,
action="Uninstall",
label=manifest["name"],
)
return True
def update(self, package, requirements=None, only_check=False):
@ -819,9 +807,6 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
self.uninstall(pkg_dir, after_update=True)
self.install(name, latest, after_update=True)
telemetry.on_event(
category=self.__class__.__name__, action="Update", label=manifest["name"]
)
return True

View File

@ -23,7 +23,11 @@ from os.path import basename, dirname, isdir, isfile, join
import click
import semantic_version
from platformio import __version__, app, exception, fs, proc, util
from platformio import __version__, app, exception, fs, proc, telemetry, util
from platformio.commands.debug.exception import (
DebugInvalidOptionsError,
DebugSupportError,
)
from platformio.compat import PY2, hashlib_encode_data, is_bytes, load_python_module
from platformio.managers.core import get_core_package_dir
from platformio.managers.package import BasePkgManager, PackageManager
@ -799,11 +803,12 @@ class PlatformBoardConfig(object):
if tool_name == "custom":
return tool_name
if not debug_tools:
raise exception.DebugSupportError(self._manifest["name"])
telemetry.send_event("Debug", "Request", self.id)
raise DebugSupportError(self._manifest["name"])
if tool_name:
if tool_name in debug_tools:
return tool_name
raise exception.DebugInvalidOptions(
raise DebugInvalidOptionsError(
"Unknown debug tool `%s`. Please use one of `%s` or `custom`"
% (tool_name, ", ".join(sorted(list(debug_tools))))
)

View File

@ -20,8 +20,9 @@ from hashlib import sha1
import click
from platformio import exception, fs
from platformio import fs
from platformio.compat import PY2, WINDOWS, hashlib_encode_data
from platformio.project import exception
from platformio.project.options import ProjectOptions
try:
@ -104,7 +105,7 @@ class ProjectConfigBase(object):
try:
self._parser.read(path)
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(path, str(e))
raise exception.InvalidProjectConfError(path, str(e))
if not parse_extra:
return
@ -273,7 +274,7 @@ class ProjectConfigBase(object):
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
pass # handle value from system environment
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(self.path, str(e))
raise exception.InvalidProjectConfError(self.path, str(e))
option_meta = ProjectOptions.get("%s.%s" % (section.split(":", 1)[0], option))
if not option_meta:
@ -327,14 +328,14 @@ class ProjectConfigBase(object):
def validate(self, envs=None, silent=False):
if not os.path.isfile(self.path):
raise exception.NotPlatformIOProject(self.path)
raise exception.NotPlatformIOProjectError(self.path)
# check envs
known = set(self.envs())
if not known:
raise exception.ProjectEnvsNotAvailable()
raise exception.ProjectEnvsNotAvailableError()
unknown = set(list(envs or []) + self.default_envs()) - known
if unknown:
raise exception.UnknownEnvNames(", ".join(unknown), ", ".join(known))
raise exception.UnknownEnvNamesError(", ".join(unknown), ", ".join(known))
if not silent:
for warning in self.warnings:
click.secho("Warning! %s" % warning, fg="yellow")

View File

@ -0,0 +1,53 @@
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from platformio.exception import PlatformioException, UserSideException
class ProjectError(PlatformioException):
pass
class NotPlatformIOProjectError(ProjectError, UserSideException):
MESSAGE = (
"Not a PlatformIO project. `platformio.ini` file has not been "
"found in current working directory ({0}). To initialize new project "
"please use `platformio init` command"
)
class InvalidProjectConfError(ProjectError, UserSideException):
MESSAGE = "Invalid '{0}' (project configuration file): '{1}'"
class UndefinedEnvPlatformError(ProjectError, UserSideException):
MESSAGE = "Please specify platform for '{0}' environment"
class ProjectEnvsNotAvailableError(ProjectError, UserSideException):
MESSAGE = "Please setup environments in `platformio.ini` file"
class UnknownEnvNamesError(ProjectError, UserSideException):
MESSAGE = "Unknown environment names '{0}'. Valid names are '{1}'"
class ProjectOptionValueError(ProjectError, UserSideException):
MESSAGE = "{0} for option `{1}` in section [{2}]"

View File

@ -13,13 +13,12 @@
# limitations under the License.
import atexit
import os
import platform
import re
import sys
import threading
from collections import deque
from os import getenv, sep
from os.path import join
from time import sleep, time
from traceback import format_exc
@ -99,8 +98,8 @@ class MeasurementProtocol(TelemetryBase):
dpdata.append("PlatformIO/%s" % __version__)
if app.get_session_var("caller_id"):
dpdata.append("Caller/%s" % app.get_session_var("caller_id"))
if getenv("PLATFORMIO_IDE"):
dpdata.append("IDE/%s" % getenv("PLATFORMIO_IDE"))
if os.getenv("PLATFORMIO_IDE"):
dpdata.append("IDE/%s" % os.getenv("PLATFORMIO_IDE"))
self["an"] = " ".join(dpdata)
def _prefill_custom_data(self):
@ -179,13 +178,10 @@ class MeasurementProtocol(TelemetryBase):
cmd_path.append(sub_cmd)
self["screen_name"] = " ".join([p.title() for p in cmd_path])
@staticmethod
def _ignore_hit():
def _ignore_hit(self):
if not app.get_setting("enable_telemetry"):
return True
if app.get_session_var("caller_id") and all(
c in sys.argv for c in ("run", "idedata")
):
if all(c in sys.argv for c in ("run", "idedata")) or self["ea"] == "Idedata":
return True
return False
@ -296,29 +292,64 @@ def on_command():
measure_ci()
def on_exception(e):
skip_conditions = [
isinstance(e, cls)
for cls in (IOError, exception.ReturnErrorCode, exception.UserSideException,)
]
try:
skip_conditions.append("[API] Account: " in str(e))
except UnicodeEncodeError as ue:
e = ue
if any(skip_conditions):
return
is_fatal = any(
[
not isinstance(e, exception.PlatformioException),
"Error" in e.__class__.__name__,
]
)
description = "%s: %s" % (
type(e).__name__,
" ".join(reversed(format_exc().split("\n"))) if is_fatal else str(e),
)
send_exception(description, is_fatal)
def measure_ci():
event = {"category": "CI", "action": "NoName", "label": None}
known_cis = ("TRAVIS", "APPVEYOR", "GITLAB_CI", "CIRCLECI", "SHIPPABLE", "DRONE")
for name in known_cis:
if getenv(name, "false").lower() == "true":
if os.getenv(name, "false").lower() == "true":
event["action"] = name
break
on_event(**event)
send_event(**event)
def on_run_environment(options, targets):
non_sensative_values = ["board", "platform", "framework"]
safe_options = []
for key, value in sorted(options.items()):
if key in non_sensative_values:
safe_options.append("%s=%s" % (key, value))
else:
safe_options.append(key)
targets = [t.title() for t in targets or ["run"]]
on_event("Env", " ".join(targets), "&".join(safe_options))
def encode_run_environment(options):
non_sensative_keys = [
"platform",
"framework",
"board",
"upload_protocol",
"check_tool",
"debug_tool",
]
safe_options = [
"%s=%s" % (k, v) for k, v in sorted(options.items()) if k in non_sensative_keys
]
return "&".join(safe_options)
def on_event(category, action, label=None, value=None, screen_name=None):
def send_run_environment(options, targets):
send_event(
"Env",
" ".join([t.title() for t in targets or ["run"]]),
encode_run_environment(options),
)
def send_event(category, action, label=None, value=None, screen_name=None):
mp = MeasurementProtocol()
mp["event_category"] = category[:150]
mp["event_action"] = action[:500]
@ -331,43 +362,21 @@ def on_event(category, action, label=None, value=None, screen_name=None):
mp.send("event")
def on_exception(e):
def _cleanup_description(text):
text = text.replace("Traceback (most recent call last):", "")
text = re.sub(
r'File "([^"]+)"',
lambda m: join(*m.group(1).split(sep)[-2:]),
text,
flags=re.M,
)
text = re.sub(r"\s+", " ", text, flags=re.M)
return text.strip()
skip_conditions = [
isinstance(e, cls)
for cls in (
IOError,
exception.ReturnErrorCode,
exception.UserSideException,
exception.PlatformIOProjectException,
)
]
try:
skip_conditions.append("[API] Account: " in str(e))
except UnicodeEncodeError as ue:
e = ue
if any(skip_conditions):
return
is_crash = any(
[
not isinstance(e, exception.PlatformioException),
"Error" in e.__class__.__name__,
]
def send_exception(description, is_fatal=False):
# cleanup sensitive information, such as paths
description = description.replace("Traceback (most recent call last):", "")
description = description.replace("\\", "/")
description = re.sub(
r'(^|\s+|")(?:[a-z]\:)?((/[^"/]+)+)(\s+|"|$)',
lambda m: " %s " % os.path.join(*m.group(2).split("/")[-2:]),
description,
re.I | re.M,
)
description = re.sub(r"\s+", " ", description, flags=re.M)
mp = MeasurementProtocol()
description = _cleanup_description(format_exc() if is_crash else str(e))
mp["exd"] = ("%s: %s" % (type(e).__name__, description))[:2048]
mp["exf"] = 1 if is_crash else 0
mp["exd"] = description[:8192].strip()
mp["exf"] = 1 if is_fatal else 0
mp.send("exception")