python-dotenvx 2.0.0__cp38-abi3-macosx_10_12_x86_64.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.
dotenvx/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .main import dotenv_values, find_dotenv, load_dotenv, load_dotenvx
2
+
3
+ __all__ = ["dotenv_values", "find_dotenv", "load_dotenv", "load_dotenvx"]
dotenvx/__version__.py ADDED
@@ -0,0 +1,9 @@
1
+ from importlib.metadata import version
2
+
3
+ __title__ = "python-dotenvx"
4
+ __description__ = "a secure dotenv–from the creator of `dotenv`"
5
+ __url__ = "https://github.com/dotenvx/python-dotenvx"
6
+ __version__ = version("python-dotenvx")
7
+ __author__ = "dotenv"
8
+ __author_email__ = "mot@dotenvx.com"
9
+ __license__ = "BSD-3-Clause"
Binary file
dotenvx/main.py ADDED
@@ -0,0 +1,181 @@
1
+ import os
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Dict, List, Optional, TextIO, Tuple, Union
5
+
6
+ from ._native import parse_dotenv
7
+
8
+ StrPath = Union[str, os.PathLike]
9
+ Values = Dict[str, Optional[str]]
10
+
11
+
12
+ def find_dotenv(
13
+ filename: str = ".env",
14
+ raise_error_if_not_found: bool = False,
15
+ usecwd: bool = False
16
+ ) -> str:
17
+ """Find ``filename`` by walking from the current directory toward root."""
18
+ del usecwd # The package intentionally uses cwd for predictable discovery.
19
+ directory = Path.cwd()
20
+
21
+ for candidate_directory in (directory, *directory.parents):
22
+ candidate = candidate_directory / filename
23
+ if candidate.is_file():
24
+ return str(candidate)
25
+
26
+ if raise_error_if_not_found:
27
+ raise IOError("File not found")
28
+ return ""
29
+
30
+
31
+ def _source(
32
+ dotenv_path: Optional[StrPath],
33
+ stream: Optional[TextIO],
34
+ encoding: Optional[str]
35
+ ) -> Tuple[str, Optional[Path]]:
36
+ if stream is not None:
37
+ return stream.read(), None
38
+
39
+ path = Path(dotenv_path) if dotenv_path is not None else Path(find_dotenv())
40
+ if not str(path) or not path.is_file():
41
+ return "", None
42
+ return path.read_text(encoding=encoding or "utf-8"), path
43
+
44
+
45
+ def _key_files(path: Optional[Path]) -> List[str]:
46
+ if path is None:
47
+ return []
48
+
49
+ candidates = [path.with_name(f"{path.name}.keys")]
50
+ if path.name != ".env":
51
+ candidates.append(path.with_name(".env.keys"))
52
+ return [str(candidate) for candidate in candidates if candidate.is_file()]
53
+
54
+
55
+ def _parse(
56
+ dotenv_path: Optional[StrPath] = None,
57
+ stream: Optional[TextIO] = None,
58
+ override: bool = False,
59
+ interpolate: bool = True,
60
+ encoding: Optional[str] = "utf-8"
61
+ ) -> Tuple[Values, Dict[str, str], bool]:
62
+ source, path = _source(dotenv_path, stream, encoding)
63
+ if path is None and not source:
64
+ return {}, {}, False
65
+
66
+ parsed, injected = parse_dotenv(
67
+ source,
68
+ dict(os.environ),
69
+ override,
70
+ _key_files(path),
71
+ interpolate,
72
+ )
73
+ has_values = bool(parsed)
74
+ for key in _bare_keys(source):
75
+ parsed.setdefault(key, None)
76
+ return parsed, injected, has_values
77
+
78
+
79
+ def _bare_keys(source: str) -> List[str]:
80
+ pattern = re.compile(
81
+ r"^([A-Za-z_][A-Za-z0-9_.-]*)\s*(?:#.*)?$"
82
+ )
83
+ keys = []
84
+ multiline_quote = None
85
+
86
+ for raw_line in source.splitlines():
87
+ if multiline_quote is not None:
88
+ if _has_unescaped_quote(raw_line, multiline_quote):
89
+ multiline_quote = None
90
+ continue
91
+
92
+ line = raw_line.strip()
93
+ if not line or line.startswith("#"):
94
+ continue
95
+ if line.startswith("export "):
96
+ line = line[7:].lstrip()
97
+
98
+ if "=" in line:
99
+ value = line.split("=", 1)[1].lstrip()
100
+ if value[:1] in {"'", '"', "`"}:
101
+ quote = value[0]
102
+ if not _has_unescaped_quote(value[1:], quote):
103
+ multiline_quote = quote
104
+ continue
105
+
106
+ match = pattern.fullmatch(line)
107
+ if match:
108
+ keys.append(match.group(1))
109
+
110
+ return keys
111
+
112
+
113
+ def _has_unescaped_quote(value: str, quote: str) -> bool:
114
+ escaped = False
115
+ for character in value:
116
+ if character == quote and not escaped:
117
+ return True
118
+ if character == "\\":
119
+ escaped = not escaped
120
+ else:
121
+ escaped = False
122
+ return False
123
+
124
+
125
+ def dotenv_values(
126
+ dotenv_path: Optional[StrPath] = None,
127
+ stream: Optional[TextIO] = None,
128
+ verbose: bool = False,
129
+ interpolate: bool = True,
130
+ encoding: Optional[str] = "utf-8"
131
+ ) -> Values:
132
+ """Parse a dotenv source without changing ``os.environ``."""
133
+ del verbose
134
+ parsed, _, _ = _parse(
135
+ dotenv_path=dotenv_path,
136
+ stream=stream,
137
+ override=True,
138
+ interpolate=interpolate,
139
+ encoding=encoding,
140
+ )
141
+ return parsed
142
+
143
+
144
+ def load_dotenv(
145
+ dotenv_path: Optional[StrPath] = None,
146
+ stream: Optional[TextIO] = None,
147
+ verbose: bool = False,
148
+ override: bool = False,
149
+ interpolate: bool = True,
150
+ encoding: Optional[str] = "utf-8"
151
+ ) -> bool:
152
+ """Load dotenv values into ``os.environ`` using native Rust primitives."""
153
+ del verbose
154
+
155
+ if os.environ.get("PYTHON_DOTENV_DISABLED", "").lower() in {
156
+ "1", "true", "t", "yes", "y"
157
+ }:
158
+ return False
159
+
160
+ _, injected, found = _parse(
161
+ dotenv_path=dotenv_path,
162
+ stream=stream,
163
+ override=override,
164
+ interpolate=interpolate,
165
+ encoding=encoding,
166
+ )
167
+ os.environ.update(injected)
168
+ return found
169
+
170
+
171
+ def load_dotenvx(
172
+ dotenv_path: Optional[StrPath] = None,
173
+ override: bool = False
174
+ ) -> Values:
175
+ """Backward-compatible dotenvx loader returning the parsed mapping."""
176
+ parsed, injected, _ = _parse(
177
+ dotenv_path=dotenv_path,
178
+ override=override,
179
+ )
180
+ os.environ.update(injected)
181
+ return parsed
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-dotenvx
3
+ Version: 2.0.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Programming Language :: Python :: 3 :: Only
6
+ Classifier: Programming Language :: Rust
7
+ Classifier: License :: OSI Approved :: BSD License
8
+ Classifier: Operating System :: OS Independent
9
+ License-File: LICENSE
10
+ Summary: a secure dotenv–from the creator of `dotenv`
11
+ Keywords: environment,environment variables,env,dotenv,python,dotenvx
12
+ Author-email: dotenv <mot@dotenvx.com>
13
+ License: BSD-3-Clause
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
16
+ Project-URL: Homepage, https://dotenvx.com
17
+ Project-URL: Repository, https://github.com/dotenvx/python-dotenvx
18
+
19
+ [![dotenvx](https://dotenvx.com/better-banner.png)](https://dotenvx.com)
20
+
21
+ *a better dotenv*–from the creator of [`dotenv`](https://github.com/motdotla/dotenv).
22
+
23
+ * run anywhere (cross-platform)
24
+ * multi-environment
25
+ * encrypted envs
26
+
27
+ &nbsp;
28
+
29
+
30
+ ### Quickstart [![PyPI version](https://badge.fury.io/py/python-dotenvx.svg)](http://badge.fury.io/py/python-dotenvx)
31
+
32
+ Install and use it in code just like `python-dotenv`.
33
+
34
+ ```sh
35
+ pip install python-dotenvx
36
+ ```
37
+
38
+ The wheel includes native dotenvx Rust primitives. No dotenvx executable or
39
+ post-install download is required.
40
+
41
+ ```python
42
+ # main.py
43
+ import os
44
+ from dotenvx import load_dotenv
45
+
46
+ load_dotenv() # take environment variables from .env
47
+
48
+ print(os.getenv("S3_BUCKET"))
49
+ ```
50
+
51
+ You can also parse values without changing the environment:
52
+
53
+ ```python
54
+ from dotenvx import dotenv_values
55
+
56
+ values = dotenv_values(".env")
57
+ ```
58
+
59
+ Encrypted values are decrypted using `DOTENV_PRIVATE_KEY` from the environment
60
+ or a neighboring `.env.keys` file.
61
+
62
+ &nbsp;
63
+
@@ -0,0 +1,9 @@
1
+ dotenvx/__init__.py,sha256=yjjqD_ZhBJhcz1UG0_ATJ-33XhgxPv5eAEjWLSLLt0w,147
2
+ dotenvx/__version__.py,sha256=RP_uGDG00O0aVt1QxPoR1krfYkPHeZjbP5zbJDdV--c,318
3
+ dotenvx/_native.abi3.so,sha256=8YlHGfCXr1IWrt3kE5Qg-vFAj3cCg5YER5yb8vZ2ImE,2681472
4
+ dotenvx/main.py,sha256=_eiB_SP4wDmKoNMTYu4QtDuvql5Fq1bdDdaksvIAidU,5011
5
+ python_dotenvx-2.0.0.dist-info/METADATA,sha256=HJF7hSz2yCThuJymnRIJ89FcTv1k-u4vP7lNNJ2tNGY,1696
6
+ python_dotenvx-2.0.0.dist-info/WHEEL,sha256=VyMvuaqQgQmqWzNqy4G89nzRACgEEQLHjJrW08Pp8ys,105
7
+ python_dotenvx-2.0.0.dist-info/licenses/LICENSE,sha256=Lt6wCRSMNsJJcSVkhx2Cl9kglLiOQXIo5PPOfbWe6e0,1498
8
+ python_dotenvx-2.0.0.dist-info/sboms/python-dotenvx-native.cyclonedx.json,sha256=wgtxlzqZPt0XH3RnUWU8OeIKI3880Ca3HoOv8LjF0Qw,81330
9
+ python_dotenvx-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.14.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp38-abi3-macosx_10_12_x86_64
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Scott Motte
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.