foamlib 0.8.5__py3-none-any.whl → 0.8.7__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 +1 -1
- foamlib/_cases/_async.py +3 -0
- foamlib/_cases/_run.py +55 -26
- foamlib/_cases/_slurm.py +3 -3
- foamlib/_cases/_subprocess.py +122 -86
- foamlib/_cases/_sync.py +3 -0
- foamlib/_cases/_util.py +41 -1
- foamlib/_files/_files.py +5 -5
- foamlib/_files/_parsing.py +27 -7
- {foamlib-0.8.5.dist-info → foamlib-0.8.7.dist-info}/METADATA +2 -1
- foamlib-0.8.7.dist-info/RECORD +20 -0
- foamlib-0.8.5.dist-info/RECORD +0 -20
- {foamlib-0.8.5.dist-info → foamlib-0.8.7.dist-info}/WHEEL +0 -0
- {foamlib-0.8.5.dist-info → foamlib-0.8.7.dist-info}/licenses/LICENSE.txt +0 -0
foamlib/__init__.py
CHANGED
foamlib/_cases/_async.py
CHANGED
foamlib/_cases/_run.py
CHANGED
@@ -10,13 +10,16 @@ from contextlib import contextmanager
|
|
10
10
|
from pathlib import Path
|
11
11
|
from typing import IO, TYPE_CHECKING, Any
|
12
12
|
|
13
|
+
from rich.progress import Progress
|
14
|
+
|
15
|
+
from ._util import SingletonContextManager
|
16
|
+
|
13
17
|
if sys.version_info >= (3, 9):
|
14
18
|
from collections.abc import (
|
15
19
|
Callable,
|
16
20
|
Collection,
|
17
21
|
Coroutine,
|
18
22
|
Generator,
|
19
|
-
Mapping,
|
20
23
|
Sequence,
|
21
24
|
)
|
22
25
|
from collections.abc import Set as AbstractSet
|
@@ -27,7 +30,6 @@ else:
|
|
27
30
|
Collection,
|
28
31
|
Coroutine,
|
29
32
|
Generator,
|
30
|
-
Mapping,
|
31
33
|
Sequence,
|
32
34
|
)
|
33
35
|
|
@@ -68,6 +70,10 @@ class FoamCaseRunBase(FoamCaseBase):
|
|
68
70
|
|
69
71
|
return ret
|
70
72
|
|
73
|
+
_SHELL = ("bash", "-c")
|
74
|
+
|
75
|
+
__progress = SingletonContextManager(Progress)
|
76
|
+
|
71
77
|
def __delitem__(self, key: int | float | str) -> None:
|
72
78
|
shutil.rmtree(self[key].path)
|
73
79
|
|
@@ -255,38 +261,60 @@ class FoamCaseRunBase(FoamCaseBase):
|
|
255
261
|
|
256
262
|
return script
|
257
263
|
|
258
|
-
def __env(self, *, shell: bool) -> Mapping[str, str] | None:
|
259
|
-
sip_workaround = os.environ.get(
|
260
|
-
"FOAM_LD_LIBRARY_PATH", ""
|
261
|
-
) and not os.environ.get("DYLD_LIBRARY_PATH", "")
|
262
|
-
|
263
|
-
if not shell or sip_workaround:
|
264
|
-
env = os.environ.copy()
|
265
|
-
|
266
|
-
if not shell:
|
267
|
-
env["PWD"] = str(self.path)
|
268
|
-
|
269
|
-
if sip_workaround:
|
270
|
-
env["DYLD_LIBRARY_PATH"] = env["FOAM_LD_LIBRARY_PATH"]
|
271
|
-
|
272
|
-
return env
|
273
|
-
return None
|
274
|
-
|
275
264
|
@contextmanager
|
276
265
|
def __output(
|
277
266
|
self, cmd: Sequence[str | os.PathLike[str]] | str, *, log: bool
|
278
|
-
) -> Generator[tuple[int | IO[
|
267
|
+
) -> Generator[tuple[int | IO[str], int | IO[str]], None, None]:
|
279
268
|
if log:
|
280
269
|
if isinstance(cmd, str):
|
281
270
|
name = shlex.split(cmd)[0]
|
282
271
|
else:
|
283
272
|
name = Path(cmd[0]).name if isinstance(cmd[0], os.PathLike) else cmd[0]
|
284
273
|
|
285
|
-
with (self.path / f"log.{name}").open("
|
274
|
+
with (self.path / f"log.{name}").open("a") as stdout:
|
286
275
|
yield stdout, STDOUT
|
287
276
|
else:
|
288
277
|
yield DEVNULL, DEVNULL
|
289
278
|
|
279
|
+
@contextmanager
|
280
|
+
def __process_stdout(
|
281
|
+
self, cmd: Sequence[str | os.PathLike[str]] | str
|
282
|
+
) -> Generator[Callable[[str], None], None, None]:
|
283
|
+
if isinstance(cmd, str):
|
284
|
+
name = shlex.split(cmd)[0]
|
285
|
+
else:
|
286
|
+
name = Path(cmd[0]).name if isinstance(cmd[0], os.PathLike) else cmd[0]
|
287
|
+
|
288
|
+
try:
|
289
|
+
with self.control_dict as control_dict:
|
290
|
+
if control_dict["stopAt"] == "endTime":
|
291
|
+
control_dict_end_time = control_dict["endTime"]
|
292
|
+
if isinstance(control_dict_end_time, (int, float)):
|
293
|
+
end_time = control_dict_end_time
|
294
|
+
else:
|
295
|
+
end_time = None
|
296
|
+
else:
|
297
|
+
end_time = None
|
298
|
+
except (KeyError, FileNotFoundError):
|
299
|
+
end_time = None
|
300
|
+
|
301
|
+
with self.__progress as progress:
|
302
|
+
task = progress.add_task(f"({self.name}) Running {name}...", total=None)
|
303
|
+
|
304
|
+
def process_stdout(line: str) -> None:
|
305
|
+
if line.startswith("Time = "):
|
306
|
+
try:
|
307
|
+
time = float(line.split()[2])
|
308
|
+
except ValueError:
|
309
|
+
progress.update(task)
|
310
|
+
else:
|
311
|
+
progress.update(task, completed=time, total=end_time)
|
312
|
+
else:
|
313
|
+
progress.update(task)
|
314
|
+
|
315
|
+
yield process_stdout
|
316
|
+
progress.update(task, completed=1, total=1)
|
317
|
+
|
290
318
|
def __mkrundir(self) -> Path:
|
291
319
|
d = Path(os.environ["FOAM_RUN"], "foamlib")
|
292
320
|
d.mkdir(parents=True, exist_ok=True)
|
@@ -379,15 +407,16 @@ class FoamCaseRunBase(FoamCaseBase):
|
|
379
407
|
if cpus is None:
|
380
408
|
cpus = 1
|
381
409
|
|
382
|
-
with self.__output(cmd, log=log) as (stdout, stderr)
|
410
|
+
with self.__output(cmd, log=log) as (stdout, stderr), self.__process_stdout(
|
411
|
+
cmd
|
412
|
+
) as process_stdout:
|
383
413
|
if parallel:
|
384
414
|
if isinstance(cmd, str):
|
385
415
|
cmd = [
|
386
416
|
"mpiexec",
|
387
417
|
"-n",
|
388
418
|
str(cpus),
|
389
|
-
|
390
|
-
"-c",
|
419
|
+
*FoamCaseRunBase._SHELL,
|
391
420
|
f"{cmd} -parallel",
|
392
421
|
]
|
393
422
|
else:
|
@@ -396,11 +425,11 @@ class FoamCaseRunBase(FoamCaseBase):
|
|
396
425
|
yield self._run(
|
397
426
|
cmd,
|
398
427
|
cpus=cpus,
|
428
|
+
case=self,
|
399
429
|
check=check,
|
400
|
-
cwd=self.path,
|
401
|
-
env=self.__env(shell=isinstance(cmd, str)),
|
402
430
|
stdout=stdout,
|
403
431
|
stderr=stderr,
|
432
|
+
process_stdout=process_stdout,
|
404
433
|
**kwargs,
|
405
434
|
)
|
406
435
|
|
foamlib/_cases/_slurm.py
CHANGED
@@ -31,10 +31,10 @@ class AsyncSlurmFoamCase(AsyncFoamCase):
|
|
31
31
|
await AsyncFoamCase._run(cmd, cpus=cpus, **kwargs)
|
32
32
|
return
|
33
33
|
|
34
|
-
if
|
35
|
-
|
36
|
-
cmd = ["/bin/sh", "-c", cmd]
|
34
|
+
if isinstance(cmd, str):
|
35
|
+
cmd = [*AsyncSlurmFoamCase._SHELL, cmd]
|
37
36
|
|
37
|
+
if cpus >= 1:
|
38
38
|
if cpus == 1:
|
39
39
|
cmd = ["srun", *cmd]
|
40
40
|
|
foamlib/_cases/_subprocess.py
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
+
import os
|
5
|
+
import selectors
|
4
6
|
import subprocess
|
5
7
|
import sys
|
6
|
-
from io import
|
7
|
-
from
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
import os
|
8
|
+
from io import StringIO
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import IO
|
11
11
|
|
12
12
|
if sys.version_info >= (3, 9):
|
13
|
-
from collections.abc import Mapping, Sequence
|
13
|
+
from collections.abc import Callable, Mapping, Sequence
|
14
14
|
else:
|
15
|
-
from typing import Mapping, Sequence
|
15
|
+
from typing import Callable, Mapping, Sequence
|
16
16
|
|
17
17
|
CompletedProcess = subprocess.CompletedProcess
|
18
18
|
|
@@ -32,46 +32,72 @@ PIPE = subprocess.PIPE
|
|
32
32
|
STDOUT = subprocess.STDOUT
|
33
33
|
|
34
34
|
|
35
|
+
def _env(case: os.PathLike[str]) -> Mapping[str, str]:
|
36
|
+
env = os.environ.copy()
|
37
|
+
|
38
|
+
env["PWD"] = str(Path(case))
|
39
|
+
|
40
|
+
if os.environ.get("FOAM_LD_LIBRARY_PATH", "") and not os.environ.get(
|
41
|
+
"DYLD_LIBRARY_PATH", ""
|
42
|
+
):
|
43
|
+
env["DYLD_LIBRARY_PATH"] = env["FOAM_LD_LIBRARY_PATH"]
|
44
|
+
|
45
|
+
return env
|
46
|
+
|
47
|
+
|
35
48
|
def run_sync(
|
36
|
-
cmd: Sequence[str | os.PathLike[str]]
|
49
|
+
cmd: Sequence[str | os.PathLike[str]],
|
37
50
|
*,
|
51
|
+
case: os.PathLike[str],
|
38
52
|
check: bool = True,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
if not isinstance(cmd, str) and sys.version_info < (3, 8):
|
53
|
+
stdout: int | IO[str] = DEVNULL,
|
54
|
+
stderr: int | IO[str] = STDOUT,
|
55
|
+
process_stdout: Callable[[str], None] = lambda _: None,
|
56
|
+
) -> CompletedProcess[str]:
|
57
|
+
if sys.version_info < (3, 8):
|
45
58
|
cmd = [str(arg) for arg in cmd]
|
46
59
|
|
47
|
-
|
60
|
+
with subprocess.Popen(
|
48
61
|
cmd,
|
49
|
-
cwd=
|
50
|
-
env=
|
51
|
-
stdout=
|
62
|
+
cwd=case,
|
63
|
+
env=_env(case),
|
64
|
+
stdout=PIPE,
|
52
65
|
stderr=PIPE,
|
53
|
-
|
54
|
-
)
|
55
|
-
|
56
|
-
if stderr == STDOUT:
|
57
|
-
stderr = stdout
|
58
|
-
if stderr not in (PIPE, DEVNULL):
|
59
|
-
stderr_copy = BytesIO()
|
60
|
-
|
61
|
-
assert not isinstance(stderr, int)
|
62
|
-
if stderr is None:
|
63
|
-
stderr = sys.stderr.buffer
|
64
|
-
|
66
|
+
text=True,
|
67
|
+
) as proc:
|
68
|
+
assert proc.stdout is not None
|
65
69
|
assert proc.stderr is not None
|
66
|
-
for line in proc.stderr:
|
67
|
-
stderr.write(line)
|
68
|
-
stderr_copy.write(line)
|
69
70
|
|
70
|
-
output
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
output = StringIO() if stdout is PIPE else None
|
72
|
+
error = StringIO()
|
73
|
+
|
74
|
+
if stderr is STDOUT:
|
75
|
+
stderr = stdout
|
76
|
+
|
77
|
+
with selectors.DefaultSelector() as selector:
|
78
|
+
selector.register(proc.stdout, selectors.EVENT_READ)
|
79
|
+
selector.register(proc.stderr, selectors.EVENT_READ)
|
80
|
+
open_streams = {proc.stdout, proc.stderr}
|
81
|
+
while open_streams:
|
82
|
+
for key, _ in selector.select():
|
83
|
+
assert key.fileobj in open_streams
|
84
|
+
line = key.fileobj.readline() # type: ignore [union-attr]
|
85
|
+
if not line:
|
86
|
+
selector.unregister(key.fileobj)
|
87
|
+
open_streams.remove(key.fileobj) # type: ignore [arg-type]
|
88
|
+
elif key.fileobj is proc.stdout:
|
89
|
+
process_stdout(line)
|
90
|
+
if output is not None:
|
91
|
+
output.write(line)
|
92
|
+
if stdout not in (DEVNULL, PIPE):
|
93
|
+
assert not isinstance(stdout, int)
|
94
|
+
stdout.write(line)
|
95
|
+
else:
|
96
|
+
assert key.fileobj is proc.stderr
|
97
|
+
error.write(line)
|
98
|
+
if stderr not in (DEVNULL, PIPE):
|
99
|
+
assert not isinstance(stderr, int)
|
100
|
+
stderr.write(line)
|
75
101
|
|
76
102
|
assert proc.returncode is not None
|
77
103
|
|
@@ -79,74 +105,84 @@ def run_sync(
|
|
79
105
|
raise CalledProcessError(
|
80
106
|
returncode=proc.returncode,
|
81
107
|
cmd=cmd,
|
82
|
-
output=output,
|
83
|
-
stderr=error,
|
108
|
+
output=output.getvalue() if output is not None else None,
|
109
|
+
stderr=error.getvalue(),
|
84
110
|
)
|
85
111
|
|
86
112
|
return CompletedProcess(
|
87
|
-
cmd,
|
113
|
+
cmd,
|
114
|
+
returncode=proc.returncode,
|
115
|
+
stdout=output.getvalue() if output is not None else None,
|
116
|
+
stderr=error.getvalue(),
|
88
117
|
)
|
89
118
|
|
90
119
|
|
91
120
|
async def run_async(
|
92
|
-
cmd: Sequence[str | os.PathLike[str]]
|
121
|
+
cmd: Sequence[str | os.PathLike[str]],
|
93
122
|
*,
|
123
|
+
case: os.PathLike[str],
|
94
124
|
check: bool = True,
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
proc = await asyncio.create_subprocess_shell(
|
102
|
-
cmd,
|
103
|
-
cwd=cwd,
|
104
|
-
env=env,
|
105
|
-
stdout=stdout,
|
106
|
-
stderr=PIPE,
|
107
|
-
)
|
125
|
+
stdout: int | IO[str] = DEVNULL,
|
126
|
+
stderr: int | IO[str] = STDOUT,
|
127
|
+
process_stdout: Callable[[str], None] = lambda _: None,
|
128
|
+
) -> CompletedProcess[str]:
|
129
|
+
if sys.version_info < (3, 8):
|
130
|
+
cmd = [str(arg) for arg in cmd]
|
108
131
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
stdout=stdout,
|
117
|
-
stderr=PIPE,
|
118
|
-
)
|
132
|
+
proc = await asyncio.create_subprocess_exec(
|
133
|
+
*cmd,
|
134
|
+
cwd=case,
|
135
|
+
env=_env(case),
|
136
|
+
stdout=PIPE,
|
137
|
+
stderr=PIPE,
|
138
|
+
)
|
119
139
|
|
120
|
-
if stderr
|
140
|
+
if stderr is STDOUT:
|
121
141
|
stderr = stdout
|
122
|
-
if stderr not in (PIPE, DEVNULL):
|
123
|
-
stderr_copy = BytesIO()
|
124
|
-
|
125
|
-
assert not isinstance(stderr, int)
|
126
|
-
if stderr is None:
|
127
|
-
stderr = sys.stderr.buffer
|
128
|
-
|
129
|
-
assert proc.stderr is not None
|
130
|
-
async for line in proc.stderr:
|
131
|
-
stderr.write(line)
|
132
|
-
stderr_copy.write(line)
|
133
|
-
|
134
|
-
output, _ = await proc.communicate()
|
135
|
-
assert not _
|
136
|
-
error = stderr_copy.getvalue()
|
137
|
-
else:
|
138
|
-
output, error = await proc.communicate()
|
139
142
|
|
143
|
+
output = StringIO() if stdout is PIPE else None
|
144
|
+
error = StringIO()
|
145
|
+
|
146
|
+
async def tee_stdout() -> None:
|
147
|
+
while True:
|
148
|
+
assert proc.stdout is not None
|
149
|
+
line = (await proc.stdout.readline()).decode()
|
150
|
+
if not line:
|
151
|
+
break
|
152
|
+
process_stdout(line)
|
153
|
+
if output is not None:
|
154
|
+
output.write(line)
|
155
|
+
if stdout not in (DEVNULL, PIPE):
|
156
|
+
assert not isinstance(stdout, int)
|
157
|
+
stdout.write(line)
|
158
|
+
|
159
|
+
async def tee_stderr() -> None:
|
160
|
+
while True:
|
161
|
+
assert proc.stderr is not None
|
162
|
+
line = (await proc.stderr.readline()).decode()
|
163
|
+
if not line:
|
164
|
+
break
|
165
|
+
error.write(line)
|
166
|
+
if stderr not in (DEVNULL, PIPE):
|
167
|
+
assert not isinstance(stderr, int)
|
168
|
+
stderr.write(line)
|
169
|
+
|
170
|
+
await asyncio.gather(tee_stdout(), tee_stderr())
|
171
|
+
|
172
|
+
await proc.wait()
|
140
173
|
assert proc.returncode is not None
|
141
174
|
|
142
175
|
if check and proc.returncode != 0:
|
143
176
|
raise CalledProcessError(
|
144
177
|
returncode=proc.returncode,
|
145
178
|
cmd=cmd,
|
146
|
-
output=output,
|
147
|
-
stderr=error,
|
179
|
+
output=output.getvalue() if output is not None else None,
|
180
|
+
stderr=error.getvalue(),
|
148
181
|
)
|
149
182
|
|
150
183
|
return CompletedProcess(
|
151
|
-
cmd,
|
184
|
+
cmd,
|
185
|
+
returncode=proc.returncode,
|
186
|
+
stdout=output.getvalue() if output is not None else None,
|
187
|
+
stderr=error.getvalue(),
|
152
188
|
)
|
foamlib/_cases/_sync.py
CHANGED
foamlib/_cases/_util.py
CHANGED
@@ -2,7 +2,17 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import functools
|
4
4
|
import sys
|
5
|
-
|
5
|
+
import threading
|
6
|
+
from typing import (
|
7
|
+
TYPE_CHECKING,
|
8
|
+
Any,
|
9
|
+
AsyncContextManager,
|
10
|
+
Callable,
|
11
|
+
ContextManager,
|
12
|
+
Generic,
|
13
|
+
TypeVar,
|
14
|
+
cast,
|
15
|
+
)
|
6
16
|
|
7
17
|
if TYPE_CHECKING:
|
8
18
|
from types import TracebackType
|
@@ -54,3 +64,33 @@ def awaitableasynccontextmanager(
|
|
54
64
|
return _AwaitableAsyncContextManager(cm(*args, **kwargs))
|
55
65
|
|
56
66
|
return f
|
67
|
+
|
68
|
+
|
69
|
+
class SingletonContextManager(Generic[R]):
|
70
|
+
def __init__(self, factory: Callable[[], ContextManager[R]]) -> None:
|
71
|
+
self._factory = factory
|
72
|
+
self._users = 0
|
73
|
+
self._cm: ContextManager[R] | None = None
|
74
|
+
self._ret: R | None = None
|
75
|
+
self._lock = threading.Lock()
|
76
|
+
|
77
|
+
def __enter__(self) -> R:
|
78
|
+
with self._lock:
|
79
|
+
if self._users == 0:
|
80
|
+
self._cm = self._factory()
|
81
|
+
self._ret = self._cm.__enter__()
|
82
|
+
self._users += 1
|
83
|
+
return cast("R", self._ret)
|
84
|
+
|
85
|
+
def __exit__(
|
86
|
+
self,
|
87
|
+
exc_type: type[BaseException] | None,
|
88
|
+
exc_val: BaseException | None,
|
89
|
+
exc_tb: TracebackType | None,
|
90
|
+
) -> bool | None:
|
91
|
+
with self._lock:
|
92
|
+
self._users -= 1
|
93
|
+
if self._users == 0:
|
94
|
+
assert self._cm is not None
|
95
|
+
return self._cm.__exit__(exc_type, exc_val, exc_tb)
|
96
|
+
return False
|
foamlib/_files/_files.py
CHANGED
@@ -100,9 +100,9 @@ class FoamFile(
|
|
100
100
|
assert isinstance(ret, dict)
|
101
101
|
v = ret[k]
|
102
102
|
assert isinstance(v, dict)
|
103
|
-
ret = cast(File, v)
|
103
|
+
ret = cast("File", v)
|
104
104
|
|
105
|
-
return cast(Dict_, ret)
|
105
|
+
return cast("Dict_", ret)
|
106
106
|
|
107
107
|
@property
|
108
108
|
def version(self) -> float:
|
@@ -127,7 +127,7 @@ class FoamFile(
|
|
127
127
|
if ret not in ("ascii", "binary"):
|
128
128
|
msg = "format is not 'ascii' or 'binary'"
|
129
129
|
raise ValueError(msg)
|
130
|
-
return cast(Literal[
|
130
|
+
return cast("Literal['ascii', 'binary']", ret)
|
131
131
|
|
132
132
|
@format.setter
|
133
133
|
def format(self, value: Literal["ascii", "binary"]) -> None:
|
@@ -412,7 +412,7 @@ class FoamFieldFile(FoamFile):
|
|
412
412
|
) -> Field:
|
413
413
|
"""Alias of `self["value"]`."""
|
414
414
|
return cast(
|
415
|
-
Field,
|
415
|
+
"Field",
|
416
416
|
self["value"],
|
417
417
|
)
|
418
418
|
|
@@ -461,7 +461,7 @@ class FoamFieldFile(FoamFile):
|
|
461
461
|
self,
|
462
462
|
) -> Field:
|
463
463
|
"""Alias of `self["internalField"]`."""
|
464
|
-
return cast(Field, self["internalField"])
|
464
|
+
return cast("Field", self["internalField"])
|
465
465
|
|
466
466
|
@internal_field.setter
|
467
467
|
def internal_field(
|
foamlib/_files/_parsing.py
CHANGED
@@ -16,6 +16,7 @@ else:
|
|
16
16
|
|
17
17
|
import numpy as np
|
18
18
|
from pyparsing import (
|
19
|
+
CaselessKeyword,
|
19
20
|
Combine,
|
20
21
|
Dict,
|
21
22
|
Forward,
|
@@ -157,6 +158,7 @@ def _dict_of(
|
|
157
158
|
data: ParserElement,
|
158
159
|
*,
|
159
160
|
directive: ParserElement | None = None,
|
161
|
+
data_entry: ParserElement | None = None,
|
160
162
|
located: bool = False,
|
161
163
|
) -> ParserElement:
|
162
164
|
dict_ = Forward()
|
@@ -164,7 +166,8 @@ def _dict_of(
|
|
164
166
|
keyword_entry = keyword + (dict_ | (data + Literal(";").suppress()))
|
165
167
|
|
166
168
|
if directive is not None:
|
167
|
-
|
169
|
+
assert data_entry is not None
|
170
|
+
keyword_entry |= directive + data_entry + LineEnd().suppress()
|
168
171
|
|
169
172
|
if located:
|
170
173
|
keyword_entry = Located(keyword_entry)
|
@@ -183,15 +186,19 @@ def _keyword_entry_of(
|
|
183
186
|
data: ParserElement,
|
184
187
|
*,
|
185
188
|
directive: ParserElement | None = None,
|
189
|
+
data_entry: ParserElement | None = None,
|
186
190
|
located: bool = False,
|
187
191
|
) -> ParserElement:
|
188
192
|
keyword_entry = keyword + (
|
189
|
-
_dict_of(
|
193
|
+
_dict_of(
|
194
|
+
keyword, data, directive=directive, data_entry=data_entry, located=located
|
195
|
+
)
|
190
196
|
| (data + Literal(";").suppress())
|
191
197
|
)
|
192
198
|
|
193
199
|
if directive is not None:
|
194
|
-
|
200
|
+
assert data_entry is not None
|
201
|
+
keyword_entry |= directive + data_entry + LineEnd().suppress()
|
195
202
|
|
196
203
|
if located:
|
197
204
|
keyword_entry = Located(keyword_entry)
|
@@ -261,7 +268,16 @@ _DICT = _dict_of(_TOKEN, _DATA)
|
|
261
268
|
_DATA_ENTRY = Forward()
|
262
269
|
_LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
|
263
270
|
_LIST = _list_of(_LIST_ENTRY)
|
264
|
-
_NUMBER =
|
271
|
+
_NUMBER = (
|
272
|
+
common.number
|
273
|
+
| CaselessKeyword("nan").set_parse_action(lambda: np.nan)
|
274
|
+
| (CaselessKeyword("inf") | CaselessKeyword("infinity")).set_parse_action(
|
275
|
+
lambda: np.inf
|
276
|
+
)
|
277
|
+
| (CaselessKeyword("-inf") | CaselessKeyword("-infinity")).set_parse_action(
|
278
|
+
lambda: -np.inf
|
279
|
+
)
|
280
|
+
)
|
265
281
|
_DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
|
266
282
|
|
267
283
|
_DATA <<= (
|
@@ -275,12 +291,16 @@ _DATA <<= (
|
|
275
291
|
def parse_data(s: str) -> Data:
|
276
292
|
if not s.strip():
|
277
293
|
return ""
|
278
|
-
return cast(Data, _DATA.parse_string(s, parse_all=True)[0])
|
294
|
+
return cast("Data", _DATA.parse_string(s, parse_all=True)[0])
|
279
295
|
|
280
296
|
|
281
297
|
_LOCATED_DICTIONARY = Group(
|
282
298
|
_keyword_entry_of(
|
283
|
-
_TOKEN,
|
299
|
+
_TOKEN,
|
300
|
+
Opt(_DATA, default=""),
|
301
|
+
directive=_DIRECTIVE,
|
302
|
+
data_entry=_DATA_ENTRY,
|
303
|
+
located=True,
|
284
304
|
)
|
285
305
|
)[...]
|
286
306
|
_LOCATED_DATA = Group(Located(_DATA.copy().add_parse_action(lambda tks: ["", tks[0]])))
|
@@ -421,7 +441,7 @@ class Parsed(Mapping[Tuple[str, ...], Union[Data, EllipsisType]]):
|
|
421
441
|
for k in keywords[:-1]:
|
422
442
|
v = r[k]
|
423
443
|
assert isinstance(v, dict)
|
424
|
-
r = cast(File, v)
|
444
|
+
r = cast("File", v)
|
425
445
|
|
426
446
|
assert isinstance(r, dict)
|
427
447
|
if keywords:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.7
|
4
4
|
Summary: A Python interface for interacting with OpenFOAM
|
5
5
|
Project-URL: Homepage, https://github.com/gerlero/foamlib
|
6
6
|
Project-URL: Repository, https://github.com/gerlero/foamlib
|
@@ -29,6 +29,7 @@ Requires-Dist: aioshutil<2,>=1
|
|
29
29
|
Requires-Dist: numpy<3,>=1
|
30
30
|
Requires-Dist: numpy<3,>=1.25.0; python_version >= '3.10'
|
31
31
|
Requires-Dist: pyparsing<4,>=3.1.2
|
32
|
+
Requires-Dist: rich<14,>=13
|
32
33
|
Requires-Dist: typing-extensions<5,>=4; python_version < '3.11'
|
33
34
|
Provides-Extra: dev
|
34
35
|
Requires-Dist: mypy<2,>=1; extra == 'dev'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
foamlib/__init__.py,sha256=TrTrsv_QjPqa16duuXQl5FgjxRPwuHlhO-MHOtxUzdY,452
|
2
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
foamlib/_cases/__init__.py,sha256=_A1TTHuQfS9FH2_33lSEyLtOJZGFHZBco1tWJCVOHks,358
|
4
|
+
foamlib/_cases/_async.py,sha256=vXpq5T2gDomuawARsLaUEMjUBhMsZK58q7iVwQpXsYE,7903
|
5
|
+
foamlib/_cases/_base.py,sha256=37oBbM3NM-hpG7dKewZvyJNtqSAogMurcbmX-wLIgMU,6727
|
6
|
+
foamlib/_cases/_run.py,sha256=IRsozHdXx5tjLYYIWMiKGeaf5188unhp5VwIAZLlr3E,15578
|
7
|
+
foamlib/_cases/_slurm.py,sha256=Hzpf5Ugahwq7sUsCfhZ6JgXDOdkoYGGsHGiO7NV8uFs,2331
|
8
|
+
foamlib/_cases/_subprocess.py,sha256=VHV2SuOLqa711an6kCuvN6UlIkeh4qqFfdrpNoKzQps,5630
|
9
|
+
foamlib/_cases/_sync.py,sha256=e06aGGZ9n6WTWK9eWZhrezsT4yebGhKPE0sn0nwQH8A,5977
|
10
|
+
foamlib/_cases/_util.py,sha256=QCizfbuJdOCeF9ogU2R-y-iWX5kfaOA4U2W68t6QlOM,2544
|
11
|
+
foamlib/_files/__init__.py,sha256=q1vkjXnjnSZvo45jPAICpWeF2LZv5V6xfzAR6S8fS5A,96
|
12
|
+
foamlib/_files/_files.py,sha256=YAKw3RHEw8eCja4JAxobp6BC-2Kyy7AvG1O7yOqyMic,15414
|
13
|
+
foamlib/_files/_io.py,sha256=BGbbm6HKxL2ka0YMCmHqZQZ1R4PPQlkvWWb4FHMAS8k,2217
|
14
|
+
foamlib/_files/_parsing.py,sha256=XrqG8IcTM--qZYqEHYO_jcCTb7ev9M9kz75T_DQtPuc,14047
|
15
|
+
foamlib/_files/_serialization.py,sha256=PvMzNyTja6OKT_GUfExTulx9nMLBjfu0-9yThCKbvxs,5227
|
16
|
+
foamlib/_files/_types.py,sha256=m-fFjJnS4sFSavDsijlXpAfEhnbh10RBumSHAT0GOgQ,3408
|
17
|
+
foamlib-0.8.7.dist-info/METADATA,sha256=HVzB10Bdz97hbXxBDunpGy-_seeNduk5msDAKVCXmx4,7996
|
18
|
+
foamlib-0.8.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
19
|
+
foamlib-0.8.7.dist-info/licenses/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
20
|
+
foamlib-0.8.7.dist-info/RECORD,,
|
foamlib-0.8.5.dist-info/RECORD
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=vhMMDd4SJsEtAYoKY35ByJmFCQcyDMEb-WpdS8ERF3g,452
|
2
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
foamlib/_cases/__init__.py,sha256=_A1TTHuQfS9FH2_33lSEyLtOJZGFHZBco1tWJCVOHks,358
|
4
|
-
foamlib/_cases/_async.py,sha256=onECxRLQCF7Kd-GeuLqH_Xv3gbMMhKOSbFaUG5Ttgmk,7822
|
5
|
-
foamlib/_cases/_base.py,sha256=37oBbM3NM-hpG7dKewZvyJNtqSAogMurcbmX-wLIgMU,6727
|
6
|
-
foamlib/_cases/_run.py,sha256=lveqKZium_qK_eTxYE8jOjwx0eiIoolCBbi56-zLw1o,14420
|
7
|
-
foamlib/_cases/_slurm.py,sha256=kj4wqgr3foMyAoUkoHOZODRBmVqH1B9KqAIEEjM8ZBg,2328
|
8
|
-
foamlib/_cases/_subprocess.py,sha256=6BlBRxknj2-BFcGkx7oVcuL63_utSaY1Axmsc1qV9j8,3887
|
9
|
-
foamlib/_cases/_sync.py,sha256=2BJXB7Nzldb4OgPukqupgYqdceUGkI2mYhhtGPWEBrc,5901
|
10
|
-
foamlib/_cases/_util.py,sha256=tK4SM5WT3eEgGsFLnidIySbom1qowBAua9z13gipKJk,1518
|
11
|
-
foamlib/_files/__init__.py,sha256=q1vkjXnjnSZvo45jPAICpWeF2LZv5V6xfzAR6S8fS5A,96
|
12
|
-
foamlib/_files/_files.py,sha256=V740S36NVunULv8pnyEsJ7lIWbSjk_-LwbP7BBqFsO0,15404
|
13
|
-
foamlib/_files/_io.py,sha256=BGbbm6HKxL2ka0YMCmHqZQZ1R4PPQlkvWWb4FHMAS8k,2217
|
14
|
-
foamlib/_files/_parsing.py,sha256=uM9eD67Coo5K-F2EWkbzLfpdTr56mUbLw2kE8Q94-PE,13549
|
15
|
-
foamlib/_files/_serialization.py,sha256=PvMzNyTja6OKT_GUfExTulx9nMLBjfu0-9yThCKbvxs,5227
|
16
|
-
foamlib/_files/_types.py,sha256=m-fFjJnS4sFSavDsijlXpAfEhnbh10RBumSHAT0GOgQ,3408
|
17
|
-
foamlib-0.8.5.dist-info/METADATA,sha256=H5RgbkxMQZf-PG38gx3Ckhu3q6eqOYndn457-htff84,7968
|
18
|
-
foamlib-0.8.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
19
|
-
foamlib-0.8.5.dist-info/licenses/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
20
|
-
foamlib-0.8.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|