Coverage for src/paperap/plugins/base.py: 100%
35 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-20 13:17 -0400
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-20 13:17 -0400
1"""
2----------------------------------------------------------------------------
4 METADATA:
6 File: base.py
7 Project: paperap
8 Created: 2025-03-04
9 Version: 0.0.8
10 Author: Jess Mann
11 Email: jess@jmann.me
12 Copyright (c) 2025 Jess Mann
14----------------------------------------------------------------------------
16 LAST MODIFIED:
18 2025-03-04 By Jess Mann
20"""
22from __future__ import annotations
24from abc import ABC, abstractmethod
25from typing import TYPE_CHECKING, Any, ClassVar, NotRequired, TypedDict, override
27import pydantic
28from pydantic import ConfigDict, field_validator
29from typing_extensions import Unpack
31from paperap.exceptions import ModelValidationError
33if TYPE_CHECKING:
34 from paperap.client import PaperlessClient
35 from paperap.plugins.manager import PluginManager
38class ConfigType(TypedDict, total=False):
39 type: NotRequired[type]
40 description: NotRequired[str]
41 required: NotRequired[bool]
44class Plugin(pydantic.BaseModel, ABC):
45 """Base class for all plugins."""
47 # Class attributes for plugin metadata
48 name: ClassVar[str]
49 description: ClassVar[str] = "No description provided"
50 version: ClassVar[str] = "0.0.1"
51 manager: "PluginManager"
53 @override
54 def __init_subclass__(cls, **kwargs: ConfigDict):
55 # Enforce name is set
56 if not getattr(cls, "name", None):
57 raise ValueError("Plugin name must be set")
58 return super().__init_subclass__(**kwargs) # type: ignore # Not sure why pyright is complaining
60 model_config = ConfigDict(
61 from_attributes=True,
62 extra="ignore",
63 use_enum_values=True,
64 arbitrary_types_allowed=True,
65 validate_default=True,
66 validate_assignment=True,
67 )
69 def __init__(self, **kwargs: Any) -> None:
70 """
71 Initialize the plugin.
73 Args:
74 **kwargs: Plugin-specific configuration.
76 """
77 # Pydantic handles config
78 super().__init__(**kwargs)
80 # Finalize setting up the plugin (defined by subclass)
81 self.setup()
83 @property
84 def client(self) -> "PaperlessClient":
85 return self.manager.client
87 @abstractmethod
88 def setup(self):
89 """Register signal handlers and perform other initialization tasks."""
91 @abstractmethod
92 def teardown(self):
93 """Clean up resources when the plugin is disabled or the application exits."""
95 @classmethod
96 def get_config_schema(cls) -> dict[str, ConfigType]:
97 """
98 Get the configuration schema for this plugin.
100 Returns:
101 A dictionary describing the expected configuration parameters.
103 Examples:
104 >>> return {
105 >>> "test_dir": {
106 >>> "type": str,
107 >>> "description": "Directory to save test data files",
108 >>> "required": False,
109 >>> }
110 >>> }
112 """
113 return {}