← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~jugmac00/lpci:add-missing-docstrings into lpci:main

 

Jürgen Gmach has proposed merging ~jugmac00/lpci:add-missing-docstrings into lpci:main.

Commit message:
Add missing docstrings

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jugmac00/lpci/+git/lpcraft/+merge/485949
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/lpci:add-missing-docstrings into lpci:main.
diff --git a/NEWS.rst b/NEWS.rst
index 42530b0..1d9f214 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -7,7 +7,7 @@ Version history
 
 - Reverts changes in 0.2.11 and 0.2.10
 - Use "security.nesting=true" for GPU LPCI builds to workaround LPCI Noble failures
-  when running on a focal host due to apparmor permission issues. 
+  when running on a focal host due to apparmor permission issues.
 
 0.2.11 (2025-04-11)
 ==================
diff --git a/lpci/__init__.py b/lpci/__init__.py
index e69de29..78b96c7 100644
--- a/lpci/__init__.py
+++ b/lpci/__init__.py
@@ -0,0 +1 @@
+"""lpci - the Launchpad CI runner - also works as a standalone."""
diff --git a/lpci/commands/__init__.py b/lpci/commands/__init__.py
index e69de29..ee51fcd 100644
--- a/lpci/commands/__init__.py
+++ b/lpci/commands/__init__.py
@@ -0,0 +1 @@
+"""Package implements all commands."""
diff --git a/lpci/commands/clean.py b/lpci/commands/clean.py
index bfd1287..30f86c5 100644
--- a/lpci/commands/clean.py
+++ b/lpci/commands/clean.py
@@ -1,3 +1,5 @@
+"""Implementation of the `clean` command."""
+
 # Copyright 2022 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/commands/release.py b/lpci/commands/release.py
index fd523eb..53968f9 100644
--- a/lpci/commands/release.py
+++ b/lpci/commands/release.py
@@ -1,3 +1,5 @@
+"""Implementation of the `release` command."""
+
 # Copyright 2023 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/commands/run.py b/lpci/commands/run.py
index e42a8a9..4f24266 100644
--- a/lpci/commands/run.py
+++ b/lpci/commands/run.py
@@ -1,3 +1,5 @@
+"""Implementation of the `run` and the `run one` commands."""
+
 # Copyright 2021-2022 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/commands/tests/test_run.py b/lpci/commands/tests/test_run.py
