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

1""" 

2---------------------------------------------------------------------------- 

3 

4 METADATA: 

5 

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 

13 

14---------------------------------------------------------------------------- 

15 

16 LAST MODIFIED: 

17 

18 2025-03-04 By Jess Mann 

19 

20""" 

21 

22from __future__ import annotations 

23 

24from abc import ABC, abstractmethod 

25from typing import TYPE_CHECKING, Any, ClassVar, NotRequired, TypedDict, override 

26 

27import pydantic 

28from pydantic import ConfigDict, field_validator 

29from typing_extensions import Unpack 

30 

31from paperap.exceptions import ModelValidationError 

32 

33if TYPE_CHECKING: 

34 from paperap.client import PaperlessClient 

35 from paperap.plugins.manager import PluginManager 

36 

37 

38class ConfigType(TypedDict, total=False): 

39 type: NotRequired[type] 

40 description: NotRequired[str] 

41 required: NotRequired[bool] 

42 

43 

44class Plugin(pydantic.BaseModel, ABC): 

45 """Base class for all plugins.""" 

46 

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" 

52 

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 

59 

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 ) 

68 

69 def __init__(self, **kwargs: Any) -> None: 

70 """ 

71 Initialize the plugin. 

72 

73 Args: 

74 **kwargs: Plugin-specific configuration. 

75 

76 """ 

77 # Pydantic handles config 

78 super().__init__(**kwargs) 

79 

80 # Finalize setting up the plugin (defined by subclass) 

81 self.setup() 

82 

83 @property 

84 def client(self) -> "PaperlessClient": 

85 return self.manager.client 

86 

87 @abstractmethod 

88 def setup(self): 

89 """Register signal handlers and perform other initialization tasks.""" 

90 

91 @abstractmethod 

92 def teardown(self): 

93 """Clean up resources when the plugin is disabled or the application exits.""" 

94 

95 @classmethod 

96 def get_config_schema(cls) -> dict[str, ConfigType]: 

97 """ 

98 Get the configuration schema for this plugin. 

99 

100 Returns: 

101 A dictionary describing the expected configuration parameters. 

102 

103 Examples: 

104 >>> return { 

105 >>> "test_dir": { 

106 >>> "type": str, 

107 >>> "description": "Directory to save test data files", 

108 >>> "required": False, 

109 >>> } 

110 >>> } 

111 

112 """ 

113 return {}