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

View File

@ -23,8 +23,10 @@ import click
from platformio import app, exception, fs, proc, util from platformio import app, exception, fs, proc, util
from platformio.commands.debug import helpers from platformio.commands.debug import helpers
from platformio.commands.debug.exception import DebugInvalidOptionsError
from platformio.managers.core import inject_contrib_pysite from platformio.managers.core import inject_contrib_pysite
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.exception import ProjectEnvsNotAvailableError
from platformio.project.helpers import is_platformio_project, load_project_ide_data 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_name = environment or helpers.get_default_debug_env(config)
env_options = config.items(env=env_name, as_dict=True) env_options = config.items(env=env_name, as_dict=True)
if not set(env_options.keys()) >= set(["platform", "board"]): if not set(env_options.keys()) >= set(["platform", "board"]):
raise exception.ProjectEnvsNotAvailable() raise ProjectEnvsNotAvailableError()
debug_options = helpers.validate_debug_options(ctx, env_options) debug_options = helpers.validate_debug_options(ctx, env_options)
assert debug_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) configuration = load_project_ide_data(project_dir, env_name)
if not configuration: if not configuration:
raise exception.DebugInvalidOptions("Could not load debug configuration") raise DebugInvalidOptionsError("Could not load debug configuration")
if "--version" in __unprocessed: if "--version" in __unprocessed:
result = proc.exec_command([configuration["gdb_path"], "--version"]) 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"]) helpers.is_prog_obsolete(configuration["prog_path"])
if not isfile(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 # run debugging client
inject_contrib_pysite() 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 import exception, fs, util
from platformio.commands import PlatformioCLI 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.platform import platform_install as cmd_platform_install
from platformio.commands.run.command import cli as cmd_run from platformio.commands.run.command import cli as cmd_run
from platformio.compat import is_bytes 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", [])) debug_port = _look_for_serial_port(tool_settings.get("hwids", []))
if not debug_port: if not debug_port:
raise exception.DebugInvalidOptions( raise DebugInvalidOptionsError("Please specify `debug_port` for environment")
"Please specify `debug_port` for environment"
)
return debug_port 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 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.helpers import escape_gdbmi_stream, is_gdbmi_mode
from platformio.commands.debug.process import BaseProcess from platformio.commands.debug.process import BaseProcess
from platformio.proc import where_is_program from platformio.proc import where_is_program
@ -53,7 +54,7 @@ class DebugServer(BaseProcess):
if not isfile(server_executable): if not isfile(server_executable):
server_executable = where_is_program(server_executable) server_executable = where_is_program(server_executable)
if not isfile(server_executable): if not isfile(server_executable):
raise exception.DebugInvalidOptions( raise DebugInvalidOptionsError(
"\nCould not launch Debug Server '%s'. Please check that it " "\nCould not launch Debug Server '%s'. Please check that it "
"is installed and is included in a system PATH\n\n" "is installed and is included in a system PATH\n\n"
"See documentation or contact contact@platformio.org:\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 import exception, fs, util
from platformio.compat import dump_json_to_unicode from platformio.compat import dump_json_to_unicode
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.exception import NotPlatformIOProjectError
@click.group(short_help="Monitor device or list existing") @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"]): with fs.cd(kwargs["project_dir"]):
project_options = get_project_options(kwargs["environment"]) project_options = get_project_options(kwargs["environment"])
kwargs = apply_project_monitor_options(kwargs, project_options) kwargs = apply_project_monitor_options(kwargs, project_options)
except exception.NotPlatformIOProject: except NotPlatformIOProjectError:
pass pass
if not kwargs["port"]: 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.ide.projectgenerator import ProjectGenerator
from platformio.managers.platform import PlatformManager from platformio.managers.platform import PlatformManager
from platformio.project.config import ProjectConfig 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.helpers import get_project_dir, is_platformio_project
from platformio.project.options import get_config_options_schema from platformio.project.options import get_config_options_schema
@ -113,7 +114,7 @@ class ProjectRPC(object):
try: try:
with fs.cd(project_dir): with fs.cd(project_dir):
data = _get_project_data() data = _get_project_data()
except exception.PlatformIOProjectException: except ProjectError:
continue continue
for board_id in data.get("boards", []): for board_id in data.get("boards", []):
@ -158,7 +159,7 @@ class ProjectRPC(object):
config = ProjectConfig(os.path.join(project_dir, "platformio.ini")) config = ProjectConfig(os.path.join(project_dir, "platformio.ini"))
config.validate(silent=True) config.validate(silent=True)
project_description = config.get("platformio", "description") project_description = config.get("platformio", "description")
except exception.PlatformIOProjectException: except ProjectError:
continue continue
path_tokens = project_dir.split(os.path.sep) path_tokens = project_dir.split(os.path.sep)

View File

@ -23,6 +23,7 @@ import click
from platformio import exception, fs from platformio import exception, fs
from platformio.commands import device from platformio.commands import device
from platformio.managers.core import pioplus_call from platformio.managers.core import pioplus_call
from platformio.project.exception import NotPlatformIOProjectError
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -198,7 +199,7 @@ def device_monitor(ctx, **kwargs):
with fs.cd(kwargs["project_dir"]): with fs.cd(kwargs["project_dir"]):
project_options = device.get_project_options(kwargs["environment"]) project_options = device.get_project_options(kwargs["environment"])
kwargs = device.apply_project_monitor_options(kwargs, project_options) kwargs = device.apply_project_monitor_options(kwargs, project_options)
except exception.NotPlatformIOProject: except NotPlatformIOProjectError:
pass pass
kwargs["baud"] = kwargs["baud"] or 9600 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.platform import platform_install as cmd_platform_install
from platformio.commands.test.processor import CTX_META_TEST_RUNNING_NAME from platformio.commands.test.processor import CTX_META_TEST_RUNNING_NAME
from platformio.managers.platform import PlatformFactory from platformio.managers.platform import PlatformFactory
from platformio.project.exception import UndefinedEnvPlatformError
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
@ -56,12 +57,12 @@ class EnvironmentProcessor(object):
def process(self): def process(self):
if "platform" not in self.options: if "platform" not in self.options:
raise exception.UndefinedEnvPlatform(self.name) raise UndefinedEnvPlatformError(self.name)
build_vars = self.get_build_variables() build_vars = self.get_build_variables()
build_targets = list(self.get_build_targets()) 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 # skip monitor target, we call it above
if "monitor" in build_targets: 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 # Library
# #
@ -319,7 +276,7 @@ class UpgradeError(PlatformioException):
""" """
class HomeDirPermissionsError(PlatformioException): class HomeDirPermissionsError(UserSideException):
MESSAGE = ( MESSAGE = (
"The directory `{0}` or its parent directory is not owned by the " "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): class TestDirNotExists(PlatformioException):
MESSAGE = ( MESSAGE = (

View File

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

View File

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

View File

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

View File

@ -23,7 +23,11 @@ from os.path import basename, dirname, isdir, isfile, join
import click import click
import semantic_version 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.compat import PY2, hashlib_encode_data, is_bytes, load_python_module
from platformio.managers.core import get_core_package_dir from platformio.managers.core import get_core_package_dir
from platformio.managers.package import BasePkgManager, PackageManager from platformio.managers.package import BasePkgManager, PackageManager
@ -799,11 +803,12 @@ class PlatformBoardConfig(object):
if tool_name == "custom": if tool_name == "custom":
return tool_name return tool_name
if not debug_tools: 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:
if tool_name in debug_tools: if tool_name in debug_tools:
return tool_name return tool_name
raise exception.DebugInvalidOptions( raise DebugInvalidOptionsError(
"Unknown debug tool `%s`. Please use one of `%s` or `custom`" "Unknown debug tool `%s`. Please use one of `%s` or `custom`"
% (tool_name, ", ".join(sorted(list(debug_tools)))) % (tool_name, ", ".join(sorted(list(debug_tools))))
) )

View File

@ -20,8 +20,9 @@ from hashlib import sha1
import click import click
from platformio import exception, fs from platformio import fs
from platformio.compat import PY2, WINDOWS, hashlib_encode_data from platformio.compat import PY2, WINDOWS, hashlib_encode_data
from platformio.project import exception
from platformio.project.options import ProjectOptions from platformio.project.options import ProjectOptions
try: try:
@ -104,7 +105,7 @@ class ProjectConfigBase(object):
try: try:
self._parser.read(path) self._parser.read(path)
except ConfigParser.Error as e: except ConfigParser.Error as e:
raise exception.InvalidProjectConf(path, str(e)) raise exception.InvalidProjectConfError(path, str(e))
if not parse_extra: if not parse_extra:
return return
@ -273,7 +274,7 @@ class ProjectConfigBase(object):
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
pass # handle value from system environment pass # handle value from system environment
except ConfigParser.Error as e: 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)) option_meta = ProjectOptions.get("%s.%s" % (section.split(":", 1)[0], option))
if not option_meta: if not option_meta:
@ -327,14 +328,14 @@ class ProjectConfigBase(object):
def validate(self, envs=None, silent=False): def validate(self, envs=None, silent=False):
if not os.path.isfile(self.path): if not os.path.isfile(self.path):
raise exception.NotPlatformIOProject(self.path) raise exception.NotPlatformIOProjectError(self.path)
# check envs # check envs
known = set(self.envs()) known = set(self.envs())
if not known: if not known:
raise exception.ProjectEnvsNotAvailable() raise exception.ProjectEnvsNotAvailableError()
unknown = set(list(envs or []) + self.default_envs()) - known unknown = set(list(envs or []) + self.default_envs()) - known
if unknown: if unknown:
raise exception.UnknownEnvNames(", ".join(unknown), ", ".join(known)) raise exception.UnknownEnvNamesError(", ".join(unknown), ", ".join(known))
if not silent: if not silent:
for warning in self.warnings: for warning in self.warnings:
click.secho("Warning! %s" % warning, fg="yellow") 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. # limitations under the License.
import atexit import atexit
import os
import platform import platform
import re import re
import sys import sys
import threading import threading
from collections import deque from collections import deque
from os import getenv, sep
from os.path import join
from time import sleep, time from time import sleep, time
from traceback import format_exc from traceback import format_exc
@ -99,8 +98,8 @@ class MeasurementProtocol(TelemetryBase):
dpdata.append("PlatformIO/%s" % __version__) dpdata.append("PlatformIO/%s" % __version__)
if app.get_session_var("caller_id"): if app.get_session_var("caller_id"):
dpdata.append("Caller/%s" % app.get_session_var("caller_id")) dpdata.append("Caller/%s" % app.get_session_var("caller_id"))
if getenv("PLATFORMIO_IDE"): if os.getenv("PLATFORMIO_IDE"):
dpdata.append("IDE/%s" % getenv("PLATFORMIO_IDE")) dpdata.append("IDE/%s" % os.getenv("PLATFORMIO_IDE"))
self["an"] = " ".join(dpdata) self["an"] = " ".join(dpdata)
def _prefill_custom_data(self): def _prefill_custom_data(self):
@ -179,13 +178,10 @@ class MeasurementProtocol(TelemetryBase):
cmd_path.append(sub_cmd) cmd_path.append(sub_cmd)
self["screen_name"] = " ".join([p.title() for p in cmd_path]) self["screen_name"] = " ".join([p.title() for p in cmd_path])
@staticmethod def _ignore_hit(self):
def _ignore_hit():
if not app.get_setting("enable_telemetry"): if not app.get_setting("enable_telemetry"):
return True return True
if app.get_session_var("caller_id") and all( if all(c in sys.argv for c in ("run", "idedata")) or self["ea"] == "Idedata":
c in sys.argv for c in ("run", "idedata")
):
return True return True
return False return False
@ -296,29 +292,64 @@ def on_command():
measure_ci() 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(): def measure_ci():
event = {"category": "CI", "action": "NoName", "label": None} event = {"category": "CI", "action": "NoName", "label": None}
known_cis = ("TRAVIS", "APPVEYOR", "GITLAB_CI", "CIRCLECI", "SHIPPABLE", "DRONE") known_cis = ("TRAVIS", "APPVEYOR", "GITLAB_CI", "CIRCLECI", "SHIPPABLE", "DRONE")
for name in known_cis: for name in known_cis:
if getenv(name, "false").lower() == "true": if os.getenv(name, "false").lower() == "true":
event["action"] = name event["action"] = name
break break
on_event(**event) send_event(**event)
def on_run_environment(options, targets): def encode_run_environment(options):
non_sensative_values = ["board", "platform", "framework"] non_sensative_keys = [
safe_options = [] "platform",
for key, value in sorted(options.items()): "framework",
if key in non_sensative_values: "board",
safe_options.append("%s=%s" % (key, value)) "upload_protocol",
else: "check_tool",
safe_options.append(key) "debug_tool",
targets = [t.title() for t in targets or ["run"]] ]
on_event("Env", " ".join(targets), "&".join(safe_options)) 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 = MeasurementProtocol()
mp["event_category"] = category[:150] mp["event_category"] = category[:150]
mp["event_action"] = action[:500] mp["event_action"] = action[:500]
@ -331,43 +362,21 @@ def on_event(category, action, label=None, value=None, screen_name=None):
mp.send("event") mp.send("event")
def on_exception(e): def send_exception(description, is_fatal=False):
def _cleanup_description(text): # cleanup sensitive information, such as paths
text = text.replace("Traceback (most recent call last):", "") description = description.replace("Traceback (most recent call last):", "")
text = re.sub( description = description.replace("\\", "/")
r'File "([^"]+)"', description = re.sub(
lambda m: join(*m.group(1).split(sep)[-2:]), r'(^|\s+|")(?:[a-z]\:)?((/[^"/]+)+)(\s+|"|$)',
text, lambda m: " %s " % os.path.join(*m.group(2).split("/")[-2:]),
flags=re.M, description,
) re.I | 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__,
]
) )
description = re.sub(r"\s+", " ", description, flags=re.M)
mp = MeasurementProtocol() mp = MeasurementProtocol()
description = _cleanup_description(format_exc() if is_crash else str(e)) mp["exd"] = description[:8192].strip()
mp["exd"] = ("%s: %s" % (type(e).__name__, description))[:2048] mp["exf"] = 1 if is_fatal else 0
mp["exf"] = 1 if is_crash else 0
mp.send("exception") mp.send("exception")