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.
Files changed (25) hide show
  1. {foamlib-0.2.3 → foamlib-0.2.5}/PKG-INFO +9 -9
  2. {foamlib-0.2.3 → foamlib-0.2.5}/README.md +1 -1
  3. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/__init__.py +1 -1
  4. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_cases.py +18 -10
  5. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_base.py +8 -1
  6. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_files.py +96 -62
  7. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_parsing.py +8 -2
  8. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/_serialization.py +35 -23
  9. foamlib-0.2.3/foamlib/_subprocesses.py → foamlib-0.2.5/foamlib/_util.py +35 -8
  10. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/PKG-INFO +9 -9
  11. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/SOURCES.txt +1 -1
  12. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/requires.txt +9 -6
  13. {foamlib-0.2.3 → foamlib-0.2.5}/pyproject.toml +3 -23
  14. {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_dictionaries.py +1 -1
  15. {foamlib-0.2.3 → foamlib-0.2.5}/LICENSE.txt +0 -0
  16. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/_dictionaries/__init__.py +0 -0
  17. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib/py.typed +0 -0
  18. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/dependency_links.txt +0 -0
  19. {foamlib-0.2.3 → foamlib-0.2.5}/foamlib.egg-info/top_level.txt +0 -0
  20. {foamlib-0.2.3 → foamlib-0.2.5}/setup.cfg +0 -0
  21. {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_basic.py +0 -0
  22. {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_flange.py +0 -0
  23. {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_flange_async.py +0 -0
  24. {foamlib-0.2.3 → foamlib-0.2.5}/tests/test_pitz.py +0 -0
  25. {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
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: mypy<2,>=1; extra == "lint"
32
- Requires-Dist: pytest<9,>=7; extra == "lint"
33
- Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "lint"
34
- Requires-Dist: numpy<2,>=1; extra == "lint"
35
- Requires-Dist: black; extra == "lint"
36
- Requires-Dist: flake8; extra == "lint"
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
  [![CI](https://github.com/gerlero/foamlib/actions/workflows/ci.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
52
52
  [![Codecov](https://codecov.io/gh/gerlero/foamlib/branch/main/graph/badge.svg)](https://codecov.io/gh/gerlero/foamlib)
53
53
  [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
54
- [![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)
55
55
  [![PyPI](https://img.shields.io/pypi/v/foamlib)](https://pypi.org/project/foamlib/)
56
56
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/foamlib)](https://pypi.org/project/foamlib/)
57
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/)
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.3"
1
+ __version__ = "0.2.5"
2
2
 
3
3
  from ._cases import FoamCase, AsyncFoamCase, FoamCaseBase
4
4
  from ._dictionaries import FoamFile, FoamFieldFile, FoamDictionaryBase
@@ -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 ._subprocesses import run_process, run_process_async, CalledProcessError
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: Set[Path] = set()
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 isinstance(cmd, str) or not isinstance(cmd, Sequence):
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, Sequence, Union
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
- from ._base import FoamDictionaryBase
15
- from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
16
- from ._serialization import serialize_value
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[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
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]} {keywords[-1]} {{\n}}\n {contents[end:]}"
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]} {keywords[-1]} {value};\n {contents[end:]}"
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
- from typing import Mapping, MutableMapping, Optional, Sequence, Tuple
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
- from contextlib import suppress
2
- from typing import Any, Sequence
1
+ import sys
3
2
 
4
- from ._base import FoamDictionaryBase
3
+ from contextlib import suppress
4
+ from typing import Any
5
5
 
6
- try:
7
- import numpy as np
8
- except ModuleNotFoundError:
9
- numpy = False
6
+ if sys.version_info >= (3, 9):
7
+ from collections.abc import Mapping
10
8
  else:
11
- numpy = True
9
+ from typing import Mapping
12
10
 
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
- )
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 _is_sequence(value):
34
- return f"({' '.join(serialize_value(v) for v in value)})"
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 _is_sequence(value):
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 _is_sequence(value) and len(value) == 7:
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)} {serialize_value(value.value)}"
68
+ return f"{value.name} {_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
77
69
  else:
78
- return f"{_serialize_dimensions(value.dimensions)} {serialize_value(value.value)}"
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 serialize_value(
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 Union, Sequence, Mapping
6
- import subprocess
7
- from subprocess import CalledProcessError
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
- __all__ = ["run_process", "run_process_async", "CalledProcessError"]
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 = isinstance(cmd, str) or not isinstance(cmd, Sequence)
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
- stdout=subprocess.PIPE,
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 isinstance(cmd, str) or not isinstance(cmd, Sequence):
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
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: mypy<2,>=1; extra == "lint"
32
- Requires-Dist: pytest<9,>=7; extra == "lint"
33
- Requires-Dist: pytest-asyncio<0.24,>=0.21; extra == "lint"
34
- Requires-Dist: numpy<2,>=1; extra == "lint"
35
- Requires-Dist: black; extra == "lint"
36
- Requires-Dist: flake8; extra == "lint"
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
  [![CI](https://github.com/gerlero/foamlib/actions/workflows/ci.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
52
52
  [![Codecov](https://codecov.io/gh/gerlero/foamlib/branch/main/graph/badge.svg)](https://codecov.io/gh/gerlero/foamlib)
53
53
  [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
54
- [![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)
55
55
  [![PyPI](https://img.shields.io/pypi/v/foamlib)](https://pypi.org/project/foamlib/)
56
56
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/foamlib)](https://pypi.org/project/foamlib/)
57
57
  [![Docker image](https://img.shields.io/badge/docker%20image-gerlero%2Ffoamlib-informational)](https://hub.docker.com/r/gerlero/foamlib/)
@@ -3,7 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  foamlib/__init__.py
5
5
  foamlib/_cases.py
6
- foamlib/_subprocesses.py
6
+ foamlib/_util.py
7
7
  foamlib/py.typed
8
8
  foamlib.egg-info/PKG-INFO
9
9
  foamlib.egg-info/SOURCES.txt
@@ -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
- mypy<2,>=1
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
- [test]
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
- ]
@@ -6,7 +6,7 @@ from typing import Sequence
6
6
 
7
7
  import numpy as np
8
8
 
9
- from foamlib import *
9
+ from foamlib import FoamCase, FoamFile, FoamFieldFile
10
10
  from foamlib._dictionaries._parsing import _VALUE
11
11
 
12
12
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes