foamlib 0.6.10__py3-none-any.whl → 0.6.11__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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.6.10"
3
+ __version__ = "0.6.11"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
foamlib/_files/_base.py CHANGED
@@ -6,35 +6,46 @@ if TYPE_CHECKING:
6
6
  import numpy as np
7
7
 
8
8
  if sys.version_info >= (3, 9):
9
- from collections.abc import Mapping, Sequence
9
+ from collections.abc import Mapping, MutableMapping, Sequence
10
10
  else:
11
- from typing import Mapping, Sequence
11
+ from typing import Mapping, MutableMapping, Sequence
12
12
 
13
13
 
14
14
  class FoamFileBase:
15
15
  class DimensionSet(NamedTuple):
16
- mass: Union[int, float] = 0
17
- length: Union[int, float] = 0
18
- time: Union[int, float] = 0
19
- temperature: Union[int, float] = 0
20
- moles: Union[int, float] = 0
21
- current: Union[int, float] = 0
22
- luminous_intensity: Union[int, float] = 0
16
+ mass: float = 0
17
+ length: float = 0
18
+ time: float = 0
19
+ temperature: float = 0
20
+ moles: float = 0
21
+ current: float = 0
22
+ luminous_intensity: float = 0
23
23
 
24
24
  def __repr__(self) -> str:
25
25
  return f"{type(self).__qualname__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
26
26
 
27
+ _Tensor = Union[
28
+ float,
29
+ Sequence[float],
30
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
31
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
32
+ ]
33
+
27
34
  @dataclass
28
35
  class Dimensioned:
29
- value: Union[int, float, Sequence[Union[int, float]]] = 0
30
- dimensions: Union["FoamFileBase.DimensionSet", Sequence[Union[int, float]]] = ()
36
+ value: "FoamFileBase._Tensor" = 0
37
+ dimensions: Union["FoamFileBase.DimensionSet", Sequence[float]] = ()
31
38
  name: Optional[str] = None
32
39
 
33
40
  def __post_init__(self) -> None:
34
41
  if not isinstance(self.dimensions, FoamFileBase.DimensionSet):
35
42
  self.dimensions = FoamFileBase.DimensionSet(*self.dimensions)
36
43
 
