From 92eb3da74b3c565fc44db46eed820ee65a09d4d8 Mon Sep 17 00:00:00 2001 From: Andrew Mirsky Date: Sat, 31 May 2025 12:58:56 -0400 Subject: [PATCH] the sys/broker plugin requires the full BrokerContext. moving 'BasePlugin' into its own file to remove a circular import. also fixing lint errors and updating pre commit config to mirror the checks being run in ci only --- .pre-commit-config.yaml | 74 --------------------------------- amqtt/plugins/authentication.py | 12 +++--- amqtt/plugins/base.py | 19 +++++++++ amqtt/plugins/logging_amqtt.py | 3 +- amqtt/plugins/manager.py | 7 +--- amqtt/plugins/sys/broker.py | 2 +- amqtt/plugins/topic_checking.py | 22 ++++++---- mkdocs.rtd.yml | 2 - 8 files changed, 41 insertions(+), 100 deletions(-) create mode 100644 amqtt/plugins/base.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9cdc91..8aaf7a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,75 +10,6 @@ ci: - pylint repos: - # Codespell for spelling corrections - - repo: https://github.com/codespell-project/codespell - rev: v2.4.1 - hooks: - - id: codespell - args: - - --ignore-words-list=ihs,ro,fo,assertIn,astroid,formated - - --skip="./.*,*.csv,*.json" - - --quiet-level=2 - exclude_types: - - csv - - json - - # General pre-commit hooks - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: detect-private-key - exclude: tests/_test_files/certs/ - - id: check-merge-conflict - - id: check-added-large-files - - id: check-case-conflict - # - id: no-commit-to-branch - # args: [--branch, main] - - id: check-executables-have-shebangs - - id: trailing-whitespace - name: Trim Trailing Whitespace - description: This hook trims trailing whitespace. - entry: trailing-whitespace-fixer - language: python - types: [text] - args: [--markdown-linebreak-ext=md] - - id: check-toml - - id: check-json - - id: check-yaml - args: [--allow-multiple-documents] - - id: mixed-line-ending - - # Prettier for code formatting - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 - hooks: - - id: prettier - additional_dependencies: - - prettier@3.2.5 - - prettier-plugin-sort-json@3.1.0 - exclude_types: - - python - - # Secret detection - - repo: https://github.com/Yelp/detect-secrets - rev: v1.5.0 - hooks: - - id: detect-secrets - args: - - --exclude-files=tests/* - - --exclude-files=samples/client_subscribe_acl.py - - --exclude-files=docs/quickstart.rst - - repo: https://github.com/gitleaks/gitleaks - rev: v8.26.0 - hooks: - - id: gitleaks - - # YAML Linting - - repo: https://github.com/adrienverge/yamllint.git - rev: v1.37.1 - hooks: - - id: yamllint - # Python-specific hooks ###################################################### - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.11.10 @@ -90,11 +21,6 @@ repos: - --line-length=130 - --exit-non-zero-on-fix - id: ruff-format - - repo: https://github.com/asottile/pyupgrade - rev: v3.19.1 - hooks: - - id: pyupgrade - args: [--py313-plus] # Local hooks for mypy and pylint - repo: local diff --git a/amqtt/plugins/authentication.py b/amqtt/plugins/authentication.py index 0e843dc..8b85917 100644 --- a/amqtt/plugins/authentication.py +++ b/amqtt/plugins/authentication.py @@ -1,9 +1,10 @@ from pathlib import Path +from typing import Any from passlib.apps import custom_app_context as pwd_context from amqtt.broker import BrokerContext -from amqtt.plugins.manager import BaseContext, BasePlugin +from amqtt.plugins.base import BasePlugin from amqtt.session import Session _PARTS_EXPECTED_LENGTH = 2 # Expected number of parts in a valid line @@ -12,10 +13,10 @@ _PARTS_EXPECTED_LENGTH = 2 # Expected number of parts in a valid line class BaseAuthPlugin(BasePlugin): """Base class for authentication plugins.""" - def __init__(self, context: BaseContext) -> None: + def __init__(self, context: BrokerContext) -> None: super().__init__(context) - self.auth_config = self.context.config.get("auth", None) if self.context.config else None + self.auth_config: dict[str, Any] | None = self._get_config_section("auth") if not self.auth_config: self.context.logger.warning("'auth' section not found in context configuration") @@ -30,7 +31,6 @@ class BaseAuthPlugin(BasePlugin): - `None` if authentication can't be achieved (then plugin result is then ignored) """ - if not self.auth_config: # auth config section not found self.context.logger.warning("'auth' section not found in context configuration") @@ -50,7 +50,6 @@ class AnonymousAuthPlugin(BaseAuthPlugin): self.context.logger.debug("Authentication success: config allows anonymous") return True - session: Session | None = session if session and session.username: self.context.logger.debug(f"Authentication success: session has username '{session.username}'") return True @@ -61,7 +60,7 @@ class AnonymousAuthPlugin(BaseAuthPlugin): class FileAuthPlugin(BaseAuthPlugin): """Authentication plugin based on a file-stored user database.""" - def __init__(self, context: BaseContext) -> None: + def __init__(self, context: BrokerContext) -> None: super().__init__(context) self._users: dict[str, str] = {} self._read_password_file() @@ -98,7 +97,6 @@ class FileAuthPlugin(BaseAuthPlugin): """Authenticate users based on the file-stored user database.""" authenticated = await super().authenticate(session=session) if authenticated: - if not session: self.context.logger.debug("Authentication failure: no session provided") return False diff --git a/amqtt/plugins/base.py b/amqtt/plugins/base.py new file mode 100644 index 0000000..9a181c9 --- /dev/null +++ b/amqtt/plugins/base.py @@ -0,0 +1,19 @@ +from typing import Any + +from amqtt.broker import BrokerContext + + +class BasePlugin: + """The base from which all plugins should inherit.""" + + def __init__(self, context: BrokerContext) -> None: + self.context = context + + def _get_config_section(self, name: str) -> dict[str, Any] | None: + if not self.context.config or not hasattr(self.context.config, name): + return None + section_config: int | dict[str, Any] | None = self.context.config.get(name, None) + # mypy has difficulty excluding int from `config`'s type, unless isinstance` is its own check + if isinstance(section_config, int): + return None + return section_config diff --git a/amqtt/plugins/logging_amqtt.py b/amqtt/plugins/logging_amqtt.py index 7652cfd..84cf6d7 100644 --- a/amqtt/plugins/logging_amqtt.py +++ b/amqtt/plugins/logging_amqtt.py @@ -3,8 +3,7 @@ from functools import partial import logging from typing import TYPE_CHECKING, Any -from amqtt.plugins.manager import BasePlugin - +from amqtt.plugins.base import BasePlugin if TYPE_CHECKING: from amqtt.session import Session diff --git a/amqtt/plugins/manager.py b/amqtt/plugins/manager.py index 20350e6..e1db79a 100644 --- a/amqtt/plugins/manager.py +++ b/amqtt/plugins/manager.py @@ -1,4 +1,4 @@ -__all__ = ["BaseContext", "PluginManager", "get_plugin_manager", "BasePlugin"] +__all__ = ["BaseContext", "PluginManager", "get_plugin_manager"] import asyncio from collections.abc import Awaitable, Callable @@ -35,11 +35,6 @@ class BaseContext: self.logger: logging.Logger = _LOGGER self.config: dict[str, Any] | None = None -class BasePlugin: - def __init__(self, context: BaseContext) -> None: - self.context = context - - class PluginManager: """Wraps contextlib Entry point mechanism to provide a basic plugin system. diff --git a/amqtt/plugins/sys/broker.py b/amqtt/plugins/sys/broker.py index 6116740..f7ede3f 100644 --- a/amqtt/plugins/sys/broker.py +++ b/amqtt/plugins/sys/broker.py @@ -2,7 +2,7 @@ import asyncio from collections import deque # pylint: disable=C0412 from typing import SupportsIndex, SupportsInt # pylint: disable=C0412 -from amqtt.plugins.manager import BasePlugin +from amqtt.plugins.base import BasePlugin try: from collections.abc import Buffer diff --git a/amqtt/plugins/topic_checking.py b/amqtt/plugins/topic_checking.py index 2805078..b9ea7db 100644 --- a/amqtt/plugins/topic_checking.py +++ b/amqtt/plugins/topic_checking.py @@ -1,21 +1,23 @@ from typing import Any -from amqtt.broker import Action -from amqtt.plugins.manager import BaseContext, BasePlugin +from amqtt.broker import Action, BrokerContext +from amqtt.plugins.base import BasePlugin from amqtt.session import Session class BaseTopicPlugin(BasePlugin): """Base class for topic plugins.""" - def __init__(self, context: BaseContext) -> None: + def __init__(self, context: BrokerContext) -> None: super().__init__(context) - self.topic_config: dict[str, Any] | None = self.context.config.get("topic-check", None) if self.context.config else None + self.topic_config: dict[str, Any] | None = self._get_config_section("topic-check") if self.topic_config is None: self.context.logger.warning("'topic-check' section not found in context configuration") - async def topic_filtering(self, *, session: Session = None, topic: str = None, action: Action = None) -> bool: + async def topic_filtering( + self, *, session: Session | None = None, topic: str | None = None, action: Action | None = None + ) -> bool: """Logic for filtering out topics. Args: @@ -35,11 +37,13 @@ class BaseTopicPlugin(BasePlugin): class TopicTabooPlugin(BaseTopicPlugin): - def __init__(self, context: BaseContext) -> None: + def __init__(self, context: BrokerContext) -> None: super().__init__(context) self._taboo: list[str] = ["prohibited", "top-secret", "data/classified"] - async def topic_filtering(self, *, session: Session = None, topic: str = None, action: Action = None) -> bool: + async def topic_filtering( + self, *, session: Session | None = None, topic: str | None = None, action: Action | None = None + ) -> bool: filter_result = await super().topic_filtering(session=session, topic=topic, action=action) if filter_result: if session and session.username == "admin": @@ -69,7 +73,9 @@ class TopicAccessControlListPlugin(BaseTopicPlugin): break return ret - async def topic_filtering(self, *, session: Session = None, topic: str = None, action: Action = None) -> bool: + async def topic_filtering( + self, *, session: Session | None = None, topic: str | None = None, action: Action | None = None + ) -> bool: filter_result = await super().topic_filtering(session=session, topic=topic, action=action) if not filter_result: return False diff --git a/mkdocs.rtd.yml b/mkdocs.rtd.yml index 3396033..67aa860 100644 --- a/mkdocs.rtd.yml +++ b/mkdocs.rtd.yml @@ -88,7 +88,6 @@ theme: extra_css: - assets/extra.css - #extra_javascript: #- assets/extra.js @@ -117,7 +116,6 @@ markdown_extensions: - toc: permalink: "ยค" - plugins: - search - autorefs