From dcecd5f922aec5e4d92c68713e342cf0b46abd26 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 31 May 2022 17:07:56 +0300 Subject: [PATCH] Refactor handling of CLI commands --- platformio/__main__.py | 4 +- .../{commands/account.py => account/cli.py} | 0 platformio/cli.py | 96 +++++++++++++++++++ platformio/commands/__init__.py | 73 -------------- platformio/commands/debug.py | 17 ---- platformio/commands/lib/command.py | 2 +- platformio/commands/remote/command.py | 2 +- platformio/commands/test.py | 17 ---- platformio/debug/{command.py => cli.py} | 3 + platformio/debug/helpers.py | 2 +- .../device/command.py => device/cli.py} | 0 platformio/maintenance.py | 2 +- .../{commands/pkg.py => package/cli.py} | 0 platformio/package/manager/base.py | 2 +- .../{commands/project.py => project/cli.py} | 0 platformio/telemetry.py | 2 +- platformio/test/{command.py => cli.py} | 3 + tests/commands/test_account_org_team.py | 2 +- tests/commands/test_lib_complex.py | 2 +- tests/commands/test_test.py | 2 +- 20 files changed, 113 insertions(+), 118 deletions(-) rename platformio/{commands/account.py => account/cli.py} (100%) create mode 100644 platformio/cli.py delete mode 100644 platformio/commands/debug.py delete mode 100644 platformio/commands/test.py rename platformio/debug/{command.py => cli.py} (99%) rename platformio/{commands/device/command.py => device/cli.py} (100%) rename platformio/{commands/pkg.py => package/cli.py} (100%) rename platformio/{commands/project.py => project/cli.py} (100%) rename platformio/test/{command.py => cli.py} (99%) diff --git a/platformio/__main__.py b/platformio/__main__.py index 464f2fb9..c7f8e245 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -19,7 +19,7 @@ from traceback import format_exc import click from platformio import __version__, exception, maintenance -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import IS_CYGWIN, ensure_python3 @@ -27,7 +27,7 @@ from platformio.compat import IS_CYGWIN, ensure_python3 cls=PlatformioCLI, context_settings=dict(help_option_names=["-h", "--help"]) ) @click.version_option(__version__, prog_name="PlatformIO Core") -@click.option("--force", "-f", is_flag=True, help="DEPRECATED") +@click.option("--force", "-f", is_flag=True, help="DEPRECATED", hidden=True) @click.option("--caller", "-c", help="Caller ID (service)") @click.option("--no-ansi", is_flag=True, help="Do not print ANSI control characters") @click.pass_context diff --git a/platformio/commands/account.py b/platformio/account/cli.py similarity index 100% rename from platformio/commands/account.py rename to platformio/account/cli.py diff --git a/platformio/cli.py b/platformio/cli.py new file mode 100644 index 00000000..bdb46a3d --- /dev/null +++ b/platformio/cli.py @@ -0,0 +1,96 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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. + +import importlib +from pathlib import Path + +import click + + +class PlatformioCLI(click.MultiCommand): + + leftover_args = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._pio_root_path = Path(__file__).parent + self._pio_cmd_aliases = dict(package="pkg") + + def _find_pio_commands(self): + def _to_module_path(p): + return ( + "platformio." + ".".join(p.relative_to(self._pio_root_path).parts)[:-3] + ) + + result = {} + for p in self._pio_root_path.rglob("cli.py"): + # skip this module + if p.parent == self._pio_root_path: + continue + cmd_name = p.parent.name + result[self._pio_cmd_aliases.get(cmd_name, cmd_name)] = _to_module_path(p) + + # find legacy commands + for p in (self._pio_root_path / "commands").iterdir(): + if p.name.startswith("_"): + continue + if (p / "command.py").is_file(): + result[p.name] = _to_module_path(p / "command.py") + elif p.name.endswith(".py"): + result[p.name[:-3]] = _to_module_path(p) + + return result + + @staticmethod + def in_silence(): + args = PlatformioCLI.leftover_args + return args and any( + [ + args[0] == "debug" and "--interpreter" in " ".join(args), + args[0] == "upgrade", + "--json-output" in args, + "--version" in args, + ] + ) + + def invoke(self, ctx): + PlatformioCLI.leftover_args = ctx.args + if hasattr(ctx, "protected_args"): + PlatformioCLI.leftover_args = ctx.protected_args + ctx.args + return super().invoke(ctx) + + def list_commands(self, ctx): + return sorted(list(self._find_pio_commands())) + + def get_command(self, ctx, cmd_name): + commands = self._find_pio_commands() + if cmd_name not in commands: + return self._handle_obsolate_command(ctx, cmd_name) + module = importlib.import_module(commands[cmd_name]) + return getattr(module, "cli") + + @staticmethod + def _handle_obsolate_command(ctx, cmd_name): + # pylint: disable=import-outside-toplevel + if cmd_name == "init": + from platformio.project.commands.init import project_init_cmd + + return project_init_cmd + + if cmd_name == "package": + from platformio.package.cli import cli + + return cli + + raise click.UsageError('No such command "%s"' % cmd_name, ctx) diff --git a/platformio/commands/__init__.py b/platformio/commands/__init__.py index 22cacc60..b0514903 100644 --- a/platformio/commands/__init__.py +++ b/platformio/commands/__init__.py @@ -11,76 +11,3 @@ # 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. - -import os - -import click - - -class PlatformioCLI(click.MultiCommand): - - leftover_args = [] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._pio_cmds_dir = os.path.dirname(__file__) - - @staticmethod - def in_silence(): - args = PlatformioCLI.leftover_args - return args and any( - [ - args[0] == "debug" and "--interpreter" in " ".join(args), - args[0] == "upgrade", - "--json-output" in args, - "--version" in args, - ] - ) - - def invoke(self, ctx): - PlatformioCLI.leftover_args = ctx.args - if hasattr(ctx, "protected_args"): - PlatformioCLI.leftover_args = ctx.protected_args + ctx.args - return super().invoke(ctx) - - def list_commands(self, ctx): - cmds = [] - for cmd_name in os.listdir(self._pio_cmds_dir): - if cmd_name.startswith("__init__"): - continue - if os.path.isfile(os.path.join(self._pio_cmds_dir, cmd_name, "command.py")): - cmds.append(cmd_name) - elif cmd_name.endswith(".py"): - cmds.append(cmd_name[:-3]) - cmds.sort() - return cmds - - def get_command(self, ctx, cmd_name): - mod = None - try: - mod_path = "platformio.commands." + cmd_name - if os.path.isfile(os.path.join(self._pio_cmds_dir, cmd_name, "command.py")): - mod_path = "platformio.commands.%s.command" % cmd_name - mod = __import__(mod_path, None, None, ["cli"]) - except ImportError: - try: - return self._handle_obsolate_command(cmd_name) - except AttributeError: - pass - raise click.UsageError('No such command "%s"' % cmd_name, ctx) - return mod.cli - - @staticmethod - def _handle_obsolate_command(name): - # pylint: disable=import-outside-toplevel - if name == "init": - from platformio.project.commands.init import project_init_cmd - - return project_init_cmd - - if name == "package": - from platformio.commands.pkg import cli - - return cli - - raise AttributeError() diff --git a/platformio/commands/debug.py b/platformio/commands/debug.py deleted file mode 100644 index 3ab61d9b..00000000 --- a/platformio/commands/debug.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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. - -# pylint: disable=unused-import - -from platformio.debug.command import debug_cmd as cli diff --git a/platformio/commands/lib/command.py b/platformio/commands/lib/command.py index 367a80d6..3b9af87e 100644 --- a/platformio/commands/lib/command.py +++ b/platformio/commands/lib/command.py @@ -24,7 +24,7 @@ import click from tabulate import tabulate from platformio import exception, fs, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.lib.helpers import get_builtin_libs, save_project_libdeps from platformio.package.exception import NotGlobalLibDir, UnknownPackageError from platformio.package.manager.library import LibraryPackageManager diff --git a/platformio/commands/remote/command.py b/platformio/commands/remote/command.py index 5f97d983..acc0d310 100644 --- a/platformio/commands/remote/command.py +++ b/platformio/commands/remote/command.py @@ -34,7 +34,7 @@ from platformio.device.commands.monitor import ( from platformio.package.manager.core import inject_contrib_pysite from platformio.project.exception import NotPlatformIOProjectError from platformio.project.options import ProjectOptions -from platformio.test.command import test_cmd +from platformio.test.cli import test_cmd @click.group("remote", short_help="Remote Development") diff --git a/platformio/commands/test.py b/platformio/commands/test.py deleted file mode 100644 index df641161..00000000 --- a/platformio/commands/test.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014-present PlatformIO -# -# 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. - -# pylint: disable=unused-import - -from platformio.test.command import test_cmd as cli diff --git a/platformio/debug/command.py b/platformio/debug/cli.py similarity index 99% rename from platformio/debug/command.py rename to platformio/debug/cli.py index e8fd6290..489261c9 100644 --- a/platformio/debug/command.py +++ b/platformio/debug/cli.py @@ -93,6 +93,9 @@ def debug_cmd( ) +cli = debug_cmd + + def _debug_in_project_dir( ctx, project_dir, diff --git a/platformio/debug/helpers.py b/platformio/debug/helpers.py index cd87f141..0b15d70d 100644 --- a/platformio/debug/helpers.py +++ b/platformio/debug/helpers.py @@ -20,7 +20,7 @@ from fnmatch import fnmatch from hashlib import sha1 from io import BytesIO -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.run.command import cli as cmd_run from platformio.commands.run.command import print_processing_header from platformio.compat import IS_WINDOWS, is_bytes diff --git a/platformio/commands/device/command.py b/platformio/device/cli.py similarity index 100% rename from platformio/commands/device/command.py rename to platformio/device/cli.py diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 9a897d62..c3f07e74 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -21,7 +21,7 @@ import semantic_version from platformio import __version__, app, exception, fs, telemetry from platformio.cache import cleanup_content_cache -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.platform import platform_update as cmd_platform_update from platformio.commands.system.prune import calculate_unnecessary_system_data from platformio.commands.upgrade import get_latest_version diff --git a/platformio/commands/pkg.py b/platformio/package/cli.py similarity index 100% rename from platformio/commands/pkg.py rename to platformio/package/cli.py diff --git a/platformio/package/manager/base.py b/platformio/package/manager/base.py index 07ca2f5b..b692140f 100644 --- a/platformio/package/manager/base.py +++ b/platformio/package/manager/base.py @@ -21,7 +21,7 @@ import click import semantic_version from platformio import fs, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import ci_strings_are_equal from platformio.package.exception import ManifestException, MissingPackageManifestError from platformio.package.lockfile import LockFile diff --git a/platformio/commands/project.py b/platformio/project/cli.py similarity index 100% rename from platformio/commands/project.py rename to platformio/project/cli.py diff --git a/platformio/telemetry.py b/platformio/telemetry.py index ba721792..c0fbf97d 100644 --- a/platformio/telemetry.py +++ b/platformio/telemetry.py @@ -28,7 +28,7 @@ from traceback import format_exc import requests from platformio import __version__, app, exception, util -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.compat import hashlib_encode_data, string_types from platformio.proc import is_ci, is_container from platformio.project.helpers import is_platformio_project diff --git a/platformio/test/command.py b/platformio/test/cli.py similarity index 99% rename from platformio/test/command.py rename to platformio/test/cli.py index 325bbb47..25279d7d 100644 --- a/platformio/test/command.py +++ b/platformio/test/cli.py @@ -175,6 +175,9 @@ def test_cmd( # pylint: disable=too-many-arguments,too-many-locals,redefined-bu raise exception.ReturnErrorCode(1) +cli = test_cmd + + def print_suite_header(test_suite): click.echo( "Processing %s in %s environment" diff --git a/tests/commands/test_account_org_team.py b/tests/commands/test_account_org_team.py index 53344502..29d85971 100644 --- a/tests/commands/test_account_org_team.py +++ b/tests/commands/test_account_org_team.py @@ -21,7 +21,7 @@ import random import pytest import requests -from platformio.commands.account import cli as cmd_account +from platformio.account.cli import cli as cmd_account from platformio.commands.org import cli as cmd_org from platformio.commands.team import cli as cmd_team diff --git a/tests/commands/test_lib_complex.py b/tests/commands/test_lib_complex.py index ef0c0c4b..31f61fc6 100644 --- a/tests/commands/test_lib_complex.py +++ b/tests/commands/test_lib_complex.py @@ -17,7 +17,7 @@ import json import re -from platformio.commands import PlatformioCLI +from platformio.cli import PlatformioCLI from platformio.commands.lib.command import cli as cmd_lib from platformio.package.exception import UnknownPackageError from platformio.util import strip_ansi_codes diff --git a/tests/commands/test_test.py b/tests/commands/test_test.py index 71094794..343dbd05 100644 --- a/tests/commands/test_test.py +++ b/tests/commands/test_test.py @@ -21,7 +21,7 @@ import pytest from platformio import proc from platformio.fs import load_json -from platformio.test.command import test_cmd as pio_test_cmd +from platformio.test.cli import test_cmd as pio_test_cmd def test_calculator_example(tmp_path: Path):