37
- Data = Union[
44
+ _Field = Union[
45
+ _Tensor, Sequence[_Tensor], "np.ndarray[Tuple[int, int], np.dtype[np.generic]]"
46
+ ]
47
+
48
+ _DataEntry = Union[
38
49
  str,
39
50
  int,
40
51
  float,
@@ -42,25 +53,22 @@ class FoamFileBase:
42
53
  Dimensioned,
43
54
  DimensionSet,
44
55
  Sequence["Data"],
56
+ _Tensor,
57
+ _Field,
58
+ ]
59
+
60
+ Data = Union[
61
+ _DataEntry,
45
62
  Mapping[str, "Data"],
46
63
  ]
47
64
  """
48
65
  A value that can be stored in an OpenFOAM file.
49
66
  """
50
67
 
68
+ _MutableData = Union[
69
+ _DataEntry,
70
+ MutableMapping[str, "_MutableData"],
71
+ ]
72
+
51
73
  _Dict = Dict[str, Union["Data", "_Dict"]]
52
74
  _File = Dict[Optional[str], Union["Data", "_Dict"]]
53
-
54
- _SetData = Union[
55
- str,
56
- int,
57
- float,
58
- bool,
59
- Dimensioned,
60
- DimensionSet,
61
- Sequence["_SetData"],
62
- Mapping[str, "_SetData"],
63
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
64
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
65
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
66
- ]
foamlib/_files/_files.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import sys
2
- from typing import TYPE_CHECKING, Any, Optional, Tuple, Union, cast
2
+ from copy import deepcopy
3
+ from typing import Any, Optional, Tuple, Union, cast
3
4
 
4
5
  if sys.version_info >= (3, 8):
5
6
  from typing import Literal
@@ -16,15 +17,12 @@ from ._io import FoamFileIO
16
17
  from ._serialization import Kind, dumps
17
18
  from ._util import is_sequence
18
19
 
19
- if TYPE_CHECKING:
20
- import numpy as np
21
-
22
20
 
23
21
  class FoamFile(
24
22
  FoamFileBase,
25
23
  MutableMapping[
26
24
  Optional[Union[str, Tuple[str, ...]]],
27
- Union["FoamFile.Data", "FoamFile.SubDict"],
25
+ FoamFileBase._MutableData,
28
26
  ],
29
27
  FoamFileIO,
30
28
  ):
@@ -37,7 +35,7 @@ class FoamFile(
37
35
  """
38
36
 
39
37
  class SubDict(
40
- MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
38
+ MutableMapping[str, FoamFileBase._MutableData],
41
39
  ):
42
40
  """An OpenFOAM dictionary within a file as a mutable mapping."""
43
41
 
@@ -47,13 +45,13 @@ class FoamFile(
47
45
 
48
46
  def __getitem__(
49
47
  self, keyword: str
50
- ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
48
+ ) -> Union[FoamFileBase._DataEntry, "FoamFile.SubDict"]:
51
49
  return self._file[(*self._keywords, keyword)]
52
50
 
53
51
  def __setitem__(
54
52
  self,
55
53
  keyword: str,
56
- data: "FoamFile._SetData",
54
+ data: FoamFileBase.Data,
57
55
  ) -> None:
58
56
  self._file[(*self._keywords, keyword)] = data
59
57
 
@@ -98,8 +96,8 @@ class FoamFile(
98
96
  def version(self) -> float:
99
97
  """Alias of `self["FoamFile", "version"]`."""
100
98
  ret = self["FoamFile", "version"]
101
- if not isinstance(ret, float):
102
- raise TypeError("version is not a float")
99
+ if not isinstance(ret, (int, float)):
100
+ raise TypeError("version is not a number")
103
101
  return ret
104
102
 
105
103
  @version.setter
@@ -158,22 +156,24 @@ class FoamFile(
158
156
 
159
157
  def __getitem__(
160
158
  self, keywords: Optional[Union[str, Tuple[str, ...]]]
161
- ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
159
+ ) -> Union[FoamFileBase._DataEntry, "FoamFile.SubDict"]:
162
160
  if not keywords:
163
161
  keywords = ()
164
162
  elif not isinstance(keywords, tuple):
165
163
  keywords = (keywords,)
166
164
 
167
- _, parsed = self._read()
165
+ parsed = self._get_parsed()
168
166
 
169
167
  value = parsed[keywords]
170
168
 
169
+ assert not isinstance(value, Mapping)
170
+
171
171
  if value is ...:
172
172
  return FoamFile.SubDict(self, keywords)
173
- return value
173
+ return deepcopy(value)
174
174
 
175
175
  def __setitem__(
176
- self, keywords: Optional[Union[str, Tuple[str, ...]]], data: "FoamFile._SetData"
176
+ self, keywords: Optional[Union[str, Tuple[str, ...]]], data: FoamFileBase.Data
177
177
  ) -> None:
178
178
  with self:
179
179
  if not keywords:
@@ -230,20 +230,28 @@ class FoamFile(
230
230
  self[keywords] = data
231
231
 
232
232
  else:
233
- contents, parsed = self._read(missing_ok=True)
233
+ parsed = self._get_parsed(missing_ok=True)
234
234
 
235
235
  start, end = parsed.entry_location(keywords, missing_ok=True)
236
236
 
237
- before = contents[:start].rstrip() + b"\n"
238
- if len(keywords) <= 1:
239
- before += b"\n"
240
-
241
- after = contents[end:]
242
- if after.startswith(b"}"):
243
- after = b" " * (len(keywords) - 2) + after
244
- if not after or after[:1] != b"\n":
245
- after = b"\n" + after
246
- if len(keywords) <= 1 and len(after) > 1 and after[:2] != b"\n\n":
237
+ before = b""
238
+ if parsed.contents[:start] and not parsed.contents[:start].endswith(
239
+ b"\n"
240
+ ):
241
+ before = b"\n"
242
+ if (
243
+ parsed.contents[:start]
244
+ and len(keywords) <= 1
245
+ and not parsed.contents[:start].endswith(b"\n\n")
246
+ ):
247
+ before = b"\n\n"
248
+
249
+ after = b""
250
+ if parsed.contents[end:].startswith(b"}"):
251
+ after = b" " * (len(keywords) - 2)
252
+ if not parsed.contents[end:] or not parsed.contents[end:].startswith(
253
+ b"\n"
254
+ ):
247
255
  after = b"\n" + after
248
256
 
249
257
  indentation = b" " * (len(keywords) - 1)
@@ -252,7 +260,9 @@ class FoamFile(
252
260
  if isinstance(data, (FoamFile, FoamFile.SubDict)):
253
261
  data = data.as_dict()
254
262
 
255
- self._write(
263
+ parsed.put(
264
+ keywords,
265
+ ...,
256
266
  before
257
267
  + indentation
258
268
  + dumps(keywords[-1])
@@ -261,25 +271,27 @@ class FoamFile(
261
271
  + b"{\n"
262
272
  + indentation
263
273
  + b"}"
264
- + after
274
+ + after,
265
275
  )
266
276
 
267
277
  for k, v in data.items():
268
278
  self[(*keywords, k)] = v
269
279
 
270
280
  elif keywords:
271
- self._write(
281
+ parsed.put(
282
+ keywords,
283
+ data,
272
284
  before
273
285
  + indentation
274
286
  + dumps(keywords[-1])
275
287
  + b" "
276
288
  + dumps(data, kind=kind)
277
289
  + b";"
278
- + after
290
+ + after,
279
291
  )
280
292
 
281
293
  else:
282
- self._write(before + dumps(data, kind=kind) + after)
294
+ parsed.put(keywords, data, before + dumps(data, kind=kind) + after)
283
295
 
284
296
  def __delitem__(self, keywords: Optional[Union[str, Tuple[str, ...]]]) -> None:
285
297
  if not keywords:
@@ -287,15 +299,13 @@ class FoamFile(
287
299
  elif not isinstance(keywords, tuple):
288
300
  keywords = (keywords,)
289
301
 
290
- contents, parsed = self._read()
291
-
292
- start, end = parsed.entry_location(keywords)
293
-
294
- self._write(contents[:start] + contents[end:])
302
+ with self:
303
+ del self._get_parsed()[keywords]
295
304
 
296
305
  def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
297
- _, parsed = self._read()
298
- yield from (k[-1] if k else None for k in parsed if k[:-1] == keywords)
306
+ yield from (
307
+ k[-1] if k else None for k in self._get_parsed() if k[:-1] == keywords
308
+ )
299
309
 
300
310
  def __iter__(self) -> Iterator[Optional[str]]:
301
311
  yield from (k for k in self._iter() if k != "FoamFile")
@@ -306,9 +316,7 @@ class FoamFile(
306
316
  elif not isinstance(keywords, tuple):
307
317
  keywords = (keywords,)
308
318
 
309
- _, parsed = self._read()
310
-
311
- return keywords in parsed
319
+ return keywords in self._get_parsed()
312
320
 
313
321
  def __len__(self) -> int:
314
322
  return len(list(iter(self)))
@@ -330,11 +338,10 @@ class FoamFile(
330
338
 
331
339
  :param include_header: Whether to include the "FoamFile" header in the output.
332
340
  """
333
- _, parsed = self._read()
334
- d = parsed.as_dict()
341
+ d = self._get_parsed().as_dict()
335
342
  if not include_header:
336
343
  d.pop("FoamFile", None)
337
- return d
344
+ return deepcopy(d)
338
345
 
339
346
 
340
347
  class FoamFieldFile(FoamFile):
@@ -366,36 +373,17 @@ class FoamFieldFile(FoamFile):
366
373
  @property
367
374
  def value(
368
375
  self,
369
- ) -> Union[
370
- int,
371
- float,
372
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
373
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
374
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
375
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
376
- ]:
376
+ ) -> FoamFile._Field:
377
377
  """Alias of `self["value"]`."""
378
- ret = self["value"]
379
- if not isinstance(ret, (int, float, Sequence)):
380
- raise TypeError("value is not a field")
381
378
  return cast(
382
- Union[
383
- int, float, Sequence[Union[int, float, Sequence[Union[int, float]]]]
384
- ],
385
- ret,
379
+ FoamFile._Field,
380
+ self["value"],
386
381
  )
387
382
 
388
383
  @value.setter
389
384
  def value(
390
385
  self,
391
- value: Union[
392
- int,
393
- float,
394
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
395
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
396
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
397
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
398
- ],
386
+ value: FoamFile._Field,
399
387
  ) -> None:
400
388
  self["value"] = value
401
389
 
@@ -405,7 +393,7 @@ class FoamFieldFile(FoamFile):
405
393
 
406
394
  def __getitem__(
407
395
  self, keywords: Optional[Union[str, Tuple[str, ...]]]
408
- ) -> Union[FoamFile.Data, FoamFile.SubDict]:
396
+ ) -> Union[FoamFileBase._DataEntry, FoamFile.SubDict]:
409
397
  if not keywords:
