foamlib 0.3.23__py3-none-any.whl → 0.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.3.23"
3
+ __version__ = "0.4.0"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -9,7 +9,7 @@ from ._cases import (
9
9
  FoamCase,
10
10
  FoamCaseBase,
11
11
  )
12
- from ._files import FoamDict, FoamFieldFile, FoamFile
12
+ from ._files import FoamFieldFile, FoamFile, FoamFileBase
13
13
 
14
14
  __all__ = [
15
15
  "FoamCase",
@@ -17,7 +17,7 @@ __all__ = [
17
17
  "FoamCaseBase",
18
18
  "FoamFile",
19
19
  "FoamFieldFile",
20
- "FoamDict",
20
+ "FoamFileBase",
21
21
  "CalledProcessError",
22
22
  "CalledProcessWarning",
23
23
  ]
foamlib/_cases/_base.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import shutil
2
3
  import sys
3
4
  from pathlib import Path
4
5
  from typing import (
@@ -62,12 +63,10 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
62
63
  return self.path.name
63
64
 
64
65
  def __getitem__(self, key: str) -> FoamFieldFile:
65
- if (self.path / key).is_file():
66
- return FoamFieldFile(self.path / key)
67
- elif (self.path / f"{key}.gz").is_file():
66
+ if (self.path / f"{key}.gz").is_file() and not (self.path / key).is_file():
68
67
  return FoamFieldFile(self.path / f"{key}.gz")
69
68
  else:
70
- raise KeyError(key)
69
+ return FoamFieldFile(self.path / key)
71
70
 
72
71
  def __contains__(self, obj: object) -> bool:
73
72
  if isinstance(obj, FoamFieldFile):
@@ -89,6 +88,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
89
88
  def __len__(self) -> int:
90
89
  return len(list(iter(self)))
91
90
 
91
+ def __delitem__(self, key: str) -> None:
92
+ if (self.path / f"{key}.gz").is_file() and not (self.path / key).is_file():
93
+ (self.path / f"{key}.gz").unlink()
94
+ else:
95
+ (self.path / key).unlink()
96
+
92
97
  def __fspath__(self) -> str:
93
98
  return str(self.path)
94
99
 
@@ -167,6 +172,9 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
167
172
 
168
173
  return paths
169
174
 
175
+ def __delitem__(self, key: Union[int, float, str]) -> None:
176
+ shutil.rmtree(self[key].path)
177
+
170
178
  def _clone_ignore(
171
179
  self,
172
180
  ) -> Callable[[Union[Path, str], Collection[str]], Collection[str]]:
@@ -1,8 +1,8 @@
1
- from ._base import FoamDict
1
+ from ._base import FoamFileBase
2
2
  from ._files import FoamFieldFile, FoamFile
3
3
 
4
4
  __all__ = [
5
5
  "FoamFile",
6
6
  "FoamFieldFile",
7
- "FoamDict",
7
+ "FoamFileBase",
8
8
  ]
foamlib/_files/_base.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from abc import abstractmethod
3
2
  from dataclasses import dataclass
4
3
  from typing import Dict, NamedTuple, Optional, Tuple, Union
5
4
 
@@ -14,7 +13,7 @@ except ModuleNotFoundError:
14
13
  pass
15
14
 
16
15
 
17
- class FoamDict:
16
+ class FoamFileBase:
18
17
  class DimensionSet(NamedTuple):
19
18
  mass: Union[int, float] = 0
20
19
  length: Union[int, float] = 0
@@ -30,12 +29,12 @@ class FoamDict:
30
29
  @dataclass
31
30
  class Dimensioned:
32
31
  value: Union[int, float, Sequence[Union[int, float]]] = 0
33
- dimensions: Union["FoamDict.DimensionSet", Sequence[Union[int, float]]] = ()
32
+ dimensions: Union["FoamFileBase.DimensionSet", Sequence[Union[int, float]]] = ()
34
33
  name: Optional[str] = None
35
34
 
36
35
  def __post_init__(self) -> None:
37
- if not isinstance(self.dimensions, FoamDict.DimensionSet):
38
- self.dimensions = FoamDict.DimensionSet(*self.dimensions)
36
+ if not isinstance(self.dimensions, FoamFileBase.DimensionSet):
37
+ self.dimensions = FoamFileBase.DimensionSet(*self.dimensions)
39
38
 
40
39
  Data = Union[
41
40
  str,
@@ -48,16 +47,11 @@ class FoamDict:
48
47
  Mapping[str, "Data"],
49
48
  ]
50
49
  """
51
- A value that can be stored in an OpenFOAM dictionary.
50
+ A value that can be stored in an OpenFOAM file.
52
51
  """
53
52
 
54
53
  _Dict = Dict[str, Union["Data", "_Dict"]]
55
54
 
56
- @abstractmethod
57
- def as_dict(self) -> _Dict:
58
- """Return a nested dict representation of the dictionary."""
59
- raise NotImplementedError
60
-
61
55
  _SetData = Union[
62
56
  str,
63
57
  int,
foamlib/_files/_files.py CHANGED
@@ -1,14 +1,24 @@
1
1
  import sys
2
- from typing import Any, Tuple, Union, cast
2
+ from typing import Any, Optional, Tuple, Union, cast
3
+
4
+ if sys.version_info >= (3, 8):
5
+ from typing import Literal
6
+ else:
7
+ from typing_extensions import Literal
3
8
 
4
9
  if sys.version_info >= (3, 9):
5
10
  from collections.abc import Iterator, Mapping, MutableMapping, Sequence
6
11
  else:
7
12
  from typing import Iterator, Mapping, MutableMapping, Sequence
8
13
 
14
+ if sys.version_info >= (3, 11):
15
+ from typing import Self
16
+ else:
17
+ from typing_extensions import Self
18
+
9
19
  from .._util import is_sequence
10
- from ._base import FoamDict
11
- from ._io import FoamFileIO
20
+ from ._base import FoamFileBase
21
+ from ._io import _FoamFileIO
12
22
  from ._serialization import Kind, dumpb
13
23
 
14
24
  try:
@@ -18,11 +28,12 @@ except ModuleNotFoundError:
18
28
 
19
29
 
20
30
  class FoamFile(
21
- FoamDict,
31
+ FoamFileBase,
22
32
  MutableMapping[
23
- Union[str, Tuple[str, ...]], Union["FoamFile.Data", "FoamFile.SubDict"]
33
+ Optional[Union[str, Tuple[str, ...]]],
34
+ Union["FoamFile.Data", "FoamFile.SubDict"],
24
35
  ],
25
- FoamFileIO,
36
+ _FoamFileIO,
26
37
  ):
27
38
  """
28
39
  An OpenFOAM data file.
@@ -33,7 +44,6 @@ class FoamFile(
33
44
  """
34
45
 
35
46
  class SubDict(
36
- FoamDict,
37
47
  MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
38
48
  ):
39
49
  """An OpenFOAM dictionary within a file as a mutable mapping."""
@@ -58,7 +68,9 @@ class FoamFile(
58
68
  del self._file[(*self._keywords, keyword)]
59
69
 
60
70
  def __iter__(self) -> Iterator[str]:
61
- return self._file._iter(self._keywords)
71
+ for k in self._file._iter(self._keywords):
72
+ assert k is not None
73
+ yield k
62
74
 
63
75
  def __contains__(self, keyword: object) -> bool:
64
76
  return (*self._keywords, keyword) in self._file
@@ -77,7 +89,7 @@ class FoamFile(
77
89
  def __repr__(self) -> str:
78
90
  return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
79
91
 
80
- def as_dict(self) -> FoamDict._Dict:
92
+ def as_dict(self) -> FoamFileBase._Dict:
81
93
  """Return a nested dict representation of the dictionary."""
82
94
  ret = self._file.as_dict()
83
95
 
@@ -89,85 +101,111 @@ class FoamFile(
89
101
 
90
102
  return ret
91
103
 
92
- class Header(SubDict):
93
- """The header of an OpenFOAM file."""
104
+ def create(self, *, exist_ok: bool = False, parents: bool = False) -> Self:
105
+ """
106
+ Create the file.
107
+
108
+ Parameters
109
+ ----------
110
+ exist_ok : bool, optional
111
+ If False (the default), raise a FileExistsError if the file already exists.
112
+ If True, do nothing if the file already exists.
113
+ parents : bool, optional
114
+ If True, also create parent directories as needed.
115
+ """
116
+ if self.path.exists():
117
+ if not exist_ok:
118
+ raise FileExistsError(self.path)
119
+ else:
120
+ return self
94
121
 
95
- def __init__(self, _file: "FoamFile") -> None:
96
- super().__init__(_file, ("FoamFile",))
122
+ if parents:
123
+ self.path.parent.mkdir(parents=True, exist_ok=True)
97
124
 
98
- @property
99
- def version(self) -> float:
100
- """Alias of `self["version"]`."""
101
- ret = self["version"]
102
- if not isinstance(ret, float):
103
- raise TypeError("version is not a float")
104
- return ret
125
+ self.path.touch()
126
+ self._write_header()
105
127
 
106
- @version.setter
107
- def version(self, data: float) -> None:
108
- self["version"] = data
128
+ return self
109
129
 
110
- @property
111
- def format(self) -> str:
112
- """Alias of `self["format"]`."""
113
- ret = self["format"]
114
- if not isinstance(ret, str):
115
- raise TypeError("format is not a string")
116
- return ret
130
+ @property
131
+ def version(self) -> float:
132
+ """Alias of `self["FoamFile", "version"]`."""
133
+ ret = self["FoamFile", "version"]
134
+ if not isinstance(ret, float):
135
+ raise TypeError("version is not a float")
136
+ return ret
117
137
 
118
- @format.setter
119
- def format(self, data: str) -> None:
120
- self["format"] = data
138
+ @version.setter
139
+ def version(self, value: float) -> None:
140
+ self["FoamFile", "version"] = value
121
141
 
122
- @property
123
- def class_(self) -> str:
124
- """Alias of `self["class"]`."""
125
- ret = self["class"]
126
- if not isinstance(ret, str):
127
- raise TypeError("class is not a string")
128
- return ret
142
+ @property
143
+ def format(self) -> Literal["ascii", "binary"]:
144
+ """Alias of `self["FoamFile", "format"]`."""
145
+ ret = self["FoamFile", "format"]
146
+ if not isinstance(ret, str):
147
+ raise TypeError("format is not a string")
148
+ if ret not in ("ascii", "binary"):
149
+ raise ValueError("format is not 'ascii' or 'binary'")
150
+ return cast(Literal["ascii", "binary"], ret)
151
+
152
+ @format.setter
153
+ def format(self, value: Literal["ascii", "binary"]) -> None:
154
+ self["FoamFile", "format"] = value
129
155
 
130
- @class_.setter
131
- def class_(self, data: str) -> None:
132
- self["class"] = data
156
+ @property
157
+ def class_(self) -> str:
158
+ """Alias of `self["FoamFile", "class"]`."""
159
+ ret = self["FoamFile", "class"]
160
+ if not isinstance(ret, str):
161
+ raise TypeError("class is not a string")
162
+ return ret
133
163
 
134
- @property
135
- def location(self) -> str:
136
- """Alias of `self["location"]`."""
137
- ret = self["location"]
138
- if not isinstance(ret, str):
139
- raise TypeError("location is not a string")
140
- return ret
164
+ @class_.setter
165
+ def class_(self, value: str) -> None:
166
+ self["FoamFile", "class"] = value
141
167
 
142
- @location.setter
143
- def location(self, data: str) -> None:
144
- self["location"] = data
168
+ @property
169
+ def location(self) -> str:
170
+ """Alias of `self["FoamFile", "location"]`."""
171
+ ret = self["FoamFile", "location"]
172
+ if not isinstance(ret, str):
173
+ raise TypeError("location is not a string")
174
+ return ret
145
175
 
146
- @property
147
- def object(self) -> str:
148
- """Alias of `self["object"]`."""
149
- ret = self["object"]
150
- if not isinstance(ret, str):
151
- raise TypeError("object is not a string")
152
- return ret
176
+ @location.setter
177
+ def location(self, value: str) -> None:
178
+ self["FoamFile", "location"] = value
153
179
 
154
180
  @property
155
- def header(self) -> Header:
156
- """Alias of `self["FoamFile"]`."""
157
- ret = self["FoamFile"]
158
- if not isinstance(ret, FoamFile.Header):
159
- assert not isinstance(ret, FoamFile.SubDict)
160
- raise TypeError("FoamFile is not a dictionary")
181
+ def object_(self) -> str:
182
+ """Alias of `self["FoamFile", "object"]`."""
183
+ ret = self["FoamFile", "object"]
184
+ if not isinstance(ret, str):
185
+ raise TypeError("object is not a string")
161
186
  return ret
162
187
 
163
- @header.setter
164
- def header(self, data: FoamDict._Dict) -> None:
165
- self["FoamFile"] = data
188
+ @object_.setter
189
+ def object_(self, value: str) -> None:
190
+ self["FoamFile", "object"] = value
191
+
192
+ def _write_header(self) -> None:
193
+ assert "FoamFile" not in self
194
+ assert not self
195
+
196
+ self["FoamFile"] = {}
197
+ self.version = 2.0
198
+ self.format = "ascii"
199
+ self.class_ = "dictionary"
200
+ self.location = f'"{self.path.parent.name}"'
201
+ self.object_ = self.path.name
166
202
 
167
203
  def __getitem__(
168
- self, keywords: Union[str, Tuple[str, ...]]
204
+ self, keywords: Optional[Union[str, Tuple[str, ...]]]
169
205
  ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
170
- if not isinstance(keywords, tuple):
206
+ if not keywords:
207
+ keywords = ()
208
+ elif not isinstance(keywords, tuple):
171
209
  keywords = (keywords,)
172
210
 
173
211
  _, parsed = self._read()
@@ -175,28 +213,21 @@ class FoamFile(
175
213
  value = parsed[keywords]
176
214
 
177
215
  if value is ...:
178
- if keywords == ("FoamFile",):
179
- return FoamFile.Header(self)
180
- else:
181
- return FoamFile.SubDict(self, keywords)
216
+ return FoamFile.SubDict(self, keywords)
182
217
  else:
183
218
  return value
184
219
 
185
220
  def __setitem__(
186
- self, keywords: Union[str, Tuple[str, ...]], data: "FoamFile._SetData"
221
+ self, keywords: Optional[Union[str, Tuple[str, ...]]], data: "FoamFile._SetData"
187
222
  ) -> None:
188
223
  with self:
189
- if not isinstance(keywords, tuple):
224
+ if not keywords:
225
+ keywords = ()
226
+ elif not isinstance(keywords, tuple):
190
227
  keywords = (keywords,)
191
228
 
192
- if not self and keywords[0] != "FoamFile":
193
- self.header = {
194
- "version": 2.0,
195
- "format": "ascii",
196
- "class": "dictionary",
197
- "location": f'"{self.path.parent.name}"',
198
- "object": self.path.name,
199
- } # type: ignore [assignment]
229
+ if not self and "FoamFile" not in self and keywords[0] != "FoamFile":
230
+ self._write_header()
200
231
 
201
232
  kind = Kind.DEFAULT
202
233
  if keywords == ("internalField",) or (
@@ -204,35 +235,13 @@ class FoamFile(
204
235
  and keywords[0] == "boundaryField"
205
236
  and keywords[2] == "value"
206
237
  ):
207
- kind = (
208
- Kind.BINARY_FIELD if self.header.format == "binary" else Kind.FIELD
209
- )
238
+ kind = Kind.BINARY_FIELD if self.format == "binary" else Kind.FIELD
210
239
  elif keywords == ("dimensions",):
211
240
  kind = Kind.DIMENSIONS
212
241
 
213
- contents, parsed = self._read()
214
-
215
- if isinstance(data, Mapping):
216
- if isinstance(data, FoamDict):
217
- data = data.as_dict()
218
-
219
- start, end = parsed.entry_location(keywords, missing_ok=True)
220
-
221
- self._write(
222
- contents[:start]
223
- + (b"\n" if contents[start - 1 : start] != b"\n" else b"")
224
- + (b" " * (len(keywords) - 1) if keywords else b"")
225
- + dumpb({keywords[-1]: {}})
226
- + b"\n"
227
- + contents[end:]
228
- )
229
-
230
- for k, v in data.items():
231
- self[(*keywords, k)] = v
232
-
233
- elif (
242
+ if (
234
243
  kind == Kind.FIELD or kind == Kind.BINARY_FIELD
235
- ) and self.header.class_ == "dictionary":
244
+ ) and self.class_ == "dictionary":
236
245
  if not is_sequence(data):
237
246
  class_ = "volScalarField"
238
247
  elif (len(data) == 3 and not is_sequence(data[0])) or len(data[0]) == 3:
@@ -244,27 +253,65 @@ class FoamFile(
244
253
  else:
245
254
  class_ = "volScalarField"
246
255
 
247
- self.header.class_ = class_
256
+ self.class_ = class_
248
257
  self[keywords] = data
249
258
 
250
259
  else:
260
+ contents, parsed = self._read()
261
+
251
262
  start, end = parsed.entry_location(keywords, missing_ok=True)
252
263
 
253
- self._write(
254
- contents[:start]
255
- + (
256
- b"\n"
257
- if start > 0 and contents[start - 1 : start] != b"\n"
258
- else b""
264
+ before = contents[:start].rstrip() + b"\n"
265
+ if len(keywords) <= 1:
266
+ before += b"\n"
267
+
268
+ after = contents[end:]
269
+ if after.startswith(b"}"):
270
+ after = b" " * (len(keywords) - 2) + after
271
+ if not after or after[:1] != b"\n":
272
+ after = b"\n" + after
273
+ if len(keywords) <= 1 and len(after) > 1 and after[:2] != b"\n\n":
274
+ after = b"\n" + after
275
+
276
+ indentation = b" " * (len(keywords) - 1)
277
+
278
+ if isinstance(data, Mapping):
279
+ if isinstance(data, (FoamFile, FoamFile.SubDict)):
280
+ data = data.as_dict()
281
+
282
+ self._write(
283
+ before
284
+ + indentation
285
+ + dumpb(keywords[-1])
286
+ + b"\n"
287
+ + indentation
288
+ + b"{\n"
289
+ + indentation
290
+ + b"}"
291
+ + after
292
+ )
293
+
294
+ for k, v in data.items():
295
+ self[(*keywords, k)] = v
296
+
297
+ elif keywords:
298
+ self._write(
299
+ before
300
+ + indentation
301
+ + dumpb(keywords[-1])
302
+ + b" "
303
+ + dumpb(data, kind=kind)
304
+ + b";"
305
+ + after
259
306
  )
260
- + (b" " * (len(keywords) - 1) if keywords else b"")
261
- + dumpb({keywords[-1]: data}, kind=kind)
262
- + b"\n"
263
- + contents[end:]
264
- )
265
-
266
- def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
267
- if not isinstance(keywords, tuple):
307
+
308
+ else:
309
+ self._write(before + dumpb(data, kind=kind) + after)
310
+
311
+ def __delitem__(self, keywords: Optional[Union[str, Tuple[str, ...]]]) -> None:
312
+ if not keywords:
313
+ keywords = ()
314
+ elif not isinstance(keywords, tuple):
268
315
  keywords = (keywords,)
269
316
 
270
317
  contents, parsed = self._read()
@@ -273,21 +320,26 @@ class FoamFile(
273
320
 
274
321
  self._write(contents[:start] + contents[end:])
275
322
 
276
- def _iter(self, keywords: Union[str, Tuple[str, ...]] = ()) -> Iterator[str]:
277
- if not isinstance(keywords, tuple):
278
- keywords = (keywords,)
279
-
323
+ def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
280
324
  _, parsed = self._read()
281
325
 
282
- yield from (k[-1] for k in parsed if k[:-1] == keywords)
326
+ yield from (
327
+ k[-1] if k else None
328
+ for k in parsed
329
+ if k != ("FoamFile",) and k[:-1] == keywords
330
+ )
283
331
 
284
- def __iter__(self) -> Iterator[str]:
332
+ def __iter__(self) -> Iterator[Optional[str]]:
285
333
  return self._iter()
286
334
 
287
335
  def __contains__(self, keywords: object) -> bool:
288
- if not isinstance(keywords, tuple):
336
+ if not keywords:
337
+ keywords = ()
338
+ elif not isinstance(keywords, tuple):
289
339
  keywords = (keywords,)
340
+
290
341
  _, parsed = self._read()
342
+
291
343
  return keywords in parsed
292
344
 
293
345
  def __len__(self) -> int:
@@ -304,10 +356,12 @@ class FoamFile(
304
356
  def __fspath__(self) -> str:
305
357
  return str(self.path)
306
358
 
307
- def as_dict(self) -> FoamDict._Dict:
359
+ def as_dict(self) -> FoamFileBase._Dict:
308
360
  """Return a nested dict representation of the file."""
309
361
  _, parsed = self._read()
310
- return parsed.as_dict()
362
+ d = parsed.as_dict()
363
+ del d["FoamFile"]
364
+ return d
311
365
 
312
366
 
313
367
  class FoamFieldFile(FoamFile):
@@ -377,9 +431,11 @@ class FoamFieldFile(FoamFile):
377
431
  del self["value"]
378
432
 
379
433
  def __getitem__(
380
- self, keywords: Union[str, Tuple[str, ...]]
434
+ self, keywords: Optional[Union[str, Tuple[str, ...]]]
381
435
  ) -> Union[FoamFile.Data, FoamFile.SubDict]:
382
- if not isinstance(keywords, tuple):
436
+ if not keywords:
437
+ keywords = ()
438
+ elif not isinstance(keywords, tuple):
383
439
  keywords = (keywords,)
384
440
 
385
441
  ret = super().__getitem__(keywords)
foamlib/_files/_io.py CHANGED
@@ -18,7 +18,7 @@ else:
18
18
  from ._parsing import Parsed
19
19
 
20
20
 
21
- class FoamFileIO:
21
+ class _FoamFileIO:
22
22
  def __init__(self, path: Union[str, Path]) -> None:
23
23
  self.path = Path(path).absolute()
24
24
 
@@ -33,7 +33,7 @@ from pyparsing import (
33
33
  printables,
34
34
  )
35
35
 
36
- from ._base import FoamDict
36
+ from ._base import FoamFileBase
37
37
 
38
38
 
39
39
  def _list_of(entry: ParserElement) -> ParserElement:
@@ -132,11 +132,11 @@ _SWITCH = (
132
132
  ).set_parse_action(lambda: False)
133
133
  _DIMENSIONS = (
134
134
  Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
135
- ).set_parse_action(lambda tks: FoamDict.DimensionSet(*tks))
135
+ ).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
136
136
  _TENSOR = _list_of(common.number) | common.number
137
137
  _IDENTIFIER = Word(identchars + "$", printables, exclude_chars="{;}")
138
138
  _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
139
- lambda tks: FoamDict.Dimensioned(*reversed(tks.as_list()))
139
+ lambda tks: FoamFileBase.Dimensioned(*reversed(tks.as_list()))
140
140
  )
141
141
  _FIELD = (
142
142
  (Keyword("uniform").suppress() + _TENSOR)
@@ -164,7 +164,7 @@ _FILE = (
164
164
  Group(
165
165
  Located(
166
166
  _DATA_ENTRY[1, ...].set_parse_action(
167
- lambda tks: ["", tuple(tks) if len(tks) > 1 else tks[0]]
167
+ lambda tks: [None, tuple(tks) if len(tks) > 1 else tks[0]]
168
168
  )
169
169
  )
170
170
  )
@@ -178,11 +178,11 @@ _FILE = (
178
178
  )
179
179
 
180
180
 
181
- class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
181
+ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
182
182
  def __init__(self, contents: bytes) -> None:
183
183
  self._parsed: MutableMapping[
184
184
  Tuple[str, ...],
185
- Tuple[int, Union[FoamDict.Data, EllipsisType], int],
185
+ Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
186
186
  ] = {}
187
187
  self._end = len(contents)
188
188
 
@@ -194,10 +194,12 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
194
194
  @staticmethod
195
195
  def _flatten_result(
196
196
  parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
197
- ) -> Mapping[Tuple[str, ...], Tuple[int, Union[FoamDict.Data, EllipsisType], int]]:
197
+ ) -> Mapping[
198
+ Tuple[str, ...], Tuple[int, Union[FoamFileBase.Data, EllipsisType], int]
199
+ ]:
198
200
  ret: MutableMapping[
199
201
  Tuple[str, ...],
200
- Tuple[int, Union[FoamDict.Data, EllipsisType], int],
202
+ Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
201
203
  ] = {}
202
204
  start = parse_result.locn_start
203
205
  assert isinstance(start, int)
@@ -206,18 +208,26 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
206
208
  end = parse_result.locn_end
207
209
  assert isinstance(end, int)
208
210
  keyword, *data = item
209
- assert isinstance(keyword, str)
210
- ret[(*_keywords, keyword)] = (start, ..., end)
211
- for d in data:
212
- if isinstance(d, ParseResults):
213
- ret.update(Parsed._flatten_result(d, _keywords=(*_keywords, keyword)))
214
- else:
215
- ret[(*_keywords, keyword)] = (start, d, end)
211
+ if keyword is None:
212
+ assert not _keywords
213
+ assert len(data) == 1
214
+ assert not isinstance(data[0], ParseResults)
215
+ ret[()] = (start, data[0], end)
216
+ else:
217
+ assert isinstance(keyword, str)
218
+ ret[(*_keywords, keyword)] = (start, ..., end)
219
+ for d in data:
220
+ if isinstance(d, ParseResults):
221
+ ret.update(
222
+ Parsed._flatten_result(d, _keywords=(*_keywords, keyword))
223
+ )
224
+ else:
225
+ ret[(*_keywords, keyword)] = (start, d, end)
216
226
  return ret
217
227
 
218
228
  def __getitem__(
219
229
  self, keywords: Union[str, Tuple[str, ...]]
220
- ) -> Union[FoamDict.Data, EllipsisType]:
230
+ ) -> Union[FoamFileBase.Data, EllipsisType]:
221
231
  if isinstance(keywords, str):
222
232
  keywords = (keywords,)
223
233
 
@@ -252,8 +262,8 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
252
262
 
253
263
  return start, end
254
264
 
255
- def as_dict(self) -> FoamDict._Dict:
256
- ret: FoamDict._Dict = {}
265
+ def as_dict(self) -> FoamFileBase._Dict:
266
+ ret: FoamFileBase._Dict = {}
257
267
  for keywords, (_, data, _) in self._parsed.items():
258
268
  r = ret
259
269
  for k in keywords[:-1]:
@@ -9,7 +9,7 @@ else:
9
9
  from typing import Mapping
10
10
 
11
11
  from .._util import is_sequence
12
- from ._base import FoamDict
12
+ from ._base import FoamFileBase
13
13
 
14
14
  try:
15
15
  import numpy as np
@@ -28,7 +28,7 @@ class Kind(Enum):
28
28
 
29
29
 
30
30
  def dumpb(
31
- data: FoamDict._SetData,
31
+ data: FoamFileBase._SetData,
32
32
  *,
33
33
  kind: Kind = Kind.DEFAULT,
34
34
  ) -> bytes:
@@ -39,18 +39,16 @@ def dumpb(
39
39
  entries = []
40
40
  for k, v in data.items():
41
41
  b = dumpb(v, kind=kind)
42
- if not k:
43
- entries.append(b)
44
- elif isinstance(v, Mapping):
45
- entries.append(dumpb(k) + b"\n" + b"{\n" + b + b"\n}")
42
+ if isinstance(v, Mapping):
43
+ entries.append(dumpb(k) + b" {" + b + b"}")
46
44
  elif not b:
47
45
  entries.append(dumpb(k) + b";")
48
46
  else:
49
47
  entries.append(dumpb(k) + b" " + b + b";")
50
48
 
51
- return b"\n".join(entries)
49
+ return b" ".join(entries)
52
50
 
53
- elif isinstance(data, FoamDict.DimensionSet) or (
51
+ elif isinstance(data, FoamFileBase.DimensionSet) or (
54
52
  kind == Kind.DIMENSIONS and is_sequence(data) and len(data) == 7
55
53
  ):
56
54
  return b"[" + b" ".join(dumpb(v) for v in data) + b"]"
@@ -93,7 +91,7 @@ def dumpb(
93
91
  elif kind != Kind.SINGLE_ENTRY and isinstance(data, tuple):
94
92
  return b" ".join(dumpb(v) for v in data)
95
93
 
96
- elif isinstance(data, FoamDict.Dimensioned):
94
+ elif isinstance(data, FoamFileBase.Dimensioned):
97
95
  if data.name is not None:
98
96
  return (
99
97
  dumpb(data.name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.3.23
3
+ Version: 0.4.0
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
@@ -45,7 +45,7 @@ Requires-Dist: numpy <3,>=1 ; extra == 'numpy'
45
45
  Provides-Extra: test
46
46
  Requires-Dist: foamlib[numpy] ; extra == 'test'
47
47
  Requires-Dist: pytest <9,>=7 ; extra == 'test'
48
- Requires-Dist: pytest-asyncio <0.24,>=0.21 ; extra == 'test'
48
+ Requires-Dist: pytest-asyncio <0.25,>=0.21 ; extra == 'test'
49
49
  Requires-Dist: pytest-cov ; extra == 'test'
50
50
  Provides-Extra: typing
51
51
  Requires-Dist: foamlib[test] ; extra == 'typing'
@@ -0,0 +1,19 @@
1
+ foamlib/__init__.py,sha256=ZL6X6uzZgGj8D5Thi9ZuZyr1Mpo6f78yT_Rf4sBU12I,446
2
+ foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
3
+ foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
5
+ foamlib/_cases/_async.py,sha256=ehmAedDepDXaRdf8f0IznxDMYAQxoUWSIxbhxG_17Rw,6035
6
+ foamlib/_cases/_base.py,sha256=JrYJplmXV84qwgerdVSCHz2rCI-KWB4IvTBPMkYpecw,12657
7
+ foamlib/_cases/_sync.py,sha256=CloQgd-93jxfSXIt7J5NxcAu3N_iF3eXMKO-NfNOgi4,4522
8
+ foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
9
+ foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
10
+ foamlib/_files/_base.py,sha256=sy1RP08pdv079KD_UBy7xUYEnXyNK35AiKVcCalYmPU,1873
11
+ foamlib/_files/_files.py,sha256=2bvl2YZ0bqHpbQV35vzYO7wWNOo_2WDF9LbyLagIvBE,16193
12
+ foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
13
+ foamlib/_files/_parsing.py,sha256=bOJkqNUMUiXZJQXXEnIAb0bcdj_AgDmDhrr19oyY_Sw,8078
14
+ foamlib/_files/_serialization.py,sha256=3yb9fgjCpDoRfZoLsbZaIFrkZ3vGBzleFRw6IbaZuuY,3408
15
+ foamlib-0.4.0.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
16
+ foamlib-0.4.0.dist-info/METADATA,sha256=3DObrf-prhkIgLQ2crezzAVkKW4ZIuNrXEsbqT3ilUc,5457
17
+ foamlib-0.4.0.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
18
+ foamlib-0.4.0.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
19
+ foamlib-0.4.0.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- foamlib/__init__.py,sha256=pFmZVC3Cy4orJQeNLbr2JbQw3fVdOgShmuvNNIU0e9U,439
2
- foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
3
- foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
5
- foamlib/_cases/_async.py,sha256=ehmAedDepDXaRdf8f0IznxDMYAQxoUWSIxbhxG_17Rw,6035
6
- foamlib/_cases/_base.py,sha256=jfKevsV5CtxsyORvp-sYrdnW4pUxs6Or0I6nGXZVjII,12336
7
- foamlib/_cases/_sync.py,sha256=CloQgd-93jxfSXIt7J5NxcAu3N_iF3eXMKO-NfNOgi4,4522
8
- foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
9
- foamlib/_files/__init__.py,sha256=vDkPj8u8bX_I_m2YfeKvXBgwg8D1ufyFCfHGHKN3JPQ,140
10
- foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
11
- foamlib/_files/_files.py,sha256=BPRbZLclOmxemuBTXpEeNoiUMx3VL_FUCGsX1YlidcA,14635
12
- foamlib/_files/_io.py,sha256=hlcqQqU-1cdIbDc3YqxnMfxALo4SFAEcRIoZM2vMtnE,2083
13
- foamlib/_files/_parsing.py,sha256=3Hp4pdVgrneCtVJl4G65cQYwOA9CwUOcH5jtIfNY014,7738
14
- foamlib/_files/_serialization.py,sha256=LCeaLWtNvkcs0dfowL7nViiByxw7U_fvgueVjFliipU,3462
15
- foamlib-0.3.23.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
16
- foamlib-0.3.23.dist-info/METADATA,sha256=0i-sNTwsELdUk54SvL0Dz70CarcmkqU2_HYsLftSwps,5458
17
- foamlib-0.3.23.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
18
- foamlib-0.3.23.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
19
- foamlib-0.3.23.dist-info/RECORD,,