Coverage for src/paperap/settings.py: 96%

53 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-20 13:17 -0400

1""" 

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

3 

4METADATA: 

5 

6File: settings.py 

7 Project: paperap 

8Created: 2025-03-09 

9 Version: 0.0.8 

10Author: Jess Mann 

11Email: jess@jmann.me 

12 Copyright (c) 2025 Jess Mann 

13 

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

15 

16LAST MODIFIED: 

17 

182025-03-09 By Jess Mann 

19 

20""" 

21 

22from __future__ import annotations 

23 

24from typing import Annotated, Any, Optional, Self, TypedDict, override 

25 

26from pydantic import Field, HttpUrl, field_validator 

27from pydantic_settings import BaseSettings, SettingsConfigDict 

28 

29from paperap.exceptions import ConfigurationError 

30 

31 

32class SettingsArgs(TypedDict, total=False): 

33 """ 

34 Arguments for the settings class 

35 """ 

36 

37 base_url: HttpUrl 

38 token: str | None 

39 username: str | None 

40 password: str | None 

41 timeout: int 

42 require_ssl: bool 

43 save_on_write: bool 

44 

45 

46class Settings(BaseSettings): 

47 """ 

48 Settings for the paperap library 

49 """ 

50 

51 token: str | None = None 

52 username: str | None = None 

53 password: str | None = None 

54 base_url: HttpUrl 

55 timeout: int = 60 

56 require_ssl: bool = False 

57 save_on_write: bool = True 

58 openai_key: str | None = Field(default=None, alias="openai_api_key") 

59 openai_model: str | None = Field(default=None, alias="openai_model_name") 

60 openai_url: str | None = Field(default=None, alias="openai_base_url") 

61 

62 model_config = SettingsConfigDict(env_prefix="PAPERLESS_", extra="ignore") 

63 

64 @field_validator("base_url", mode="after") 

65 @classmethod 

66 def validate_url(cls, value: HttpUrl) -> HttpUrl: 

67 """Ensure the URL is properly formatted.""" 

68 # Make sure the URL has a scheme 

69 if not all([value.scheme, value.host]): 

70 raise ConfigurationError("Base URL must have a scheme and host") 

71 

72 return value 

73 

74 @field_validator("timeout", mode="before") 

75 @classmethod 

76 def validate_timeout(cls, value: Any) -> int: 

77 """Ensure the timeout is a positive integer.""" 

78 try: 

79 if isinstance(value, str): 

80 # May raise ValueError 

81 value = int(value) 

82 

83 if not isinstance(value, int): 

84 raise TypeError("Unknown type for timeout") 

85 except ValueError as ve: 

86 raise TypeError(f"Timeout must be an integer. Provided {value=} of type {type(value)}") from ve 

87 

88 if value < 0: 

89 raise ConfigurationError("Timeout must be a positive integer") 

90 return value 

91 

92 @override 

93 def model_post_init(self, __context: Any): 

94 """ 

95 Validate the settings after they have been initialized. 

96 """ 

97 if self.token is None and (self.username is None or self.password is None): 

98 raise ConfigurationError("Provide a token, or a username and password") 

99 

100 if not self.base_url: 

101 raise ConfigurationError("Base URL is required") 

102 

103 if self.require_ssl and self.base_url.scheme != "https": 

104 raise ConfigurationError(f"URL must use HTTPS. Url: {self.base_url}. Scheme: {self.base_url.scheme}") 

105 

106 return super().model_post_init(__context)