410
398
  keywords = ()
411
399
  elif not isinstance(keywords, tuple):
@@ -420,7 +408,7 @@ class FoamFieldFile(FoamFile):
420
408
  return ret
421
409
 
422
410
  @property
423
- def dimensions(self) -> Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]:
411
+ def dimensions(self) -> Union[FoamFile.DimensionSet, Sequence[float]]:
424
412
  """Alias of `self["dimensions"]`."""
425
413
  ret = self["dimensions"]
426
414
  if not isinstance(ret, FoamFile.DimensionSet):
@@ -428,39 +416,20 @@ class FoamFieldFile(FoamFile):
428
416
  return ret
429
417
 
430
418
  @dimensions.setter
431
- def dimensions(
432
- self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
433
- ) -> None:
419
+ def dimensions(self, value: Union[FoamFile.DimensionSet, Sequence[float]]) -> None:
434
420
  self["dimensions"] = value
435
421
 
436
422
  @property
437
423
  def internal_field(
438
424
  self,
439
- ) -> Union[
440
- int,
441
- float,
442
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
443
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
444
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
445
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
446
- ]:
425
+ ) -> FoamFile._Field:
447
426
  """Alias of `self["internalField"]`."""
448
- ret = self["internalField"]
449
- if not isinstance(ret, (int, float, Sequence)):
450
- raise TypeError("internalField is not a field")
451
- return cast(Union[int, float, Sequence[Union[int, float]]], ret)
427
+ return cast(FoamFile._Field, self["internalField"])
452
428
 
