foamlib 0.2.4__tar.gz → 0.2.6__tar.gz

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.
Files changed (27) hide show
  1. {foamlib-0.2.4 → foamlib-0.2.6}/PKG-INFO +9 -10
  2. {foamlib-0.2.4 → foamlib-0.2.6}/README.md +1 -1
  3. foamlib-0.2.6/foamlib/__init__.py +15 -0
  4. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_cases.py +46 -85
  5. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_dictionaries/__init__.py +1 -1
  6. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_dictionaries/_base.py +8 -4
  7. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_dictionaries/_files.py +21 -32
  8. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_dictionaries/_parsing.py +17 -17
  9. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/_dictionaries/_serialization.py +14 -23
  10. foamlib-0.2.4/foamlib/_subprocesses.py → foamlib-0.2.6/foamlib/_util.py +48 -14
  11. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib.egg-info/PKG-INFO +9 -10
  12. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib.egg-info/SOURCES.txt +2 -7
  13. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib.egg-info/requires.txt +8 -6
  14. {foamlib-0.2.4 → foamlib-0.2.6}/pyproject.toml +8 -23
  15. foamlib-0.2.6/tests/test_serialization.py +52 -0
  16. foamlib-0.2.4/foamlib/__init__.py +0 -13
  17. foamlib-0.2.4/tests/test_basic.py +0 -8
  18. foamlib-0.2.4/tests/test_dictionaries.py +0 -205
  19. foamlib-0.2.4/tests/test_flange.py +0 -60
  20. foamlib-0.2.4/tests/test_flange_async.py +0 -65
  21. foamlib-0.2.4/tests/test_pitz.py +0 -37
  22. foamlib-0.2.4/tests/test_pitz_async.py +0 -42
  23. {foamlib-0.2.4 → foamlib-0.2.6}/LICENSE.txt +0 -0
  24. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib/py.typed +0 -0
  25. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib.egg-info/dependency_links.txt +0 -0
  26. {foamlib-0.2.4 → foamlib-0.2.6}/foamlib.egg-info/top_level.txt +0 -0
  27. {foamlib-0.2.4 → foamlib-0.2.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.2.4
3
+ Version: 0.2.6
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
@@ -27,15 +27,14 @@ Description-Content-Type: text/markdown
27
27
  License-File: LICENSE.txt
28
28
  Requires-Dist: aioshutil<2,>=1
29
29
  Requires-Dist: pyparsing<4,>=3
30
- Requires-Dist: typing-extensions<5,>=4
30
+ Requires-Dist: typing-extensions<5,>=4; python_version < "3.11"
31
31
  Provides-Extra: lint
32
- Requires-Dist: mypy<2,>=1; extra == "lint"
33
- Requires-Dist: pytest<9,>=7; extra == "lint"
34
- Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "lint"
35
- Requires-Dist: numpy<2,>=1; extra == "lint"
36
- Requires-Dist: black; extra == "lint"
37
- Requires-Dist: flake8; extra == "lint"
38
- Requires-Dist: Flake8-pyproject; extra == "lint"
32
+ Requires-Dist: ruff; extra == "lint"
33
+ Provides-Extra: typing
34
+ Requires-Dist: mypy<2,>=1; extra == "typing"
35
+ Requires-Dist: pytest<9,>=7; extra == "typing"
36
+ Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "typing"
37
+ Requires-Dist: numpy<2,>=1; extra == "typing"
39
38
  Provides-Extra: test
40
39
  Requires-Dist: pytest<9,>=7; extra == "test"
41
40
  Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "test"
@@ -52,7 +51,7 @@ Requires-Dist: numpy<2,>=1; extra == "docs"
52
51
  [![CI](https://github.com/gerlero/foamlib/actions/workflows/ci.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
53
52
  [![Codecov](https://codecov.io/gh/gerlero/foamlib/branch/main/graph/badge.svg)](https://codecov.io/gh/gerlero/foamlib)
54
53
  [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
55
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
54
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
56
55
  [![PyPI](https://img.shields.io/pypi/v/foamlib)](https://pypi.org/project/foamlib/)
57
56
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/foamlib)](https://pypi.org/project/foamlib/)
58
57
  [![Docker image](https://img.shields.io/badge/docker%20image-gerlero%2Ffoamlib-informational)](https://hub.docker.com/r/gerlero/foamlib/)
@@ -4,7 +4,7 @@
4
4
  [![CI](https://github.com/gerlero/foamlib/actions/workflows/ci.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
5
5
  [![Codecov](https://codecov.io/gh/gerlero/foamlib/branch/main/graph/badge.svg)](https://codecov.io/gh/gerlero/foamlib)
6
6
  [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
7
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
8
  [![PyPI](https://img.shields.io/pypi/v/foamlib)](https://pypi.org/project/foamlib/)
9
9
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/foamlib)](https://pypi.org/project/foamlib/)
10
10
  [![Docker image](https://img.shields.io/badge/docker%20image-gerlero%2Ffoamlib-informational)](https://hub.docker.com/r/gerlero/foamlib/)
@@ -0,0 +1,15 @@
1
+ """A Python interface for interacting with OpenFOAM."""
2
+
3
+ __version__ = "0.2.6"
4
+
5
+ from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
6
+ from ._dictionaries import FoamDictionaryBase, FoamFieldFile, FoamFile
7
+
8
+ __all__ = [
9
+ "FoamCase",
10
+ "AsyncFoamCase",
11
+ "FoamCaseBase",
12
+ "FoamFile",
13
+ "FoamFieldFile",
14
+ "FoamDictionaryBase",
15
+ ]
@@ -1,27 +1,34 @@
1
- import os
2
1
  import asyncio
3
2
  import multiprocessing
3
+ import os
4
4
  import shutil
5
-
6
- from pathlib import Path
5
+ import sys
7
6
  from contextlib import asynccontextmanager
7
+ from pathlib import Path
8
8
  from typing import (
9
9
  Optional,
10
10
  Union,
11
- Collection,
12
- Mapping,
13
- Set,
14
- Sequence,
15
- AsyncGenerator,
16
- Callable,
17
- Iterator,
18
11
  overload,
19
12
  )
20
13
 
14
+ if sys.version_info >= (3, 9):
15
+ from collections.abc import (
16
+ AsyncGenerator,
17
+ Callable,
18
+ Collection,
19
+ Iterator,
20
+ Mapping,
21
+ Sequence,
22
+ Set,
23
+ )
24
+ else:
25
+ from typing import AbstractSet as Set
26
+ from typing import AsyncGenerator, Callable, Collection, Iterator, Mapping, Sequence
27
+
21
28
  import aioshutil
22
29
 
23
- from ._subprocesses import run_process, run_process_async, CalledProcessError
24
- from ._dictionaries import FoamFile, FoamFieldFile
30
+ from ._dictionaries import FoamFieldFile, FoamFile
31
+ from ._util import CalledProcessError, is_sequence, run_process, run_process_async
25
32
 
26
33
 
27
34
  class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
@@ -46,16 +53,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
46
53
 
47
54
  @property
48
55
  def time(self) -> float:
49
- """
50
- The time that corresponds to this directory.
51
- """
56
+ """The time that corresponds to this directory."""
52
57
  return float(self.path.name)
53
58
 
54
59
  @property
55
60
  def name(self) -> str:
56
- """
57
- The name of this time directory.
58
- """
61
+ """The name of this time directory."""
59
62
  return self.path.name
60
63
 
61
64
  def __getitem__(self, key: str) -> FoamFieldFile:
@@ -132,7 +135,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
132
135
  has_decompose_par_dict = (self.path / "system" / "decomposeParDict").is_file()
133
136
  has_block_mesh_dict = (self.path / "system" / "blockMeshDict").is_file()
134
137
 
135
- paths: Set[Path] = set()
138
+ paths = set()
136
139
 
137
140
  for p in self.path.iterdir():
138
141
  if p.is_dir():
@@ -164,9 +167,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
164
167
  return ignore
165
168
 
166
169
  def _clean_script(self) -> Optional[Path]:
167
- """
168
- Return the path to the (All)clean script, or None if no clean script is found.
169
- """
170
+ """Return the path to the (All)clean script, or None if no clean script is found."""
170
171
  clean = self.path / "clean"
171
172
  all_clean = self.path / "Allclean"
172
173
 
@@ -178,9 +179,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
178
179
  return None
179
180
 
180
181
  def _run_script(self, *, parallel: Optional[bool]) -> Optional[Path]:
181
- """
182
- Return the path to the (All)run script, or None if no run script is found.
183
- """
182
+ """Return the path to the (All)run script, or None if no run script is found."""
184
183
  run = self.path / "run"
185
184
  run_parallel = self.path / "run-parallel"
186
185
  all_run = self.path / "Allrun"
@@ -205,9 +204,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
205
204
  return None
206
205
 
207
206
  def _env(self) -> Mapping[str, str]:
208
- """
209
- Return the environment variables for this case.
210
- """
207
+ """Return the environment variables for this case."""
211
208
  env = os.environ.copy()
212
209
  env["PWD"] = str(self.path)
213
210
  return env
@@ -215,7 +212,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
215
212
  def _parallel_cmd(
216
213
  self, cmd: Union[Sequence[Union[str, Path]], str, Path]
217
214
  ) -> Union[Sequence[Union[str, Path]], str]:
218
- if isinstance(cmd, str) or not isinstance(cmd, Sequence):
215
+ if not is_sequence(cmd):
219
216
  return f"mpiexec -np {self._nprocessors} {cmd} -parallel"
220
217
  else:
221
218
  return [
@@ -229,22 +226,16 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
229
226
 
230
227
  @property
231
228
  def name(self) -> str:
232
- """
233
- The name of the case.
234
- """
229
+ """The name of the case."""
235
230
  return self.path.name
236
231
 
237
232
  def file(self, path: Union[Path, str]) -> FoamFile:
238
- """
239
- Return a FoamFile object for the given path in the case.
240
- """
233
+ """Return a FoamFile object for the given path in the case."""
241
234
  return FoamFile(self.path / path)
242
235
 
243
236
  @property
244
237
  def _nsubdomains(self) -> Optional[int]:
245
- """
246
- Return the number of subdomains as set in the decomposeParDict, or None if no decomposeParDict is found.
247
- """
238
+ """Return the number of subdomains as set in the decomposeParDict, or None if no decomposeParDict is found."""
248
239
  try:
249
240
  nsubdomains = self.decompose_par_dict["numberOfSubdomains"]
250
241
  if not isinstance(nsubdomains, int):
@@ -257,16 +248,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
257
248
 
258
249
  @property
259
250
  def _nprocessors(self) -> int:
260
- """
261
- Return the number of processor directories in the case.
262
- """
251
+ """Return the number of processor directories in the case."""
263
252
  return len(list(self.path.glob("processor*")))
264
253
 
265
254
  @property
266
255
  def application(self) -> str:
267
- """
268
- The application name as set in the controlDict.
269
- """
256
+ """The application name as set in the controlDict."""
270
257
  application = self.control_dict["application"]
271
258
  if not isinstance(application, str):
272
259
  raise TypeError(f"application in {self.control_dict} is not a string")
@@ -274,51 +261,37 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
274
261
 
275
262
  @property
276
263
  def control_dict(self) -> FoamFile:
277
- """
278
- The controlDict file.
279
- """
264
+ """The controlDict file."""
280
265
  return self.file("system/controlDict")
281
266
 
282
267
  @property
283
268
  def fv_schemes(self) -> FoamFile:
284
- """
285
- The fvSchemes file.
286
- """
269
+ """The fvSchemes file."""
287
270
  return self.file("system/fvSchemes")
288
271
 
289
272
  @property
290
273
  def fv_solution(self) -> FoamFile:
291
- """
292
- The fvSolution file.
293
- """
274
+ """The fvSolution file."""
294
275
  return self.file("system/fvSolution")
295
276
 
296
277
  @property
297
278
  def decompose_par_dict(self) -> FoamFile:
298
- """
299
- The decomposeParDict file.
300
- """
279
+ """The decomposeParDict file."""
301
280
  return self.file("system/decomposeParDict")
302
281
 
303
282
  @property
304
283
  def block_mesh_dict(self) -> FoamFile:
305
- """
306
- The blockMeshDict file.
307
- """
284
+ """The blockMeshDict file."""
308
285
  return self.file("system/blockMeshDict")
309
286
 
310
287
  @property
311
288
  def transport_properties(self) -> FoamFile:
312
- """
313
- The transportProperties file.
314
- """
289
+ """The transportProperties file."""
315
290
  return self.file("constant/transportProperties")
316
291
 
317
292
  @property
318
293
  def turbulence_properties(self) -> FoamFile:
319
- """
320
- The turbulenceProperties file.
321
- """
294
+ """The turbulenceProperties file."""
322
295
  return self.file("constant/turbulenceProperties")
323
296
 
324
297
  def __fspath__(self) -> str:
@@ -391,7 +364,7 @@ class FoamCase(FoamCaseBase):
391
364
  )
392
365
  except CalledProcessError as e:
393
366
  raise RuntimeError(
394
- f"{e.cmd} failed with return code {e.returncode}\n{e.stderr.decode()}"
367
+ f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
395
368
  ) from None
396
369
 
397
370
  else:
@@ -424,21 +397,15 @@ class FoamCase(FoamCaseBase):
424
397
  )
425
398
 
426
399
  def block_mesh(self, *, check: bool = True) -> None:
427
- """
428
- Run blockMesh on this case.
429
- """
400
+ """Run blockMesh on this case."""
430
401
  self.run(["blockMesh"], check=check)
431
402
 
432
403
  def decompose_par(self, *, check: bool = True) -> None:
433
- """
434
- Decompose this case for parallel running.
435
- """
404
+ """Decompose this case for parallel running."""
436
405
  self.run(["decomposePar"], check=check)
437
406
 
438
407
  def reconstruct_par(self, *, check: bool = True) -> None:
439
- """
440
- Reconstruct this case after parallel running.
441
- """
408
+ """Reconstruct this case after parallel running."""
442
409
  self.run(["reconstructPar"], check=check)
443
410
 
444
411
  def copy(self, dest: Union[Path, str]) -> "FoamCase":
@@ -566,7 +533,7 @@ class AsyncFoamCase(FoamCaseBase):
566
533
  )
567
534
  except CalledProcessError as e:
568
535
  raise RuntimeError(
569
- f"{e.cmd} failed with return code {e.returncode}\n{e.stderr.decode()}"
536
+ f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
570
537
  ) from None
571
538
 
572
539
  else:
@@ -616,21 +583,15 @@ class AsyncFoamCase(FoamCaseBase):
616
583
  )
617
584
 
618
585
  async def block_mesh(self, *, check: bool = True) -> None:
619
- """
620
- Run blockMesh on this case.
621
- """
586
+ """Run blockMesh on this case."""
622
587
  await self.run(["blockMesh"], check=check)
623
588
 
624
589
  async def decompose_par(self, *, check: bool = True) -> None:
625
- """
626
- Decompose this case for parallel running.
627
- """
590
+ """Decompose this case for parallel running."""
628
591
  await self.run(["decomposePar"], check=check)
629
592
 
630
593
  async def reconstruct_par(self, *, check: bool = True) -> None:
631
- """
632
- Reconstruct this case after parallel running.
633
- """
594
+ """Reconstruct this case after parallel running."""
634
595
  await self.run(["reconstructPar"], check=check)
635
596
 
636
597
  async def copy(self, dest: Union[Path, str]) -> "AsyncFoamCase":
@@ -1,5 +1,5 @@
1
- from ._files import FoamFile, FoamFieldFile
2
1
  from ._base import FoamDictionaryBase
2
+ from ._files import FoamFieldFile, FoamFile
3
3
 
4
4
  __all__ = [
5
5
  "FoamFile",
@@ -1,6 +1,12 @@
1
+ import sys
1
2
  from abc import abstractmethod
2
3
  from dataclasses import dataclass
3
- from typing import Dict, NamedTuple, Optional, Sequence, Union
4
+ from typing import Dict, NamedTuple, Optional, Union
5
+
6
+ if sys.version_info >= (3, 9):
7
+ from collections.abc import Sequence
8
+ else:
9
+ from typing import Sequence
4
10
 
5
11
 
6
12
  class FoamDictionaryBase:
@@ -37,7 +43,5 @@ class FoamDictionaryBase:
37
43
 
38
44
  @abstractmethod
39
45
  def as_dict(self) -> _Dict:
40
- """
41
- Return a nested dict representation of the dictionary.
42
- """
46
+ """Return a nested dict representation of the dictionary."""
43
47
  raise NotImplementedError
@@ -1,21 +1,22 @@
1
+ import sys
1
2
  from pathlib import Path
2
3
  from typing import (
3
4
  Any,
4
- Iterator,
5
- Mapping,
6
- MutableMapping,
7
5
  Optional,
8
- Sequence,
9
6
  Tuple,
10
7
  Union,
11
8
  cast,
12
9
  )
13
10
 
14
- from typing_extensions import Self
11
+ if sys.version_info >= (3, 9):
12
+ from collections.abc import Iterator, Mapping, MutableMapping, Sequence
13
+ else:
14
+ from typing import Iterator, Mapping, MutableMapping, Sequence
15
15
 
16
- from ._base import FoamDictionaryBase
17
- from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
18
- from ._serialization import serialize_entry
16
+ if sys.version_info >= (3, 11):
17
+ from typing import Self
18
+ else:
19
+ from typing_extensions import Self
19
20
 
20
21
  try:
21
22
  import numpy as np
@@ -23,6 +24,10 @@ try:
23
24
  except ModuleNotFoundError:
24
25
  pass
25
26
 
27
+ from ._base import FoamDictionaryBase
28
+ from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
29
+ from ._serialization import serialize_entry
30
+
26
31
 
27
32
  class _FoamFileBase:
28
33
  def __init__(self, path: Union[str, Path]) -> None:
@@ -93,9 +98,7 @@ class FoamFile(
93
98
  FoamDictionaryBase,
94
99
  MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
95
100
  ):
96
- """
97
- An OpenFOAM dictionary within a file as a mutable mapping.
98
- """
101
+ """An OpenFOAM dictionary within a file as a mutable mapping."""
99
102
 
100
103
  def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
101
104
  self._file = _file
@@ -148,9 +151,7 @@ class FoamFile(
148
151
  return f"{type(self).__qualname__}({self._file}, {self._keywords})"
149
152
 
150
153
  def as_dict(self) -> FoamDictionaryBase._Dict:
151
- """
152
- Return a nested dict representation of the dictionary.
153
- """
154
+ """Return a nested dict representation of the dictionary."""
154
155
  ret = self._file.as_dict()
155
156
 
156
157
  for k in self._keywords:
@@ -258,9 +259,7 @@ class FoamFile(
258
259
  return f"{type(self).__name__}({self.path})"
259
260
 
260
261
  def as_dict(self) -> FoamDictionaryBase._Dict:
261
- """
262
- Return a nested dict representation of the file.
263
- """
262
+ """Return a nested dict representation of the file."""
264
263
  _, parsed = self._read()
265
264
  return as_dict(parsed)
266
265
 
@@ -283,9 +282,7 @@ class FoamFieldFile(FoamFile):
283
282
 
284
283
  @property
285
284
  def type(self) -> str:
286
- """
287
- Alias of `self["type"]`.
288
- """
285
+ """Alias of `self["type"]`."""
289
286
  ret = self["type"]
290
287
  if not isinstance(ret, str):
291
288
  raise TypeError("type is not a string")
@@ -304,9 +301,7 @@ class FoamFieldFile(FoamFile):
304
301
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
305
302
  "NDArray[np.generic]",
306
303
  ]:
307
- """
308
- Alias of `self["value"]`.
309
- """
304
+ """Alias of `self["value"]`."""
310
305
  ret = self["value"]
311
306
  if not isinstance(ret, (int, float, Sequence)):
312
307
  raise TypeError("value is not a field")
@@ -355,9 +350,7 @@ class FoamFieldFile(FoamFile):
355
350
 
356
351
  @property
357
352
  def dimensions(self) -> FoamFile.DimensionSet:
358
- """
359
- Alias of `self["dimensions"]`.
360
- """
353
+ """Alias of `self["dimensions"]`."""
361
354
  ret = self["dimensions"]
362
355
  if not isinstance(ret, FoamFile.DimensionSet):
363
356
  raise TypeError("dimensions is not a DimensionSet")
@@ -378,9 +371,7 @@ class FoamFieldFile(FoamFile):
378
371
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
379
372
  "NDArray[np.generic]",
380
373
  ]:
381
- """
382
- Alias of `self["internalField"]`.
383
- """
374
+ """Alias of `self["internalField"]`."""
384
375
  ret = self["internalField"]
385
376
  if not isinstance(ret, (int, float, Sequence)):
386
377
  raise TypeError("internalField is not a field")
@@ -400,9 +391,7 @@ class FoamFieldFile(FoamFile):
400
391
 
401
392
  @property
402
393
  def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
403
- """
404
- Alias of `self["boundaryField"]`.
405
- """
394
+ """Alias of `self["boundaryField"]`."""
406
395
  ret = self["boundaryField"]
407
396
  if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
408
397
  assert not isinstance(ret, FoamFile.Dictionary)
@@ -1,4 +1,10 @@
1
- from typing import Mapping, MutableMapping, Optional, Sequence, Tuple
1
+ import sys
2
+ from typing import Optional, Tuple
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
2
8
 
3
9
  from pyparsing import (
4
10
  Dict,
@@ -9,8 +15,8 @@ from pyparsing import (
9
15
  Literal,
10
16
  Located,
11
17
  Opt,
12
- ParseResults,
13
18
  ParserElement,
19
+ ParseResults,
14
20
  QuotedString,
15
21
  Word,
16
22
  c_style_comment,
@@ -22,8 +28,11 @@ from pyparsing import (
22
28
 
23
29
  from ._base import FoamDictionaryBase
24
30
 
25
- _YES = Keyword("yes").set_parse_action(lambda: True)
26
- _NO = Keyword("no").set_parse_action(lambda: False)
31
+ _SWITCH = (
32
+ Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
33
+ ).set_parse_action(lambda: True) | (
34
+ Keyword("no") | Keyword("false") | Keyword("off") | Keyword("n") | Keyword("f")
35
+ ).set_parse_action(lambda: False)
27
36
  _DIMENSIONS = (
28
37
  Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
29
38
  ).set_parse_action(lambda tks: FoamDictionaryBase.DimensionSet(*tks))
@@ -58,9 +67,7 @@ _FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
58
67
  _TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
59
68
  _ITEM = Forward()
60
69
  _LIST = _list_of(_ITEM)
61
- _ITEM <<= (
62
- _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
63
- )
70
+ _ITEM <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
64
71
  _TOKENS = (
65
72
  QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
66
73
  )[2, ...].set_parse_action(lambda tks: " ".join(tks))
@@ -124,9 +131,7 @@ def get_value(
124
131
  parsed: Parsed,
125
132
  keywords: Tuple[str, ...],
126
133
  ) -> Optional[FoamDictionaryBase.Value]:
127
- """
128
- Value of an entry.
129
- """
134
+ """Value of an entry."""
130
135
  _, value, _ = parsed[keywords]
131
136
  return value
132
137
 
@@ -137,9 +142,7 @@ def get_entry_locn(
137
142
  *,
138
143
  missing_ok: bool = False,
139
144
  ) -> Tuple[int, int]:
140
- """
141
- Location of an entry or where it should be inserted.
142
- """
145
+ """Location of an entry or where it should be inserted."""
143
146
  try:
144
147
  start, _, end = parsed[keywords]
145
148
  except KeyError:
@@ -158,12 +161,9 @@ def get_entry_locn(
158
161
 
159
162
 
160
163
  def as_dict(parsed: Parsed) -> FoamDictionaryBase._Dict:
161
- """
162
- Return a nested dict representation of the file.
163
- """
164
+ """Return a nested dict representation of the file."""
164
165
  ret: FoamDictionaryBase._Dict = {}
165
166
  for keywords, (_, value, _) in parsed.items():
166
-
167
167
  r = ret
168
168
  for k in keywords[:-1]:
169
169
  assert isinstance(r, dict)
@@ -1,26 +1,17 @@
1
+ import sys
1
2
  from contextlib import suppress
2
- from typing import Any, Mapping, Sequence
3
+ from typing import Any
3
4
 
4
- from ._base import FoamDictionaryBase
5
-
6
- try:
7
- import numpy as np
8
- except ModuleNotFoundError:
9
- numpy = False
5
+ if sys.version_info >= (3, 9):
6
+ from collections.abc import Mapping
10
7
  else:
11
- numpy = True
8
+ from typing import Mapping
12
9
 
13
-
14
- def _is_sequence(value: Any) -> bool:
15
- return (
16
- isinstance(value, Sequence)
17
- and not isinstance(value, str)
18
- or numpy
19
- and isinstance(value, np.ndarray)
20
- )
10
+ from .._util import is_sequence
11
+ from ._base import FoamDictionaryBase
21
12
 
22
13
 
23
- def _serialize_bool(value: Any) -> str:
14
+ def _serialize_switch(value: Any) -> str:
24
15
  if value is True:
25
16
  return "yes"
26
17
  elif value is False:
@@ -30,23 +21,23 @@ def _serialize_bool(value: Any) -> str:
30
21
 
31
22
 
32
23
  def _serialize_list(value: Any) -> str:
33
- if _is_sequence(value):
24
+ if is_sequence(value):
34
25
  return f"({' '.join(_serialize_value(v) for v in value)})"
35
26
  else:
36
27
  raise TypeError(f"Not a valid sequence: {type(value)}")
37
28
 
38
29
 
39
30
  def _serialize_field(value: Any) -> str:
40
- if _is_sequence(value):
31
+ if is_sequence(value):
41
32
  try:
42
33
  s = _serialize_list(value)
43
34
  except TypeError:
44
35
  raise TypeError(f"Not a valid field: {type(value)}") from None
45
36
  else:
46
- if len(value) < 10:
37
+ if not is_sequence(value[0]) and len(value) < 10:
47
38
  return f"uniform {s}"
48
39
  else:
49
- if isinstance(value[0], (int, float)):
40
+ if not is_sequence(value[0]):
50
41
  kind = "scalar"
51
42
  elif len(value[0]) == 3:
52
43
  kind = "vector"
@@ -64,7 +55,7 @@ def _serialize_field(value: Any) -> str:
64
55
 
65
56
 
66
57
  def _serialize_dimensions(value: Any) -> str:
67
- if _is_sequence(value) and len(value) == 7:
58
+ if is_sequence(value) and len(value) == 7:
68
59
  return f"[{' '.join(str(v) for v in value)}]"
69
60
  else:
70
61
  raise TypeError(f"Not a valid dimension set: {type(value)}")
@@ -98,7 +89,7 @@ def _serialize_value(
98
89
  return _serialize_list(value)
99
90
 
100
91
  with suppress(TypeError):
101
- return _serialize_bool(value)
92
+ return _serialize_switch(value)
102
93
 
103
94
  return str(value)
104
95