foamlib 0.3.0__py3-none-any.whl → 0.3.2__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,10 +1,10 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.2"
4
4
 
5
5
  from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
6
6
  from ._files import FoamDict, FoamFieldFile, FoamFile
7
- from ._util import CalledProcessError
7
+ from ._util import CalledProcessError, CalledProcessWarning
8
8
 
9
9
  __all__ = [
10
10
  "FoamCase",
@@ -14,4 +14,5 @@ __all__ = [
14
14
  "FoamFieldFile",
15
15
  "FoamDict",
16
16
  "CalledProcessError",
17
+ "CalledProcessWarning",
17
18
  ]
foamlib/_files/_base.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from abc import abstractmethod
3
3
  from dataclasses import dataclass
4
- from typing import Dict, NamedTuple, Optional, Union
4
+ from typing import Dict, NamedTuple, Optional, Tuple, Union
5
5
 
6
6
  if sys.version_info >= (3, 9):
7
7
  from collections.abc import Mapping, Sequence
@@ -10,7 +10,6 @@ else:
10
10
 
11
11
  try:
12
12
  import numpy as np
13
- from numpy.typing import NDArray
14
13
  except ModuleNotFoundError:
15
14
  pass
16
15
 
@@ -59,4 +58,16 @@ class FoamDict:
59
58
  """Return a nested dict representation of the dictionary."""
60
59
  raise NotImplementedError
61
60
 
62
- _SetData = Union[Data, "NDArray[np.generic]"]
61
+ _SetData = Union[
62
+ str,
63
+ int,
64
+ float,
65
+ bool,
66
+ Dimensioned,
67
+ DimensionSet,
68
+ Sequence["_SetData"],
69
+ Mapping[str, "_SetData"],
70
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
71
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
72
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
73
+ ]
foamlib/_files/_fields.py CHANGED
@@ -10,7 +10,6 @@ from ._files import FoamFile
10
10
 
11
11
  try:
12
12
  import numpy as np
13
- from numpy.typing import NDArray
14
13
  except ModuleNotFoundError:
15
14
  pass
16
15
 
@@ -58,7 +57,9 @@ class FoamFieldFile(FoamFile):
58
57
  int,
59
58
  float,
60
59
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
61
- "NDArray[np.generic]",
60
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
61
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
62
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
62
63
  ]:
63
64
  """Alias of `self["value"]`."""
64
65
  ret = self["value"]
@@ -73,7 +74,9 @@ class FoamFieldFile(FoamFile):
73
74
  int,
74
75
  float,
75
76
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
76
- "NDArray[np.generic]",
77
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
78
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
79
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
77
80
  ],
78
81
  ) -> None:
79
82
  self["value"] = value
@@ -128,7 +131,9 @@ class FoamFieldFile(FoamFile):
128
131
  int,
129
132
  float,
130
133
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
131
- "NDArray[np.generic]",
134
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
135
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
136
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
132
137
  ]:
133
138
  """Alias of `self["internalField"]`."""
134
139
  ret = self["internalField"]
@@ -143,7 +148,9 @@ class FoamFieldFile(FoamFile):
143
148
  int,
144
149
  float,
145
150
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
146
- "NDArray[np.generic]",
151
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
152
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
153
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
147
154
  ],
148
155
  ) -> None:
149
156
  self["internalField"] = value
foamlib/_files/_files.py CHANGED
@@ -12,7 +12,7 @@ else:
12
12
 
13
13
  from ._base import FoamDict
14
14
  from ._io import FoamFileIO
15
- from ._serialization import serialize_keyword_entry
15
+ from ._serialization import dumps
16
16
 
17
17
 