453
429
  @internal_field.setter
454
430
  def internal_field(
455
431
  self,
456
- value: Union[
457
- int,
458
- float,
459
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
460
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
461
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
462
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
463
- ],
432
+ value: FoamFile._Field,
464
433
  ) -> None:
465
434
  self["internalField"] = value
466
435
 
foamlib/_files/_io.py CHANGED
@@ -1,12 +1,10 @@
1
1
  import gzip
2
2
  import sys
3
- from copy import deepcopy
4
3
  from pathlib import Path
5
4
  from types import TracebackType
6
5
  from typing import (
7
6
  TYPE_CHECKING,
8
7
  Optional,
9
- Tuple,
10
8
  Type,
11
9
  Union,
12
10
  )
@@ -26,14 +24,13 @@ class FoamFileIO:
26
24
  def __init__(self, path: Union["os.PathLike[str]", str]) -> None:
27
25
  self.path = Path(path).absolute()
28
26
 
29
- self.__contents: Optional[bytes] = None
30
27
  self.__parsed: Optional[Parsed] = None
28
+ self.__missing: Optional[bool] = None
31
29
  self.__defer_io = 0
32
- self.__dirty = False
33
30
 
34
31
  def __enter__(self) -> Self:
35
32
  if self.__defer_io == 0:
