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 #
#####################