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

1""" 

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

3 

4 METADATA: 

5 

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 

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 

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

58 

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 ) 

67 

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

69 """ 

70 Initialize the plugin. 

71 

72 Args: 

73 **kwargs: Plugin-specific configuration. 

74 

75 """ 

76 # Pydantic handles config 

77 super().__init__(**kwargs) 

78 

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

80 self.setup() 

81 

82 @property 

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

84 return self.manager.client 

85 

86 @abstractmethod 

87 def setup(self): 

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

89 

90 @abstractmethod 

91 def teardown(self): 

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

93 

94 @classmethod 

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

96 """ 

97 Get the configuration schema for this plugin. 

98 

99 Returns: 

100 A dictionary describing the expected configuration parameters. 

101 

102 Examples: 

103 >>> return { 

104 >>> "test_dir": { 

105 >>> "type": str, 

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

107 >>> "required": False, 

108 >>> } 

109 >>> } 

110 

111 """ 

112 return {}