Coverage for src/paperap/plugins/base.py: 100%
34 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-18 12:26 -0400
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-18 12:26 -0400
1"""
2----------------------------------------------------------------------------
4 METADATA:
6 File: base.py
7 Project: paperap
8 Created: 2025-03-04
9 Version: 0.0.7
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
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 def __init_subclass__(cls, **kwargs: ConfigDict):
54 # Enforce name is set
55 if not getattr(cls, "name", None):
56 raise ValueError("Plugin name must be set")
57 return super().__init_subclass__(**kwargs) # type: ignore # Not sure why pyright is complaining
59 model_config = ConfigDict(
60 from_attributes=True,
61 extra="ignore",
62 use_enum_values=True,
63 arbitrary_types_allowed=True,
64 validate_default=True,
65 validate_assignment=True,
66 )
68 def __init__(self, **kwargs: Any) -> None:
69 """
70 Initialize the plugin.
72 Args:
73 **kwargs: Plugin-specific configuration.
75 """
76 # Pydantic handles config
77 super().__init__(**kwargs)
79 # Finalize setting up the plugin (defined by subclass)
80 self.setup()
82 @property
83 def client(self) -> "PaperlessClient":
84 return self.manager.client
86 @abstractmethod
87 def setup(self):
88 """Register signal handlers and perform other initialization tasks."""
90 @abstractmethod
91 def teardown(self):
92 """Clean up resources when the plugin is disabled or the application exits."""
94 @classmethod
95 def get_config_schema(cls) -> dict[str, ConfigType]:
96 """
97 Get the configuration schema for this plugin.
99 Returns:
100 A dictionary describing the expected configuration parameters.
102 Examples:
103 >>> return {
104 >>> "test_dir": {
105 >>> "type": str,
106 >>> "description": "Directory to save test data files",
107 >>> "required": False,
108 >>> }
109 >>> }
111 """
112 return {}