36
- self._read(missing_ok=True)
33
+ self._get_parsed(missing_ok=True)
37
34
  self.__defer_io += 1
38
35
  return self
39
36
 
@@ -44,47 +41,40 @@ class FoamFileIO:
44
41
  exc_tb: Optional[TracebackType],
45
42
  ) -> None:
46
43
  self.__defer_io -= 1
47
- if self.__defer_io == 0 and self.__dirty:
48
- assert self.__contents is not None
49
- self._write(self.__contents)
44
+ if self.__defer_io == 0:
45
+ assert self.__parsed is not None
46
+ if self.__parsed.modified:
47
+ contents = self.__parsed.contents
48
+
49
+ if self.path.suffix == ".gz":
50
+ contents = gzip.compress(contents)
50
51
 
51
- def _read(self, *, missing_ok: bool = False) -> Tuple[bytes, Parsed]:
52
+ self.path.write_bytes(contents)
53
+ self.__parsed.modified = False
54
+ self.__missing = False
55
+
56
+ def _get_parsed(self, *, missing_ok: bool = False) -> Parsed:
52
57
  if not self.__defer_io:
53
58
  try:
54
59
  contents = self.path.read_bytes()
55
60
  except FileNotFoundError:
56
- contents = None
61
+ self.__missing = True
62
+ contents = b""
57
63
  else:
58
- assert isinstance(contents, bytes)
64
+ self.__missing = False
59
65
  if self.path.suffix == ".gz":
60
66
  contents = gzip.decompress(contents)
61
67
 
62
- if contents != self.__contents:
63
- self.__contents = contents
64
- self.__parsed = None
68
+ if self.__parsed is None or self.__parsed.contents != contents:
69
+ self.__parsed = Parsed(contents)
65
70
 
66
- if self.__contents is None:
67
- if missing_ok:
68
- return b"", Parsed(b"")
69
- raise FileNotFoundError(self.path)
71
+ assert self.__parsed is not None
72
+ assert self.__missing is not None
70
73
 
71
- if self.__parsed is None:
72
- parsed = Parsed(self.__contents)
73
- self.__parsed = parsed
74
-
75
- return self.__contents, deepcopy(self.__parsed)
76
-
77
- def _write(self, contents: bytes) -> None:
78
- self.__contents = contents
79
- self.__parsed = None
80
- if not self.__defer_io:
81
- if self.path.suffix == ".gz":
82
- contents = gzip.compress(contents)
74
+ if self.__missing and not self.__parsed.modified and not missing_ok:
75
+ raise FileNotFoundError(self.path)
83
76
 
84
- self.path.write_bytes(contents)
85
- self.__dirty = False
86
- else:
87
- self.__dirty = True
77
+ return self.__parsed
88
78
 
89
79
  def __repr__(self) -> str:
90
80
  return f"{type(self).__qualname__}('{self.path}')"
@@ -207,28 +207,29 @@ _FILE = (
207
207
  )
208
208
 
209
209
 
210
- class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
210
+ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase._DataEntry, EllipsisType]]):
211
211
  def __init__(self, contents: bytes) -> None:
212
212
  self._parsed: MutableMapping[
213
213
  Tuple[str, ...],
214
- Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
214
+ Tuple[int, Union[FoamFileBase._DataEntry, EllipsisType], int],
215
215
  ] = {}
216
- self._end = len(contents)
217
-
218
216
  for parse_result in _FILE.parse_string(
219
217
  contents.decode("latin-1"), parse_all=True
220
218
  ):
