foamlib 0.2.3__tar.gz → 0.2.5__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.
- {foamlib-0.2.3 → foamlib-0.2.5}/PKG-INFO +9 -9
- {foamlib-0.2.3 → foamlib-0.2.5}/README.md +1 -1
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/__init__.py +1 -1
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_cases.py +18 -10
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_base.py +8 -1
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_files.py +96 -62
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_parsing.py +8 -2
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_serialization.py +35 -23
- foamlib-0.2.3/foamlib/_subprocesses.py → foamlib-0.2.5/foamlib/_util.py +35 -8
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/PKG-INFO +9 -9
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/SOURCES.txt +1 -1
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/requires.txt +9 -6
- {foamlib-0.2.3 → foamlib-0.2.5}/pyproject.toml +3 -23
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_dictionaries.py +1 -1
- {foamlib-0.2.3 → foamlib-0.2.5}/LICENSE.txt +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/__init__.py +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/py.typed +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/dependency_links.txt +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/top_level.txt +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/setup.cfg +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_basic.py +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_flange.py +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_flange_async.py +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_pitz.py +0 -0
- {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_pitz_async.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5
|
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,14 +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; python_version < "3.11"
|
30
31
|
Provides-Extra: lint
|
31
|
-
Requires-Dist:
|
32
|
-
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist:
|
35
|
-
Requires-Dist:
|
36
|
-
Requires-Dist:
|
37
|
-
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"
|
38
38
|
Provides-Extra: test
|
39
39
|
Requires-Dist: pytest<9,>=7; extra == "test"
|
40
40
|
Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "test"
|
@@ -51,7 +51,7 @@ Requires-Dist: numpy<2,>=1; extra == "docs"
|
|
51
51
|
[](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
|
52
52
|
[](https://codecov.io/gh/gerlero/foamlib)
|
53
53
|
[](http://mypy-lang.org/)
|
54
|
-
[](https://github.com/astral-sh/ruff)
|
55
55
|
[](https://pypi.org/project/foamlib/)
|
56
56
|
[](https://pypi.org/project/foamlib/)
|
57
57
|
[](https://hub.docker.com/r/gerlero/foamlib/)
|
@@ -4,7 +4,7 @@
|
|
4
4
|
[](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
|
5
5
|
[](https://codecov.io/gh/gerlero/foamlib)
|
6
6
|
[](http://mypy-lang.org/)
|
7
|
-
[](https://github.com/astral-sh/ruff)
|
8
8
|
[](https://pypi.org/project/foamlib/)
|
9
9
|
[](https://pypi.org/project/foamlib/)
|
10
10
|
[](https://hub.docker.com/r/gerlero/foamlib/)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import sys
|
1
2
|
import os
|
2
3
|
import asyncio
|
3
4
|
import multiprocessing
|
@@ -8,19 +9,26 @@ from contextlib import asynccontextmanager
|
|
8
9
|
from typing import (
|
9
10
|
Optional,
|
10
11
|
Union,
|
11
|
-
Collection,
|
12
|
-
Mapping,
|
13
|
-
Set,
|
14
|
-
Sequence,
|
15
|
-
AsyncGenerator,
|
16
|
-
Callable,
|
17
|
-
Iterator,
|
18
12
|
overload,
|
19
13
|
)
|
20
14
|
|
15
|
+
if sys.version_info >= (3, 9):
|
16
|
+
from collections.abc import (
|
17
|
+
Collection,
|
18
|
+
Mapping,
|
19
|
+
Set,
|
20
|
+
Sequence,
|
21
|
+
AsyncGenerator,
|
22
|
+
Callable,
|
23
|
+
Iterator,
|
24
|
+
)
|
25
|
+
else:
|
26
|
+
from typing import Collection, Mapping, Sequence, AsyncGenerator, Callable, Iterator
|
27
|
+
from typing import AbstractSet as Set
|
28
|
+
|
21
29
|
import aioshutil
|
22
30
|
|
23
|
-
from .
|
31
|
+
from ._util import is_sequence, run_process, run_process_async, CalledProcessError
|
24
32
|
from ._dictionaries import FoamFile, FoamFieldFile
|
25
33
|
|
26
34
|
|
@@ -132,7 +140,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
132
140
|
has_decompose_par_dict = (self.path / "system" / "decomposeParDict").is_file()
|
133
141
|
has_block_mesh_dict = (self.path / "system" / "blockMeshDict").is_file()
|
134
142
|
|
135
|
-
paths
|
143
|
+
paths = set()
|
136
144
|
|
137
145
|
for p in self.path.iterdir():
|
138
146
|
if p.is_dir():
|
@@ -215,7 +223,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
215
223
|
def _parallel_cmd(
|
216
224
|
self, cmd: Union[Sequence[Union[str, Path]], str, Path]
|
217
225
|
) -> Union[Sequence[Union[str, Path]], str]:
|
218
|
-
if
|
226
|
+
if not is_sequence(cmd):
|
219
227
|
return f"mpiexec -np {self._nprocessors} {cmd} -parallel"
|
220
228
|
else:
|
221
229
|
return [
|
@@ -1,6 +1,13 @@
|
|
1
|
+
import sys
|
2
|
+
|
1
3
|
from abc import abstractmethod
|
2
4
|
from dataclasses import dataclass
|
3
|
-
from typing import Dict, NamedTuple, Optional,
|
5
|
+
from typing import Dict, NamedTuple, Optional, Union
|
6
|
+
|
7
|
+
if sys.version_info >= (3, 9):
|
8
|
+
from collections.abc import Sequence
|
9
|
+
else:
|
10
|
+
from typing import Sequence
|
4
11
|
|
5
12
|
|
6
13
|
class FoamDictionaryBase:
|
@@ -1,19 +1,23 @@
|
|
1
|
+
import sys
|
2
|
+
|
1
3
|
from pathlib import Path
|
2
4
|
from typing import (
|
3
5
|
Any,
|
4
|
-
Iterator,
|
5
|
-
Mapping,
|
6
|
-
MutableMapping,
|
7
6
|
Optional,
|
8
|
-
Sequence,
|
9
7
|
Tuple,
|
10
8
|
Union,
|
11
9
|
cast,
|
12
10
|
)
|
13
11
|
|
14
|
-
|
15
|
-
from .
|
16
|
-
|
12
|
+
if sys.version_info >= (3, 9):
|
13
|
+
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
14
|
+
else:
|
15
|
+
from typing import Iterator, Mapping, MutableMapping, Sequence
|
16
|
+
|
17
|
+
if sys.version_info >= (3, 11):
|
18
|
+
from typing import Self
|
19
|
+
else:
|
20
|
+
from typing_extensions import Self
|
17
21
|
|
18
22
|
try:
|
19
23
|
import numpy as np
|
@@ -21,10 +25,67 @@ try:
|
|
21
25
|
except ModuleNotFoundError:
|
22
26
|
pass
|
23
27
|
|
28
|
+
from ._base import FoamDictionaryBase
|
29
|
+
from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
|
30
|
+
from ._serialization import serialize_entry
|
31
|
+
|
32
|
+
|
33
|
+
class _FoamFileBase:
|
34
|
+
def __init__(self, path: Union[str, Path]) -> None:
|
35
|
+
self.path = Path(path).absolute()
|
36
|
+
if self.path.is_dir():
|
37
|
+
raise IsADirectoryError(self.path)
|
38
|
+
elif not self.path.is_file():
|
39
|
+
raise FileNotFoundError(self.path)
|
40
|
+
|
41
|
+
self.__contents: Optional[str] = None
|
42
|
+
self.__parsed: Optional[Parsed] = None
|
43
|
+
self.__defer_io = 0
|
44
|
+
self.__dirty = False
|
45
|
+
|
46
|
+
def __enter__(self) -> Self:
|
47
|
+
if self.__defer_io == 0:
|
48
|
+
self._read()
|
49
|
+
self.__defer_io += 1
|
50
|
+
return self
|
51
|
+
|
52
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
53
|
+
self.__defer_io -= 1
|
54
|
+
if self.__defer_io == 0 and self.__dirty:
|
55
|
+
assert self.__contents is not None
|
56
|
+
self._write(self.__contents)
|
57
|
+
assert not self.__dirty
|
58
|
+
|
59
|
+
def _read(self) -> Tuple[str, Parsed]:
|
60
|
+
if not self.__defer_io:
|
61
|
+
contents = self.path.read_text()
|
62
|
+
if contents != self.__contents:
|
63
|
+
self.__contents = contents
|
64
|
+
self.__parsed = None
|
65
|
+
|
66
|
+
assert self.__contents is not None
|
67
|
+
|
68
|
+
if self.__parsed is None:
|
69
|
+
self.__parsed = parse(self.__contents)
|
70
|
+
|
71
|
+
return self.__contents, self.__parsed
|
72
|
+
|
73
|
+
def _write(self, contents: str) -> None:
|
74
|
+
self.__contents = contents
|
75
|
+
self.__parsed = None
|
76
|
+
if not self.__defer_io:
|
77
|
+
self.path.write_text(contents)
|
78
|
+
self.__dirty = False
|
79
|
+
else:
|
80
|
+
self.__dirty = True
|
81
|
+
|
24
82
|
|
25
83
|
class FoamFile(
|
84
|
+
_FoamFileBase,
|
26
85
|
FoamDictionaryBase,
|
27
|
-
MutableMapping[
|
86
|
+
MutableMapping[
|
87
|
+
Union[str, Tuple[str, ...]], Union["FoamFile.Value", "FoamFile.Dictionary"]
|
88
|
+
],
|
28
89
|
):
|
29
90
|
"""
|
30
91
|
An OpenFOAM dictionary file.
|
@@ -75,9 +136,20 @@ class FoamFile(
|
|
75
136
|
def __iter__(self) -> Iterator[str]:
|
76
137
|
return self._file._iter(tuple(self._keywords))
|
77
138
|
|
139
|
+
def __contains__(self, keyword: object) -> bool:
|
140
|
+
return (*self._keywords, keyword) in self._file
|
141
|
+
|
78
142
|
def __len__(self) -> int:
|
79
143
|
return len(list(iter(self)))
|
80
144
|
|
145
|
+
def update(self, *args: Any, **kwargs: Any) -> None:
|
146
|
+
with self._file:
|
147
|
+
super().update(*args, **kwargs)
|
148
|
+
|
149
|
+
def clear(self) -> None:
|
150
|
+
with self._file:
|
151
|
+
super().clear()
|
152
|
+
|
81
153
|
def __repr__(self) -> str:
|
82
154
|
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
83
155
|
|
@@ -95,54 +167,6 @@ class FoamFile(
|
|
95
167
|
|
96
168
|
return ret
|
97
169
|
|
98
|
-
def __init__(self, path: Union[str, Path]) -> None:
|
99
|
-
self.path = Path(path).absolute()
|
100
|
-
if self.path.is_dir():
|
101
|
-
raise IsADirectoryError(self.path)
|
102
|
-
elif not self.path.is_file():
|
103
|
-
raise FileNotFoundError(self.path)
|
104
|
-
|
105
|
-
self._contents: Optional[str] = None
|
106
|
-
self._parsed: Optional[Parsed] = None
|
107
|
-
self._defer_io = 0
|
108
|
-
self._dirty = False
|
109
|
-
|
110
|
-
def __enter__(self) -> "FoamFile":
|
111
|
-
if self._defer_io == 0:
|
112
|
-
self._read()
|
113
|
-
self._defer_io += 1
|
114
|
-
return self
|
115
|
-
|
116
|
-
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
117
|
-
self._defer_io -= 1
|
118
|
-
if self._defer_io == 0 and self._dirty:
|
119
|
-
assert self._contents is not None
|
120
|
-
self._write(self._contents)
|
121
|
-
assert not self._dirty
|
122
|
-
|
123
|
-
def _read(self) -> Tuple[str, Parsed]:
|
124
|
-
if not self._defer_io:
|
125
|
-
contents = self.path.read_text()
|
126
|
-
if contents != self._contents:
|
127
|
-
self._contents = contents
|
128
|
-
self._parsed = None
|
129
|
-
|
130
|
-
assert self._contents is not None
|
131
|
-
|
132
|
-
if self._parsed is None:
|
133
|
-
self._parsed = parse(self._contents)
|
134
|
-
|
135
|
-
return self._contents, self._parsed
|
136
|
-
|
137
|
-
def _write(self, contents: str) -> None:
|
138
|
-
self._contents = contents
|
139
|
-
self._parsed = None
|
140
|
-
if not self._defer_io:
|
141
|
-
self.path.write_text(contents)
|
142
|
-
self._dirty = False
|
143
|
-
else:
|
144
|
-
self._dirty = True
|
145
|
-
|
146
170
|
def __getitem__(
|
147
171
|
self, keywords: Union[str, Tuple[str, ...]]
|
148
172
|
) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
|
@@ -179,7 +203,7 @@ class FoamFile(
|
|
179
203
|
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
180
204
|
|
181
205
|
self._write(
|
182
|
-
f"{contents[:start]}
|
206
|
+
f"{contents[:start]}\n{serialize_entry(keywords[-1], {})}\n{contents[end:]}"
|
183
207
|
)
|
184
208
|
|
185
209
|
for k, v in value.items():
|
@@ -187,12 +211,8 @@ class FoamFile(
|
|
187
211
|
else:
|
188
212
|
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
189
213
|
|
190
|
-
value = serialize_value(
|
191
|
-
value, assume_field=assume_field, assume_dimensions=assume_dimensions
|
192
|
-
)
|
193
|
-
|
194
214
|
self._write(
|
195
|
-
f"{contents[:start]}
|
215
|
+
f"{contents[:start]}\n{serialize_entry(keywords[-1], value, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
|
196
216
|
)
|
197
217
|
|
198
218
|
def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
|
@@ -220,9 +240,23 @@ class FoamFile(
|
|
220
240
|
def __iter__(self) -> Iterator[str]:
|
221
241
|
return self._iter()
|
222
242
|
|
243
|
+
def __contains__(self, keywords: object) -> bool:
|
244
|
+
if not isinstance(keywords, tuple):
|
245
|
+
keywords = (keywords,)
|
246
|
+
_, parsed = self._read()
|
247
|
+
return keywords in parsed
|
248
|
+
|
223
249
|
def __len__(self) -> int:
|
224
250
|
return len(list(iter(self)))
|
225
251
|
|
252
|
+
def update(self, *args: Any, **kwargs: Any) -> None:
|
253
|
+
with self:
|
254
|
+
super().update(*args, **kwargs)
|
255
|
+
|
256
|
+
def clear(self) -> None:
|
257
|
+
with self:
|
258
|
+
super().clear()
|
259
|
+
|
226
260
|
def __fspath__(self) -> str:
|
227
261
|
return str(self.path)
|
228
262
|
|
@@ -1,4 +1,11 @@
|
|
1
|
-
|
1
|
+
import sys
|
2
|
+
|
3
|
+
from typing import Optional, Tuple
|
4
|
+
|
5
|
+
if sys.version_info >= (3, 9):
|
6
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
7
|
+
else:
|
8
|
+
from typing import Mapping, MutableMapping, Sequence
|
2
9
|
|
3
10
|
from pyparsing import (
|
4
11
|
Dict,
|
@@ -163,7 +170,6 @@ def as_dict(parsed: Parsed) -> FoamDictionaryBase._Dict:
|
|
163
170
|
"""
|
164
171
|
ret: FoamDictionaryBase._Dict = {}
|
165
172
|
for keywords, (_, value, _) in parsed.items():
|
166
|
-
|
167
173
|
r = ret
|
168
174
|
for k in keywords[:-1]:
|
169
175
|
assert isinstance(r, dict)
|
@@ -1,23 +1,15 @@
|
|
1
|
-
|
2
|
-
from typing import Any, Sequence
|
1
|
+
import sys
|
3
2
|
|
4
|
-
from
|
3
|
+
from contextlib import suppress
|
4
|
+
from typing import Any
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
except ModuleNotFoundError:
|
9
|
-
numpy = False
|
6
|
+
if sys.version_info >= (3, 9):
|
7
|
+
from collections.abc import Mapping
|
10
8
|
else:
|
11
|
-
|
9
|
+
from typing import Mapping
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
return (
|
16
|
-
isinstance(value, Sequence)
|
17
|
-
and not isinstance(value, str)
|
18
|
-
or numpy
|
19
|
-
and isinstance(value, np.ndarray)
|
20
|
-
)
|
11
|
+
from ._base import FoamDictionaryBase
|
12
|
+
from .._util import is_sequence
|
21
13
|
|
22
14
|
|
23
15
|
def _serialize_bool(value: Any) -> str:
|
@@ -30,14 +22,14 @@ def _serialize_bool(value: Any) -> str:
|
|
30
22
|
|
31
23
|
|
32
24
|
def _serialize_list(value: Any) -> str:
|
33
|
-
if
|
34
|
-
return f"({' '.join(
|
25
|
+
if is_sequence(value):
|
26
|
+
return f"({' '.join(_serialize_value(v) for v in value)})"
|
35
27
|
else:
|
36
28
|
raise TypeError(f"Not a valid sequence: {type(value)}")
|
37
29
|
|
38
30
|
|
39
31
|
def _serialize_field(value: Any) -> str:
|
40
|
-
if
|
32
|
+
if is_sequence(value):
|
41
33
|
try:
|
42
34
|
s = _serialize_list(value)
|
43
35
|
except TypeError:
|
@@ -64,7 +56,7 @@ def _serialize_field(value: Any) -> str:
|
|
64
56
|
|
65
57
|
|
66
58
|
def _serialize_dimensions(value: Any) -> str:
|
67
|
-
if
|
59
|
+
if is_sequence(value) and len(value) == 7:
|
68
60
|
return f"[{' '.join(str(v) for v in value)}]"
|
69
61
|
else:
|
70
62
|
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
@@ -73,14 +65,14 @@ def _serialize_dimensions(value: Any) -> str:
|
|
73
65
|
def _serialize_dimensioned(value: Any) -> str:
|
74
66
|
if isinstance(value, FoamDictionaryBase.Dimensioned):
|
75
67
|
if value.name is not None:
|
76
|
-
return f"{value.name} {_serialize_dimensions(value.dimensions)} {
|
68
|
+
return f"{value.name} {_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
|
77
69
|
else:
|
78
|
-
return f"{_serialize_dimensions(value.dimensions)} {
|
70
|
+
return f"{_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
|
79
71
|
else:
|
80
72
|
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
81
73
|
|
82
74
|
|
83
|
-
def
|
75
|
+
def _serialize_value(
|
84
76
|
value: Any, *, assume_field: bool = False, assume_dimensions: bool = False
|
85
77
|
) -> str:
|
86
78
|
if isinstance(value, FoamDictionaryBase.DimensionSet) or assume_dimensions:
|
@@ -101,3 +93,23 @@ def serialize_value(
|
|
101
93
|
return _serialize_bool(value)
|
102
94
|
|
103
95
|
return str(value)
|
96
|
+
|
97
|
+
|
98
|
+
def _serialize_dictionary(value: Any) -> str:
|
99
|
+
if isinstance(value, Mapping):
|
100
|
+
return "\n".join(serialize_entry(k, v) for k, v in value.items())
|
101
|
+
else:
|
102
|
+
raise TypeError(f"Not a valid dictionary: {type(value)}")
|
103
|
+
|
104
|
+
|
105
|
+
def serialize_entry(
|
106
|
+
keyword: str,
|
107
|
+
value: Any,
|
108
|
+
*,
|
109
|
+
assume_field: bool = False,
|
110
|
+
assume_dimensions: bool = False,
|
111
|
+
) -> str:
|
112
|
+
try:
|
113
|
+
return f"{keyword}\n{{\n{_serialize_dictionary(value)}\n}}"
|
114
|
+
except TypeError:
|
115
|
+
return f"{keyword} {_serialize_value(value, assume_field=assume_field, assume_dimensions=assume_dimensions)};"
|
@@ -1,12 +1,40 @@
|
|
1
1
|
import asyncio
|
2
2
|
import sys
|
3
|
+
import subprocess
|
3
4
|
|
4
5
|
from pathlib import Path
|
5
|
-
from typing import
|
6
|
-
|
7
|
-
|
6
|
+
from typing import Any, Union
|
7
|
+
|
8
|
+
if sys.version_info >= (3, 9):
|
9
|
+
from collections.abc import Mapping, Sequence
|
10
|
+
else:
|
11
|
+
from typing import Mapping, Sequence
|
12
|
+
|
13
|
+
if sys.version_info >= (3, 10):
|
14
|
+
from typing import TypeGuard
|
15
|
+
else:
|
16
|
+
from typing_extensions import TypeGuard
|
17
|
+
|
18
|
+
try:
|
19
|
+
import numpy as np
|
20
|
+
except ModuleNotFoundError:
|
21
|
+
numpy = False
|
22
|
+
else:
|
23
|
+
numpy = True
|
24
|
+
|
25
|
+
|
26
|
+
def is_sequence(
|
27
|
+
value: Any,
|
28
|
+
) -> TypeGuard[Union["Sequence[Any]", "np.ndarray[Any, Any]"]]:
|
29
|
+
return (
|
30
|
+
isinstance(value, Sequence)
|
31
|
+
and not isinstance(value, str)
|
32
|
+
or numpy
|
33
|
+
and isinstance(value, np.ndarray)
|
34
|
+
)
|
35
|
+
|
8
36
|
|
9
|
-
|
37
|
+
CalledProcessError = subprocess.CalledProcessError
|
10
38
|
|
11
39
|
|
12
40
|
def run_process(
|
@@ -16,7 +44,7 @@ def run_process(
|
|
16
44
|
cwd: Union[None, str, Path] = None,
|
17
45
|
env: Union[None, Mapping[str, str]] = None,
|
18
46
|
) -> "subprocess.CompletedProcess[bytes]":
|
19
|
-
shell =
|
47
|
+
shell = not is_sequence(cmd)
|
20
48
|
|
21
49
|
if sys.version_info < (3, 8):
|
22
50
|
if shell:
|
@@ -28,8 +56,7 @@ def run_process(
|
|
28
56
|
cmd,
|
29
57
|
cwd=cwd,
|
30
58
|
env=env,
|
31
|
-
|
32
|
-
stderr=subprocess.PIPE,
|
59
|
+
capture_output=True,
|
33
60
|
shell=shell,
|
34
61
|
check=check,
|
35
62
|
)
|
@@ -44,7 +71,7 @@ async def run_process_async(
|
|
44
71
|
cwd: Union[None, str, Path] = None,
|
45
72
|
env: Union[None, Mapping[str, str]] = None,
|
46
73
|
) -> "subprocess.CompletedProcess[bytes]":
|
47
|
-
if
|
74
|
+
if not is_sequence(cmd):
|
48
75
|
proc = await asyncio.create_subprocess_shell(
|
49
76
|
str(cmd),
|
50
77
|
cwd=cwd,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5
|
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,14 +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; python_version < "3.11"
|
30
31
|
Provides-Extra: lint
|
31
|
-
Requires-Dist:
|
32
|
-
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist:
|
35
|
-
Requires-Dist:
|
36
|
-
Requires-Dist:
|
37
|
-
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"
|
38
38
|
Provides-Extra: test
|
39
39
|
Requires-Dist: pytest<9,>=7; extra == "test"
|
40
40
|
Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "test"
|
@@ -51,7 +51,7 @@ Requires-Dist: numpy<2,>=1; extra == "docs"
|
|
51
51
|
[](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
|
52
52
|
[](https://codecov.io/gh/gerlero/foamlib)
|
53
53
|
[](http://mypy-lang.org/)
|
54
|
-
[](https://github.com/astral-sh/ruff)
|
55
55
|
[](https://pypi.org/project/foamlib/)
|
56
56
|
[](https://pypi.org/project/foamlib/)
|
57
57
|
[](https://hub.docker.com/r/gerlero/foamlib/)
|
@@ -1,22 +1,25 @@
|
|
1
1
|
aioshutil<2,>=1
|
2
2
|
pyparsing<4,>=3
|
3
3
|
|
4
|
+
[:python_version < "3.11"]
|
5
|
+
typing-extensions<5,>=4
|
6
|
+
|
4
7
|
[docs]
|
5
8
|
sphinx<8,>=7
|
6
9
|
sphinx_rtd_theme
|
7
10
|
numpy<2,>=1
|
8
11
|
|
9
12
|
[lint]
|
10
|
-
|
13
|
+
ruff
|
14
|
+
|
15
|
+
[test]
|
11
16
|
pytest<9,>=7
|
12
17
|
pytest-asyncio<0.24,>=0.21
|
18
|
+
pytest-cov
|
13
19
|
numpy<2,>=1
|
14
|
-
black
|
15
|
-
flake8
|
16
|
-
Flake8-pyproject
|
17
20
|
|
18
|
-
[
|
21
|
+
[typing]
|
22
|
+
mypy<2,>=1
|
19
23
|
pytest<9,>=7
|
20
24
|
pytest-asyncio<0.24,>=0.21
|
21
|
-
pytest-cov
|
22
25
|
numpy<2,>=1
|
@@ -30,19 +30,18 @@ classifiers = [
|
|
30
30
|
dependencies = [
|
31
31
|
"aioshutil>=1,<2",
|
32
32
|
"pyparsing>=3,<4",
|
33
|
+
"typing-extensions>=4,<5; python_version<'3.11'",
|
33
34
|
]
|
34
35
|
|
35
36
|
dynamic = ["version"]
|
36
37
|
|
37
38
|
[project.optional-dependencies]
|
38
|
-
lint = [
|
39
|
+
lint = ["ruff"]
|
40
|
+
typing = [
|
39
41
|
"mypy>=1,<2",
|
40
42
|
"pytest>=7,<9",
|
41
43
|
"pytest-asyncio>=0.21,<0.24",
|
42
44
|
"numpy>=1,<2",
|
43
|
-
"black",
|
44
|
-
"flake8",
|
45
|
-
"Flake8-pyproject",
|
46
45
|
]
|
47
46
|
test = [
|
48
47
|
"pytest>=7,<9",
|
@@ -73,22 +72,3 @@ packages = [
|
|
73
72
|
"tests",
|
74
73
|
]
|
75
74
|
strict = true
|
76
|
-
|
77
|
-
[tool.flake8]
|
78
|
-
count = true
|
79
|
-
ignore = [
|
80
|
-
"E203", # whitespace before ':'
|
81
|
-
"E501", # line too long
|
82
|
-
"E704", # multiple statements on one line (def)
|
83
|
-
"F403", # 'from foamlib import *' used; unable to detect undefined names
|
84
|
-
"F405", # 'FoamDimensionSet' may be undefined, or defined from star imports: foamlib
|
85
|
-
"W503", # line break before binary operator
|
86
|
-
]
|
87
|
-
exclude = [
|
88
|
-
".git",
|
89
|
-
"__pycache__",
|
90
|
-
"docs/source/conf.py",
|
91
|
-
"build",
|
92
|
-
"dist",
|
93
|
-
"venv",
|
94
|
-
]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|