18
18
  class FoamFile(
@@ -134,7 +134,7 @@ class FoamFile(
134
134
  start, end = parsed.entry_location(keywords, missing_ok=True)
135
135
 
136
136
  self._write(
137
- f"{contents[:start]}\n{serialize_keyword_entry(keywords[-1], {})}\n{contents[end:]}"
137
+ f"{contents[:start]}\n{dumps({keywords[-1]: {}})}\n{contents[end:]}"
138
138
  )
139
139
 
140
140
  for k, v in data.items():
@@ -143,7 +143,7 @@ class FoamFile(
143
143
  start, end = parsed.entry_location(keywords, missing_ok=True)
144
144
 
145
145
  self._write(
146
- f"{contents[:start]}\n{serialize_keyword_entry(keywords[-1], data, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
146
+ f"{contents[:start]}\n{dumps({keywords[-1]: data}, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
147
147
  )
148
148
 
149
149
  def __setitem__(
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from contextlib import suppress
3
2
 
4
3
  if sys.version_info >= (3, 9):
5
4
  from collections.abc import Mapping
@@ -9,148 +8,92 @@ else:
9
8
  from .._util import is_sequence
10
9
  from ._base import FoamDict
11
10
 
11
+ try:
12
+ import numpy as np
12
13
 
13
- def _serialize_switch(data: FoamDict._SetData) -> str:
14
- if data is True:
15
- return "yes"
16
- elif data is False:
17
- return "no"
18
- else:
19
- raise TypeError(f"Not a bool: {type(data)}")
20
-
21
-
22
- def _serialize_list(
23
- data: FoamDict._SetData,
24
- ) -> str:
25
- if is_sequence(data):
26
- return f"({' '.join(_serialize_data_entry(v) for v in data)})"
27
- else:
28
- raise TypeError(f"Not a valid sequence: {type(data)}")
14
+ numpy = True
15
+ except ModuleNotFoundError:
16
+ numpy = False
29
17
 
30
18
 
31
- def _serialize_field(
19
+ def dumps(
32
20
  data: FoamDict._SetData,
21
+ *,
22
+ assume_field: bool = False,
23
+ assume_dimensions: bool = False,
24
+ assume_data_entries: bool = False,
33
25
  ) -> str:
34
- if is_sequence(data):
35
- try:
36
- s = _serialize_list(data)
37
- except TypeError:
38
- raise TypeError(f"Not a valid field: {type(data)}") from None
39
- else:
40
- if not is_sequence(data[0]) and len(data) < 10:
41
- return f"uniform {s}"
26
+ if numpy and isinstance(data, np.ndarray):
27
+ return dumps(
28
+ data.tolist(),
29
+ assume_field=assume_field,
30
+ assume_dimensions=assume_dimensions,
31
+ )
32
+
33
+ elif isinstance(data, Mapping):
34
+ entries = []
35
+ for k, v in data.items():
36
+ s = dumps(
37
+ v,
38
+ assume_field=assume_field,
39
+ assume_dimensions=assume_dimensions,
40
+ assume_data_entries=True,
41
+ )
42
+ if isinstance(v, Mapping):
43
+ entries.append(f"{k}\n{{\n{s}\n}}")
44
+ elif s:
45
+ entries.append(f"{k} {s};")
42
46
  else:
43
- if not is_sequence(data[0]):
44
- kind = "scalar"
45
- elif len(data[0]) == 3:
46
- kind = "vector"
47
- elif len(data[0]) == 6:
48
- kind = "symmTensor"
49
- elif len(data[0]) == 9:
50
- kind = "tensor"
51
- else:
52
- raise TypeError(
53
- f"Unsupported sequence length for field: {len(data[0])}"
54
- )
55
- return f"nonuniform List<{kind}> {len(data)}{s}"
56
- else:
57
- return f"uniform {data}"
58
-
47
+ entries.append(f"{k};")
48
+ return "\n".join(entries)
59
49
 
60
- def _serialize_dimensions(
61
- data: FoamDict._SetData,
62
- ) -> str:
63
- if is_sequence(data) and len(data) == 7:
50
+ elif isinstance(data, FoamDict.DimensionSet) or (
51
+ assume_dimensions and is_sequence(data) and len(data) == 7
52
+ ):
64
53
  return f"[{' '.join(str(v) for v in data)}]"
65
- else:
66
- raise TypeError(f"Not a valid dimension set: {type(data)}")
67
54
 
55
+ elif assume_field and isinstance(data, (int, float)):
56
+ return f"uniform {data}"
68
57
 
69
- def _serialize_dimensioned(
70
- data: FoamDict._SetData,
71
- ) -> str:
72
- if isinstance(data, FoamDict.Dimensioned):
58
+ elif assume_field and is_sequence(data):
59
+ if isinstance(data[0], (int, float)) and len(data) in (3, 6, 9):
60
+ return f"uniform {dumps(data)}"
61
+ elif isinstance(data[0], (int, float)):
62
+ return f"nonuniform List<scalar> {len(data)}{dumps(data)}"
63
+ elif len(data[0]) == 3:
64
+ return f"nonuniform List<vector> {len(data)}{dumps(data)}"
65
+ elif len(data[0]) == 6:
66
+ return f"nonuniform List<symmTensor> {len(data)}{dumps(data)}"
67
+ elif len(data[0]) == 9:
68
+ return f"nonuniform List<tensor> {len(data)}{dumps(data)}"
69
+ else:
70
+ return dumps(
71
+ data,
72
+ assume_dimensions=assume_dimensions,
73
+ assume_data_entries=assume_data_entries,
74
+ )
75
+
76
+ elif assume_data_entries and isinstance(data, tuple):
77
+ return " ".join(
78
+ dumps(v, assume_field=assume_field, assume_dimensions=assume_dimensions)
79
+ for v in data
80
+ )
81
+
82
+ elif isinstance(data, FoamDict.Dimensioned):
73
83
  if data.name is not None:
74
- return f"{data.name} {_serialize_dimensions(data.dimensions)} {_serialize_data_entry(data.value)}"
84
+ return f"{data.name} {dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
75
85
  else:
76
- return f"{_serialize_dimensions(data.dimensions)} {_serialize_data_entry(data.value)}"
77
- else:
78
- raise TypeError(f"Not a valid dimensioned value: {type(data)}")
79
-
80
-
81
- def _serialize_data_entry(
82
- data: FoamDict._SetData,
83
- *,
84
- assume_field: bool = False,
85
- assume_dimensions: bool = False,
86
- ) -> str:
87
- if isinstance(data, FoamDict.DimensionSet) or assume_dimensions:
88
- with suppress(TypeError):
89
- return _serialize_dimensions(data)
86
+ return (
87
+ f"{dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
88
+ )
90
89
 
91
- if assume_field:
92
- with suppress(TypeError):
93
- return _serialize_field(data)
90
+ elif is_sequence(data):
91
+ return f"({' '.join(dumps(v) for v in data)})"
94
92
 
95
- with suppress(TypeError):
96
- return _serialize_dimensioned(data)
97
-
98
- with suppress(TypeError):
99
- return _serialize_list(data)
100
-
101
- with suppress(TypeError):
102
- return _serialize_switch(data)
103
-
104
- with suppress(TypeError):
105
- return _serialize_dictionary(data)
106
-
107
- return str(data)
108
-
109
-
110
- def _serialize_data_entries(
111
- data: FoamDict._SetData,
112
- *,
113
- assume_field: bool = False,
114
- assume_dimensions: bool = False,
115
- ) -> str:
116
- if isinstance(data, FoamDict.DimensionSet) or assume_dimensions:
117
- with suppress(TypeError):
118
- return _serialize_dimensions(data)
119
-
120
- if assume_field:
121
- with suppress(TypeError):
122
- return _serialize_field(data)
123
-
124
- if isinstance(data, tuple):
125
- return " ".join(_serialize_data_entry(v) for v in data)
126
-
127
- return _serialize_data_entry(data)
128
-
129
-
130
- def _serialize_dictionary(
131
- data: FoamDict._SetData,
132
- ) -> str:
133
- if isinstance(data, Mapping):
134
- return "\n".join(serialize_keyword_entry(k, v) for k, v in data.items())
135
- else:
136
- raise TypeError(f"Not a valid dictionary: {type(data)}")
137
-
138
-
139
- def serialize_keyword_entry(
140
- keyword: str,
141
- data: FoamDict._SetData,
142
- *,
143
- assume_field: bool = False,
144
- assume_dimensions: bool = False,
145
- ) -> str:
146
- with suppress(TypeError):
147
- return f"{keyword}\n{{\n{_serialize_dictionary(data)}\n}}"
148
-
149
- data = _serialize_data_entries(
150
- data, assume_field=assume_field, assume_dimensions=assume_dimensions
151
- )
93
+ elif data is True:
94
+ return "yes"
95
+ elif data is False:
96
+ return "no"
152
97
 
153
- if not data:
154
- return f"{keyword};"
155
98
  else:
156
- return f"{keyword} {data};"
99
+ return str(data)
foamlib/_util.py CHANGED
@@ -2,7 +2,8 @@ import asyncio
2
2
  import subprocess
3
3
  import sys
4
4
  from pathlib import Path
5
- from typing import Any, Union
5
+ from typing import Any, Optional, Union
6
+ from warnings import warn
6
7
 
7
8
  if sys.version_info >= (3, 9):
8
9
  from collections.abc import Mapping, Sequence
@@ -14,31 +15,36 @@ if sys.version_info >= (3, 10):
14
15
  else:
15
16
  from typing_extensions import TypeGuard
16
17
 
17
- try:
18
- import numpy as np
19
- except ModuleNotFoundError:
20
- numpy = False
21
- else:
22
- numpy = True
23
-
24
18
 
25
19
  def is_sequence(
26
20
  value: Any,
27
- ) -> TypeGuard[Union["Sequence[Any]", "np.ndarray[Any, Any]"]]:
28
- return (
29
- isinstance(value, Sequence)
30
- and not isinstance(value, str)
31
- or numpy
32
- and isinstance(value, np.ndarray)
33
- )
21
+ ) -> TypeGuard[Sequence[Any]]:
22
+ return isinstance(value, Sequence) and not isinstance(value, str)
34
23
 
35
24
 
36
- CalledProcessError = subprocess.CalledProcessError
25
+ class CalledProcessError(subprocess.CalledProcessError):
26
+ """Exception raised when a process fails and `check=True`."""
37
27
 
38
- if sys.version_info >= (3, 9):
39
- CompletedProcess = subprocess.CompletedProcess[str]
40
- else:
41
- CompletedProcess = subprocess.CompletedProcess
28
+ def __str__(self) -> str:
29
+ msg = super().__str__()
30
+ if self.stderr:
31
+ msg += f"\n{self.stderr}"
32
+ return msg
33
+
34
+
35
+ class CalledProcessWarning(Warning):
36
+ """Warning raised when a process prints to stderr and `check=True`."""
37
+
38
+
39
+ def _check(
40
+ retcode: int,
41
+ cmd: Union[Sequence[Union[str, Path]], str, Path],
42
+ stderr: Optional[str],
43
+ ) -> None:
44
+ if retcode != 0:
45
+ raise CalledProcessError(retcode, cmd, None, stderr)
46
+ elif stderr:
47
+ warn(f"Command {cmd} printed to stderr.\n{stderr}", CalledProcessWarning)
42
48
 
43
49
 
44
50
  def run_process(
@@ -47,7 +53,7 @@ def run_process(
47
53
  check: bool = True,
48
54
  cwd: Union[None, str, Path] = None,
49
55
  env: Union[None, Mapping[str, str]] = None,
50
- ) -> CompletedProcess:
56
+ ) -> None:
51
57
  shell = not is_sequence(cmd)
52
58
 
53
59
  if sys.version_info < (3, 8):
@@ -60,14 +66,14 @@ def run_process(
60
66
  cmd,
61
67
  cwd=cwd,
62
68
  env=env,
63
- stdout=asyncio.subprocess.DEVNULL,
64
- stderr=asyncio.subprocess.PIPE,
69
+ stdout=subprocess.DEVNULL,
70
+ stderr=subprocess.PIPE if check else subprocess.DEVNULL,
65
71
  text=True,
66
72
  shell=shell,
67
- check=check,
68
73
  )
69
74
 
70
- return proc
75
+ if check:
76
+ _check(proc.returncode, cmd, proc.stderr)
71
77
 
72
78
 
73
79
  async def run_process_async(
@@ -76,14 +82,14 @@ async def run_process_async(
76
82
  check: bool = True,
77
83
  cwd: Union[None, str, Path] = None,
78
84
  env: Union[None, Mapping[str, str]] = None,
79
- ) -> CompletedProcess:
85
+ ) -> None:
80
86
  if not is_sequence(cmd):
81
87
  proc = await asyncio.create_subprocess_shell(
82
88
  str(cmd),
83
89
  cwd=cwd,
84
90
  env=env,
85
91
  stdout=asyncio.subprocess.DEVNULL,
86
- stderr=asyncio.subprocess.PIPE,
92
+ stderr=asyncio.subprocess.PIPE if check else asyncio.subprocess.DEVNULL,
87
93
  )
88
94
 
89
95
  else:
@@ -94,7 +100,7 @@ async def run_process_async(
94
100
  cwd=cwd,
95
101
  env=env,
96
102
  stdout=asyncio.subprocess.DEVNULL,
97
- stderr=asyncio.subprocess.PIPE,
103
+ stderr=asyncio.subprocess.PIPE if check else asyncio.subprocess.DEVNULL,
98
104
  )
99
105
 
100
106
  stdout, stderr = await proc.communicate()
@@ -102,9 +108,5 @@ async def run_process_async(
102
108
  assert stdout is None
103
109
  assert proc.returncode is not None
104
110
 
105
- ret = CompletedProcess(cmd, proc.returncode, None, stderr.decode())
106
-
107
111
  if check:
108
- ret.check_returncode()
109
-
110
- return ret
112
+ _check(proc.returncode, cmd, stderr.decode())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -0,0 +1,16 @@
1
+ foamlib/__init__.py,sha256=RKXFmt6ZgoSEiW8H5loLPjL4VY_2oIla-Dd0C7pEUKY,431
2
+ foamlib/_cases.py,sha256=SkUthKb98zXPMYACaGyfGC0Fo1jzD6cC4qEGesHfNfc,20554
3
+ foamlib/_util.py,sha256=0HJ9n8QG9K7jC_xUDzOJo_NA2DBCJKJRLpmd5Yu42kU,2901
4
+ foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ foamlib/_files/__init__.py,sha256=m4y_7wFV2Voly1aJrTYALjT3S1Aq7EbaijxdGFbJrmU,160
6
+ foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
7
+ foamlib/_files/_fields.py,sha256=jVHOHEV70i6QrWzqhF_W5CywUZyJxN5Eh5S1MrNn1G0,5632
8
+ foamlib/_files/_files.py,sha256=aS8VDGifnDSX40HdgT512fuhQ9iGlPls3p133d64Rak,5975
9
+ foamlib/_files/_io.py,sha256=alxMyxQh0zb6BZYqomZwOo9dQujMpRuS5yfpIKBEnaM,1976
10
+ foamlib/_files/_parsing.py,sha256=blyt1kpYruoW5I6DMDg8jhg6f5oz7gzedmAImuJ5b4k,5966
11
+ foamlib/_files/_serialization.py,sha256=H9STIpUxEBRwXh5WnqtjicCkGTpTYSwfWUwbVJf9x9A,2961
12
+ foamlib-0.3.2.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
13
+ foamlib-0.3.2.dist-info/METADATA,sha256=DBX2JpB09uqjPowv_ShTp7BZ9ryRI_WqVmcpp7T2-DQ,4650
14
+ foamlib-0.3.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
+ foamlib-0.3.2.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
16
+ foamlib-0.3.2.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- foamlib/__init__.py,sha256=erRnbigN4XBPDC0ykv-Lw7LVsw_xtuJ7h0tgkBTNQZ8,381
2
- foamlib/_cases.py,sha256=SkUthKb98zXPMYACaGyfGC0Fo1jzD6cC4qEGesHfNfc,20554
3
- foamlib/_util.py,sha256=PBTpBwt_j1GXASncSDZUR8pH2u_h8UyJXm8GeFKebTY,2552
4
- foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- foamlib/_files/__init__.py,sha256=m4y_7wFV2Voly1aJrTYALjT3S1Aq7EbaijxdGFbJrmU,160
6
- foamlib/_files/_base.py,sha256=vONemjJA-NxRuIjuxwz8PynjtF1UImrfBQm1EbgZEec,1768
7
- foamlib/_files/_fields.py,sha256=jZZiMoBn0tixGnUGpITpRxcauqhPvYk65PIktBFvEHk,5073
8
- foamlib/_files/_files.py,sha256=1fV1TUwtNsxzt1R1YKMbB_AGpjMl-W-7YQXl7Ynoyzc,6025
9
- foamlib/_files/_io.py,sha256=alxMyxQh0zb6BZYqomZwOo9dQujMpRuS5yfpIKBEnaM,1976
10
- foamlib/_files/_parsing.py,sha256=blyt1kpYruoW5I6DMDg8jhg6f5oz7gzedmAImuJ5b4k,5966
11
- foamlib/_files/_serialization.py,sha256=kbl1OTvWqHwcDwS2k5CuqjDRmiv3NpA9hIXyv4boWBA,4268
12
- foamlib-0.3.0.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
13
- foamlib-0.3.0.dist-info/METADATA,sha256=hl5EKAs6_mbW-1qcuIFst4I6tDQqCkG4LyZlx3DMhZY,4650
14
- foamlib-0.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
- foamlib-0.3.0.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
16
- foamlib-0.3.0.dist-info/RECORD,,