221
219
  self._parsed.update(self._flatten_result(parse_result))
222
220
 
221
+ self.contents = contents
222
+ self.modified = False
223
+
223
224
  @staticmethod
224
225
  def _flatten_result(
225
226
  parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
226
227
  ) -> Mapping[
227
- Tuple[str, ...], Tuple[int, Union[FoamFileBase.Data, EllipsisType], int]
228
+ Tuple[str, ...], Tuple[int, Union[FoamFileBase._DataEntry, EllipsisType], int]
228
229
  ]:
229
230
  ret: MutableMapping[
230
231
  Tuple[str, ...],
231
- Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
232
+ Tuple[int, Union[FoamFileBase._DataEntry, EllipsisType], int],
232
233
  ] = {}
233
234
  start = parse_result.locn_start
234
235
  assert isinstance(start, int)
@@ -256,13 +257,58 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
256
257
 
257
258
  def __getitem__(
258
259
  self, keywords: Union[str, Tuple[str, ...]]
259
- ) -> Union[FoamFileBase.Data, EllipsisType]:
260
+ ) -> Union[FoamFileBase._DataEntry, EllipsisType]:
260
261
  if isinstance(keywords, str):
261
262
  keywords = (keywords,)
262
263
 
263
264
  _, data, _ = self._parsed[keywords]
264
265
  return data
265
266
 
267
+ def put(
268
+ self,
269
+ keywords: Tuple[str, ...],
270
+ data: Union[FoamFileBase._DataEntry, EllipsisType],
271
+ content: bytes,
272
+ ) -> None:
273
+ start, end = self.entry_location(keywords, missing_ok=True)
274
+
275
+ diff = len(content) - (end - start)
276
+ for k, (s, d, e) in self._parsed.items():
277
+ if s > end:
278
+ self._parsed[k] = (s + diff, d, e + diff)
279
+ elif e > start:
280
+ self._parsed[k] = (s, d, e + diff)
281
+
282
+ self._parsed[keywords] = (start, data, end + diff)
283
+
284
+ self.contents = self.contents[:start] + content + self.contents[end:]
285
+ self.modified = True
286
+
287
+ for k in list(self._parsed):
288
+ if keywords != k and keywords == k[: len(keywords)]:
289
+ del self._parsed[k]
290
+
291
+ def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
292
+ if isinstance(keywords, str):
293
+ keywords = (keywords,)
294
+
295
+ start, end = self.entry_location(keywords)
296
+ del self._parsed[keywords]
297
+
298
+ for k in list(self._parsed):
299
+ if keywords == k[: len(keywords)]:
300
+ del self._parsed[k]
301
+
302
+ diff = end - start
303
+ for k, (s, d, e) in self._parsed.items():
304
+ if s > end:
305
+ self._parsed[k] = (s - diff, d, e - diff)
306
+ elif e > start:
307
+ self._parsed[k] = (s, d, e - diff)
308
+
309
+ self.contents = self.contents[:start] + self.contents[end:]
310
+ self.modified = True
311
+
266
312
  def __contains__(self, keywords: object) -> bool:
267
313
  return keywords in self._parsed
268
314
 
@@ -280,10 +326,11 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
280
326
  except KeyError:
281
327
  if missing_ok:
282
328
  if len(keywords) > 1:
283
- _, _, end = self._parsed[keywords[:-1]]
284
- end -= 1
329
+ assert self[keywords[:-1]] is ...
330
+ start, end = self.entry_location(keywords[:-1])
331
+ end = self.contents.rindex(b"}", start, end)
285
332
  else:
286
- end = self._end
333
+ end = len(self.contents)
287
334
 
288
335
  start = end
289
336
  else:
@@ -28,7 +28,7 @@ class Kind(Enum):
28
28
 
29
29
 
