diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fd50494..f96edf1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Pinned Ruff version used by requirements.txt. - rev: v0.15.12 + rev: v0.16.1 hooks: - id: ruff name: lint with ruff @@ -71,7 +71,7 @@ repos: name: format with ruff - repo: https://github.com/DavidAnson/markdownlint-cli2 - rev: v0.22.1 + rev: v0.23.2 hooks: - id: markdownlint-cli2 name: Lint markdown files diff --git a/src/addonStoreApi/addonApiVersion.py b/src/addonStoreApi/addonApiVersion.py index 369ddc7..5b0c7b7 100644 --- a/src/addonStoreApi/addonApiVersion.py +++ b/src/addonStoreApi/addonApiVersion.py @@ -3,6 +3,7 @@ # For more details see COPYING.md from typing import NamedTuple + from addonStoreApi.transformedSubmissions import StoreInfoProvider diff --git a/src/addonStoreApi/addonCollector.py b/src/addonStoreApi/addonCollector.py index 6f1e6fd..0ed2d77 100644 --- a/src/addonStoreApi/addonCollector.py +++ b/src/addonStoreApi/addonCollector.py @@ -2,19 +2,19 @@ # This file may be used under the terms of the AGPL3 (GNU Affero General Public License version 3). # For more details see COPYING.md -from collections.abc import Generator, Iterable -from functools import lru_cache import json import re +from collections.abc import Generator, Iterable +from functools import lru_cache from glob import glob from tasks.dataFolder import DataFolder -from .supportedLanguage import SupportedLanguage from .addonApiVersion import SupportedAddonApiVersion +from .supportedLanguage import SupportedLanguage from .transformedSubmissions import ( - StoreInfoProvider, Channels, + StoreInfoProvider, ) diff --git a/src/addonStoreApi/transformedSubmissions.py b/src/addonStoreApi/transformedSubmissions.py index 128c362..184fe22 100644 --- a/src/addonStoreApi/transformedSubmissions.py +++ b/src/addonStoreApi/transformedSubmissions.py @@ -5,13 +5,14 @@ """Module to abstract the details for the layout for the transformed submissions""" import enum -from tasks.dataFolder import DataFolder import glob import json import logging import os from typing import TYPE_CHECKING +from tasks.dataFolder import DataFolder + if TYPE_CHECKING: from addonStoreApi.addonApiVersion import MajorMinorPatch diff --git a/src/app.py b/src/app.py index 8fec493..4cd3a28 100644 --- a/src/app.py +++ b/src/app.py @@ -2,45 +2,42 @@ # This file may be used under the terms of the AGPL3 (GNU Affero General Public License version 3). # For more details see COPYING.md -import typing +import hashlib +import hmac import logging import os -from http import HTTPStatus - -from werkzeug.routing import BaseConverter, ValidationError -from addonStoreApi.addonApiVersion import MajorMinorPatch -from tasks.dataFolder import DataFolder -from tasks.health import check_health -from datetime import datetime +import re +import subprocess import threading -from queue import Queue, Empty -from werkzeug.exceptions import NotFound +from datetime import datetime +from http import HTTPStatus +from queue import Empty, Queue from flask import ( Flask, Response, - request, - jsonify, current_app, + jsonify, + request, ) from flask_cors import CORS, cross_origin -from prometheus_flask_exporter import PrometheusMetrics from prometheus_client import Counter -import re +from prometheus_flask_exporter import PrometheusMetrics +from werkzeug.exceptions import NotFound +from werkzeug.routing import BaseConverter, ValidationError -from addonStoreApi.addonApiVersion import SupportedAddonApiVersion -from addonStoreApi.supportedLanguage import SupportedLanguage +from addonStoreApi.addonApiVersion import MajorMinorPatch, SupportedAddonApiVersion from addonStoreApi.addonCollector import ( FileCollector, ) +from addonStoreApi.supportedLanguage import SupportedLanguage from addonStoreApi.transformedSubmissions import ( - StoreInfoProvider, Channels, + StoreInfoProvider, ) -import hmac -import hashlib -import subprocess from frontend import frontend +from tasks.dataFolder import DataFolder +from tasks.health import check_health """ app.py gets loaded automatically by Flask Use this to configure routing. @@ -86,7 +83,7 @@ def process_metrics_queue(): metric_func() except Exception as e: - log.warning(f"Error processing metrics: {str(e)}") + log.warning(f"Error processing metrics: {e!s}") def ensure_metrics_worker(): """Ensure the metrics worker thread is running.""" @@ -104,7 +101,7 @@ def queue_metric(metric_func): timeout=0.1, ) # Short timeout to prevent blocking except Exception as e: - log.warning(f"Failed to queue metric: {str(e)}") + log.warning(f"Failed to queue metric: {e!s}") # Initialize Prometheus metrics with path excluded from auth metrics = PrometheusMetrics(app) @@ -305,7 +302,7 @@ def throw(): @DataFolder.accessForReading def _all( - includeChannels: typing.List[Channels], + includeChannels: list[Channels], language: str, apiVersion: MajorMinorPatch, ) -> Response: @@ -344,7 +341,7 @@ def _all( @DataFolder.accessForReading def _latest( - includeChannels: typing.List[Channels], + includeChannels: list[Channels], language: str, ) -> Response: try: @@ -447,7 +444,7 @@ def verify_github_signature(payload_body, signature_header): # Compare signatures return hmac.compare_digest(signature, expected_signature) except Exception as e: - log.error(f"Error verifying webhook signature: {str(e)}") + log.error(f"Error verifying webhook signature: {e!s}") return False @app.route("/update", methods=["POST"]) @@ -587,9 +584,9 @@ def update_repo(): log.info(f"Repository updated successfully to {target_hash}") except subprocess.CalledProcessError as e: - log.error(f"Git operation failed: {str(e)}") + log.error(f"Git operation failed: {e!s}") except Exception as e: - log.error(f"Update failed: {str(e)}") + log.error(f"Update failed: {e!s}") finally: DataFolder._update_in_progress = False diff --git a/src/frontend/__init__.py b/src/frontend/__init__.py index 7b9020d..9abd33a 100644 --- a/src/frontend/__init__.py +++ b/src/frontend/__init__.py @@ -2,25 +2,25 @@ # This file may be used under the terms of the AGPL3 (GNU Affero General Public License version 3). # For more details see COPYING.md -from dataclasses import dataclass import dataclasses -from enum import Enum -from functools import cached_property, lru_cache import json import logging import os +from dataclasses import dataclass +from datetime import date +from enum import Enum +from functools import cached_property, lru_cache from typing import Any, Literal + +from babel import Locale +from babel.dates import format_date from flask import Blueprint, render_template, request +from addonStoreApi.addonApiVersion import MajorMinorPatch, SupportedAddonApiVersion from addonStoreApi.addonCollector import FileCollector from addonStoreApi.supportedLanguage import SupportedLanguage - -from addonStoreApi.addonApiVersion import MajorMinorPatch, SupportedAddonApiVersion from addonStoreApi.transformedSubmissions import Channels, StoreInfoProvider from tasks.dataFolder import DataFolder -from babel.dates import format_date -from babel import Locale -from datetime import date # Don't use getenv or environ.get, # as we want to fail if $COPYRIGHT_YEARS is not set @@ -164,12 +164,10 @@ def matchingAddons(self) -> list[dict[str, Any]]: else: # No search term; # just return all add-ons sorted by the specified field - return list( - sorted( - addonList, - key=self._getSortValue, - reverse=self.sortReverse, - ), + return sorted( + addonList, + key=self._getSortValue, + reverse=self.sortReverse, ) @cached_property diff --git a/src/tasks/dataFolder.py b/src/tasks/dataFolder.py index e953d67..c55a2e2 100644 --- a/src/tasks/dataFolder.py +++ b/src/tasks/dataFolder.py @@ -2,18 +2,19 @@ # This file may be used under the terms of the AGPL3 (GNU Affero General Public License version 3). # For more details see COPYING.md -from enum import IntEnum -from functools import wraps import logging import os import pathlib import subprocess +import threading +from collections.abc import Callable +from contextlib import contextmanager +from enum import IntEnum +from functools import wraps from time import time -from typing import Callable, Optional + from portalocker import AlreadyLocked, Lock from portalocker.constants import LockFlags -import threading -from contextlib import contextmanager """ A set of tools which are used to ensure safe access to the addon store data folder. @@ -156,7 +157,7 @@ def initialize(): DataFolder.log.warning("Found stale git index lock, cleaning up") os.remove(os.path.join(repo_path, ".git", "index.lock")) except Exception as e: - DataFolder.log.error(f"Error cleaning up git locks: {str(e)}") + DataFolder.log.error(f"Error cleaning up git locks: {e!s}") # Configure git for safe directory access DataFolder.log.info("Configuring git safe directory") @@ -192,7 +193,7 @@ def _updateCacheHash(): f"Cache hash updated to: {DataFolder._current_hash}", ) except subprocess.CalledProcessError as e: - DataFolder.log.error(f"Failed to update cache hash: {str(e)}") + DataFolder.log.error(f"Failed to update cache hash: {e!s}") # If we fail to get the hash but had a previous hash, keep using it if DataFolder._current_hash is None: # If we've never had a hash, use a fallback @@ -304,7 +305,7 @@ def readData(folder: str): def wrapper(*args, **kwargs): # If this thread is already performing a read, # there is no need to create a new read file. - readFile: Optional[pathlib.Path] = None + readFile: pathlib.Path | None = None if not _ReadTracker.threadIsInRead(): # Ensure the writer is aware a read is ongoing # This call can be blocked by the writer diff --git a/src/tasks/health.py b/src/tasks/health.py index 52cc237..9fa3dce 100644 --- a/src/tasks/health.py +++ b/src/tasks/health.py @@ -6,7 +6,9 @@ import logging from http import HTTPStatus + from flask import jsonify + from .dataFolder import DataFolder log = logging.getLogger("addonStore.health") @@ -39,7 +41,7 @@ def check_health(): except Exception as e: # Log the actual stack trace/error internally - log.exception(f"Healthcheck failed: {str(e)}") + log.exception(f"Healthcheck failed: {e!s}") # Return a generic, opaque error to the client return jsonify(