Skip to content

Commit 2482ab4

Browse files
committed
Update file updating logic
1 parent 694afb1 commit 2482ab4

File tree

3 files changed

+201
-172
lines changed

3 files changed

+201
-172
lines changed

src/foamlib/_files/_serialization.py

Lines changed: 81 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from ._typing import (
99
Data,
1010
DataLike,
11+
File,
12+
FileLike,
1113
KeywordEntryLike,
1214
StandaloneData,
1315
StandaloneDataLike,
@@ -20,7 +22,21 @@
2022

2123
@overload
2224
def normalize(
23-
data: DataLike, *, keywords: tuple[str, ...] | None = None, bool_ok: bool = True
25+
data: FileLike,
26+
*,
27+
keywords: tuple[()],
28+
bool_ok: bool = True,
29+
tuple_is_keyword_entry: bool = False,
30+
) -> File: ...
31+
32+
33+
@overload
34+
def normalize(
35+
data: DataLike,
36+
*,
37+
keywords: tuple[str, ...] | None = None,
38+
bool_ok: bool = True,
39+
tuple_is_keyword_entry: bool = False,
2440
) -> Data: ...
2541

2642

@@ -30,28 +46,50 @@ def normalize(
3046
*,
3147
keywords: tuple[str, ...] | None = None,
3248
bool_ok: bool = True,
49+
tuple_is_keyword_entry: bool = False,
3350
) -> StandaloneData: ...
3451

3552

3653
@overload
3754
def normalize(
38-
data: SubDictLike, *, keywords: tuple[str, ...] | None = None, bool_ok: bool = True
55+
data: SubDictLike,
56+
*,
57+
keywords: tuple[str, ...] | None = None,
58+
bool_ok: bool = True,
59+
tuple_is_keyword_entry: bool = False,
3960
) -> SubDict: ...
4061

4162

63+
@overload
4264
def normalize(
43-
data: DataLike | StandaloneDataLike | SubDictLike,
65+
data: None,
66+
*,
67+
keywords: tuple[()],
68+
bool_ok: bool = True,
69+
tuple_is_keyword_entry: bool = False,
70+
) -> None: ...
71+
72+
73+
def normalize(
74+
data: FileLike | DataLike | StandaloneDataLike | SubDictLike | None,
4475
*,
4576
keywords: tuple[str, ...] | None = None,
4677
bool_ok: bool = True,
47-
) -> Data | StandaloneData | SubDict:
78+
tuple_is_keyword_entry: bool = False,
79+
) -> File | Data | StandaloneData | SubDict | None:
4880
if isinstance(data, Mapping):
49-
items = ((normalize(k, bool_ok=False), normalize(v)) for k, v in data.items()) # ty: ignore[no-matching-overload]
81+
items = (
82+
(
83+
normalize(k, keywords=keywords, bool_ok=False), # ty: ignore[no-matching-overload]
84+
normalize(v, keywords=(*keywords, k) if keywords is not None else None), # ty: ignore[no-matching-overload]
85+
)
86+
for k, v in data.items()
87+
)
5088
if keywords is None:
5189
return as_dict_check_unique(items)
5290
ret1: SubDict = {}
5391
for k, v in items:
54-
if k.startswith("#"):
92+
if k is not None and k.startswith("#"):
5593
if isinstance(v, Mapping):
5694
msg = f"Directive {k} cannot have a dictionary as value"
5795
raise ValueError(msg)
@@ -103,9 +141,11 @@ def normalize(
103141
if isinstance(data, int):
104142
return float(data)
105143

106-
return normalize(data)
144+
return normalize(data) # ty: ignore[no-matching-overload]
107145

108146
if isinstance(data, np.ndarray):
147+
if keywords == (None,):
148+
return data # ty: ignore[invalid-return-type]
109149
ret2 = data.tolist()
110150
assert isinstance(ret2, (int, float, list))
111151
return ret2
@@ -120,14 +160,18 @@ def normalize(
120160
):
121161
return DimensionSet(*data) # ty: ignore[invalid-argument-type]
122162

123-
if keywords is None and isinstance(data, tuple) and len(data) == 2:
124-
k2, v2 = data
125-
if isinstance(k2, Mapping):
126-
msg = "Keyword in keyword entry cannot be a dictionary"
127-
raise ValueError(msg)
128-
k2 = normalize(k2, bool_ok=False) # ty: ignore[no-matching-overload]
129-
v2 = normalize(v2) # ty: ignore[no-matching-overload]
130-
return (k2, v2)
163+
if isinstance(data, tuple) and not isinstance(data, DimensionSet):
164+
if tuple_is_keyword_entry:
165+
k2, v2 = data
166+
if isinstance(k2, Mapping):
167+
msg = "Keyword in keyword entry cannot be a dictionary"
168+
raise ValueError(msg)
169+
k2 = normalize(k2, keywords=keywords, bool_ok=False) # ty: ignore[no-matching-overload]
170+
v2 = normalize(
171+
v2, keywords=(*keywords, k2) if keywords is not None else keywords
172+
) # ty: ignore[no-matching-overload]
173+
return (k2, v2)
174+
return tuple(normalize(d, keywords=keywords) for d in data) # ty: ignore[no-matching-overload]
131175

132176
if (
133177
is_sequence(data)
@@ -136,9 +180,6 @@ def normalize(
136180
):
137181
return [normalize(d) for d in data] # ty: ignore[not-iterable]
138182

139-
if isinstance(data, tuple) and not isinstance(data, DimensionSet):
140-
return tuple(normalize(d, keywords=keywords) for d in data) # ty: ignore[no-matching-overload]
141-
142183
if isinstance(data, str):
143184
with contextlib.suppress(ValueError, KeyError):
144185
s = Parsed(data)[()]
@@ -153,34 +194,43 @@ def normalize(
153194
):
154195
return data
155196

197+
if data is None:
198+
if keywords != ():
199+
msg = "Unexpected None key in subdictionary"
200+
raise TypeError(msg)
201+
return data
202+
156203
msg = f"Unsupported data type: {type(data)}"
157204
raise TypeError(msg)
158205

159206

160207
def dumps(
161-
data: DataLike | StandaloneDataLike | KeywordEntryLike | SubDictLike,
208+
data: FileLike | DataLike | StandaloneDataLike | KeywordEntryLike | SubDictLike,
162209
*,
163210
keywords: tuple[str, ...] | None = None,
164211
header: SubDictLike | None = None,
165212
tuple_is_keyword_entry: bool = False,
166213
) -> bytes:
167-
data = normalize(data, keywords=keywords)
214+
data = normalize( # type: ignore[no-matching-overload]
215+
data, keywords=keywords, tuple_is_keyword_entry=tuple_is_keyword_entry
216+
)
168217

169218
if isinstance(data, Mapping):
170219
return (
171-
b"{"
220+
(b"{" if keywords != () else b"")
172221
+ b" ".join(
173222
dumps(
174223
(k, v),
175224
keywords=keywords,
225+
header=header,
176226
tuple_is_keyword_entry=True,
177227
)
178228
for k, v in data.items()
179229
)
180-
+ b"}"
230+
+ (b"}" if keywords != () else b"")
181231
)
182232

183-
if keywords == () and isinstance(data, np.ndarray):
233+
if keywords == (None,) and isinstance(data, np.ndarray):
184234
if (header.get("format", "") if header else "") == "binary":
185235
return dumps(len(data)) + b"(" + data.tobytes() + b")"
186236

@@ -254,18 +304,19 @@ def dumps(
254304
if tuple_is_keyword_entry:
255305
k, v = data
256306
ret = b"\n" if isinstance(k, str) and k[0] == "#" else b""
257-
ret += dumps(k)
307+
if k is not None:
308+
ret += dumps(k, keywords=keywords)
258309
val = dumps(
259310
v,
260-
keywords=(*keywords, k)
261-
if keywords is not None and isinstance(k, str)
262-
else None,
311+
keywords=(*keywords, k) if keywords is not None else None,
312+
header=header,
263313
)
264-
if val:
265-
ret += b" " + val
314+
if k is not None and val:
315+
ret += b" "
316+
ret += val
266317
if isinstance(k, str) and k[0] == "#":
267318
ret += b"\n"
268-
elif not isinstance(v, Mapping):
319+
elif k is not None and not isinstance(v, Mapping):
269320
ret += b";"
270321
return ret
271322

0 commit comments

Comments
 (0)