omlish 0.0.0.dev213__py3-none-any.whl → 0.0.0.dev215__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/.manifests.json +4 -4
- omlish/__about__.py +3 -5
- omlish/antlr/__init__.py +3 -0
- omlish/antlr/parsing.py +34 -2
- omlish/asyncs/asyncio/asyncio.py +34 -0
- omlish/asyncs/ioproxy/all.py +32 -0
- omlish/asyncs/ioproxy/io.py +13 -13
- omlish/asyncs/ioproxy/proxier.py +30 -30
- omlish/asyncs/ioproxy/typing.py +3 -3
- omlish/formats/json/stream/lex.py +1 -0
- omlish/formats/json5/Json5.g4 +172 -0
- omlish/formats/json5/__init__.py +8 -0
- omlish/formats/json5/_antlr/Json5Lexer.py +353 -0
- omlish/formats/json5/_antlr/Json5Listener.py +78 -0
- omlish/formats/json5/_antlr/Json5Parser.py +616 -0
- omlish/formats/json5/_antlr/Json5Visitor.py +51 -0
- omlish/formats/json5/_antlr/__init__.py +0 -0
- omlish/formats/{json5.py → json5/codec.py} +6 -11
- omlish/formats/json5/errors.py +2 -0
- omlish/formats/json5/literals.py +130 -0
- omlish/formats/json5/parsing.py +79 -0
- omlish/lang/__init__.py +2 -0
- omlish/lang/imports.py +4 -0
- omlish/lang/strings.py +33 -1
- omlish/lite/marshal.py +79 -12
- omlish/os/files.py +17 -30
- omlish/os/temp.py +50 -0
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/METADATA +5 -7
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/RECORD +33 -21
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev213.dist-info → omlish-0.0.0.dev215.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
"""
|
2
|
+
https://spec.json5.org/
|
3
|
+
"""
|
4
|
+
import io
|
5
|
+
import json
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from ... import lang
|
9
|
+
from .errors import Json5Error
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
LITERAL_VALUES: ta.Mapping[str, ta.Any] = {
|
16
|
+
'true': True,
|
17
|
+
'false': False,
|
18
|
+
'null': None,
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
|
24
|
+
|
25
|
+
STRING_LITERAL_ESCAPES: ta.Mapping[str, str] = {
|
26
|
+
'b': '\\u0008',
|
27
|
+
'f': '\\u000C',
|
28
|
+
'n': '\\u000A',
|
29
|
+
'r': '\\u000D',
|
30
|
+
't': '\\u0009',
|
31
|
+
'v': '\\u000B',
|
32
|
+
'0': '\\u0000',
|
33
|
+
'u': '\\u',
|
34
|
+
'"': '\\"',
|
35
|
+
"'": "'",
|
36
|
+
'\\': '\\\\',
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
def _check_state(b: bool, fmt: str = 'Json5 error', *args: ta.Any) -> None:
|
41
|
+
if not b:
|
42
|
+
raise Json5Error(fmt % args)
|
43
|
+
|
44
|
+
|
45
|
+
def translate_string_literal(s: str) -> str:
|
46
|
+
_check_state(len(s) > 1)
|
47
|
+
q = s[0]
|
48
|
+
_check_state(q in '\'"')
|
49
|
+
_check_state(s[-1] == q)
|
50
|
+
|
51
|
+
c = 1
|
52
|
+
e = len(s) - 1
|
53
|
+
|
54
|
+
b = io.StringIO()
|
55
|
+
b.write('"')
|
56
|
+
|
57
|
+
ds = '\\\'"'
|
58
|
+
while True:
|
59
|
+
n = lang.find_any(s, ds, c, e)
|
60
|
+
if n < 0:
|
61
|
+
b.write(s[c:e])
|
62
|
+
break
|
63
|
+
|
64
|
+
_check_state(n < e)
|
65
|
+
b.write(s[c:n])
|
66
|
+
|
67
|
+
x = s[n]
|
68
|
+
if x == '\\':
|
69
|
+
_check_state(n < (e - 1))
|
70
|
+
|
71
|
+
y = s[n + 1]
|
72
|
+
if y in '\n\u2028\u2029':
|
73
|
+
c = n + 2
|
74
|
+
|
75
|
+
elif y == '\r':
|
76
|
+
c = n + 2
|
77
|
+
if c < e and s[c] == '\n':
|
78
|
+
c += 1
|
79
|
+
|
80
|
+
elif y in 'x':
|
81
|
+
_check_state(n < (e - 3))
|
82
|
+
u = int(s[n + 2:n + 4], 16)
|
83
|
+
b.write(f'\\u00{u:02x}')
|
84
|
+
c = n + 4
|
85
|
+
|
86
|
+
elif (g := STRING_LITERAL_ESCAPES.get(y)) is not None:
|
87
|
+
b.write(g)
|
88
|
+
c = n + 2
|
89
|
+
|
90
|
+
elif not ('0' <= y <= '9'):
|
91
|
+
b.write(y)
|
92
|
+
c = n + 2
|
93
|
+
|
94
|
+
else:
|
95
|
+
raise Json5Error(f'Invalid string literal escape: {x}{y}')
|
96
|
+
|
97
|
+
elif x in '\\\'"':
|
98
|
+
_check_state(x != q)
|
99
|
+
if x == '"':
|
100
|
+
b.write('\\"')
|
101
|
+
else:
|
102
|
+
b.write(x)
|
103
|
+
c = n + 1
|
104
|
+
|
105
|
+
else:
|
106
|
+
raise RuntimeError
|
107
|
+
|
108
|
+
b.write('"')
|
109
|
+
return b.getvalue()
|
110
|
+
|
111
|
+
|
112
|
+
def parse_string_literal(s: str) -> str:
|
113
|
+
j = translate_string_literal(s)
|
114
|
+
|
115
|
+
try:
|
116
|
+
return json.loads(j)
|
117
|
+
except json.JSONDecodeError as e:
|
118
|
+
raise Json5Error from e
|
119
|
+
|
120
|
+
|
121
|
+
##
|
122
|
+
|
123
|
+
|
124
|
+
def parse_number_literal(s: str) -> int | float:
|
125
|
+
s = s.lower()
|
126
|
+
|
127
|
+
if 'x' in s:
|
128
|
+
return int(s, 16)
|
129
|
+
else:
|
130
|
+
return float(s)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# ruff: noqa: N802
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish import antlr
|
5
|
+
|
6
|
+
from ._antlr.Json5Lexer import Json5Lexer # type: ignore
|
7
|
+
from ._antlr.Json5Parser import Json5Parser # type: ignore
|
8
|
+
from ._antlr.Json5Visitor import Json5Visitor # type: ignore
|
9
|
+
from .errors import Json5Error
|
10
|
+
from .literals import LITERAL_VALUES
|
11
|
+
from .literals import parse_number_literal
|
12
|
+
from .literals import parse_string_literal
|
13
|
+
|
14
|
+
|
15
|
+
class Json5ParseVisitor(antlr.parsing.StandardParseTreeVisitor, Json5Visitor):
|
16
|
+
def visitArr(self, ctx: Json5Parser.ArrContext):
|
17
|
+
return [self.visit(e) for e in ctx.value()]
|
18
|
+
|
19
|
+
def visitKey(self, ctx: Json5Parser.KeyContext):
|
20
|
+
if (s := ctx.STRING()) is not None:
|
21
|
+
return parse_string_literal(s.getText())
|
22
|
+
|
23
|
+
elif (i := ctx.IDENTIFIER()) is not None:
|
24
|
+
return parse_string_literal(''.join(['"', i.getText(), '"']))
|
25
|
+
|
26
|
+
elif (l := ctx.LITERAL()) is not None:
|
27
|
+
return LITERAL_VALUES[l.getText()]
|
28
|
+
|
29
|
+
elif (n := ctx.NUMERIC_LITERAL()) is not None:
|
30
|
+
return n.getText()
|
31
|
+
|
32
|
+
else:
|
33
|
+
raise RuntimeError(ctx)
|
34
|
+
|
35
|
+
def visitNumber(self, ctx: Json5Parser.NumberContext):
|
36
|
+
return parse_number_literal(ctx.getText())
|
37
|
+
|
38
|
+
def visitObj(self, ctx: Json5Parser.ObjContext):
|
39
|
+
dct: dict[ta.Any, ta.Any] = {}
|
40
|
+
for pair in ctx.pair():
|
41
|
+
key, value = self.visit(pair)
|
42
|
+
dct[key] = value
|
43
|
+
return dct
|
44
|
+
|
45
|
+
def visitPair(self, ctx: Json5Parser.PairContext):
|
46
|
+
key = self.visit(ctx.key())
|
47
|
+
value = self.visit(ctx.value())
|
48
|
+
return (key, value)
|
49
|
+
|
50
|
+
def visitValue(self, ctx: Json5Parser.ValueContext):
|
51
|
+
if (s := ctx.STRING()) is not None:
|
52
|
+
return parse_string_literal(s.getText())
|
53
|
+
|
54
|
+
elif (n := ctx.LITERAL()) is not None:
|
55
|
+
return LITERAL_VALUES[n.getText()]
|
56
|
+
|
57
|
+
else:
|
58
|
+
return super().visitChildren(ctx)
|
59
|
+
|
60
|
+
|
61
|
+
def parse(buf: str) -> ta.Any:
|
62
|
+
try:
|
63
|
+
parser = antlr.parsing.make_parser(
|
64
|
+
buf,
|
65
|
+
Json5Lexer,
|
66
|
+
Json5Parser,
|
67
|
+
silent_errors=True,
|
68
|
+
)
|
69
|
+
|
70
|
+
root = parser.json5()
|
71
|
+
|
72
|
+
except antlr.errors.ParseError as e:
|
73
|
+
raise Json5Error from e
|
74
|
+
|
75
|
+
if antlr.parsing.is_eof_context(root):
|
76
|
+
raise Json5Error('Empty input')
|
77
|
+
|
78
|
+
visitor = Json5ParseVisitor()
|
79
|
+
return visitor.visit(root)
|
omlish/lang/__init__.py
CHANGED
@@ -200,6 +200,7 @@ from .strings import ( # noqa
|
|
200
200
|
BOOL_TRUE_STRINGS,
|
201
201
|
STRING_BOOL_VALUES,
|
202
202
|
camel_case,
|
203
|
+
find_any,
|
203
204
|
indent_lines,
|
204
205
|
is_dunder,
|
205
206
|
is_ident,
|
@@ -209,6 +210,7 @@ from .strings import ( # noqa
|
|
209
210
|
prefix_delimited,
|
210
211
|
prefix_lines,
|
211
212
|
replace_many,
|
213
|
+
rfind_any,
|
212
214
|
snake_case,
|
213
215
|
strip_prefix,
|
214
216
|
strip_suffix,
|
omlish/lang/imports.py
CHANGED
omlish/lang/strings.py
CHANGED
@@ -42,7 +42,8 @@ def strip_suffix(s: StrOrBytesT, sfx: StrOrBytesT) -> StrOrBytesT:
|
|
42
42
|
def replace_many(
|
43
43
|
s: StrOrBytesT,
|
44
44
|
old: ta.Iterable[StrOrBytesT],
|
45
|
-
new: StrOrBytesT,
|
45
|
+
new: StrOrBytesT,
|
46
|
+
count_each: int = -1,
|
46
47
|
) -> StrOrBytesT:
|
47
48
|
for o in old:
|
48
49
|
s = s.replace(o, new, count_each) # type: ignore
|
@@ -52,6 +53,37 @@ def replace_many(
|
|
52
53
|
##
|
53
54
|
|
54
55
|
|
56
|
+
def find_any(
|
57
|
+
string: StrOrBytesT,
|
58
|
+
subs: ta.Iterable[StrOrBytesT],
|
59
|
+
start: int | None = None,
|
60
|
+
end: int | None = None,
|
61
|
+
) -> int:
|
62
|
+
r = -1
|
63
|
+
for sub in subs:
|
64
|
+
if (p := string.find(sub, start, end)) >= 0: # type: ignore
|
65
|
+
if r < 0 or p < r:
|
66
|
+
r = p
|
67
|
+
return r
|
68
|
+
|
69
|
+
|
70
|
+
def rfind_any(
|
71
|
+
string: StrOrBytesT,
|
72
|
+
subs: ta.Iterable[StrOrBytesT],
|
73
|
+
start: int | None = None,
|
74
|
+
end: int | None = None,
|
75
|
+
) -> int:
|
76
|
+
r = -1
|
77
|
+
for sub in subs:
|
78
|
+
if (p := string.rfind(sub, start, end)) >= 0: # type: ignore
|
79
|
+
if r < 0 or p > r:
|
80
|
+
r = p
|
81
|
+
return r
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
|
86
|
+
|
55
87
|
def camel_case(name: str, *, lower: bool = False) -> str:
|
56
88
|
if not name:
|
57
89
|
return ''
|
omlish/lite/marshal.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
TODO:
|
3
3
|
- pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
|
4
4
|
- literals
|
5
|
+
- Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
|
5
6
|
"""
|
6
7
|
# ruff: noqa: UP006 UP007
|
7
8
|
import abc
|
@@ -183,21 +184,55 @@ class IterableObjMarshaler(ObjMarshaler):
|
|
183
184
|
@dc.dataclass(frozen=True)
|
184
185
|
class FieldsObjMarshaler(ObjMarshaler):
|
185
186
|
ty: type
|
186
|
-
|
187
|
+
|
188
|
+
@dc.dataclass(frozen=True)
|
189
|
+
class Field:
|
190
|
+
att: str
|
191
|
+
key: str
|
192
|
+
m: ObjMarshaler
|
193
|
+
|
194
|
+
omit_if_none: bool = False
|
195
|
+
|
196
|
+
fs: ta.Sequence[Field]
|
197
|
+
|
187
198
|
non_strict: bool = False
|
188
199
|
|
200
|
+
#
|
201
|
+
|
202
|
+
_fs_by_att: ta.ClassVar[ta.Mapping[str, Field]]
|
203
|
+
_fs_by_key: ta.ClassVar[ta.Mapping[str, Field]]
|
204
|
+
|
205
|
+
def __post_init__(self) -> None:
|
206
|
+
fs_by_att: dict = {}
|
207
|
+
fs_by_key: dict = {}
|
208
|
+
for f in self.fs:
|
209
|
+
check.not_in(check.non_empty_str(f.att), fs_by_att)
|
210
|
+
check.not_in(check.non_empty_str(f.key), fs_by_key)
|
211
|
+
fs_by_att[f.att] = f
|
212
|
+
fs_by_key[f.key] = f
|
213
|
+
self.__dict__['_fs_by_att'] = fs_by_att
|
214
|
+
self.__dict__['_fs_by_key'] = fs_by_key
|
215
|
+
|
216
|
+
#
|
217
|
+
|
189
218
|
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
219
|
+
d = {}
|
220
|
+
for f in self.fs:
|
221
|
+
mv = f.m.marshal(getattr(o, f.att), ctx)
|
222
|
+
if mv is None and f.omit_if_none:
|
223
|
+
continue
|
224
|
+
d[f.key] = mv
|
225
|
+
return d
|
194
226
|
|
195
227
|
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
228
|
+
kw = {}
|
229
|
+
for k, v in o.items():
|
230
|
+
if (f := self._fs_by_key.get(k)) is None:
|
231
|
+
if not (self.non_strict or ctx.options.non_strict_fields):
|
232
|
+
raise KeyError(k)
|
233
|
+
continue
|
234
|
+
kw[f.att] = f.m.unmarshal(v, ctx)
|
235
|
+
return self.ty(**kw)
|
201
236
|
|
202
237
|
|
203
238
|
@dc.dataclass(frozen=True)
|
@@ -332,6 +367,22 @@ def register_single_field_type_obj_marshaler(fld, ty=None):
|
|
332
367
|
##
|
333
368
|
|
334
369
|
|
370
|
+
class ObjMarshalerFieldMetadata:
|
371
|
+
def __new__(cls, *args, **kwargs): # noqa
|
372
|
+
raise TypeError
|
373
|
+
|
374
|
+
|
375
|
+
class OBJ_MARSHALER_FIELD_KEY(ObjMarshalerFieldMetadata): # noqa
|
376
|
+
pass
|
377
|
+
|
378
|
+
|
379
|
+
class OBJ_MARSHALER_OMIT_IF_NONE(ObjMarshalerFieldMetadata): # noqa
|
380
|
+
pass
|
381
|
+
|
382
|
+
|
383
|
+
##
|
384
|
+
|
385
|
+
|
335
386
|
class ObjMarshalerManager:
|
336
387
|
def __init__(
|
337
388
|
self,
|
@@ -391,14 +442,30 @@ class ObjMarshalerManager:
|
|
391
442
|
if dc.is_dataclass(ty):
|
392
443
|
return FieldsObjMarshaler(
|
393
444
|
ty,
|
394
|
-
|
445
|
+
[
|
446
|
+
FieldsObjMarshaler.Field(
|
447
|
+
att=f.name,
|
448
|
+
key=check.non_empty_str(fk),
|
449
|
+
m=rec(f.type),
|
450
|
+
omit_if_none=check.isinstance(f.metadata.get(OBJ_MARSHALER_OMIT_IF_NONE, False), bool),
|
451
|
+
)
|
452
|
+
for f in dc.fields(ty)
|
453
|
+
if (fk := f.metadata.get(OBJ_MARSHALER_FIELD_KEY, f.name)) is not None
|
454
|
+
],
|
395
455
|
non_strict=non_strict_fields,
|
396
456
|
)
|
397
457
|
|
398
458
|
if issubclass(ty, tuple) and hasattr(ty, '_fields'):
|
399
459
|
return FieldsObjMarshaler(
|
400
460
|
ty,
|
401
|
-
|
461
|
+
[
|
462
|
+
FieldsObjMarshaler.Field(
|
463
|
+
att=p.name,
|
464
|
+
key=p.name,
|
465
|
+
m=rec(p.annotation),
|
466
|
+
)
|
467
|
+
for p in inspect.signature(ty).parameters.values()
|
468
|
+
],
|
402
469
|
non_strict=non_strict_fields,
|
403
470
|
)
|
404
471
|
|
omlish/os/files.py
CHANGED
@@ -1,38 +1,10 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
1
3
|
import contextlib
|
2
4
|
import os
|
3
|
-
import shutil
|
4
|
-
import tempfile
|
5
5
|
import typing as ta
|
6
6
|
|
7
7
|
|
8
|
-
@contextlib.contextmanager
|
9
|
-
def tmp_dir(
|
10
|
-
root_dir: str | None = None,
|
11
|
-
cleanup: bool = True,
|
12
|
-
**kwargs: ta.Any,
|
13
|
-
) -> ta.Iterator[str]:
|
14
|
-
path = tempfile.mkdtemp(dir=root_dir, **kwargs)
|
15
|
-
try:
|
16
|
-
yield path
|
17
|
-
finally:
|
18
|
-
if cleanup:
|
19
|
-
shutil.rmtree(path, ignore_errors=True)
|
20
|
-
|
21
|
-
|
22
|
-
@contextlib.contextmanager
|
23
|
-
def tmp_file(
|
24
|
-
root_dir: str | None = None,
|
25
|
-
cleanup: bool = True,
|
26
|
-
**kwargs: ta.Any,
|
27
|
-
) -> ta.Iterator[tempfile._TemporaryFileWrapper]: # noqa
|
28
|
-
with tempfile.NamedTemporaryFile(dir=root_dir, delete=False, **kwargs) as f:
|
29
|
-
try:
|
30
|
-
yield f
|
31
|
-
finally:
|
32
|
-
if cleanup:
|
33
|
-
shutil.rmtree(f.name, ignore_errors=True)
|
34
|
-
|
35
|
-
|
36
8
|
def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
|
37
9
|
if exist_ok:
|
38
10
|
# First try to bump modification time
|
@@ -48,3 +20,18 @@ def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
|
|
48
20
|
flags |= os.O_EXCL
|
49
21
|
fd = os.open(self, flags, mode)
|
50
22
|
os.close(fd)
|
23
|
+
|
24
|
+
|
25
|
+
def unlink_if_exists(path: str) -> None:
|
26
|
+
try:
|
27
|
+
os.unlink(path)
|
28
|
+
except FileNotFoundError:
|
29
|
+
pass
|
30
|
+
|
31
|
+
|
32
|
+
@contextlib.contextmanager
|
33
|
+
def unlinking_if_exists(path: str) -> ta.Iterator[None]:
|
34
|
+
try:
|
35
|
+
yield
|
36
|
+
finally:
|
37
|
+
unlink_if_exists(path)
|
omlish/os/temp.py
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import contextlib
|
4
|
+
import os
|
5
|
+
import shutil
|
6
|
+
import tempfile
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from .files import unlink_if_exists
|
10
|
+
|
11
|
+
|
12
|
+
def make_temp_file(**kwargs: ta.Any) -> str:
|
13
|
+
file_fd, file = tempfile.mkstemp(**kwargs)
|
14
|
+
os.close(file_fd)
|
15
|
+
return file
|
16
|
+
|
17
|
+
|
18
|
+
@contextlib.contextmanager
|
19
|
+
def temp_file_context(**kwargs: ta.Any) -> ta.Iterator[str]:
|
20
|
+
path = make_temp_file(**kwargs)
|
21
|
+
try:
|
22
|
+
yield path
|
23
|
+
finally:
|
24
|
+
unlink_if_exists(path)
|
25
|
+
|
26
|
+
|
27
|
+
@contextlib.contextmanager
|
28
|
+
def temp_dir_context(
|
29
|
+
root_dir: ta.Optional[str] = None,
|
30
|
+
**kwargs: ta.Any,
|
31
|
+
) -> ta.Iterator[str]:
|
32
|
+
path = tempfile.mkdtemp(dir=root_dir, **kwargs)
|
33
|
+
try:
|
34
|
+
yield path
|
35
|
+
finally:
|
36
|
+
shutil.rmtree(path, ignore_errors=True)
|
37
|
+
|
38
|
+
|
39
|
+
@contextlib.contextmanager
|
40
|
+
def temp_named_file_context(
|
41
|
+
root_dir: ta.Optional[str] = None,
|
42
|
+
cleanup: bool = True,
|
43
|
+
**kwargs: ta.Any,
|
44
|
+
) -> ta.Iterator[tempfile._TemporaryFileWrapper]: # noqa
|
45
|
+
with tempfile.NamedTemporaryFile(dir=root_dir, delete=False, **kwargs) as f:
|
46
|
+
try:
|
47
|
+
yield f
|
48
|
+
finally:
|
49
|
+
if cleanup:
|
50
|
+
shutil.rmtree(f.name, ignore_errors=True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: omlish
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev215
|
4
4
|
Summary: omlish
|
5
5
|
Author: wrmsr
|
6
6
|
License: BSD-3-Clause
|
@@ -23,11 +23,10 @@ Requires-Dist: python-snappy~=0.7; extra == "all"
|
|
23
23
|
Requires-Dist: zstandard~=0.23; extra == "all"
|
24
24
|
Requires-Dist: brotli~=1.1; extra == "all"
|
25
25
|
Requires-Dist: asttokens~=3.0; extra == "all"
|
26
|
-
Requires-Dist: executing~=2.
|
26
|
+
Requires-Dist: executing~=2.2; extra == "all"
|
27
27
|
Requires-Dist: psutil~=6.0; extra == "all"
|
28
28
|
Requires-Dist: orjson~=3.10; extra == "all"
|
29
29
|
Requires-Dist: ujson~=5.10; extra == "all"
|
30
|
-
Requires-Dist: json5~=0.9; extra == "all"
|
31
30
|
Requires-Dist: pyyaml~=6.0; extra == "all"
|
32
31
|
Requires-Dist: cbor2~=5.6; extra == "all"
|
33
32
|
Requires-Dist: cloudpickle~=3.1; extra == "all"
|
@@ -47,7 +46,7 @@ Requires-Dist: pytest~=8.0; extra == "all"
|
|
47
46
|
Requires-Dist: anyio~=4.8; extra == "all"
|
48
47
|
Requires-Dist: sniffio~=1.3; extra == "all"
|
49
48
|
Requires-Dist: asttokens~=3.0; extra == "all"
|
50
|
-
Requires-Dist: executing~=2.
|
49
|
+
Requires-Dist: executing~=2.2; extra == "all"
|
51
50
|
Requires-Dist: orjson~=3.10; extra == "all"
|
52
51
|
Requires-Dist: pyyaml~=6.0; extra == "all"
|
53
52
|
Requires-Dist: wrapt~=1.17; extra == "all"
|
@@ -64,12 +63,11 @@ Requires-Dist: zstandard~=0.23; extra == "compress"
|
|
64
63
|
Requires-Dist: brotli~=1.1; extra == "compress"
|
65
64
|
Provides-Extra: diag
|
66
65
|
Requires-Dist: asttokens~=3.0; extra == "diag"
|
67
|
-
Requires-Dist: executing~=2.
|
66
|
+
Requires-Dist: executing~=2.2; extra == "diag"
|
68
67
|
Requires-Dist: psutil~=6.0; extra == "diag"
|
69
68
|
Provides-Extra: formats
|
70
69
|
Requires-Dist: orjson~=3.10; extra == "formats"
|
71
70
|
Requires-Dist: ujson~=5.10; extra == "formats"
|
72
|
-
Requires-Dist: json5~=0.9; extra == "formats"
|
73
71
|
Requires-Dist: pyyaml~=6.0; extra == "formats"
|
74
72
|
Requires-Dist: cbor2~=5.6; extra == "formats"
|
75
73
|
Requires-Dist: cloudpickle~=3.1; extra == "formats"
|
@@ -96,7 +94,7 @@ Provides-Extra: plus
|
|
96
94
|
Requires-Dist: anyio~=4.8; extra == "plus"
|
97
95
|
Requires-Dist: sniffio~=1.3; extra == "plus"
|
98
96
|
Requires-Dist: asttokens~=3.0; extra == "plus"
|
99
|
-
Requires-Dist: executing~=2.
|
97
|
+
Requires-Dist: executing~=2.2; extra == "plus"
|
100
98
|
Requires-Dist: orjson~=3.10; extra == "plus"
|
101
99
|
Requires-Dist: pyyaml~=6.0; extra == "plus"
|
102
100
|
Requires-Dist: wrapt~=1.17; extra == "plus"
|