diff --git a/snapcraft/cli.py b/snapcraft/cli.py index a5c765fd75..aa0e3f778d 100644 --- a/snapcraft/cli.py +++ b/snapcraft/cli.py @@ -1,6 +1,6 @@ # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # -# Copyright 2022-2023 Canonical Ltd. +# Copyright 2022-2024 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as @@ -27,6 +27,7 @@ import craft_cli import craft_store from craft_application.errors import RemoteBuildError +from craft_application.util import strtobool from craft_cli import ArgumentParsingError, EmitterMode, ProvideHelpException, emit from craft_providers import ProviderError @@ -183,7 +184,7 @@ def get_verbosity() -> EmitterMode: with contextlib.suppress(ValueError): # Parse environment variable for backwards compatibility with launchpad - if utils.strtobool(os.getenv("SNAPCRAFT_ENABLE_DEVELOPER_DEBUG", "n").strip()): + if strtobool(os.getenv("SNAPCRAFT_ENABLE_DEVELOPER_DEBUG", "n").strip()): verbosity = EmitterMode.DEBUG # if defined, use environmental variable SNAPCRAFT_VERBOSITY_LEVEL diff --git a/snapcraft/commands/core22/lifecycle.py b/snapcraft/commands/core22/lifecycle.py index 541674f62e..8b5a4cf8e6 100644 --- a/snapcraft/commands/core22/lifecycle.py +++ b/snapcraft/commands/core22/lifecycle.py @@ -1,6 +1,6 @@ # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # -# Copyright 2022 Canonical Ltd. +# Copyright 2022,2024 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as @@ -21,6 +21,7 @@ import os import textwrap +from craft_application.util import strtobool from craft_cli import BaseCommand, emit from overrides import overrides @@ -52,7 +53,7 @@ def fill_parser(self, parser: "argparse.ArgumentParser") -> None: parser.add_argument( "--enable-manifest", # Deprecated and removed in core24 action="store_true", - default=utils.strtobool(os.getenv("SNAPCRAFT_BUILD_INFO", "n")), + default=strtobool(os.getenv("SNAPCRAFT_BUILD_INFO", "n")), help=argparse.SUPPRESS, ) parser.add_argument( diff --git a/snapcraft/services/package.py b/snapcraft/services/package.py index 7f95d27506..9d6a3554d4 100644 --- a/snapcraft/services/package.py +++ b/snapcraft/services/package.py @@ -25,9 +25,10 @@ from craft_application import AppMetadata, PackageService from craft_application.models import BuildInfo +from craft_application.util import strtobool from overrides import override -from snapcraft import errors, linters, models, pack, utils +from snapcraft import errors, linters, models, pack from snapcraft.linters import LinterStatus from snapcraft.meta import component_yaml, snap_yaml from snapcraft.parts import extract_metadata as extract @@ -147,7 +148,7 @@ def write_metadata(self, path: pathlib.Path) -> None: meta_dir.mkdir(parents=True, exist_ok=True) self.metadata.to_yaml_file(meta_dir / "snap.yaml") - enable_manifest = utils.strtobool(os.getenv("SNAPCRAFT_BUILD_INFO", "n")) + enable_manifest = strtobool(os.getenv("SNAPCRAFT_BUILD_INFO", "n")) # Snapcraft's Lifecycle implementation is what we need to refer to for typing lifecycle_service = cast(Lifecycle, self._services.lifecycle) diff --git a/snapcraft/utils.py b/snapcraft/utils.py index e74c184a85..999a64ec7d 100644 --- a/snapcraft/utils.py +++ b/snapcraft/utils.py @@ -29,6 +29,7 @@ from pathlib import Path from typing import Iterable, List, Optional +from craft_application.util import strtobool from craft_cli import emit from craft_parts.sources.git_source import GitSource @@ -161,23 +162,6 @@ def convert_architecture_deb_to_platform(architecture: str) -> str: return platform_arch -def strtobool(value: str) -> bool: - """Convert a string representation of truth to true (1) or false (0). - - :param value: a True value of 'y', 'yes', 't', 'true', 'on', and '1' - or a False value of 'n', 'no', 'f', 'false', 'off', and '0'. - :raises ValueError: if `value` is not a valid boolean value. - """ - parsed_value = value.lower() - - if parsed_value in ("y", "yes", "t", "true", "on", "1"): - return True - if parsed_value in ("n", "no", "f", "false", "off", "0"): - return False - - raise ValueError(f"Invalid boolean value of {value!r}") - - def is_managed_mode() -> bool: """Check if snapcraft is running in a managed environment.""" managed_flag = os.getenv("CRAFT_MANAGED_MODE", "n") diff --git a/snapcraft_legacy/cli/_errors.py b/snapcraft_legacy/cli/_errors.py index 7eb72c2ed5..3ced16ad13 100644 --- a/snapcraft_legacy/cli/_errors.py +++ b/snapcraft_legacy/cli/_errors.py @@ -12,7 +12,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import distutils.util import logging import os import shutil @@ -23,6 +22,7 @@ from typing import Dict import click +from craft_application.util import strtobool import craft_store from raven import Client as RavenClient from raven.transport import RequestsHTTPTransport @@ -74,7 +74,7 @@ def _is_connected_to_tty() -> bool: # Used by inner instance, SNAPCRAFT_HAS_TTY is set by outer instance. if os.getenv("SNAPCRAFT_BUILD_ENVIRONMENT") == "managed-host": - return distutils.util.strtobool(os.getenv("SNAPCRAFT_HAS_TTY", "n")) == 1 + return strtobool(os.getenv("SNAPCRAFT_HAS_TTY", "n")) == 1 return sys.stdout.isatty() @@ -284,13 +284,13 @@ def _print_trace_output(exc_info, file=sys.stdout) -> None: def _is_send_to_sentry(exc_info) -> bool: # noqa: C901 # Check to see if error reporting has been disabled if ( - distutils.util.strtobool(os.getenv("SNAPCRAFT_ENABLE_ERROR_REPORTING", "y")) + strtobool(os.getenv("SNAPCRAFT_ENABLE_ERROR_REPORTING", "y")) == 0 ): return False # Check the environment to see if we should allow for silent reporting - if distutils.util.strtobool(os.getenv("SNAPCRAFT_ENABLE_SILENT_REPORT", "n")) == 1: + if strtobool(os.getenv("SNAPCRAFT_ENABLE_SILENT_REPORT", "n")) == 1: click.echo(_MSG_SILENT_REPORT) return True diff --git a/snapcraft_legacy/cli/_options.py b/snapcraft_legacy/cli/_options.py index 17e0667303..3d025569f8 100644 --- a/snapcraft_legacy/cli/_options.py +++ b/snapcraft_legacy/cli/_options.py @@ -1,6 +1,6 @@ # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # -# Copyright (C) 2016-2019 Canonical Ltd +# Copyright (C) 2016-2019,2024 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as @@ -14,12 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import distutils.util import os import sys from typing import Any, Dict, List, Optional import click +from craft_application.util import strtobool from snapcraft_legacy.cli.echo import confirm, prompt, warning from snapcraft_legacy.internal import common, errors @@ -53,13 +53,13 @@ class BoolParamType(click.ParamType): def convert(self, value, param, ctx): """Convert option string to value. - Unlike click's BoolParamType, use distutils.util.strtobool to + Unlike click's BoolParamType, use craft_application.util.strtobool to convert values. """ if isinstance(value, bool): return value try: - return bool(distutils.util.strtobool(value)) + return strtobool(value) except ValueError: self.fail("%r is not a valid boolean" % value, param, ctx) diff --git a/snapcraft_legacy/cli/echo.py b/snapcraft_legacy/cli/echo.py index 3650019f98..ade1831393 100644 --- a/snapcraft_legacy/cli/echo.py +++ b/snapcraft_legacy/cli/echo.py @@ -18,20 +18,20 @@ These methods, which are named after common logging levels, wrap around click.echo adding the corresponding color codes for each level. """ -import distutils.util import os import shutil import sys from typing import Any, Optional import click +from craft_application.util import strtobool from snapcraft_legacy.internal import common def is_tty_connected() -> bool: """Check to see if running under TTY.""" - if distutils.util.strtobool(os.getenv("SNAPCRAFT_HAS_TTY", "n")) == 1: + if strtobool(os.getenv("SNAPCRAFT_HAS_TTY", "n")) == 1: return True return sys.stdin.isatty() diff --git a/snapcraft_legacy/internal/meta/_snap_packaging.py b/snapcraft_legacy/internal/meta/_snap_packaging.py index 704e89aa42..4b5277acde 100644 --- a/snapcraft_legacy/internal/meta/_snap_packaging.py +++ b/snapcraft_legacy/internal/meta/_snap_packaging.py @@ -1,6 +1,6 @@ # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # -# Copyright (C) 2016-2020 Canonical Ltd +# Copyright (C) 2016-2020,2024 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as @@ -16,7 +16,6 @@ import contextlib import copy -import distutils.util import itertools import logging import os @@ -28,6 +27,7 @@ from typing import Any, Dict, Optional, Set import requests +from craft_application.util import strtobool from snapcraft_legacy import ( extractors, @@ -631,7 +631,7 @@ def _record_manifest_and_source_snapcraft_yaml(self): os.unlink(manifest_file_path) # FIXME hide this functionality behind a feature flag for now - if distutils.util.strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): + if strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): os.makedirs(prime_snap_dir, exist_ok=True) shutil.copy2(self._snapcraft_yaml_path, recorded_snapcraft_yaml_path) annotated_snapcraft = _manifest.annotate_snapcraft( diff --git a/snapcraft_legacy/internal/xattrs.py b/snapcraft_legacy/internal/xattrs.py index 9167a33eff..ee8dccc118 100644 --- a/snapcraft_legacy/internal/xattrs.py +++ b/snapcraft_legacy/internal/xattrs.py @@ -1,6 +1,6 @@ # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # -# Copyright (C) 2019 Canonical Ltd +# Copyright (C) 2019,2024 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as @@ -14,11 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import distutils.util import os import sys from typing import Optional +from craft_application.util import strtobool + from snapcraft_legacy.internal.errors import XAttributeError, XAttributeTooLongError @@ -77,7 +78,7 @@ def read_origin_stage_package(path: str) -> Optional[str]: return _read_snapcraft_xattr(path, "origin_stage_package") except XAttributeError: # Ignore error if origin stage package not required. - if distutils.util.strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): + if strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): raise return None @@ -88,5 +89,5 @@ def write_origin_stage_package(path: str, value: str) -> None: _write_snapcraft_xattr(path, "origin_stage_package", value) except XAttributeError: # Ignore error if origin stage package not required. - if distutils.util.strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): + if strtobool(os.environ.get("SNAPCRAFT_BUILD_INFO", "n")): raise diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 5650d2b175..3d9dbdd9e8 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -40,69 +40,6 @@ def mock_is_managed_mode(mocker): yield mocker.patch("snapcraft.utils.is_managed_mode", return_value=False) -@pytest.mark.parametrize( - "value", - [ - "y", - "Y", - "yes", - "YES", - "Yes", - "t", - "T", - "true", - "TRUE", - "True", - "On", - "ON", - "oN", - "1", - ], -) -def test_strtobool_true(value: str): - assert utils.strtobool(value) is True - - -@pytest.mark.parametrize( - "value", - [ - "n", - "N", - "no", - "NO", - "No", - "f", - "F", - "false", - "FALSE", - "False", - "off", - "OFF", - "oFF", - "0", - ], -) -def test_strtobool_false(value: str): - assert utils.strtobool(value) is False - - -@pytest.mark.parametrize( - "value", - [ - "not", - "yup", - "negative", - "positive", - "whatever", - "2", - "3", - ], -) -def test_strtobool_value_error(value: str): - with pytest.raises(ValueError): - utils.strtobool(value) - - ##################### # Get Host Platform # #####################