Added a new --lint option to the pio project config command // Resolve #4644

This commit is contained in:
Ivan Kravets
2023-06-20 14:25:30 +03:00
parent 5ffa42a5a2
commit e9cf551101
4 changed files with 117 additions and 4 deletions

View File

@ -18,6 +18,7 @@ PlatformIO Core 6
6.1.8 (2023-??-??)
~~~~~~~~~~~~~~~~~~
* Added a new ``--lint`` option to the `pio project config <https://docs.platformio.org/en/latest/core/userguide/project/cmd_config.html>`__ command, enabling users to efficiently perform linting on the |PIOCONF|
* Enhanced the parsing of the |PIOCONF| to provide comprehensive diagnostic information
* Optimized project integration templates to address the issue of long paths on Windows (`issue #4652 <https://github.com/platformio/platformio-core/issues/4652>`_)
* Refactored |UNITTESTING| engine to resolve compiler warnings with "-Wpedantic" option (`pull #4671 <https://github.com/platformio/platformio-core/pull/4671>`_)

View File

@ -30,16 +30,23 @@ from platformio.project.helpers import is_platformio_project
default=os.getcwd,
type=click.Path(exists=True, file_okay=False, dir_okay=True),
)
@click.option("--lint", is_flag=True)
@click.option("--json-output", is_flag=True)
def project_config_cmd(project_dir, json_output):
def project_config_cmd(project_dir, lint, json_output):
if not is_platformio_project(project_dir):
raise NotPlatformIOProjectError(project_dir)
with fs.cd(project_dir):
config = ProjectConfig.get_instance()
if lint:
return lint_configuration(json_output)
return print_configuration(json_output)
def print_configuration(json_output=False):
config = ProjectConfig.get_instance()
if json_output:
return click.echo(config.to_json())
click.echo(
"Computed project configuration for %s" % click.style(project_dir, fg="cyan")
"Computed project configuration for %s" % click.style(os.getcwd(), fg="cyan")
)
for section, options in config.as_tuple():
click.secho(section, fg="cyan")
@ -55,3 +62,43 @@ def project_config_cmd(project_dir, json_output):
)
click.echo()
return None
def lint_configuration(json_output=False):
result = ProjectConfig.lint()
errors = result["errors"]
warnings = result["warnings"]
if json_output:
return click.echo(result)
if not errors and not warnings:
return click.secho(
'The "platformio.ini" configuration file is free from linting errors.',
fg="green",
)
if errors:
click.echo(
tabulate(
[
(
click.style(error["type"], fg="red"),
error["message"],
error.get("source", "") + (f":{error.get('lineno')}")
if "lineno" in error
else "",
)
for error in errors
],
tablefmt="plain",
)
)
if warnings:
click.echo(
tabulate(
[
(click.style("Warning", fg="yellow"), warning)
for warning in warnings
],
tablefmt="plain",
)
)
return None

View File

@ -433,7 +433,41 @@ class ProjectConfigDirsMixin:
return self.get("platformio", f"{name}_dir")
class ProjectConfig(ProjectConfigBase, ProjectConfigDirsMixin):
class ProjectConfigLintMixin:
@classmethod
def lint(cls, path=None):
errors = []
warnings = []
try:
config = cls.get_instance(path)
config.validate(silent=True)
warnings = config.warnings
config.as_tuple()
except Exception as exc: # pylint: disable=broad-exception-caught
if exc.__cause__ is not None:
exc = exc.__cause__
item = {"type": exc.__class__.__name__, "message": str(exc)}
for attr in ("lineno", "source"):
if hasattr(exc, attr):
item[attr] = getattr(exc, attr)
if item["type"] == "ParsingError" and hasattr(exc, "errors"):
for lineno, line in getattr(exc, "errors"):
errors.append(
{
"type": item["type"],
"message": f"Parsing error: {line}",
"lineno": lineno,
"source": item["source"],
}
)
else:
errors.append(item)
return {"errors": errors, "warnings": warnings}
class ProjectConfig(ProjectConfigBase, ProjectConfigDirsMixin, ProjectConfigLintMixin):
_instances = {}
@staticmethod

View File

@ -684,3 +684,34 @@ def test_invalid_env_names(tmp_path: Path):
config = ProjectConfig(str(project_conf))
with pytest.raises(InvalidEnvNameError, match=r".*Invalid environment name 'app:1"):
config.validate()
def test_linting_errors(tmp_path: Path):
project_conf = tmp_path / "platformio.ini"
project_conf.write_text(
"""
[env:app1]
lib_use = 1
broken_line
"""
)
result = ProjectConfig.lint(str(project_conf))
assert not result["warnings"]
assert result["errors"] and len(result["errors"]) == 1
error = result["errors"][0]
assert error["type"] == "ParsingError"
assert error["lineno"] == 4
def test_linting_warnings(tmp_path: Path):
project_conf = tmp_path / "platformio.ini"
project_conf.write_text(
"""
[env:app1]
lib_use = 1
"""
)
result = ProjectConfig.lint(str(project_conf))
assert not result["errors"]
assert result["warnings"] and len(result["warnings"]) == 1
assert "deprecated" in result["warnings"][0]