envproof 0.1.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.
envguard/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .core import EnvGuard, EnvGuardError, guard
2
+
3
+ __all__ = ["EnvGuard", "EnvGuardError", "guard"]
4
+ __version__ = "0.1.0"
envguard/core.py ADDED
@@ -0,0 +1,84 @@
1
+ import os
2
+ from typing import Any, get_type_hints
3
+
4
+ _MISSING = object()
5
+
6
+
7
+ class EnvGuardError(Exception):
8
+ pass
9
+
10
+
11
+ class EnvGuard:
12
+ """
13
+ Subclass this and declare your env vars as type-annotated class attributes.
14
+
15
+ Required var: DATABASE_URL: str
16
+ Optional var: PORT: int = 8080
17
+ """
18
+
19
+ def __init__(self):
20
+ hints = get_type_hints(self.__class__)
21
+ missing = []
22
+ invalid = []
23
+
24
+ for name, type_ in hints.items():
25
+ if name.startswith("_"):
26
+ continue
27
+
28
+ raw = os.environ.get(name)
29
+ has_default = name in self.__class__.__dict__
30
+ default = self.__class__.__dict__.get(name, _MISSING)
31
+
32
+ if raw is None:
33
+ if not has_default:
34
+ missing.append((name, type_))
35
+ else:
36
+ setattr(self, name, default)
37
+ continue
38
+
39
+ try:
40
+ setattr(self, name, _coerce(raw, type_))
41
+ except (ValueError, TypeError):
42
+ invalid.append((name, type_.__name__, raw))
43
+
44
+ errors = []
45
+ if missing:
46
+ errors.append("Missing required environment variables:")
47
+ for name, type_ in missing:
48
+ errors.append(f" - {name} ({type_.__name__}): not set")
49
+ if invalid:
50
+ if errors:
51
+ errors.append("")
52
+ errors.append("Invalid environment variable values:")
53
+ for name, type_name, raw in invalid:
54
+ errors.append(f" - {name}: expected {type_name}, got '{raw}'")
55
+
56
+ if errors:
57
+ raise EnvGuardError("\n" + "\n".join(errors))
58
+
59
+
60
+ def _coerce(value: str, type_: type) -> Any:
61
+ if type_ is bool:
62
+ if value.lower() in ("true", "1", "yes", "on"):
63
+ return True
64
+ if value.lower() in ("false", "0", "no", "off"):
65
+ return False
66
+ raise ValueError(f"Cannot convert to bool: {value!r}")
67
+ if type_ is list:
68
+ return [v.strip() for v in value.split(",") if v.strip()]
69
+ return type_(value)
70
+
71
+
72
+ def guard(**schema) -> Any:
73
+ """
74
+ One-liner usage without subclassing:
75
+
76
+ env = guard(DATABASE_URL=str, PORT=int, DEBUG=bool)
77
+ print(env.DATABASE_URL)
78
+ """
79
+
80
+ class _Env(EnvGuard):
81
+ pass
82
+
83
+ _Env.__annotations__ = schema
84
+ return _Env()
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: envproof
3
+ Version: 0.1.0
4
+ Summary: Validate environment variables at startup with typed, clear error messages. Zero dependencies.
5
+ Project-URL: Homepage, https://github.com/CoderSufiyan/envguard
6
+ Project-URL: Repository, https://github.com/CoderSufiyan/envguard
7
+ Project-URL: Issues, https://github.com/CoderSufiyan/envguard/issues
8
+ License: MIT
9
+ Keywords: config,dotenv,env,environment,settings,validation
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+
23
+ # envguard
24
+
25
+ Validate environment variables at startup with typed, clear error messages. Zero dependencies.
26
+
27
+ ```python
28
+ from envguard import EnvGuard
29
+
30
+ class Env(EnvGuard):
31
+ DATABASE_URL: str
32
+ PORT: int = 8080
33
+ DEBUG: bool = False
34
+ ALLOWED_HOSTS: list = []
35
+
36
+ env = Env()
37
+ print(env.PORT) # 8080 (int, not string)
38
+ ```
39
+
40
+ If `DATABASE_URL` is missing, you get this instead of a cryptic `KeyError` somewhere deep in your app:
41
+
42
+ ```
43
+ EnvGuardError:
44
+ Missing required environment variables:
45
+ - DATABASE_URL (str): not set
46
+ ```
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install envguard
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ ### Subclass style (recommended)
57
+
58
+ ```python
59
+ from envguard import EnvGuard
60
+
61
+ class Env(EnvGuard):
62
+ # Required — raises if not set
63
+ DATABASE_URL: str
64
+ API_KEY: str
65
+
66
+ # Optional — uses default if not set
67
+ PORT: int = 8080
68
+ DEBUG: bool = False
69
+ LOG_LEVEL: str = "INFO"
70
+ ALLOWED_HOSTS: list = []
71
+
72
+ env = Env()
73
+ ```
74
+
75
+ ### One-liner style
76
+
77
+ ```python
78
+ from envguard import guard
79
+
80
+ env = guard(DATABASE_URL=str, PORT=int, DEBUG=bool)
81
+ print(env.DATABASE_URL)
82
+ ```
83
+
84
+ ## Supported types
85
+
86
+ | Type | Example env value | Python value |
87
+ |---------|---------------------------|--------------------------|
88
+ | `str` | `"hello"` | `"hello"` |
89
+ | `int` | `"8080"` | `8080` |
90
+ | `float` | `"3.14"` | `3.14` |
91
+ | `bool` | `"true"`, `"1"`, `"yes"` | `True` |
92
+ | `bool` | `"false"`, `"0"`, `"no"` | `False` |
93
+ | `list` | `"a,b,c"` | `["a", "b", "c"]` |
94
+
95
+ ## Error messages
96
+
97
+ All errors are collected and reported together — you won't fix one missing var only to discover another:
98
+
99
+ ```
100
+ EnvGuardError:
101
+ Missing required environment variables:
102
+ - DATABASE_URL (str): not set
103
+ - API_KEY (str): not set
104
+
105
+ Invalid environment variable values:
106
+ - PORT: expected int, got 'abc'
107
+ - DEBUG: expected bool, got 'maybe'
108
+ ```
109
+
110
+ ## Why not pydantic-settings?
111
+
112
+ `pydantic-settings` is great but pulls in Pydantic as a dependency (~2MB). `envguard` is a single file with zero dependencies — useful when you want validation without adding weight to your project.
113
+
114
+ ## License
115
+
116
+ MIT
@@ -0,0 +1,5 @@
1
+ envguard/__init__.py,sha256=Hd4HRBh0brb7sAxI6TrOZd9fxkvfrLhpArYgVIc97iE,121
2
+ envguard/core.py,sha256=C52SdIjUFC6PB4-_tVHAhezTMJw0Rb9TUu-Rvcnhll4,2328
3
+ envproof-0.1.0.dist-info/METADATA,sha256=dIZBj7jlLKtNDqUE4AUrkgtQIgmCSbpX8CHxbZ0eUsw,3232
4
+ envproof-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
5
+ envproof-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any