djresttoolkit 0.17.5__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
- from ._env_settings import EnvBaseSettings
1
+ from ._base_env_config import BaseEnvConfig
2
+ from dotenv import load_dotenv
2
3
 
3
- __all__ = ["EnvBaseSettings"]
4
+ __all__ = ["load_dotenv", "BaseEnvConfig"]
@@ -0,0 +1,84 @@
1
+ import threading
2
+ import json
3
+ from os import getenv
4
+ from typing import Any, Self, get_type_hints
5
+ import logging
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class BaseEnvConfig:
11
+ """Production-ready environment loader."""
12
+
13
+ _instance_lock = threading.Lock()
14
+ _instance = None
15
+
16
+ def __new__(cls, *args: Any, **kwargs: Any) -> Self:
17
+ if cls._instance is None:
18
+ with cls._instance_lock:
19
+ if cls._instance is None:
20
+ cls._instance = super().__new__(cls)
21
+ return cls._instance
22
+
23
+ def __init__(self) -> None:
24
+ if getattr(self, "_initialized", False):
25
+ return
26
+
27
+ self._env_cache: dict[str, Any] = {}
28
+ self._sync_env()
29
+ self._initialized = True
30
+
31
+ def _sync_env(self) -> None:
32
+ hints = get_type_hints(self.__class__)
33
+ for field, _ in hints.items():
34
+ raw = getenv(field)
35
+
36
+ if raw is None:
37
+ if hasattr(self, field):
38
+ value = getattr(self, field)
39
+ logger.info(f"{field} not set, using default: {value}")
40
+ else:
41
+ raise EnvironmentError(
42
+ f"Missing required environment variable: {field}"
43
+ )
44
+ else:
45
+ if field in self._env_cache:
46
+ value = self._env_cache[field]
47
+ else:
48
+ value = self._parse_env_value(raw)
49
+ self._env_cache[field] = value
50
+
51
+ setattr(self, field, value)
52
+
53
+ def _parse_env_value(self, raw: str) -> Any:
54
+ """Parse string from environment."""
55
+ lowered = raw.lower()
56
+
57
+ # Boolean parsing
58
+ if lowered == "true":
59
+ return True
60
+ if lowered == "false":
61
+ return False
62
+
63
+ # JSON parsing
64
+ try:
65
+ return json.loads(raw)
66
+ except json.JSONDecodeError:
67
+ pass
68
+
69
+ # Numeric parsing
70
+ if raw.isdigit():
71
+ return int(raw)
72
+ try:
73
+ return float(raw)
74
+ except ValueError:
75
+ pass
76
+
77
+ # Fallback: plain string
78
+ return raw
79
+
80
+ def reload(self) -> None:
81
+ """Reload environment variables at runtime."""
82
+ self._env_cache.clear()
83
+ self._sync_env()
84
+ logger.info("Environment variables reloaded.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: djresttoolkit
3
- Version: 0.17.5
3
+ Version: 1.0.0
4
4
  Summary: A collection of Django and DRF utilities to simplify API development.
5
5
  Project-URL: Homepage, https://github.com/shaileshpandit141/djresttoolkit
6
6
  Project-URL: Documentation, https://shaileshpandit141.github.io/djresttoolkit
@@ -47,9 +47,7 @@ Classifier: Topic :: Utilities
47
47
  Classifier: Typing :: Typed
48
48
  Requires-Python: >=3.13
49
49
  Requires-Dist: faker>=37.5.3
50
- Requires-Dist: pydantic-settings>=2.10.1
51
50
  Requires-Dist: pydantic>=2.11.7
52
- Requires-Dist: pyyaml>=6.0.2
53
51
  Provides-Extra: dev
54
52
  Requires-Dist: mypy; extra == 'dev'
55
53
  Requires-Dist: pytest; extra == 'dev'
@@ -72,8 +70,8 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
72
70
  - **DB Flush Command (`dbflush`)**
73
71
  Management command to flush all models or a specific model, resetting auto-increment IDs safely with transaction support.
74
72
 
75
- - **EnvBaseSettings**
76
- Typed settings loader using **YAML + .env**, supports nested keys and overrides. Great for structured configuration management.
73
+ - **BaseEnvConfig**
74
+ Singleton environment loader that reads and parses environment variables.
77
75
 
78
76
  - **EmailSender**
79
77
  Custom class to send templated emails (`text` and `html`) with context. Supports error handling and logging.
@@ -186,10 +184,6 @@ python manage.py dbseed --count 10
186
184
  python manage.py dbseed --model User --seed 42
187
185
  ```
188
186
 
189
- Here’s a **concise API reference** for your database flush management command for `djresttoolkit`:
190
-
191
- ---
192
-
193
187
  ### 2. DB Flush Command — API Reference
194
188
 
195
189
  ```python
@@ -246,77 +240,54 @@ or
246
240
  Flushed 120 records from all models and reset IDs.
247
241
  ```
248
242
 
249
- ### 3. EnvBaseSettings — API Reference
243
+ ### 3. BaseEnvConfig — API Reference
250
244
 
251
245
  ```python
252
- from djresttoolkit.envconfig import EnvBaseSettings
246
+ from djresttoolkit.envconfig import BaseEnvConfig
253
247
  ```
254
248
 
255
- #### `EnvBaseSettings`
256
-
257
- A **base settings class** for managing application configuration using:
258
-
259
- - YAML files (default `.environ.yaml`)
260
- - Environment variables (default `.env`)
261
-
262
- Supports **nested configuration** using double underscores (`__`) in environment variable names.
263
-
264
- #### Class Attributes
265
-
266
- - Attributes
267
- - `env_file`
268
- - Type: `str`
269
- - Default: `.env`
270
- - Description: Environment variable file path.
271
- - `yaml_file`
272
- - Type: `str`
273
- - Default: `.environ.yaml`
274
- - Description: YAML configuration file path.
275
- - `model_config`
276
- - Type: `SettingsConfigDict`
277
- - Description: Pydantic settings configuration (file encoding, nested delimiter).
249
+ #### `BaseEnvConfig`
278
250
 
279
- #### Methods
251
+ Singleton environment loader that reads and parses environment variables with support for booleans, numbers, and JSON.
280
252
 
281
- #### `load(cls, *, env_file: str | None = None, ymal_file: str | None = None, warning: bool = True) -> EnvBaseSettings`
253
+ > ⚠️ **Note:** Note: If you are using a .env file, load it first with python-dotenv:
254
+ >
255
+ > ```python
256
+ > from dotenv import load_dotenv
257
+ > load_dotenv()
258
+ > ```
282
259
 
283
- Loads configuration from **YAML first**, then overrides with **environment variables**.
284
-
285
- #### Parameters
286
-
287
- - `env_file` — Optional custom `.env` file path.
288
- - `ymal_file` — Optional custom YAML file path.
289
- - `warning` — Emit a warning if YAML file is missing (default `True`).
290
-
291
- #### Returns
292
-
293
- - Instance of `EnvBaseSettings` (or subclass) with loaded configuration.
294
-
295
- #### Raises
296
-
297
- - `UserWarning` if YAML file not found and `warning=True`.
260
+ or
261
+ >
262
+ > ```python
263
+ > from djresttoolkit.envconfig import load_dotenv
264
+ > load_dotenv()
265
+ > ```
298
266
 
299
- ### Usage Example
267
+ #### Usage Example
300
268
 
301
269
  ```python
302
- from djresttoolkit.envconfig import EnvBaseSettings
270
+ from djresttoolkit.envconfig import load_dotenv, BaseEnvConfig
271
+
272
+ load_dotenv()
303
273
 
304
- class EnvSettings(EnvBaseSettings["EnvSettings"]):
305
- debug: bool = False
306
- database_url: str
274
+ class EnvConfig(BaseEnvConfig):
275
+ DEBUG: bool = False
276
+ DATABASE_URL: str
307
277
 
308
- # Load settings
309
- settings = EnvSettings.load(warning=False)
278
+ config = EnvConfig()
279
+ print(config.DEBUG)
280
+ print(config.DATABASE_URL)
310
281
 
311
- print(settings.debug)
312
- print(settings.database_url)
282
+ config.reload()
313
283
  ```
314
284
 
315
285
  #### Features
316
286
 
317
- - Prioritizes `.env` variables over YAML.
318
- - Supports nested keys: `DATABASE__HOST`:- `settings.database.host`.
319
- - Designed to be subclassed for project-specific settings.
287
+ - Thread-safe singleton.
288
+ - Automatic type parsing for environment variables.
289
+ - Supports default values and runtime reloading.
290
+ - Designed for subclassing with project-specific settings.
320
291
 
321
292
  ### 4. EmailSender — API Reference
322
293
 
@@ -14,8 +14,8 @@ djresttoolkit/dbseed/models/__init__.py,sha256=uuynQIcfVqEaZN9hF_caI24zm8az23JdX
14
14
  djresttoolkit/dbseed/models/_choice_field.py,sha256=T7LAzbyXqlYp2mtCAKL8E1Da_MEh9RzgLZrFJ7fa4gM,446
15
15
  djresttoolkit/dbseed/models/_gen.py,sha256=qBPQaLvh1rcEam0YmE4JBJqpa-Vv5IFlIIagkEMHDVw,206
16
16
  djresttoolkit/dbseed/models/_seed_model.py,sha256=0cmbi0VNKjmJbwhjeCFsvb3iKYjok6TJOk6Y2MF_3N4,2443
17
- djresttoolkit/envconfig/__init__.py,sha256=PcLaPaVfQmz3-4m6SwoOQF2W4U7F0agBGJ4Qjqbcyfw,74
18
- djresttoolkit/envconfig/_env_settings.py,sha256=X-pgHqNtUNQLBHiGpbYB4ViCuY14n6aff5HZqYL4Tlc,3236
17
+ djresttoolkit/envconfig/__init__.py,sha256=ABYwK1rsH15txGsbh5myqerF_ls6RafUlGhLyvaF2t8,119
18
+ djresttoolkit/envconfig/_base_env_config.py,sha256=7buy7khKDR1PLag9w4rtjRhk9EUtIQvqcyqn_LehuV4,2340
19
19
  djresttoolkit/mail/__init__.py,sha256=tB9SdMlhfWQ640q4aobZ0H1c7fTWalpDL2I-onkr2VI,268
20
20
  djresttoolkit/mail/_email_sender.py,sha256=nXnH1JSiiu51IFQSKw8W6pzaGjDPg2GUKEq0XQ2gSJM,3207
21
21
  djresttoolkit/mail/_models.py,sha256=of5KsLGvsN2OWgDYgdtLEijulg817TXgsLKuUdsnDQc,1447
@@ -50,8 +50,8 @@ djresttoolkit/views/_exceptions/__init__.py,sha256=DrCUxuPNyBR4WhzNutn5HDxLa--q5
50
50
  djresttoolkit/views/_exceptions/_exception_handler.py,sha256=_o7If47bzWLl57LeSXSWsIDsJGo2RIpwYAwNQ-hsHVY,2839
51
51
  djresttoolkit/views/mixins/__init__.py,sha256=K-1tk5d8tCVViMynw5DdffJ3Oo5uHpEx32E3_4X2UxM,154
52
52
  djresttoolkit/views/mixins/_retrieve_object_mixin.py,sha256=Q9znYPb07YXXUhsL7VIrk3BC-zDwjOhwLJKe2GPJ-k0,1155
53
- djresttoolkit-0.17.5.dist-info/METADATA,sha256=3sSysAflkxpff1N8jlZ7_5lv4CJSWZ9be8_6ZQFUp5w,32929
54
- djresttoolkit-0.17.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- djresttoolkit-0.17.5.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
56
- djresttoolkit-0.17.5.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
57
- djresttoolkit-0.17.5.dist-info/RECORD,,
53
+ djresttoolkit-1.0.0.dist-info/METADATA,sha256=pyiY9lXJeoLC9xHgZSuE4f72eRkugl0Zkt00hWaDrSU,31832
54
+ djresttoolkit-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ djresttoolkit-1.0.0.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
56
+ djresttoolkit-1.0.0.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
57
+ djresttoolkit-1.0.0.dist-info/RECORD,,
@@ -1,84 +0,0 @@
1
- import warnings
2
- from pathlib import Path
3
- from typing import Any, ClassVar
4
-
5
- import yaml
6
- from pydantic_settings import BaseSettings, SettingsConfigDict
7
-
8
-
9
- class EnvBaseSettings[T: "EnvBaseSettings"](BaseSettings):
10
- """ "
11
- EnvBaseSettings is a base settings class for managing application configuration
12
- using both YAML files and environment variables.
13
- This class is designed to load configuration values from a YAML file first,
14
- and then override those values with environment variables if present. It supports
15
- nested configuration using a double underscore (`__`) as the delimiter in
16
- environment variable names, allowing for hierarchical settings.
17
-
18
- Class Attributes:
19
- env_file (str): The default filename for the environment variables file (default: ".env").
20
- yaml_file (str): The default filename for the YAML configuration file (default: ".environ.yaml").
21
- model_config (SettingsConfigDict): Configuration for environment variable parsing, including file encoding and nested delimiter.
22
-
23
- Methods:
24
- load(cls, *, env_file: str | None = None, ymal_file: str | None = None, warning: bool = True) -> "EnvBaseSettings":
25
- Loads configuration from a YAML file (if it exists), then overrides with environment variables.
26
- - env_file: Optional custom path to the .env file.
27
- - ymal_file: Optional custom path to the YAML file.
28
- - warning: If True, emits a warning if the YAML file is not found.
29
- Returns an instance of EnvBaseSettings with the loaded configuration.
30
-
31
- Usage:
32
- - Define your settings as subclasses of EnvBaseSettings.
33
- - Call `YourSettingsClass.load()` to load configuration from files and environment variables.
34
- - Supports nested configuration via double underscore in environment variable names (e.g., `DATABASE__HOST`).
35
-
36
- Raises:
37
- - UserWarning: If the YAML file is not found and `warning` is True.
38
-
39
- Example:
40
- ```python
41
- from djresttoolkit.envconfig import EnvBaseSettings
42
-
43
- class EnvSettings(EnvBaseSettings):
44
- debug: bool = False
45
- database_url: str
46
-
47
- settings = EnvSettings.load(warning=False)
48
- ```
49
-
50
- """
51
-
52
- env_file: ClassVar[str] = ".env"
53
- yaml_file: ClassVar[str] = ".environ.yaml"
54
-
55
- model_config = SettingsConfigDict(
56
- env_file=env_file,
57
- env_file_encoding="utf-8",
58
- env_nested_delimiter="__",
59
- )
60
-
61
- @classmethod
62
- def load(
63
- cls: type[T],
64
- *,
65
- env_file: str | None = None,
66
- ymal_file: str | None = None,
67
- warning: bool = True,
68
- ) -> T:
69
- """Load from YAML first, then override with .env."""
70
- if env_file:
71
- cls.env_file = env_file
72
- if ymal_file:
73
- cls.yaml_file = ymal_file
74
-
75
- config_file = Path(cls.yaml_file)
76
- yaml_data: dict[str, Any] = {}
77
- if config_file.exists():
78
- with config_file.open("r") as f:
79
- yaml_data = yaml.safe_load(f) or {}
80
- elif warning:
81
- msg: str = f"Config file {config_file} not found, using only env vars."
82
- warnings.warn(msg, UserWarning, stacklevel=1)
83
-
84
- return cls(**yaml_data)