Source code for craft_application.lint.base

# This file is part of craft-application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
"""Abstract base for linters used by the linter service."""

from __future__ import annotations

import inspect as _inspect
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from ._types import Stage as _Stage

if TYPE_CHECKING:  # pragma: no cover
    from collections.abc import Iterable

    from ._types import LintContext, LinterIssue, Stage


[docs] class AbstractLinter(ABC): """Base class for all linters. Linters should set: - name: stable identifier (used in ignore config) - stage: Stage.PRE or Stage.POST """ name: str stage: Stage def __init_subclass__(cls) -> None: """Validate subclass has required attributes.""" super().__init_subclass__() if _inspect.isabstract(cls): return if not isinstance(getattr(cls, "name", None), str) or not cls.name: raise TypeError("Linter subclass must define a non-empty 'name' string.") if not isinstance(getattr(cls, "stage", None), _Stage): raise TypeError("Linter subclass must define 'stage' as a Stage enum.")
[docs] @abstractmethod def run(self, ctx: LintContext) -> Iterable[LinterIssue]: """Execute the linter and yield issues."""