index efd180c..6078a7d 100644
--- a/lpci/commands/tests/test_run.py
+++ b/lpci/commands/tests/test_run.py
@@ -3545,7 +3545,10 @@ class TestRun(RunBaseTestCase):
         lxc.profile_edit.assert_called_once_with(
             profile="default",
             config={
-                "config": {"nvidia.runtime": "true", "security.nesting": "true"},
+                "config": {
+                    "nvidia.runtime": "true",
+                    "security.nesting": "true",
+                },
                 "devices": {"gpu": {"type": "gpu"}},
             },
             project="test-project",
@@ -4816,7 +4819,10 @@ class TestRunOne(RunBaseTestCase):
         lxc.profile_edit.assert_called_once_with(
             profile="default",
             config={
-                "config": {"nvidia.runtime": "true", "security.nesting": "true"},
+                "config": {
+                    "nvidia.runtime": "true",
+                    "security.nesting": "true",
+                },
                 "devices": {"gpu": {"type": "gpu"}},
             },
             project="test-project",
diff --git a/lpci/commands/version.py b/lpci/commands/version.py
index b527140..640abfa 100644
--- a/lpci/commands/version.py
+++ b/lpci/commands/version.py
@@ -1,3 +1,5 @@
+"""Implementation of the `version` command."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/config.py b/lpci/config.py
index ccedfdc..49aa02c 100644
--- a/lpci/config.py
+++ b/lpci/config.py
@@ -1,3 +1,5 @@
+"""Implementation of the configuration format, based on pydantic."""
+
 # Copyright 2021-2022 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
@@ -58,6 +60,7 @@ class Output(ModelConfigDefaults):
 
     @pydantic.validator("expires")
     def validate_expires(cls, v: timedelta) -> timedelta:
+        """Validate for the `expires` field."""
         if v < timedelta(0):
             raise ValueError("non-negative duration expected")
         return v
@@ -165,6 +168,7 @@ class PackageRepository(ModelConfigDefaults):
     def validate_multiple_fields(
         cls, values: Dict[str, Any]
     ) -> Dict[str, Any]:
+        """Make sure the usage of `ppa`, `url` and `components` is correct."""
         if "url" in values:
             if "ppa" in values:
                 raise ValueError(
@@ -194,6 +198,7 @@ class PackageRepository(ModelConfigDefaults):
     def infer_components_if_ppa_is_set(
         cls, v: List[PackageComponent], values: Dict[str, Any]
     ) -> List[PackageComponent]:
+        """Infer the component in case the shortform notation is used."""
         if v is None and values["ppa"]:
             return ["main"]
         return v
@@ -202,6 +207,7 @@ class PackageRepository(ModelConfigDefaults):
     def infer_url_if_ppa_is_set(
         cls, v: AnyHttpUrl, values: Dict[str, Any]
     ) -> AnyHttpUrl:
+        """Infer the URL in case the shortform notation is used."""
         if v is None and values["ppa"]:
             owner, distribution, archive = get_ppa_url_parts(values["ppa"])
             v = "{}/{}/{}/{}".format(
@@ -216,13 +222,14 @@ class PackageRepository(ModelConfigDefaults):
     def set_formats_default_value(
         cls, v: List[PackageFormat]
     ) -> List[PackageFormat]:
+        """Set package format to `deb` by default."""
         if not v:
             v = [PackageFormat.deb]
         return v
 
     @validator("trusted")
     def convert_trusted(cls, v: bool) -> str:
-        # trusted is True or False, but we need `yes` or `no`
+        """`trusted` is True or False, but we need `yes` or `no`."""
         return v and "yes" or "no"
 
     def sources_list_lines(self) -> Iterator[str]:
@@ -250,6 +257,7 @@ class Snap(ModelConfigDefaults):
 
     @validator("channel")
     def prevent_channel_none(cls, v: StrictStr) -> Any:
+        """Disallow an empty `channel` field."""
         if v is None:
             raise ValueError(
                 "You configured a Snap `channel`, "
@@ -259,6 +267,7 @@ class Snap(ModelConfigDefaults):
 
     @validator("classic")
     def prevent_classic_none(cls, v: bool) -> Any:
+        """Disallow an empty `classic` field."""
         if v is None:
             raise ValueError(
                 "You configured a Snap `classic`, "
@@ -294,12 +303,14 @@ class Job(ModelConfigDefaults):
     def validate_architectures(
         cls, v: Union[_Identifier, List[_Identifier]]
     ) -> List[_Identifier]:
+        """Validate the `architectures` field."""
         if isinstance(v, str):
             v = [v]
         return v
 
     @pydantic.validator("root", pre=True)
     def validate_root(cls, v: Any) -> Any:
+        """Validate the `root` field."""
         if type(v) is not bool or v is None:
             raise ValueError(
                 "You configured `root` parameter, "
@@ -310,6 +321,7 @@ class Job(ModelConfigDefaults):
 
     @pydantic.validator("snaps", pre=True)
     def validate_snaps(cls, v: List[Any]) -> Any:
+        """Validate the `snaps` field."""
         clean_values = []
         for value in v:
             # Backward compatibility, i.e. [chromium, firefox]
@@ -352,6 +364,7 @@ class Job(ModelConfigDefaults):
     def validate_package_repositories(
         cls, v: List[PackageRepository], values: Dict[StrictStr, Any]
     ) -> List[PackageRepository]:
+        """Validate the `package_repositories` field."""
         package_repositories = None
         for index, package_repository in enumerate(v):
             if not package_repository.suites:
@@ -415,6 +428,7 @@ class License(ModelConfigDefaults):
     def disallow_setting_both_sources(
         cls, path: str, values: Dict[str, str]
     ) -> str:
+        """Only allow either `spdx` or `path` to be set."""
         if values.get("spdx") and path:
             raise ValueError(
                 "You cannot set `spdx` and `path` at the same time."
@@ -433,15 +447,22 @@ class Config(ModelConfigDefaults):
     def validate_pipeline(
         cls, v: List[Union[_Identifier, List[_Identifier]]]
     ) -> List[List[_Identifier]]:
+        """Validate the pipeline field.
+
+        Also, expand to a list of stages.
+        """
         return [[stage] if isinstance(stage, str) else stage for stage in v]
 
-    # XXX cjwatson 2021-11-17: This expansion strategy works, but it may
-    # produce suboptimal error messages, and doesn't have a good way to do
-    # things like limiting the keys that can be set in a matrix.
     @pydantic.root_validator(pre=True)
     def expand_matrix(
         cls, values: Dict[StrictStr, Any]
     ) -> Dict[StrictStr, Any]:
+        """Use the validator to expand the matrix.
+
+        # XXX cjwatson 2021-11-17: This expansion strategy works, but it may
+        # produce suboptimal error messages, and doesn't have a good way to do
+        # things like limiting the keys that can be set in a matrix.
+        """
         expanded_values = values.copy()
         expanded_values["jobs"] = {
             job_name: _expand_job_values(job_values)
diff --git a/lpci/env.py b/lpci/env.py
index 0aa9b96..2b4ac88 100644
--- a/lpci/env.py
+++ b/lpci/env.py
@@ -7,6 +7,7 @@ from pathlib import Path
 
 
 def get_non_root_user() -> str:
+    """Return the non-root user."""
     return "_lpci"
 
 
diff --git a/lpci/errors.py b/lpci/errors.py
index 3f45abb..bb67bba 100644
--- a/lpci/errors.py
+++ b/lpci/errors.py
@@ -17,9 +17,11 @@ class CommandError(CraftError):
     """Base exception for all error commands."""
 
     def __init__(self, message: str, retcode: int = 1):
+        """Class initialization."""
         super().__init__(message, retcode=retcode)
 
     def __eq__(self, other: Any) -> bool:
+        """Implement comparison."""
         if type(self) != type(other):
             return NotImplemented
         return str(self) == str(other) and self.retcode == other.retcode
diff --git a/lpci/git.py b/lpci/git.py
index f4da298..11de192 100644
--- a/lpci/git.py
+++ b/lpci/git.py
@@ -1,3 +1,5 @@
+"""Provide helper functions for git handling."""
+
 # Copyright 2023 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/plugin/__init__.py b/lpci/plugin/__init__.py
index a3ab6b4..76d587c 100644
--- a/lpci/plugin/__init__.py
+++ b/lpci/plugin/__init__.py
@@ -1,3 +1,5 @@
+"""Implementation of the plugin system via pluggy."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/plugin/hookspecs.py b/lpci/plugin/hookspecs.py
index aee2b34..8697ff6 100644
--- a/lpci/plugin/hookspecs.py
+++ b/lpci/plugin/hookspecs.py
@@ -1,3 +1,5 @@
+"""Providing the hooks which plugins can use for lpci."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/plugin/lib.py b/lpci/plugin/lib.py
index 1ea3bf7..71f22c4 100644
--- a/lpci/plugin/lib.py
+++ b/lpci/plugin/lib.py
@@ -15,20 +15,27 @@ from lpci.plugin import hookimpl
 
 
 class InternalPlugins:
+    """Provide default implementation via internal plugins.
+
+    The hooks can be overwritten by custom plugins.
+    """
 
     INTERPOLATES_RUN_COMMAND: bool = False
 
     def __init__(self, config: Job) -> None:
+        """Class initialization."""
         self.config = config
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for internal plugins."""
         if self.config.packages:
             return self.config.packages
         return []
 
     @hookimpl
     def lpci_install_snaps(self) -> list[Snap]:
+        """`install_snaps` implementation for internal plugins."""
         if self.config.snaps:
             return self.config.snaps
         return []
diff --git a/lpci/plugin/manager.py b/lpci/plugin/manager.py
index dc234d9..02c0a0f 100644
--- a/lpci/plugin/manager.py
+++ b/lpci/plugin/manager.py
@@ -1,3 +1,5 @@
+"""Implementation of the plugin manager."""
+
 from typing import Dict, Optional
 
 import pluggy
@@ -11,6 +13,7 @@ from lpci.plugins import PLUGINS
 def get_plugin_manager(
     job: Job, plugin_settings: Optional[Dict[str, str]] = None
 ) -> pluggy.PluginManager:
+    """Return the plugin manager."""
     pm = pluggy.PluginManager(NAME)
     pm.add_hookspecs(hookspecs)
 
diff --git a/lpci/plugins/__init__.py b/lpci/plugins/__init__.py
index 38cd38d..d78dc69 100644
--- a/lpci/plugins/__init__.py
+++ b/lpci/plugins/__init__.py
@@ -1,3 +1,5 @@
+"""Package provides all available plugins which come with lpci."""
+
 from typing import Any, Callable, Type, TypeVar
 
 PLUGINS = dict()  #: Collection of builtin plugins
@@ -7,10 +9,12 @@ TypeT = TypeVar("TypeT", bound=Type[Any])
 
 
 def register(name: str) -> Callable[[TypeT], TypeT]:
-    # this function registers all decorated plugin classes
-    # the result looks like:
-    #
-    # PLUGINS = {'tox': <class 'lpci.plugins.plugins.ToxPlugin'>}
+    """Register all decorated plugin classes.
+
+    the result looks like:
+    PLUGINS = {'tox': <class 'lpci.plugins.plugins.ToxPlugin'>}
+    """
+
     def inner(cls: TypeT) -> TypeT:
         PLUGINS[name] = cls
         return cls
diff --git a/lpci/plugins/plugins.py b/lpci/plugins/plugins.py
index 6f8708f..2c158cb 100644
--- a/lpci/plugins/plugins.py
+++ b/lpci/plugins/plugins.py
@@ -1,3 +1,5 @@
+"""Module contains all built-in plugins for lpci."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
@@ -66,16 +68,19 @@ class ToxPlugin(BasePlugin):
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for the tox plugin."""
         return ["python3-pip"]
 
     @hookimpl
     def lpci_execute_run(self) -> str:
+        """`execute_run` implementation for the tox plugin."""
         # XXX jugmac00 2022-01-07: we should consider using a requirements.txt
         # as this allows updating via `pip-tools`
         return "python3 -m pip install tox==3.24.5; tox"
 
     @hookimpl
     def lpci_set_environment(self) -> dict[str, str | None]:
+        """`set_environment` implementation for the tox plugin."""
         # Work around https://github.com/tox-dev/tox/issues/2372: without
         # this, tox won't pass through the lower-case proxy environment
         # variables set by launchpad-buildd.
@@ -93,6 +98,7 @@ class PyProjectBuildPlugin(BasePlugin):
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for the pyproject build plugin."""
         # Ubuntu 20.04 does not provide a packaged version of build,
         # so we need pip to install it
         #
@@ -105,6 +111,7 @@ class PyProjectBuildPlugin(BasePlugin):
 
     @hookimpl
     def lpci_execute_run(self) -> str:
+        """`execute_run` implementation for the pyproject build plugin."""
         # XXX jugmac00 2022-01-20: we should consider using a PPA
         return "python3 -m pip install build==0.7.0; python3 -m build"
 
@@ -134,12 +141,15 @@ class MiniCondaPlugin(BasePlugin):
     """
 
     class Config(BaseConfig):
+        """Configuration for the mini conda plugin."""
+
         conda_packages: Optional[List[StrictStr]]
         conda_python: Optional[StrictStr]
         conda_channels: Optional[List[StrictStr]]
 
         @pydantic.validator("conda_python", pre=True)
         def validate_conda_python(cls, v: str | float | int) -> str:
+            """Validate the Python version identifier."""
             return str(v)
 
     INTERPOLATES_RUN_COMMAND = True
@@ -148,10 +158,12 @@ class MiniCondaPlugin(BasePlugin):
     DEFAULT_CONDA_CHANNELS = ("defaults",)
 
     def get_plugin_config(self) -> "MiniCondaPlugin.Config":
+        """Return the mini conda plugin configuration."""
         return cast(MiniCondaPlugin.Config, self.config.plugin_config)
 
     @property
     def conda_packages(self) -> list[str]:
+        """List of conda packages."""
         conda_packages: set[str] = set()
         conda_python = self.DEFAULT_CONDA_PYTHON
         plugin_config = self.get_plugin_config()
@@ -165,6 +177,7 @@ class MiniCondaPlugin(BasePlugin):
 
     @property
     def conda_channels(self) -> list[str]:
+        """List of conda channels."""
         conda_channels: list[str] = []
         plugin_config = self.get_plugin_config()
         if plugin_config.conda_channels:
@@ -181,11 +194,13 @@ class MiniCondaPlugin(BasePlugin):
 
     @hookimpl
     def lpci_set_environment(self) -> dict[str, str]:
+        """`set_environment` implementation for the mini conda plugin."""
         # `CONDA_ENV` sets the name of the Conda virtual environment
         return {"CONDA_ENV": "lpci"}
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for the mini conda plugin."""
         return [
             "git",
             "python3-dev",
@@ -196,6 +211,7 @@ class MiniCondaPlugin(BasePlugin):
 
     @hookimpl
     def lpci_execute_before_run(self) -> str:
+        """`execute_before_run` implementation for the mini conda plugin."""
         run = self.config.run_before or ""
         conda_channels = " ".join(f"-c {_}" for _ in self.conda_channels)
         return textwrap.dedent(
@@ -212,11 +228,13 @@ class MiniCondaPlugin(BasePlugin):
 
     @hookimpl
     def lpci_execute_run(self) -> str:
+        """`execute_run` implementation for the mini conda plugin."""
         run = self.config.run or ""
         return textwrap.dedent(f"{run}")
 
     @hookimpl
     def lpci_execute_after_run(self) -> str:
+        """`execute_after_run` implementation for the mini conda plugin."""
         run = f"; {self.config.run_after}" if self.config.run_after else ""
         return f"export PATH=$HOME/miniconda3/bin:$PATH; conda env export{run}"
 
@@ -250,6 +268,8 @@ class CondaBuildPlugin(MiniCondaPlugin):
     """
 
     class Config(MiniCondaPlugin.Config):
+        """Configuration for the conda build plugin."""
+
         build_target: Optional[StrictStr]
         conda_channels: Optional[List[StrictStr]]
         conda_packages: Optional[List[StrictStr]]
@@ -260,10 +280,12 @@ class CondaBuildPlugin(MiniCondaPlugin):
     DEFAULT_RECIPE_FOLDER = "./info"
 
     def get_plugin_config(self) -> "CondaBuildPlugin.Config":
+        """Return the conda build plugin configuration."""
         return cast(CondaBuildPlugin.Config, self.config.plugin_config)
 
     @property
     def recipe_folder(self) -> str:
+        """Return the folder where the recipe is in."""
         recipe_folder = self.DEFAULT_RECIPE_FOLDER
         plugin_config = self.get_plugin_config()
         if plugin_config.recipe_folder:
@@ -288,6 +310,8 @@ class CondaBuildPlugin(MiniCondaPlugin):
             template_path.replace(dir_ / "meta.yaml")
 
     def find_recipe(self) -> Path:
+        """Find the recipe for the conda build."""
+
         def _find_recipe_dir(path: Path) -> Path:
             for subpath in path.iterdir():
                 if subpath.is_dir():
@@ -303,6 +327,8 @@ class CondaBuildPlugin(MiniCondaPlugin):
         return _find_recipe_dir(Path(self.recipe_folder))
 
     def find_build_target(self) -> str:
+        """Find the build target for conda builds."""
+
         def find_parents(pth: Path) -> Path:
             for parent in pth.iterdir():
                 if parent.is_dir():
@@ -327,6 +353,7 @@ class CondaBuildPlugin(MiniCondaPlugin):
 
     @property
     def build_configs(self) -> list[str]:
+        """`build_configuration` for conda builds."""
         try:
             recipe = self.find_recipe()
         except FileNotFoundError:
@@ -338,6 +365,7 @@ class CondaBuildPlugin(MiniCondaPlugin):
 
     @property
     def build_target(self) -> str:
+        """`build_target` for the conda builds."""
         build_target = self.get_plugin_config().build_target
         if not build_target:
             return self.find_build_target()
@@ -345,24 +373,28 @@ class CondaBuildPlugin(MiniCondaPlugin):
 
     @hookimpl
     def lpci_set_environment(self) -> dict[str, str]:
+        """`set_environment` implementation for the conda build plugin."""
         # XXX techalchemy 2022-04-01: mypy is struggling with the super() call
         rv: dict[str, str] = super().lpci_set_environment()
         return rv
 
     @hookimpl
     def lpci_execute_before_run(self) -> str:
+        """`execute_before_run` implementation for the conda build plugin."""
         # XXX techalchemy 2022-04-01: mypy is struggling with the super() call
         rv: str = super().lpci_execute_before_run()
         return rv
 
     @hookimpl
     def lpci_execute_after_run(self) -> str:
+        """`execute_after_run` implementation for the conda build plugin."""
         # XXX techalchemy 2022-04-01: mypy is struggling with the super() call
         rv: str = super().lpci_execute_after_run()
         return rv
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for the conda build plugin."""
         # XXX techalchemy 2022-04-01: mypy is struggling with the super() call
         base_packages: list[str] = super().lpci_install_packages()
         base_packages.extend(
@@ -387,6 +419,7 @@ class CondaBuildPlugin(MiniCondaPlugin):
 
     @hookimpl
     def lpci_execute_run(self) -> str:
+        """`execute_run` implementation for the conda build plugin."""
         conda_channels = " ".join(f"-c {_}" for _ in self.conda_channels)
         conda_channels = f" {conda_channels}" if conda_channels else ""
         configs = " ".join(f"-m {_}" for _ in self.build_configs)
@@ -429,20 +462,25 @@ class GolangPlugin(BasePlugin):
     """
 
     class Config(BaseConfig):
+        """Configuration for the golang plugin."""
+
         golang_version: StrictStr
 
     INTERPOLATES_RUN_COMMAND = True
 
     def get_plugin_config(self) -> "GolangPlugin.Config":
+        """Return the golang plugin configuration."""
         return cast(GolangPlugin.Config, self.config.plugin_config)
 
     @hookimpl
     def lpci_install_packages(self) -> list[str]:
+        """`install_packages` implementation for the golang plugin."""
         version = self.get_plugin_config().golang_version
         return [f"golang-{version}"]
 
     @hookimpl
     def lpci_execute_run(self) -> str:
+        """`execute_run` implementation for the golang plugin."""
         version = self.get_plugin_config().golang_version
         run_command = self.config.run or ""
         return textwrap.dedent(
diff --git a/lpci/providers/__init__.py b/lpci/providers/__init__.py
index a2df69b..851e38a 100644
--- a/lpci/providers/__init__.py
+++ b/lpci/providers/__init__.py
@@ -1,3 +1,5 @@
+"""The package contains implementations for various providers."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/lpci/providers/tests/test_lxd.py b/lpci/providers/tests/test_lxd.py
index 8bf3940..cf49fd1 100644
--- a/lpci/providers/tests/test_lxd.py
+++ b/lpci/providers/tests/test_lxd.py
@@ -933,7 +933,10 @@ class TestLXDProvider(TestCase):
             mock_lxc.profile_edit.assert_called_once_with(
                 profile="default",
                 config={
-                    "config": {"nvidia.runtime": "true", "security.nesting": "true"},
+                    "config": {
+                        "nvidia.runtime": "true",
+                        "security.nesting": "true",
+                    },
                     "devices": {"gpu": {"type": "gpu"}},
                 },
                 project="test-project",
diff --git a/lpci/utils.py b/lpci/utils.py
index cd17360..5a29ab8 100644
--- a/lpci/utils.py
+++ b/lpci/utils.py
@@ -1,3 +1,5 @@
+"""Utils module which provides various helper functions."""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
diff --git a/setup.py b/setup.py
index 5f30036..e553d28 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,9 @@
+"""Compatibility configuration for tools not supporting PEP517/PEP660.
+
+We probably could get rid of it, see
+https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html.
+"""
+
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).