30
30
  def dumps(
31
- data: FoamFileBase._SetData,
31
+ data: FoamFileBase.Data,
32
32
  *,
33
33
  kind: Kind = Kind.DEFAULT,
34
34
  ) -> bytes:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.10
3
+ Version: 0.6.11
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -1,4 +1,4 @@
1
- foamlib/__init__.py,sha256=7AIRMyiByWRuEUEkR_MGE1zy0GuXaEzOlzXnUkCvdtw,487
1
+ foamlib/__init__.py,sha256=tS4b8eyl8WWPCCt7EmgXM2lcx8V1Zead47kwB3zJlNY,487
2
2
  foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  foamlib/_cases/__init__.py,sha256=wTUHcUgU1CBgpu0cUMtksQ5VKG6B8CFu9xc3dWwsQuo,358
4
4
  foamlib/_cases/_async.py,sha256=oQ2XnD9sansJLJDuUl3_vHaUM4L9hq2G97IcRjQAOPY,7920
@@ -9,14 +9,14 @@ foamlib/_cases/_subprocess.py,sha256=tzsBGe44EnQw3GWqRW1ktEZ4sNQ1sSHdJ_89zIfF5L0
9
9
  foamlib/_cases/_sync.py,sha256=ENvn8zseRh6YOjAgqXHzmKhKFYXdzGIW5NoMHS_NKPY,6049
10
10
  foamlib/_cases/_util.py,sha256=GNndpqw3Jg_S-Hxzl5vwRgD0czcTNb9NYHMhcfBoMBg,1493
11
11
  foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
12
- foamlib/_files/_base.py,sha256=mrZuk90sdIrSGiOeEtCcEHoz6RK4qUc6GFoDSuo7U3w,1920
13
- foamlib/_files/_files.py,sha256=6ZbUxb2VF225ceEHBg4L23oxHvD7M18QC9zqMPm0ZS4,16025
14
- foamlib/_files/_io.py,sha256=ArD9lgMw-Kjj-wl_5qU6eRdW4lZWyGBIEMWLCNV4a0Y,2467
15
- foamlib/_files/_parsing.py,sha256=SvPAbNb_xJwOBRGii4oLOUvhj9OdJ8auMIzdx77AYwI,9394
16
- foamlib/_files/_serialization.py,sha256=iIiPUbgoQzg6xeYeeTsRPWKWqrYFtAQPgglBbylPkms,3341
12
+ foamlib/_files/_base.py,sha256=A8084rU92w9Hh2BZAEIdE31qvm15fto1qOyQJhrP5DI,1945
13
+ foamlib/_files/_files.py,sha256=rvhb9mZFg1z6JSpb02E_8B2YD7Zmt5hh2v7rq19qC_c,14712
14
+ foamlib/_files/_io.py,sha256=07gaGhoJuXDB9i4AxvnIT_Np71RAU2ut2G5Lcyx8TaU,2244
15
+ foamlib/_files/_parsing.py,sha256=pqwWzlZwCTe7PxncYywO3OWd9Ppl9Oq8JYVpofxKJsY,11046
16
+ foamlib/_files/_serialization.py,sha256=Gy9v3iZGD-RF3pS7rauft7fpT33UIzRuOm0XH4rFHvA,3337
17
17
  foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
18
- foamlib-0.6.10.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
19
- foamlib-0.6.10.dist-info/METADATA,sha256=0IYRom9JRsqS6OE6x3rlRDdL6zYUubtpYl5hafW_2Bg,7742
20
- foamlib-0.6.10.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
21
- foamlib-0.6.10.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
22
- foamlib-0.6.10.dist-info/RECORD,,
18
+ foamlib-0.6.11.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
19
+ foamlib-0.6.11.dist-info/METADATA,sha256=VGwnlinUiBszBq9RzNnRWvWx0hkO9ziOY4jaKNmoJwc,7742
20
+ foamlib-0.6.11.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
21
+ foamlib-0.6.11.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
22
+ foamlib-0.6.11.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5