omlish 0.0.0.dev130__py3-none-any.whl → 0.0.0.dev131__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.
- omlish/__about__.py +3 -3
- omlish/formats/dotenv.py +166 -152
- omlish/inject/__init__.py +1 -1
- omlish/inject/impl/injector.py +2 -2
- omlish/inject/impl/scopes.py +4 -4
- omlish/inject/scopes.py +2 -2
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/RECORD +12 -12
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev130.dist-info → omlish-0.0.0.dev131.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
__version__ = '0.0.0.
|
2
|
-
__revision__ = '
|
1
|
+
__version__ = '0.0.0.dev131'
|
2
|
+
__revision__ = 'd1c2a72e639d89e78ab32d95eaa3e46bf716249c'
|
3
3
|
|
4
4
|
|
5
5
|
#
|
@@ -99,7 +99,7 @@ class Project(ProjectBase):
|
|
99
99
|
'aiosqlite ~= 0.20',
|
100
100
|
'asyncpg ~= 0.30',
|
101
101
|
|
102
|
-
'apsw ~= 3.
|
102
|
+
'apsw ~= 3.47',
|
103
103
|
|
104
104
|
'sqlean.py ~= 3.45',
|
105
105
|
|
omlish/formats/dotenv.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# @omlish-lite
|
2
|
+
# ruff: noqa: UP006 UP007 UP037
|
1
3
|
# Copyright (c) 2014, Saurabh Kumar (python-dotenv), 2013, Ted Tieken (django-dotenv-rw), 2013, Jacob Kaplan-Moss
|
2
4
|
# (django-dotenv)
|
3
5
|
#
|
@@ -37,13 +39,7 @@ import typing as ta
|
|
37
39
|
##
|
38
40
|
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
##
|
44
|
-
|
45
|
-
|
46
|
-
_posix_variable: ta.Pattern[str] = re.compile(
|
42
|
+
_dotenv_posix_variable_pat: ta.Pattern[str] = re.compile(
|
47
43
|
r"""
|
48
44
|
\$\{
|
49
45
|
(?P<name>[^}:]*)
|
@@ -56,7 +52,7 @@ _posix_variable: ta.Pattern[str] = re.compile(
|
|
56
52
|
)
|
57
53
|
|
58
54
|
|
59
|
-
class
|
55
|
+
class DotenvAtom(metaclass=abc.ABCMeta):
|
60
56
|
def __ne__(self, other: object) -> bool:
|
61
57
|
result = self.__eq__(other)
|
62
58
|
if result is NotImplemented:
|
@@ -64,16 +60,16 @@ class Atom(metaclass=abc.ABCMeta):
|
|
64
60
|
return not result
|
65
61
|
|
66
62
|
@abc.abstractmethod
|
67
|
-
def resolve(self, env: ta.Mapping[str, str
|
63
|
+
def resolve(self, env: ta.Mapping[str, ta.Optional[str]]) -> str: ...
|
68
64
|
|
69
65
|
|
70
|
-
class
|
66
|
+
class DotenvLiteral(DotenvAtom):
|
71
67
|
def __init__(self, value: str) -> None:
|
72
68
|
super().__init__()
|
73
69
|
self.value = value
|
74
70
|
|
75
71
|
def __repr__(self) -> str:
|
76
|
-
return f'
|
72
|
+
return f'DotenvLiteral(value={self.value})'
|
77
73
|
|
78
74
|
def __eq__(self, other: object) -> bool:
|
79
75
|
if not isinstance(other, self.__class__):
|
@@ -83,18 +79,18 @@ class Literal(Atom):
|
|
83
79
|
def __hash__(self) -> int:
|
84
80
|
return hash((self.__class__, self.value))
|
85
81
|
|
86
|
-
def resolve(self, env: ta.Mapping[str, str
|
82
|
+
def resolve(self, env: ta.Mapping[str, ta.Optional[str]]) -> str:
|
87
83
|
return self.value
|
88
84
|
|
89
85
|
|
90
|
-
class
|
91
|
-
def __init__(self, name: str, default: str
|
86
|
+
class DotenvVariable(DotenvAtom):
|
87
|
+
def __init__(self, name: str, default: ta.Optional[str]) -> None:
|
92
88
|
super().__init__()
|
93
89
|
self.name = name
|
94
90
|
self.default = default
|
95
91
|
|
96
92
|
def __repr__(self) -> str:
|
97
|
-
return f'
|
93
|
+
return f'DotenvVariable(name={self.name}, default={self.default})'
|
98
94
|
|
99
95
|
def __eq__(self, other: object) -> bool:
|
100
96
|
if not isinstance(other, self.__class__):
|
@@ -104,96 +100,96 @@ class Variable(Atom):
|
|
104
100
|
def __hash__(self) -> int:
|
105
101
|
return hash((self.__class__, self.name, self.default))
|
106
102
|
|
107
|
-
def resolve(self, env: ta.Mapping[str, str
|
103
|
+
def resolve(self, env: ta.Mapping[str, ta.Optional[str]]) -> str:
|
108
104
|
default = self.default if self.default is not None else ''
|
109
105
|
result = env.get(self.name, default)
|
110
106
|
return result if result is not None else ''
|
111
107
|
|
112
108
|
|
113
|
-
def
|
109
|
+
def parse_dotenv_variables(value: str) -> ta.Iterator[DotenvAtom]:
|
114
110
|
cursor = 0
|
115
111
|
|
116
|
-
for match in
|
112
|
+
for match in _dotenv_posix_variable_pat.finditer(value):
|
117
113
|
(start, end) = match.span()
|
118
114
|
name = match['name']
|
119
115
|
default = match['default']
|
120
116
|
|
121
117
|
if start > cursor:
|
122
|
-
yield
|
118
|
+
yield DotenvLiteral(value=value[cursor:start])
|
123
119
|
|
124
|
-
yield
|
120
|
+
yield DotenvVariable(name=name, default=default)
|
125
121
|
cursor = end
|
126
122
|
|
127
123
|
length = len(value)
|
128
124
|
if cursor < length:
|
129
|
-
yield
|
125
|
+
yield DotenvLiteral(value=value[cursor:length])
|
130
126
|
|
131
127
|
|
132
128
|
##
|
133
129
|
|
134
130
|
|
135
|
-
def
|
131
|
+
def _make_dotenv_regex(string: str, extra_flags: int = 0) -> ta.Pattern[str]:
|
136
132
|
return re.compile(string, re.UNICODE | extra_flags)
|
137
133
|
|
138
134
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
class
|
135
|
+
_dotenv_newline_pat = _make_dotenv_regex(r'(\r\n|\n|\r)')
|
136
|
+
_dotenv_multiline_whitespace_pat = _make_dotenv_regex(r'\s*', extra_flags=re.MULTILINE)
|
137
|
+
_dotenv_whitespace_pat = _make_dotenv_regex(r'[^\S\r\n]*')
|
138
|
+
_dotenv_export_pat = _make_dotenv_regex(r'(?:export[^\S\r\n]+)?')
|
139
|
+
_dotenv_single_quoted_key_pat = _make_dotenv_regex(r"'([^']+)'")
|
140
|
+
_dotenv_unquoted_key_pat = _make_dotenv_regex(r'([^=\#\s]+)')
|
141
|
+
_dotenv_equal_sign_pat = _make_dotenv_regex(r'(=[^\S\r\n]*)')
|
142
|
+
_dotenv_single_quoted_value_pat = _make_dotenv_regex(r"'((?:\\'|[^'])*)'")
|
143
|
+
_dotenv_double_quoted_value_pat = _make_dotenv_regex(r'"((?:\\"|[^"])*)"')
|
144
|
+
_dotenv_unquoted_value_pat = _make_dotenv_regex(r'([^\r\n]*)')
|
145
|
+
_dotenv_comment_pat = _make_dotenv_regex(r'(?:[^\S\r\n]*#[^\r\n]*)?')
|
146
|
+
_dotenv_end_of_line_pat = _make_dotenv_regex(r'[^\S\r\n]*(?:\r\n|\n|\r|$)')
|
147
|
+
_dotenv_rest_of_line_pat = _make_dotenv_regex(r'[^\r\n]*(?:\r|\n|\r\n)?')
|
148
|
+
_dotenv_double_quote_escapes_pat = _make_dotenv_regex(r"\\[\\'\"abfnrtv]")
|
149
|
+
_dotenv_single_quote_escapes_pat = _make_dotenv_regex(r"\\[\\']")
|
150
|
+
|
151
|
+
|
152
|
+
class DotenvOriginal(ta.NamedTuple):
|
157
153
|
string: str
|
158
154
|
line: int
|
159
155
|
|
160
156
|
|
161
|
-
class
|
162
|
-
key: str
|
163
|
-
value: str
|
164
|
-
original:
|
157
|
+
class DotenvBinding(ta.NamedTuple):
|
158
|
+
key: ta.Optional[str]
|
159
|
+
value: ta.Optional[str]
|
160
|
+
original: DotenvOriginal
|
165
161
|
error: bool
|
166
162
|
|
167
163
|
|
168
|
-
class
|
164
|
+
class _DotenvPosition:
|
169
165
|
def __init__(self, chars: int, line: int) -> None:
|
170
166
|
super().__init__()
|
171
167
|
self.chars = chars
|
172
168
|
self.line = line
|
173
169
|
|
174
170
|
@classmethod
|
175
|
-
def start(cls) -> '
|
171
|
+
def start(cls) -> '_DotenvPosition':
|
176
172
|
return cls(chars=0, line=1)
|
177
173
|
|
178
|
-
def set(self, other: '
|
174
|
+
def set(self, other: '_DotenvPosition') -> None:
|
179
175
|
self.chars = other.chars
|
180
176
|
self.line = other.line
|
181
177
|
|
182
178
|
def advance(self, string: str) -> None:
|
183
179
|
self.chars += len(string)
|
184
|
-
self.line += len(re.findall(
|
180
|
+
self.line += len(re.findall(_dotenv_newline_pat, string))
|
185
181
|
|
186
182
|
|
187
|
-
class
|
183
|
+
class DotenvError(Exception):
|
188
184
|
pass
|
189
185
|
|
190
186
|
|
191
|
-
class
|
187
|
+
class _DotenvReader:
|
192
188
|
def __init__(self, stream: ta.IO[str]) -> None:
|
193
189
|
super().__init__()
|
194
190
|
self.string = stream.read()
|
195
|
-
self.position =
|
196
|
-
self.mark =
|
191
|
+
self.position = _DotenvPosition.start()
|
192
|
+
self.mark = _DotenvPosition.start()
|
197
193
|
|
198
194
|
def has_next(self) -> bool:
|
199
195
|
return self.position.chars < len(self.string)
|
@@ -201,8 +197,8 @@ class _Reader:
|
|
201
197
|
def set_mark(self) -> None:
|
202
198
|
self.mark.set(self.position)
|
203
199
|
|
204
|
-
def get_marked(self) ->
|
205
|
-
return
|
200
|
+
def get_marked(self) -> DotenvOriginal:
|
201
|
+
return DotenvOriginal(
|
206
202
|
string=self.string[self.mark.chars:self.position.chars],
|
207
203
|
line=self.mark.line,
|
208
204
|
)
|
@@ -213,85 +209,85 @@ class _Reader:
|
|
213
209
|
def read(self, count: int) -> str:
|
214
210
|
result = self.string[self.position.chars:self.position.chars + count]
|
215
211
|
if len(result) < count:
|
216
|
-
raise
|
212
|
+
raise DotenvError('read: End of string')
|
217
213
|
self.position.advance(result)
|
218
214
|
return result
|
219
215
|
|
220
216
|
def read_regex(self, regex: ta.Pattern[str]) -> ta.Sequence[str]:
|
221
217
|
match = regex.match(self.string, self.position.chars)
|
222
218
|
if match is None:
|
223
|
-
raise
|
219
|
+
raise DotenvError('read_regex: Pattern not found')
|
224
220
|
self.position.advance(self.string[match.start():match.end()])
|
225
221
|
return match.groups()
|
226
222
|
|
227
223
|
|
228
|
-
def
|
224
|
+
def _decode_dotenv_escapes(regex: ta.Pattern[str], string: str) -> str:
|
229
225
|
def decode_match(match: ta.Match[str]) -> str:
|
230
226
|
return codecs.decode(match.group(0), 'unicode-escape')
|
231
227
|
|
232
228
|
return regex.sub(decode_match, string)
|
233
229
|
|
234
230
|
|
235
|
-
def
|
231
|
+
def _parse_dotenv_key(reader: _DotenvReader) -> ta.Optional[str]:
|
236
232
|
char = reader.peek(1)
|
237
233
|
if char == '#':
|
238
234
|
return None
|
239
235
|
elif char == "'":
|
240
|
-
(key,) = reader.read_regex(
|
236
|
+
(key,) = reader.read_regex(_dotenv_single_quoted_key_pat)
|
241
237
|
else:
|
242
|
-
(key,) = reader.read_regex(
|
238
|
+
(key,) = reader.read_regex(_dotenv_unquoted_key_pat)
|
243
239
|
return key
|
244
240
|
|
245
241
|
|
246
|
-
def
|
247
|
-
(part,) = reader.read_regex(
|
242
|
+
def _parse_dotenv_unquoted_value(reader: _DotenvReader) -> str:
|
243
|
+
(part,) = reader.read_regex(_dotenv_unquoted_value_pat)
|
248
244
|
return re.sub(r'\s+#.*', '', part).rstrip()
|
249
245
|
|
250
246
|
|
251
|
-
def
|
247
|
+
def _parse_dotenv_value(reader: _DotenvReader) -> str:
|
252
248
|
char = reader.peek(1)
|
253
249
|
if char == "'":
|
254
|
-
(value,) = reader.read_regex(
|
255
|
-
return
|
250
|
+
(value,) = reader.read_regex(_dotenv_single_quoted_value_pat)
|
251
|
+
return _decode_dotenv_escapes(_dotenv_single_quote_escapes_pat, value)
|
256
252
|
elif char == '"':
|
257
|
-
(value,) = reader.read_regex(
|
258
|
-
return
|
253
|
+
(value,) = reader.read_regex(_dotenv_double_quoted_value_pat)
|
254
|
+
return _decode_dotenv_escapes(_dotenv_double_quote_escapes_pat, value)
|
259
255
|
elif char in ('', '\n', '\r'):
|
260
256
|
return ''
|
261
257
|
else:
|
262
|
-
return
|
258
|
+
return _parse_dotenv_unquoted_value(reader)
|
263
259
|
|
264
260
|
|
265
|
-
def
|
261
|
+
def _parse_dotenv_binding(reader: _DotenvReader) -> DotenvBinding:
|
266
262
|
reader.set_mark()
|
267
263
|
try:
|
268
|
-
reader.read_regex(
|
264
|
+
reader.read_regex(_dotenv_multiline_whitespace_pat)
|
269
265
|
if not reader.has_next():
|
270
|
-
return
|
266
|
+
return DotenvBinding(
|
271
267
|
key=None,
|
272
268
|
value=None,
|
273
269
|
original=reader.get_marked(),
|
274
270
|
error=False,
|
275
271
|
)
|
276
|
-
reader.read_regex(
|
277
|
-
key =
|
278
|
-
reader.read_regex(
|
272
|
+
reader.read_regex(_dotenv_export_pat)
|
273
|
+
key = _parse_dotenv_key(reader)
|
274
|
+
reader.read_regex(_dotenv_whitespace_pat)
|
279
275
|
if reader.peek(1) == '=':
|
280
|
-
reader.read_regex(
|
281
|
-
value: str
|
276
|
+
reader.read_regex(_dotenv_equal_sign_pat)
|
277
|
+
value: ta.Optional[str] = _parse_dotenv_value(reader)
|
282
278
|
else:
|
283
279
|
value = None
|
284
|
-
reader.read_regex(
|
285
|
-
reader.read_regex(
|
286
|
-
return
|
280
|
+
reader.read_regex(_dotenv_comment_pat)
|
281
|
+
reader.read_regex(_dotenv_end_of_line_pat)
|
282
|
+
return DotenvBinding(
|
287
283
|
key=key,
|
288
284
|
value=value,
|
289
285
|
original=reader.get_marked(),
|
290
286
|
error=False,
|
291
287
|
)
|
292
|
-
except
|
293
|
-
reader.read_regex(
|
294
|
-
return
|
288
|
+
except DotenvError:
|
289
|
+
reader.read_regex(_dotenv_rest_of_line_pat)
|
290
|
+
return DotenvBinding(
|
295
291
|
key=None,
|
296
292
|
value=None,
|
297
293
|
original=reader.get_marked(),
|
@@ -299,54 +295,54 @@ def _parse_binding(reader: _Reader) -> Binding:
|
|
299
295
|
)
|
300
296
|
|
301
297
|
|
302
|
-
def
|
303
|
-
reader =
|
298
|
+
def parse_dotenv_stream(stream: ta.IO[str]) -> ta.Iterator[DotenvBinding]:
|
299
|
+
reader = _DotenvReader(stream)
|
304
300
|
while reader.has_next():
|
305
|
-
yield
|
301
|
+
yield _parse_dotenv_binding(reader)
|
306
302
|
|
307
303
|
|
308
304
|
##
|
309
305
|
|
310
306
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
def _with_warn_for_invalid_lines(mappings: ta.Iterator[Binding]) -> ta.Iterator[Binding]:
|
307
|
+
def _dotenv_with_warn_for_invalid_lines(
|
308
|
+
mappings: ta.Iterator[DotenvBinding],
|
309
|
+
log: ta.Optional[logging.Logger] = None,
|
310
|
+
) -> ta.Iterator[DotenvBinding]:
|
318
311
|
for mapping in mappings:
|
319
312
|
if mapping.error:
|
320
|
-
log
|
321
|
-
|
322
|
-
|
323
|
-
|
313
|
+
if log is not None:
|
314
|
+
log.warning(
|
315
|
+
'dotenv could not parse statement starting at line %s',
|
316
|
+
mapping.original.line,
|
317
|
+
)
|
324
318
|
yield mapping
|
325
319
|
|
326
320
|
|
327
|
-
|
321
|
+
StrStrMutableMappingT = ta.TypeVar('StrStrMutableMappingT', bound=ta.MutableMapping[str, str])
|
328
322
|
|
329
323
|
|
330
|
-
class
|
324
|
+
class Dotenv:
|
331
325
|
def __init__(
|
332
326
|
self,
|
333
|
-
path:
|
334
|
-
stream: ta.IO[str]
|
327
|
+
path: ta.Union[str, 'os.PathLike[str]', None] = None,
|
328
|
+
stream: ta.Optional[ta.IO[str]] = None,
|
335
329
|
verbose: bool = False,
|
336
|
-
encoding: str
|
330
|
+
encoding: ta.Optional[str] = None,
|
337
331
|
interpolate: bool = True,
|
338
332
|
override: bool = True,
|
339
|
-
env: ta.Mapping[str, str]
|
333
|
+
env: ta.Optional[ta.Mapping[str, str]] = None,
|
334
|
+
log: ta.Optional[logging.Logger] = None,
|
340
335
|
) -> None:
|
341
336
|
super().__init__()
|
342
|
-
self.path:
|
343
|
-
self.stream: ta.IO[str]
|
344
|
-
self._dict:
|
337
|
+
self.path: ta.Union[str, 'os.PathLike[str]', None] = path
|
338
|
+
self.stream: ta.Optional[ta.IO[str]] = stream
|
339
|
+
self._dict: ta.Optional[ta.Dict[str, ta.Optional[str]]] = None
|
345
340
|
self.verbose: bool = verbose
|
346
|
-
self.encoding: str
|
341
|
+
self.encoding: ta.Optional[str] = encoding
|
347
342
|
self.interpolate: bool = interpolate
|
348
343
|
self.override: bool = override
|
349
344
|
self.env = env or {}
|
345
|
+
self.log = log
|
350
346
|
|
351
347
|
@contextlib.contextmanager
|
352
348
|
def _get_stream(self) -> ta.Iterator[ta.IO[str]]:
|
@@ -357,26 +353,27 @@ class DotEnv:
|
|
357
353
|
yield self.stream
|
358
354
|
else:
|
359
355
|
if self.verbose:
|
360
|
-
log
|
361
|
-
|
362
|
-
|
363
|
-
|
356
|
+
if self.log is not None:
|
357
|
+
self.log.info(
|
358
|
+
'dotenv could not find configuration file %s.',
|
359
|
+
self.path or '.env',
|
360
|
+
)
|
364
361
|
yield io.StringIO('')
|
365
362
|
|
366
|
-
def dict(self) ->
|
363
|
+
def dict(self) -> ta.Dict[str, ta.Optional[str]]:
|
367
364
|
if self._dict:
|
368
365
|
return self._dict
|
369
366
|
|
370
367
|
raw_values = self.parse()
|
371
368
|
|
372
369
|
if self.interpolate:
|
373
|
-
self._dict =
|
370
|
+
self._dict = dotenv_resolve_variables(raw_values, override=self.override, env=self.env)
|
374
371
|
else:
|
375
372
|
self._dict = dict(raw_values)
|
376
373
|
|
377
374
|
return self._dict
|
378
375
|
|
379
|
-
def apply_to(self, dst:
|
376
|
+
def apply_to(self, dst: StrStrMutableMappingT) -> StrStrMutableMappingT:
|
380
377
|
for k, v in self.dict().items():
|
381
378
|
if v is not None:
|
382
379
|
dst[k] = v
|
@@ -384,20 +381,21 @@ class DotEnv:
|
|
384
381
|
del dst[k]
|
385
382
|
return dst
|
386
383
|
|
387
|
-
def parse(self) -> ta.Iterator[
|
384
|
+
def parse(self) -> ta.Iterator[ta.Tuple[str, ta.Optional[str]]]:
|
388
385
|
with self._get_stream() as stream:
|
389
|
-
for mapping in
|
386
|
+
for mapping in _dotenv_with_warn_for_invalid_lines(parse_dotenv_stream(stream), self.log):
|
390
387
|
if mapping.key is not None:
|
391
388
|
yield mapping.key, mapping.value
|
392
389
|
|
393
|
-
def get(self, key: str) -> str
|
390
|
+
def get(self, key: str) -> ta.Optional[str]:
|
394
391
|
data = self.dict()
|
395
392
|
|
396
393
|
if key in data:
|
397
394
|
return data[key]
|
398
395
|
|
399
396
|
if self.verbose:
|
400
|
-
log
|
397
|
+
if self.log is not None:
|
398
|
+
self.log.warning('Key %s not found in %s.', key, self.path)
|
401
399
|
|
402
400
|
return None
|
403
401
|
|
@@ -405,25 +403,32 @@ class DotEnv:
|
|
405
403
|
##
|
406
404
|
|
407
405
|
|
408
|
-
def
|
409
|
-
path:
|
406
|
+
def dotenv_get_key(
|
407
|
+
path: ta.Union[str, 'os.PathLike[str]'],
|
410
408
|
key_to_get: str,
|
411
409
|
*,
|
412
|
-
encoding: str
|
413
|
-
|
410
|
+
encoding: ta.Optional[str] = 'utf-8',
|
411
|
+
log: ta.Optional[logging.Logger] = None,
|
412
|
+
) -> ta.Optional[str]:
|
414
413
|
"""
|
415
414
|
Get the value of a given key from the given .env.
|
416
415
|
|
417
416
|
Returns `None` if the key isn't found or doesn't have a value.
|
418
417
|
"""
|
419
|
-
|
418
|
+
|
419
|
+
return Dotenv(
|
420
|
+
path,
|
421
|
+
verbose=True,
|
422
|
+
encoding=encoding,
|
423
|
+
log=log,
|
424
|
+
).get(key_to_get)
|
420
425
|
|
421
426
|
|
422
427
|
@contextlib.contextmanager
|
423
|
-
def
|
424
|
-
path:
|
425
|
-
encoding: str
|
426
|
-
) -> ta.Iterator[
|
428
|
+
def _dotenv_rewrite(
|
429
|
+
path: ta.Union[str, 'os.PathLike[str]'],
|
430
|
+
encoding: ta.Optional[str],
|
431
|
+
) -> ta.Iterator[ta.Tuple[ta.IO[str], ta.IO[str]]]:
|
427
432
|
pathlib.Path(path).touch()
|
428
433
|
|
429
434
|
with tempfile.NamedTemporaryFile(mode='w', encoding=encoding, delete=False) as dest:
|
@@ -441,21 +446,23 @@ def _rewrite(
|
|
441
446
|
raise error from None
|
442
447
|
|
443
448
|
|
444
|
-
def
|
445
|
-
path:
|
449
|
+
def dotenv_set_key(
|
450
|
+
path: ta.Union[str, 'os.PathLike[str]'],
|
446
451
|
key_to_set: str,
|
447
452
|
value_to_set: str,
|
448
453
|
*,
|
449
454
|
quote_mode: str = 'always',
|
450
455
|
export: bool = False,
|
451
|
-
encoding: str
|
452
|
-
|
456
|
+
encoding: ta.Optional[str] = 'utf-8',
|
457
|
+
log: ta.Optional[logging.Logger] = None,
|
458
|
+
) -> ta.Tuple[ta.Optional[bool], str, str]:
|
453
459
|
"""
|
454
460
|
Adds or Updates a key/value to the given .env
|
455
461
|
|
456
462
|
If the .env path given doesn't exist, fails instead of risking creating
|
457
463
|
an orphan .env somewhere in the filesystem
|
458
464
|
"""
|
465
|
+
|
459
466
|
if quote_mode not in ('always', 'auto', 'never'):
|
460
467
|
raise ValueError(f'Unknown quote_mode: {quote_mode}')
|
461
468
|
|
@@ -473,10 +480,10 @@ def set_key(
|
|
473
480
|
else:
|
474
481
|
line_out = f'{key_to_set}={value_out}\n'
|
475
482
|
|
476
|
-
with
|
483
|
+
with _dotenv_rewrite(path, encoding=encoding) as (source, dest):
|
477
484
|
replaced = False
|
478
485
|
missing_newline = False
|
479
|
-
for mapping in
|
486
|
+
for mapping in _dotenv_with_warn_for_invalid_lines(parse_dotenv_stream(source), log):
|
480
487
|
if mapping.key == key_to_set:
|
481
488
|
dest.write(line_out)
|
482
489
|
replaced = True
|
@@ -491,51 +498,55 @@ def set_key(
|
|
491
498
|
return True, key_to_set, value_to_set
|
492
499
|
|
493
500
|
|
494
|
-
def
|
495
|
-
path:
|
501
|
+
def dotenv_unset_key(
|
502
|
+
path: ta.Union[str, 'os.PathLike[str]'],
|
496
503
|
key_to_unset: str,
|
497
504
|
*,
|
498
505
|
quote_mode: str = 'always',
|
499
|
-
encoding: str
|
500
|
-
|
506
|
+
encoding: ta.Optional[str] = 'utf-8',
|
507
|
+
log: ta.Optional[logging.Logger] = None,
|
508
|
+
) -> ta.Tuple[ta.Optional[bool], str]:
|
501
509
|
"""
|
502
510
|
Removes a given key from the given `.env` file.
|
503
511
|
|
504
512
|
If the .env path given doesn't exist, fails.
|
505
513
|
If the given key doesn't exist in the .env, fails.
|
506
514
|
"""
|
515
|
+
|
507
516
|
if not os.path.exists(path):
|
508
|
-
log
|
517
|
+
if log is not None:
|
518
|
+
log.warning("Can't delete from %s - it doesn't exist.", path)
|
509
519
|
return None, key_to_unset
|
510
520
|
|
511
521
|
removed = False
|
512
|
-
with
|
513
|
-
for mapping in
|
522
|
+
with _dotenv_rewrite(path, encoding=encoding) as (source, dest):
|
523
|
+
for mapping in _dotenv_with_warn_for_invalid_lines(parse_dotenv_stream(source), log):
|
514
524
|
if mapping.key == key_to_unset:
|
515
525
|
removed = True
|
516
526
|
else:
|
517
527
|
dest.write(mapping.original.string)
|
518
528
|
|
519
529
|
if not removed:
|
520
|
-
log
|
530
|
+
if log is not None:
|
531
|
+
log.warning("Key %s not removed from %s - key doesn't exist.", key_to_unset, path)
|
521
532
|
return None, key_to_unset
|
522
533
|
|
523
534
|
return removed, key_to_unset
|
524
535
|
|
525
536
|
|
526
|
-
def
|
527
|
-
values: ta.Iterable[
|
537
|
+
def dotenv_resolve_variables(
|
538
|
+
values: ta.Iterable[ta.Tuple[str, ta.Optional[str]]],
|
528
539
|
override: bool,
|
529
540
|
env: ta.Mapping[str, str],
|
530
|
-
) ->
|
531
|
-
new_values:
|
541
|
+
) -> ta.Dict[str, ta.Optional[str]]:
|
542
|
+
new_values: ta.Dict[str, ta.Optional[str]] = {}
|
532
543
|
|
533
544
|
for (name, value) in values:
|
534
545
|
if value is None:
|
535
546
|
result = None
|
536
547
|
else:
|
537
|
-
atoms =
|
538
|
-
aenv:
|
548
|
+
atoms = parse_dotenv_variables(value)
|
549
|
+
aenv: ta.Dict[str, ta.Optional[str]] = {}
|
539
550
|
if override:
|
540
551
|
aenv.update(env)
|
541
552
|
aenv.update(new_values)
|
@@ -550,14 +561,15 @@ def resolve_variables(
|
|
550
561
|
|
551
562
|
|
552
563
|
def dotenv_values(
|
553
|
-
path:
|
554
|
-
stream: ta.IO[str]
|
564
|
+
path: ta.Union[str, 'os.PathLike[str]', None] = None,
|
565
|
+
stream: ta.Optional[ta.IO[str]] = None,
|
555
566
|
*,
|
556
567
|
verbose: bool = False,
|
557
568
|
interpolate: bool = True,
|
558
|
-
encoding: str
|
559
|
-
env: ta.Mapping[str, str]
|
560
|
-
|
569
|
+
encoding: ta.Optional[str] = 'utf-8',
|
570
|
+
env: ta.Optional[ta.Mapping[str, str]] = None,
|
571
|
+
log: ta.Optional[logging.Logger] = None,
|
572
|
+
) -> ta.Dict[str, ta.Optional[str]]:
|
561
573
|
"""
|
562
574
|
Parse a .env file and return its content as a dict.
|
563
575
|
|
@@ -574,10 +586,11 @@ def dotenv_values(
|
|
574
586
|
If both `path` and `stream` are `None`, `find_dotenv()` is used to find the
|
575
587
|
.env file.
|
576
588
|
"""
|
589
|
+
|
577
590
|
if path is None and stream is None:
|
578
591
|
raise ValueError('must set path or stream')
|
579
592
|
|
580
|
-
return
|
593
|
+
return Dotenv(
|
581
594
|
path=path,
|
582
595
|
stream=stream,
|
583
596
|
verbose=verbose,
|
@@ -585,4 +598,5 @@ def dotenv_values(
|
|
585
598
|
override=True,
|
586
599
|
encoding=encoding,
|
587
600
|
env=env,
|
601
|
+
log=log,
|
588
602
|
).dict()
|
omlish/inject/__init__.py
CHANGED
omlish/inject/impl/injector.py
CHANGED
@@ -33,7 +33,7 @@ from ..listeners import ProvisionListener
|
|
33
33
|
from ..listeners import ProvisionListenerBinding
|
34
34
|
from ..scopes import ScopeBinding
|
35
35
|
from ..scopes import Singleton
|
36
|
-
from ..scopes import
|
36
|
+
from ..scopes import ThreadScope
|
37
37
|
from ..types import Scope
|
38
38
|
from ..types import Unscoped
|
39
39
|
from .elements import ElementCollection
|
@@ -48,7 +48,7 @@ log = logging.getLogger(__name__)
|
|
48
48
|
DEFAULT_SCOPES: list[Scope] = [
|
49
49
|
Unscoped(),
|
50
50
|
Singleton(),
|
51
|
-
|
51
|
+
ThreadScope(),
|
52
52
|
]
|
53
53
|
|
54
54
|
|
omlish/inject/impl/scopes.py
CHANGED
@@ -24,7 +24,7 @@ from ..providers import Provider
|
|
24
24
|
from ..scopes import ScopeSeededProvider
|
25
25
|
from ..scopes import SeededScope
|
26
26
|
from ..scopes import Singleton
|
27
|
-
from ..scopes import
|
27
|
+
from ..scopes import ThreadScope
|
28
28
|
from ..types import Scope
|
29
29
|
from ..types import Unscoped
|
30
30
|
from .bindings import BindingImpl
|
@@ -86,8 +86,8 @@ class ThreadScopeImpl(ScopeImpl, lang.Final):
|
|
86
86
|
self._local = threading.local()
|
87
87
|
|
88
88
|
@property
|
89
|
-
def scope(self) ->
|
90
|
-
return
|
89
|
+
def scope(self) -> ThreadScope:
|
90
|
+
return ThreadScope()
|
91
91
|
|
92
92
|
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
93
93
|
dct: dict[BindingImpl, ta.Any]
|
@@ -190,7 +190,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
190
190
|
SCOPE_IMPLS_BY_SCOPE: dict[type[Scope], ta.Callable[..., ScopeImpl]] = {
|
191
191
|
Unscoped: lambda _: UnscopedScopeImpl(),
|
192
192
|
Singleton: lambda _: SingletonScopeImpl(),
|
193
|
-
|
193
|
+
ThreadScope: lambda _: ThreadScopeImpl(),
|
194
194
|
SeededScope: lambda s: SeededScopeImpl(s),
|
195
195
|
}
|
196
196
|
|
omlish/inject/scopes.py
CHANGED
@@ -49,11 +49,11 @@ SCOPE_ALIASES['singleton'] = Singleton()
|
|
49
49
|
##
|
50
50
|
|
51
51
|
|
52
|
-
class
|
52
|
+
class ThreadScope(Scope, lang.Singleton, lang.Final):
|
53
53
|
pass
|
54
54
|
|
55
55
|
|
56
|
-
SCOPE_ALIASES['thread'] =
|
56
|
+
SCOPE_ALIASES['thread'] = ThreadScope()
|
57
57
|
|
58
58
|
|
59
59
|
##
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: omlish
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev131
|
4
4
|
Summary: omlish
|
5
5
|
Author: wrmsr
|
6
6
|
License: BSD-3-Clause
|
@@ -39,7 +39,7 @@ Requires-Dist: pymysql~=1.1; extra == "all"
|
|
39
39
|
Requires-Dist: aiomysql~=0.2; extra == "all"
|
40
40
|
Requires-Dist: aiosqlite~=0.20; extra == "all"
|
41
41
|
Requires-Dist: asyncpg~=0.30; extra == "all"
|
42
|
-
Requires-Dist: apsw~=3.
|
42
|
+
Requires-Dist: apsw~=3.47; extra == "all"
|
43
43
|
Requires-Dist: sqlean.py~=3.45; extra == "all"
|
44
44
|
Requires-Dist: duckdb~=1.1; extra == "all"
|
45
45
|
Requires-Dist: pytest~=8.0; extra == "all"
|
@@ -85,7 +85,7 @@ Requires-Dist: pymysql~=1.1; extra == "sqldrivers"
|
|
85
85
|
Requires-Dist: aiomysql~=0.2; extra == "sqldrivers"
|
86
86
|
Requires-Dist: aiosqlite~=0.20; extra == "sqldrivers"
|
87
87
|
Requires-Dist: asyncpg~=0.30; extra == "sqldrivers"
|
88
|
-
Requires-Dist: apsw~=3.
|
88
|
+
Requires-Dist: apsw~=3.47; extra == "sqldrivers"
|
89
89
|
Requires-Dist: sqlean.py~=3.45; extra == "sqldrivers"
|
90
90
|
Requires-Dist: duckdb~=1.1; extra == "sqldrivers"
|
91
91
|
Provides-Extra: testing
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=CxGnj-UiRPlZgmgWoovDWrOnqpSEmBy_kqA7cdfSA3w,1431
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=d7BMVkW2N3aBZ6Tdsm0Fd2b0CcKLA6TPpoZPJHMAGDM,3379
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
|
5
5
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
@@ -178,7 +178,7 @@ omlish/docker/helpers.py,sha256=9uyHpPVbsB2jqTzvU7jiLzTkDN1omqofse1w4B4GH5E,612
|
|
178
178
|
omlish/docker/hub.py,sha256=7LIuJGdA-N1Y1dmo50ynKM1KUTcnQM_5XbtPbdT_QLU,3940
|
179
179
|
omlish/docker/manifests.py,sha256=LR4FpOGNUT3bZQ-gTjB6r_-1C3YiG30QvevZjrsVUQM,7068
|
180
180
|
omlish/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
181
|
-
omlish/formats/dotenv.py,sha256=
|
181
|
+
omlish/formats/dotenv.py,sha256=qoDG4Ayu7B-8LjBBhcmNiLZW0_9LgCi3Ri2aPo9DEQ8,19314
|
182
182
|
omlish/formats/props.py,sha256=cek3JLFLIrpE76gvs8rs_B8yF4SpY8ooDH8apWsquwE,18953
|
183
183
|
omlish/formats/xml.py,sha256=ggiOwSERt4d9XmZwLZiDIh5qnFJS4jdmow9m9_9USps,1491
|
184
184
|
omlish/formats/yaml.py,sha256=wTW8ECG9jyA7qIFUqKZUro4KAKpN4IvcW_qhlrKveXM,6836
|
@@ -232,7 +232,7 @@ omlish/http/multipart.py,sha256=R9ycpHsXRcmh0uoc43aYb7BdWL-8kSQHe7J-M81aQZM,2240
|
|
232
232
|
omlish/http/sessions.py,sha256=VZ_WS5uiQG5y7i3u8oKuQMqf8dPKUOjFm_qk_0OvI8c,4793
|
233
233
|
omlish/http/sse.py,sha256=MDs9RvxQXoQliImcc6qK1ERajEYM7Q1l8xmr-9ceNBc,2315
|
234
234
|
omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
|
235
|
-
omlish/inject/__init__.py,sha256=
|
235
|
+
omlish/inject/__init__.py,sha256=n0RC9UDGsBQQ39cST39-XJqJPq2M0tnnh9yJubW9azo,1891
|
236
236
|
omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
|
237
237
|
omlish/inject/bindings.py,sha256=pLXn2U3kvmAS-68IOG-tr77DbiI-wp9hGyy4lhG6_H8,525
|
238
238
|
omlish/inject/eagers.py,sha256=5AkGYuwijG0ihsH9NSaZotggalJ5_xWXhHE9mkn6IBA,329
|
@@ -248,20 +248,20 @@ omlish/inject/origins.py,sha256=OVQkiuRxx6ZtE8ZliufdndtFexcfpj-wZSDkUeGUCYM,534
|
|
248
248
|
omlish/inject/overrides.py,sha256=hrm243slCw_DDRbn3dK5QK1jfHezVokG-WYO2JaQOV8,535
|
249
249
|
omlish/inject/privates.py,sha256=hZOa_keY3KlXAzyiZ-sfN697UKXpkfXXNUIEmGT5TAA,641
|
250
250
|
omlish/inject/providers.py,sha256=Z6UzNCwRhKHHR0L5CyBMo4F-1M_xElLkPA6EKQWcqlw,754
|
251
|
-
omlish/inject/scopes.py,sha256=
|
251
|
+
omlish/inject/scopes.py,sha256=bxbpEPqRs9N61GDKD-4ZWXkB6xiLDrILLjcE2IvZEtM,1989
|
252
252
|
omlish/inject/types.py,sha256=11WVEPkZ-_8cv1BeTDRU-soIYxB_6x7dyWtsa2Iej9U,251
|
253
253
|
omlish/inject/utils.py,sha256=_UOZqA8IcLWPqf4Mcg9iIusQ5yxP_6Txg6PWtUYl23o,408
|
254
254
|
omlish/inject/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
255
255
|
omlish/inject/impl/bindings.py,sha256=8H586RCgmvwq53XBL9WMbb-1-Tdw_hh9zxIDCwcHA1c,414
|
256
256
|
omlish/inject/impl/elements.py,sha256=bJBbHce_eZyIua2wbcejMwd9Uv-QeYcQ-c5N1qOXSmU,5950
|
257
|
-
omlish/inject/impl/injector.py,sha256=
|
257
|
+
omlish/inject/impl/injector.py,sha256=8Pm2TbI-ySfibpHG2kFeF1nBHkh5v-NEynykljkQ8ew,7560
|
258
258
|
omlish/inject/impl/inspect.py,sha256=J0d2HJ-Z2-cHD4mJ0Kf5oJCOa2bMVG68Oh0Mhe3Cay4,3120
|
259
259
|
omlish/inject/impl/multis.py,sha256=rRIWNCiTGaSWQUz_jxEy8LUmzdJDAlG94sLHYDS-ncg,2048
|
260
260
|
omlish/inject/impl/origins.py,sha256=-cdcwz3BWb5LuC9Yn5ynYOwyPsKH06-kCc-3U0PxZ5w,1640
|
261
261
|
omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCFQ,2613
|
262
262
|
omlish/inject/impl/providers.py,sha256=QnwhsujJFIHC0JTgd2Wlo1kP53i3CWTrj1nKU2DNxwg,2375
|
263
263
|
omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1636
|
264
|
-
omlish/inject/impl/scopes.py,sha256=
|
264
|
+
omlish/inject/impl/scopes.py,sha256=hKnzNieB-fJSFEXDP_QG1mCfIKoVFIfFlf9LiIt5tk4,5920
|
265
265
|
omlish/io/__init__.py,sha256=aaIEsXTSfytW-oEkUWczdUJ_ifFY7ihIpyidIbfjkwY,56
|
266
266
|
omlish/io/_abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
|
267
267
|
omlish/io/pyio.py,sha256=YB3g6yg64MzcFwbzKBo4adnbsbZ3FZMlOZfjNtWmYoc,95316
|
@@ -488,9 +488,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
|
|
488
488
|
omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
|
489
489
|
omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
|
490
490
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
491
|
-
omlish-0.0.0.
|
492
|
-
omlish-0.0.0.
|
493
|
-
omlish-0.0.0.
|
494
|
-
omlish-0.0.0.
|
495
|
-
omlish-0.0.0.
|
496
|
-
omlish-0.0.0.
|
491
|
+
omlish-0.0.0.dev131.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
492
|
+
omlish-0.0.0.dev131.dist-info/METADATA,sha256=V7J6J12IhBiLRFrCaAkvQgvB1VPvsZSzu32VsYQGSdE,4173
|
493
|
+
omlish-0.0.0.dev131.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
494
|
+
omlish-0.0.0.dev131.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
495
|
+
omlish-0.0.0.dev131.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
496
|
+
omlish-0.0.0.dev131.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|