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

pull/189/head
Andrew Mirsky 2025-05-31 12:58:56 -04:00
rodzic 1e1e2026d3
commit 92eb3da74b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: A98E67635CDF2C39
8 zmienionych plików z 41 dodań i 100 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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.

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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