foamlib 0.2.9__py3-none-any.whl → 0.3.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.
- foamlib/__init__.py +5 -3
- foamlib/_cases.py +20 -32
- foamlib/_files/__init__.py +9 -0
- foamlib/{_dictionaries → _files}/_base.py +9 -11
- foamlib/_files/_fields.py +158 -0
- foamlib/_files/_files.py +203 -0
- foamlib/_files/_io.py +73 -0
- foamlib/_files/_parsing.py +195 -0
- foamlib/_files/_serialization.py +156 -0
- {foamlib-0.2.9.dist-info → foamlib-0.3.0.dist-info}/METADATA +1 -1
- foamlib-0.3.0.dist-info/RECORD +16 -0
- foamlib/_dictionaries/__init__.py +0 -8
- foamlib/_dictionaries/_files.py +0 -422
- foamlib/_dictionaries/_parsing.py +0 -198
- foamlib/_dictionaries/_serialization.py +0 -129
- foamlib-0.2.9.dist-info/RECORD +0 -14
- {foamlib-0.2.9.dist-info → foamlib-0.3.0.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.2.9.dist-info → foamlib-0.3.0.dist-info}/WHEEL +0 -0
- {foamlib-0.2.9.dist-info → foamlib-0.3.0.dist-info}/top_level.txt +0 -0
foamlib/_dictionaries/_files.py
DELETED
@@ -1,422 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
from copy import deepcopy
|
3
|
-
from pathlib import Path
|
4
|
-
from types import TracebackType
|
5
|
-
from typing import (
|
6
|
-
Any,
|
7
|
-
Optional,
|
8
|
-
Tuple,
|
9
|
-
Type,
|
10
|
-
Union,
|
11
|
-
cast,
|
12
|
-
)
|
13
|
-
|
14
|
-
if sys.version_info >= (3, 9):
|
15
|
-
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
16
|
-
else:
|
17
|
-
from typing import Iterator, Mapping, MutableMapping, Sequence
|
18
|
-
|
19
|
-
if sys.version_info >= (3, 11):
|
20
|
-
from typing import Self
|
21
|
-
else:
|
22
|
-
from typing_extensions import Self
|
23
|
-
|
24
|
-
try:
|
25
|
-
import numpy as np
|
26
|
-
from numpy.typing import NDArray
|
27
|
-
except ModuleNotFoundError:
|
28
|
-
pass
|
29
|
-
|
30
|
-
from ._base import FoamDictionaryBase
|
31
|
-
from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
|
32
|
-
from ._serialization import serialize_entry
|
33
|
-
|
34
|
-
|
35
|
-
class _FoamFileBase:
|
36
|
-
def __init__(self, path: Union[str, Path]) -> None:
|
37
|
-
self.path = Path(path).absolute()
|
38
|
-
if self.path.is_dir():
|
39
|
-
raise IsADirectoryError(self.path)
|
40
|
-
elif not self.path.is_file():
|
41
|
-
raise FileNotFoundError(self.path)
|
42
|
-
|
43
|
-
self.__contents: Optional[str] = None
|
44
|
-
self.__parsed: Optional[Parsed] = None
|
45
|
-
self.__defer_io = 0
|
46
|
-
self.__dirty = False
|
47
|
-
|
48
|
-
def __enter__(self) -> Self:
|
49
|
-
if self.__defer_io == 0:
|
50
|
-
self._read()
|
51
|
-
self.__defer_io += 1
|
52
|
-
return self
|
53
|
-
|
54
|
-
def __exit__(
|
55
|
-
self,
|
56
|
-
exc_type: Optional[Type[BaseException]],
|
57
|
-
exc_val: Optional[BaseException],
|
58
|
-
exc_tb: Optional[TracebackType],
|
59
|
-
) -> None:
|
60
|
-
self.__defer_io -= 1
|
61
|
-
if self.__defer_io == 0 and self.__dirty:
|
62
|
-
assert self.__contents is not None
|
63
|
-
self._write(self.__contents)
|
64
|
-
assert not self.__dirty
|
65
|
-
|
66
|
-
def _read(self) -> Tuple[str, Parsed]:
|
67
|
-
if not self.__defer_io:
|
68
|
-
contents = self.path.read_text()
|
69
|
-
if contents != self.__contents:
|
70
|
-
self.__contents = contents
|
71
|
-
self.__parsed = None
|
72
|
-
|
73
|
-
assert self.__contents is not None
|
74
|
-
|
75
|
-
if self.__parsed is None:
|
76
|
-
parsed = parse(self.__contents)
|
77
|
-
self.__parsed = parsed
|
78
|
-
else:
|
79
|
-
parsed = deepcopy(self.__parsed)
|
80
|
-
|
81
|
-
return self.__contents, parsed
|
82
|
-
|
83
|
-
def _write(self, contents: str) -> None:
|
84
|
-
self.__contents = contents
|
85
|
-
self.__parsed = None
|
86
|
-
if not self.__defer_io:
|
87
|
-
self.path.write_text(contents)
|
88
|
-
self.__dirty = False
|
89
|
-
else:
|
90
|
-
self.__dirty = True
|
91
|
-
|
92
|
-
|
93
|
-
class FoamFile(
|
94
|
-
_FoamFileBase,
|
95
|
-
FoamDictionaryBase,
|
96
|
-
MutableMapping[
|
97
|
-
Union[str, Tuple[str, ...]], Union["FoamFile.Value", "FoamFile.Dictionary"]
|
98
|
-
],
|
99
|
-
):
|
100
|
-
"""
|
101
|
-
An OpenFOAM dictionary file.
|
102
|
-
|
103
|
-
Use as a mutable mapping (i.e., like a dict) to access and modify entries.
|
104
|
-
|
105
|
-
Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
|
106
|
-
"""
|
107
|
-
|
108
|
-
class Dictionary(
|
109
|
-
FoamDictionaryBase,
|
110
|
-
MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
|
111
|
-
):
|
112
|
-
"""An OpenFOAM dictionary within a file as a mutable mapping."""
|
113
|
-
|
114
|
-
def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
|
115
|
-
self._file = _file
|
116
|
-
self._keywords = _keywords
|
117
|
-
|
118
|
-
def __getitem__(
|
119
|
-
self, keyword: str
|
120
|
-
) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
|
121
|
-
return self._file[(*self._keywords, keyword)]
|
122
|
-
|
123
|
-
def _setitem(
|
124
|
-
self,
|
125
|
-
keyword: str,
|
126
|
-
value: Any,
|
127
|
-
*,
|
128
|
-
assume_field: bool = False,
|
129
|
-
assume_dimensions: bool = False,
|
130
|
-
) -> None:
|
131
|
-
self._file._setitem(
|
132
|
-
(*self._keywords, keyword),
|
133
|
-
value,
|
134
|
-
assume_field=assume_field,
|
135
|
-
assume_dimensions=assume_dimensions,
|
136
|
-
)
|
137
|
-
|
138
|
-
def __setitem__(self, keyword: str, value: "FoamFile._SetValue") -> None:
|
139
|
-
self._setitem(keyword, value)
|
140
|
-
|
141
|
-
def __delitem__(self, keyword: str) -> None:
|
142
|
-
del self._file[(*self._keywords, keyword)]
|
143
|
-
|
144
|
-
def __iter__(self) -> Iterator[str]:
|
145
|
-
return self._file._iter(tuple(self._keywords))
|
146
|
-
|
147
|
-
def __contains__(self, keyword: object) -> bool:
|
148
|
-
return (*self._keywords, keyword) in self._file
|
149
|
-
|
150
|
-
def __len__(self) -> int:
|
151
|
-
return len(list(iter(self)))
|
152
|
-
|
153
|
-
def update(self, *args: Any, **kwargs: Any) -> None:
|
154
|
-
with self._file:
|
155
|
-
super().update(*args, **kwargs)
|
156
|
-
|
157
|
-
def clear(self) -> None:
|
158
|
-
with self._file:
|
159
|
-
super().clear()
|
160
|
-
|
161
|
-
def __repr__(self) -> str:
|
162
|
-
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
163
|
-
|
164
|
-
def as_dict(self) -> FoamDictionaryBase._Dict:
|
165
|
-
"""Return a nested dict representation of the dictionary."""
|
166
|
-
ret = self._file.as_dict()
|
167
|
-
|
168
|
-
for k in self._keywords:
|
169
|
-
assert isinstance(ret, dict)
|
170
|
-
v = ret[k]
|
171
|
-
assert isinstance(v, dict)
|
172
|
-
ret = v
|
173
|
-
|
174
|
-
return ret
|
175
|
-
|
176
|
-
def __getitem__(
|
177
|
-
self, keywords: Union[str, Tuple[str, ...]]
|
178
|
-
) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
|
179
|
-
if not isinstance(keywords, tuple):
|
180
|
-
keywords = (keywords,)
|
181
|
-
|
182
|
-
_, parsed = self._read()
|
183
|
-
|
184
|
-
value = get_value(parsed, keywords)
|
185
|
-
|
186
|
-
if value is None:
|
187
|
-
return FoamFile.Dictionary(self, keywords)
|
188
|
-
else:
|
189
|
-
return value
|
190
|
-
|
191
|
-
def _setitem(
|
192
|
-
self,
|
193
|
-
keywords: Union[str, Tuple[str, ...]],
|
194
|
-
value: "FoamFile._SetValue",
|
195
|
-
*,
|
196
|
-
assume_field: bool = False,
|
197
|
-
assume_dimensions: bool = False,
|
198
|
-
) -> None:
|
199
|
-
if not isinstance(keywords, tuple):
|
200
|
-
keywords = (keywords,)
|
201
|
-
|
202
|
-
contents, parsed = self._read()
|
203
|
-
|
204
|
-
if isinstance(value, Mapping):
|
205
|
-
with self:
|
206
|
-
if isinstance(value, FoamDictionaryBase):
|
207
|
-
value = value.as_dict()
|
208
|
-
|
209
|
-
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
210
|
-
|
211
|
-
self._write(
|
212
|
-
f"{contents[:start]}\n{serialize_entry(keywords[-1], {})}\n{contents[end:]}"
|
213
|
-
)
|
214
|
-
|
215
|
-
for k, v in value.items():
|
216
|
-
self[(*keywords, k)] = v
|
217
|
-
else:
|
218
|
-
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
219
|
-
|
220
|
-
self._write(
|
221
|
-
f"{contents[:start]}\n{serialize_entry(keywords[-1], value, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
|
222
|
-
)
|
223
|
-
|
224
|
-
def __setitem__(
|
225
|
-
self,
|
226
|
-
keywords: Union[str, Tuple[str, ...]],
|
227
|
-
value: "FoamFile._SetValue",
|
228
|
-
) -> None:
|
229
|
-
self._setitem(keywords, value)
|
230
|
-
|
231
|
-
def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
|
232
|
-
if not isinstance(keywords, tuple):
|
233
|
-
keywords = (keywords,)
|
234
|
-
|
235
|
-
contents, parsed = self._read()
|
236
|
-
|
237
|
-
start, end = get_entry_locn(parsed, keywords)
|
238
|
-
|
239
|
-
self._write(contents[:start] + contents[end:])
|
240
|
-
|
241
|
-
def _iter(self, keywords: Union[str, Tuple[str, ...]] = ()) -> Iterator[str]:
|
242
|
-
if not isinstance(keywords, tuple):
|
243
|
-
keywords = (keywords,)
|
244
|
-
|
245
|
-
contents = self.path.read_text()
|
246
|
-
parsed = parse(contents)
|
247
|
-
|
248
|
-
yield from (k[-1] for k in parsed if k[:-1] == keywords)
|
249
|
-
|
250
|
-
def __iter__(self) -> Iterator[str]:
|
251
|
-
return self._iter()
|
252
|
-
|
253
|
-
def __contains__(self, keywords: object) -> bool:
|
254
|
-
if not isinstance(keywords, tuple):
|
255
|
-
keywords = (keywords,)
|
256
|
-
_, parsed = self._read()
|
257
|
-
return keywords in parsed
|
258
|
-
|
259
|
-
def __len__(self) -> int:
|
260
|
-
return len(list(iter(self)))
|
261
|
-
|
262
|
-
def update(self, *args: Any, **kwargs: Any) -> None:
|
263
|
-
with self:
|
264
|
-
super().update(*args, **kwargs)
|
265
|
-
|
266
|
-
def clear(self) -> None:
|
267
|
-
with self:
|
268
|
-
super().clear()
|
269
|
-
|
270
|
-
def __fspath__(self) -> str:
|
271
|
-
return str(self.path)
|
272
|
-
|
273
|
-
def __repr__(self) -> str:
|
274
|
-
return f"{type(self).__name__}({self.path})"
|
275
|
-
|
276
|
-
def as_dict(self) -> FoamDictionaryBase._Dict:
|
277
|
-
"""Return a nested dict representation of the file."""
|
278
|
-
_, parsed = self._read()
|
279
|
-
return as_dict(parsed)
|
280
|
-
|
281
|
-
|
282
|
-
class FoamFieldFile(FoamFile):
|
283
|
-
"""An OpenFOAM dictionary file representing a field as a mutable mapping."""
|
284
|
-
|
285
|
-
class BoundariesDictionary(FoamFile.Dictionary):
|
286
|
-
def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundaryDictionary":
|
287
|
-
value = super().__getitem__(keyword)
|
288
|
-
if not isinstance(value, FoamFieldFile.BoundaryDictionary):
|
289
|
-
assert not isinstance(value, FoamFile.Dictionary)
|
290
|
-
raise TypeError(f"boundary {keyword} is not a dictionary")
|
291
|
-
return value
|
292
|
-
|
293
|
-
class BoundaryDictionary(FoamFile.Dictionary):
|
294
|
-
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
295
|
-
|
296
|
-
def __setitem__(
|
297
|
-
self,
|
298
|
-
key: str,
|
299
|
-
value: FoamFile._SetValue,
|
300
|
-
) -> None:
|
301
|
-
if key == "value":
|
302
|
-
self._setitem(key, value, assume_field=True)
|
303
|
-
else:
|
304
|
-
self._setitem(key, value)
|
305
|
-
|
306
|
-
@property
|
307
|
-
def type(self) -> str:
|
308
|
-
"""Alias of `self["type"]`."""
|
309
|
-
ret = self["type"]
|
310
|
-
if not isinstance(ret, str):
|
311
|
-
raise TypeError("type is not a string")
|
312
|
-
return ret
|
313
|
-
|
314
|
-
@type.setter
|
315
|
-
def type(self, value: str) -> None:
|
316
|
-
self["type"] = value
|
317
|
-
|
318
|
-
@property
|
319
|
-
def value(
|
320
|
-
self,
|
321
|
-
) -> Union[
|
322
|
-
int,
|
323
|
-
float,
|
324
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
325
|
-
"NDArray[np.generic]",
|
326
|
-
]:
|
327
|
-
"""Alias of `self["value"]`."""
|
328
|
-
ret = self["value"]
|
329
|
-
if not isinstance(ret, (int, float, Sequence)):
|
330
|
-
raise TypeError("value is not a field")
|
331
|
-
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
332
|
-
|
333
|
-
@value.setter
|
334
|
-
def value(
|
335
|
-
self,
|
336
|
-
value: Union[
|
337
|
-
int,
|
338
|
-
float,
|
339
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
340
|
-
"NDArray[np.generic]",
|
341
|
-
],
|
342
|
-
) -> None:
|
343
|
-
self["value"] = value
|
344
|
-
|
345
|
-
@value.deleter
|
346
|
-
def value(self) -> None:
|
347
|
-
del self["value"]
|
348
|
-
|
349
|
-
def __getitem__(
|
350
|
-
self, keywords: Union[str, Tuple[str, ...]]
|
351
|
-
) -> Union[FoamFile.Value, FoamFile.Dictionary]:
|
352
|
-
if not isinstance(keywords, tuple):
|
353
|
-
keywords = (keywords,)
|
354
|
-
|
355
|
-
ret = super().__getitem__(keywords)
|
356
|
-
if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.Dictionary):
|
357
|
-
if len(keywords) == 1:
|
358
|
-
ret = FoamFieldFile.BoundariesDictionary(self, keywords)
|
359
|
-
elif len(keywords) == 2:
|
360
|
-
ret = FoamFieldFile.BoundaryDictionary(self, keywords)
|
361
|
-
return ret
|
362
|
-
|
363
|
-
def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
|
364
|
-
if not isinstance(keywords, tuple):
|
365
|
-
keywords = (keywords,)
|
366
|
-
|
367
|
-
if keywords == ("internalField",):
|
368
|
-
self._setitem(keywords, value, assume_field=True)
|
369
|
-
elif keywords == ("dimensions",):
|
370
|
-
self._setitem(keywords, value, assume_dimensions=True)
|
371
|
-
else:
|
372
|
-
self._setitem(keywords, value)
|
373
|
-
|
374
|
-
@property
|
375
|
-
def dimensions(self) -> FoamFile.DimensionSet:
|
376
|
-
"""Alias of `self["dimensions"]`."""
|
377
|
-
ret = self["dimensions"]
|
378
|
-
if not isinstance(ret, FoamFile.DimensionSet):
|
379
|
-
raise TypeError("dimensions is not a DimensionSet")
|
380
|
-
return ret
|
381
|
-
|
382
|
-
@dimensions.setter
|
383
|
-
def dimensions(
|
384
|
-
self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
|
385
|
-
) -> None:
|
386
|
-
self["dimensions"] = value
|
387
|
-
|
388
|
-
@property
|
389
|
-
def internal_field(
|
390
|
-
self,
|
391
|
-
) -> Union[
|
392
|
-
int,
|
393
|
-
float,
|
394
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
395
|
-
"NDArray[np.generic]",
|
396
|
-
]:
|
397
|
-
"""Alias of `self["internalField"]`."""
|
398
|
-
ret = self["internalField"]
|
399
|
-
if not isinstance(ret, (int, float, Sequence)):
|
400
|
-
raise TypeError("internalField is not a field")
|
401
|
-
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
402
|
-
|
403
|
-
@internal_field.setter
|
404
|
-
def internal_field(
|
405
|
-
self,
|
406
|
-
value: Union[
|
407
|
-
int,
|
408
|
-
float,
|
409
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
410
|
-
"NDArray[np.generic]",
|
411
|
-
],
|
412
|
-
) -> None:
|
413
|
-
self["internalField"] = value
|
414
|
-
|
415
|
-
@property
|
416
|
-
def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
|
417
|
-
"""Alias of `self["boundaryField"]`."""
|
418
|
-
ret = self["boundaryField"]
|
419
|
-
if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
|
420
|
-
assert not isinstance(ret, FoamFile.Dictionary)
|
421
|
-
raise TypeError("boundaryField is not a dictionary")
|
422
|
-
return ret
|
@@ -1,198 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
from typing import Optional, Tuple, Union
|
3
|
-
|
4
|
-
if sys.version_info >= (3, 9):
|
5
|
-
from collections.abc import Mapping, MutableMapping, Sequence
|
6
|
-
else:
|
7
|
-
from typing import Mapping, MutableMapping, Sequence
|
8
|
-
|
9
|
-
if sys.version_info >= (3, 10):
|
10
|
-
from types import EllipsisType
|
11
|
-
else:
|
12
|
-
from typing import Any as EllipsisType
|
13
|
-
|
14
|
-
from pyparsing import (
|
15
|
-
Dict,
|
16
|
-
Forward,
|
17
|
-
Group,
|
18
|
-
Keyword,
|
19
|
-
LineEnd,
|
20
|
-
Literal,
|
21
|
-
Located,
|
22
|
-
Opt,
|
23
|
-
ParserElement,
|
24
|
-
ParseResults,
|
25
|
-
QuotedString,
|
26
|
-
Word,
|
27
|
-
c_style_comment,
|
28
|
-
common,
|
29
|
-
cpp_style_comment,
|
30
|
-
identbodychars,
|
31
|
-
printables,
|
32
|
-
)
|
33
|
-
|
34
|
-
from ._base import FoamDictionaryBase
|
35
|
-
|
36
|
-
_SWITCH = (
|
37
|
-
Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
|
38
|
-
).set_parse_action(lambda: True) | (
|
39
|
-
Keyword("no") | Keyword("false") | Keyword("off") | Keyword("n") | Keyword("f")
|
40
|
-
).set_parse_action(lambda: False)
|
41
|
-
_DIMENSIONS = (
|
42
|
-
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
43
|
-
).set_parse_action(lambda tks: FoamDictionaryBase.DimensionSet(*tks))
|
44
|
-
|
45
|
-
|
46
|
-
def _list_of(elem: ParserElement) -> ParserElement:
|
47
|
-
return Opt(
|
48
|
-
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
49
|
-
).suppress() + (
|
50
|
-
(
|
51
|
-
Opt(common.integer).suppress()
|
52
|
-
+ (
|
53
|
-
Literal("(").suppress()
|
54
|
-
+ Group((elem)[...], aslist=True)
|
55
|
-
+ Literal(")").suppress()
|
56
|
-
)
|
57
|
-
)
|
58
|
-
| (
|
59
|
-
common.integer + Literal("{").suppress() + elem + Literal("}").suppress()
|
60
|
-
).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
|
61
|
-
)
|
62
|
-
|
63
|
-
|
64
|
-
def _dictionary_of(
|
65
|
-
keyword: ParserElement,
|
66
|
-
value: ParserElement,
|
67
|
-
*,
|
68
|
-
len: Union[int, EllipsisType] = ...,
|
69
|
-
located: bool = False,
|
70
|
-
) -> ParserElement:
|
71
|
-
subdict = Forward()
|
72
|
-
|
73
|
-
entry = keyword + (
|
74
|
-
(Literal("{").suppress() + subdict + Literal("}").suppress())
|
75
|
-
| (value + Literal(";").suppress())
|
76
|
-
)
|
77
|
-
|
78
|
-
if located:
|
79
|
-
entry = Located(entry)
|
80
|
-
|
81
|
-
subdict <<= Dict(Group(entry)[...], asdict=not located)
|
82
|
-
|
83
|
-
return Dict(Group(entry)[len], asdict=not located)
|
84
|
-
|
85
|
-
|
86
|
-
_TENSOR = _list_of(common.number) | common.number
|
87
|
-
_IDENTIFIER = Word(identbodychars + "$", printables.replace(";", ""))
|
88
|
-
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
89
|
-
lambda tks: FoamDictionaryBase.Dimensioned(*reversed(tks.as_list()))
|
90
|
-
)
|
91
|
-
_FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
92
|
-
Keyword("nonuniform").suppress() + _list_of(_TENSOR)
|
93
|
-
)
|
94
|
-
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
95
|
-
_ITEM = Forward()
|
96
|
-
_ENTRY = _dictionary_of(_IDENTIFIER, _ITEM, len=1)
|
97
|
-
_LIST = _list_of(_ENTRY | _ITEM)
|
98
|
-
_ITEM <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
|
99
|
-
|
100
|
-
_TOKENS = (
|
101
|
-
QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
|
102
|
-
)[2, ...].set_parse_action(lambda tks: " ".join(tks))
|
103
|
-
|
104
|
-
_VALUE = _ITEM ^ _TOKENS
|
105
|
-
|
106
|
-
_FILE = (
|
107
|
-
_dictionary_of(_TOKEN, Opt(_VALUE, default=""), located=True)
|
108
|
-
.ignore(c_style_comment)
|
109
|
-
.ignore(cpp_style_comment)
|
110
|
-
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
|
111
|
-
)
|
112
|
-
|
113
|
-
Parsed = Mapping[Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]]
|
114
|
-
|
115
|
-
|
116
|
-
def _flatten_result(
|
117
|
-
parse_result: ParseResults, *, _keywords: Sequence[str] = ()
|
118
|
-
) -> Parsed:
|
119
|
-
ret: MutableMapping[
|
120
|
-
Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]
|
121
|
-
] = {}
|
122
|
-
start = parse_result.locn_start
|
123
|
-
assert isinstance(start, int)
|
124
|
-
item = parse_result.value
|
125
|
-
assert isinstance(item, Sequence)
|
126
|
-
end = parse_result.locn_end
|
127
|
-
assert isinstance(end, int)
|
128
|
-
key, *values = item
|
129
|
-
assert isinstance(key, str)
|
130
|
-
ret[(*_keywords, key)] = (start, None, end)
|
131
|
-
for value in values:
|
132
|
-
if isinstance(value, ParseResults):
|
133
|
-
ret.update(_flatten_result(value, _keywords=(*_keywords, key)))
|
134
|
-
else:
|
135
|
-
ret[(*_keywords, key)] = (start, value, end)
|
136
|
-
return ret
|
137
|
-
|
138
|
-
|
139
|
-
def parse(
|
140
|
-
contents: str,
|
141
|
-
) -> Parsed:
|
142
|
-
parse_results = _FILE.parse_string(contents, parse_all=True)
|
143
|
-
ret: MutableMapping[
|
144
|
-
Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]
|
145
|
-
] = {}
|
146
|
-
for parse_result in parse_results:
|
147
|
-
ret.update(_flatten_result(parse_result))
|
148
|
-
return ret
|
149
|
-
|
150
|
-
|
151
|
-
def get_value(
|
152
|
-
parsed: Parsed,
|
153
|
-
keywords: Tuple[str, ...],
|
154
|
-
) -> Optional[FoamDictionaryBase.Value]:
|
155
|
-
"""Value of an entry."""
|
156
|
-
_, value, _ = parsed[keywords]
|
157
|
-
return value
|
158
|
-
|
159
|
-
|
160
|
-
def get_entry_locn(
|
161
|
-
parsed: Parsed,
|
162
|
-
keywords: Tuple[str, ...],
|
163
|
-
*,
|
164
|
-
missing_ok: bool = False,
|
165
|
-
) -> Tuple[int, int]:
|
166
|
-
"""Location of an entry or where it should be inserted."""
|
167
|
-
try:
|
168
|
-
start, _, end = parsed[keywords]
|
169
|
-
except KeyError:
|
170
|
-
if missing_ok:
|
171
|
-
if len(keywords) > 1:
|
172
|
-
_, _, end = parsed[keywords[:-1]]
|
173
|
-
end -= 1
|
174
|
-
else:
|
175
|
-
end = -1
|
176
|
-
|
177
|
-
start = end
|
178
|
-
else:
|
179
|
-
raise
|
180
|
-
|
181
|
-
return start, end
|
182
|
-
|
183
|
-
|
184
|
-
def as_dict(parsed: Parsed) -> FoamDictionaryBase._Dict:
|
185
|
-
"""Return a nested dict representation of the file."""
|
186
|
-
ret: FoamDictionaryBase._Dict = {}
|
187
|
-
for keywords, (_, value, _) in parsed.items():
|
188
|
-
r = ret
|
189
|
-
for k in keywords[:-1]:
|
190
|
-
assert isinstance(r, dict)
|
191
|
-
v = r[k]
|
192
|
-
assert isinstance(v, dict)
|
193
|
-
r = v
|
194
|
-
|
195
|
-
assert isinstance(r, dict)
|
196
|
-
r[keywords[-1]] = {} if value is None else value
|
197
|
-
|
198
|
-
return ret
|