Multiclass-interface 1.17__tar.gz → 1.18__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 (26) hide show
  1. {multiclass_interface-1.17 → multiclass_interface-1.18/Multiclass_interface.egg-info}/PKG-INFO +1 -1
  2. {multiclass_interface-1.17 → multiclass_interface-1.18}/Multiclass_interface.egg-info/SOURCES.txt +2 -0
  3. multiclass_interface-1.18/Multiclass_interface.egg-info/scm_file_list.json +19 -0
  4. multiclass_interface-1.18/Multiclass_interface.egg-info/scm_version.json +8 -0
  5. {multiclass_interface-1.17/Multiclass_interface.egg-info → multiclass_interface-1.18}/PKG-INFO +1 -1
  6. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/mpi_interface.py +70 -0
  7. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/multi_object_list.py +6 -0
  8. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/multiprocess_interface.py +3 -2
  9. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/tests/my_test_cls.py +1 -0
  10. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/tests/test_mpi.py +26 -8
  11. multiclass_interface-1.18/multiclass_interface/version.py +24 -0
  12. multiclass_interface-1.17/multiclass_interface/version.py +0 -21
  13. {multiclass_interface-1.17 → multiclass_interface-1.18}/.gitignore +0 -0
  14. {multiclass_interface-1.17 → multiclass_interface-1.18}/.gitlab-ci.yml +0 -0
  15. {multiclass_interface-1.17 → multiclass_interface-1.18}/LICENSE +0 -0
  16. {multiclass_interface-1.17 → multiclass_interface-1.18}/Multiclass_interface.egg-info/dependency_links.txt +0 -0
  17. {multiclass_interface-1.17 → multiclass_interface-1.18}/Multiclass_interface.egg-info/requires.txt +0 -0
  18. {multiclass_interface-1.17 → multiclass_interface-1.18}/Multiclass_interface.egg-info/top_level.txt +0 -0
  19. {multiclass_interface-1.17 → multiclass_interface-1.18}/README.md +0 -0
  20. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/__init__.py +0 -0
  21. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/tests/__init__.py +0 -0
  22. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/tests/test_multiclass_list.py +0 -0
  23. {multiclass_interface-1.17 → multiclass_interface-1.18}/multiclass_interface/tests/test_multiprocessinterface.py +0 -0
  24. {multiclass_interface-1.17 → multiclass_interface-1.18}/pyproject.toml +0 -0
  25. {multiclass_interface-1.17 → multiclass_interface-1.18}/sequence_diagrams.md +0 -0
  26. {multiclass_interface-1.17 → multiclass_interface-1.18}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Multiclass_interface
3
- Version: 1.17
3
+ Version: 1.18
4
4
  Summary: Multiclass interface
5
5
  Author-email: "Mads M. Pedersen" <mmpe@dtu.dk>
6
6
  Project-URL: Homepage, https://gitlab.windenergy.dtu.dk/DYNAMIKS/multiclass_interface
@@ -8,6 +8,8 @@ Multiclass_interface.egg-info/PKG-INFO
8
8
  Multiclass_interface.egg-info/SOURCES.txt
9
9
  Multiclass_interface.egg-info/dependency_links.txt
10
10
  Multiclass_interface.egg-info/requires.txt
11
+ Multiclass_interface.egg-info/scm_file_list.json
12
+ Multiclass_interface.egg-info/scm_version.json
11
13
  Multiclass_interface.egg-info/top_level.txt
12
14
  multiclass_interface/__init__.py
13
15
  multiclass_interface/mpi_interface.py
@@ -0,0 +1,19 @@
1
+ {
2
+ "files": [
3
+ ".gitignore",
4
+ ".gitlab-ci.yml",
5
+ "LICENSE",
6
+ "README.md",
7
+ "multiclass_interface/__init__.py",
8
+ "multiclass_interface/mpi_interface.py",
9
+ "multiclass_interface/multi_object_list.py",
10
+ "multiclass_interface/multiprocess_interface.py",
11
+ "multiclass_interface/tests/__init__.py",
12
+ "multiclass_interface/tests/my_test_cls.py",
13
+ "multiclass_interface/tests/test_mpi.py",
14
+ "multiclass_interface/tests/test_multiclass_list.py",
15
+ "multiclass_interface/tests/test_multiprocessinterface.py",
16
+ "pyproject.toml",
17
+ "sequence_diagrams.md"
18
+ ]
19
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.18",
3
+ "distance": 0,
4
+ "node": "gb934019843ac9640a5beae7880add16d3c7e3838",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-23"
8
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Multiclass_interface
3
- Version: 1.17
3
+ Version: 1.18
4
4
  Summary: Multiclass interface
5
5
  Author-email: "Mads M. Pedersen" <mmpe@dtu.dk>
6
6
  Project-URL: Homepage, https://gitlab.windenergy.dtu.dk/DYNAMIKS/multiclass_interface
@@ -1,4 +1,9 @@
1
+ import inspect
2
+ from pathlib import Path
3
+ import pytest
4
+ import os
1
5
  import numpy as np
6
+ import shutil
2
7
  import sys
3
8
  import traceback
4
9
  from contextlib import contextmanager
@@ -234,3 +239,68 @@ class SubsetMPIClassInterface(MPIClassInterface):
234
239
 
235
240
  def close(self):
236
241
  raise Exception('Cannot close SubsetMPIClassInterface. Please close all instances at once')
242
+
243
+
244
+ def barrier_print(s):
245
+ from mpi4py import MPI
246
+
247
+ newData = comm.gather(rank, root=0)
248
+ newData = comm.gather(rank, root=0)
249
+ import sys
250
+ sys.stdout.flush()
251
+ if rank == 0:
252
+ print("#########", s, newData, flush=True)
253
+
254
+
255
+ def bcast_function_result(f):
256
+ # Decorator to broadcast the return value of a function as a wt dependent list to all mpi workers
257
+ # collect the return value from each mpiworker and broadcast the full list with wt as first dimension to all workers
258
+ def wrapper(*args, **kwargs):
259
+ if mpi:
260
+ try:
261
+ v = f(*args, **kwargs)[0]
262
+ except ChildProcessError:
263
+ v = None
264
+
265
+ # collect the return value from each mpiworker (wt as first dimension)
266
+ v = comm.gather(v, root=0)
267
+ # broadcast the full list to all workers
268
+ return comm.bcast(v, root=0)
269
+ else:
270
+ return f(*args, **kwargs)
271
+ return wrapper
272
+
273
+
274
+ def mpi_run_test(n, cwd='.', mpirun_cmd='mpirun'):
275
+ if callable(n):
276
+ return mpi_run_test(n=4)(n)
277
+
278
+ def decorator(test_function):
279
+ try:
280
+ import mpi4py
281
+ assert shutil.which('mpirun') is not None
282
+ except Exception: # pragma: no cover
283
+ return lambda: pytest.xfail('mpirun or mpi4py missing')
284
+ name = test_function.__name__
285
+
286
+ def wrap(mpirun=False):
287
+ cwd = os.getcwd()
288
+ try:
289
+ if mpirun:
290
+ os.chdir(cwd)
291
+ return test_function()
292
+ else:
293
+
294
+ import_path = Path(inspect.getsourcefile(test_function)).relative_to(os.getcwd()).as_posix()
295
+ import_path = import_path[:-3].replace('/', '.')
296
+ cmd = f'''{mpirun_cmd} -n {n} {sys.executable} -c "from {import_path} import {name}; {name}(mpirun=True)"'''
297
+ print(f"cd {os.getcwd()} && {cmd}", flush=True)
298
+ assert os.system(cmd) == 0, name
299
+
300
+ finally:
301
+ os.chdir(cwd)
302
+
303
+ wrap.__name__ = name
304
+ return wrap
305
+
306
+ return decorator
@@ -9,6 +9,12 @@ class MultiObjectList():
9
9
  def __str__(self):
10
10
  return f'{self.__class__.__name__}({self.obj_lst})'
11
11
 
12
+ def __enter__(self):
13
+ return self
14
+
15
+ def __exit__(self, exc_type, exc_val, exc_tb):
16
+ pass
17
+
12
18
  def get_obj_args_lst(self, args, kwargs):
13
19
  N = len(self.obj_lst)
14
20
 
@@ -21,7 +21,8 @@ from multiclass_interface.multi_object_list import MultiObjectList
21
21
 
22
22
  def run(cls, inputQueue, outputQueue, cls_args, **kwargs):
23
23
  o = cls(*cls_args, **kwargs)
24
- while True:
24
+ running = True
25
+ while running:
25
26
  method, args, kwargs = inputQueue.get()
26
27
  try:
27
28
  if method == 'getattr':
@@ -48,7 +49,7 @@ def run(cls, inputQueue, outputQueue, cls_args, **kwargs):
48
49
  finally:
49
50
  if method == 'close':
50
51
  outputQueue.put('Exit process')
51
- return
52
+ running = False
52
53
 
53
54
 
54
55
  class ProcessClass():
@@ -1,5 +1,6 @@
1
1
  import time
2
2
  import os
3
+ from multiclass_interface.mpi_interface import bcast_function_result
3
4
 
4
5
 
5
6
  class MyTest():
@@ -10,6 +10,8 @@ from multiclass_interface.multiprocess_interface import ProcessClass
10
10
  import numpy as np
11
11
  from multiclass_interface.tests.my_test_cls import MyTest
12
12
  from multiclass_interface import mpi_interface
13
+ from multiclass_interface.mpi_interface import mpi_run_test
14
+ from multiclass_interface.multi_object_list import MultiClassInterface
13
15
 
14
16
 
15
17
  def mpirun(f):
@@ -27,7 +29,7 @@ def mpirun(f):
27
29
  return f
28
30
 
29
31
 
30
- @mpirun
32
+ @mpi_run_test
31
33
  def test_mpi_collective_too_few_slots():
32
34
  mpi_interface.activate_mpi()
33
35
  N = 4
@@ -42,7 +44,7 @@ def test_mpi_collective_too_few_slots():
42
44
  assert str(e) == 'Not enough mpi slots. Slots: 4, Requested: 5'
43
45
 
44
46
 
45
- @mpirun
47
+ @mpi_run_test
46
48
  def test_mpi_collective_MyTest():
47
49
  mpi_interface.activate_mpi()
48
50
  N = 4
@@ -92,7 +94,7 @@ def test_mpi_collective_MyTest():
92
94
  m[:3][1]
93
95
 
94
96
 
95
- @mpirun
97
+ @mpi_run_test
96
98
  def test_mpi_collective_close_released_mpi_workers():
97
99
  mpi_interface.activate_mpi()
98
100
  N = 4
@@ -102,7 +104,7 @@ def test_mpi_collective_close_released_mpi_workers():
102
104
  m.close()
103
105
 
104
106
 
105
- @mpirun
107
+ @mpi_run_test
106
108
  def test_mpi_ProcessClass():
107
109
  mpi_interface.activate_mpi()
108
110
  with ProcessClass(MyTest) as cls:
@@ -110,13 +112,19 @@ def test_mpi_ProcessClass():
110
112
  assert myTest.get_id() == 1
111
113
 
112
114
 
113
- @mpirun
115
+ @mpi_run_test
114
116
  def test_non_collective_mpi2():
115
117
  mpi_interface.activate_mpi(collective_mpi=False)
116
118
  rank = mpi_interface.rank
117
119
  N = 3
118
120
  with MPIClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
119
121
 
122
+ id_lst = mpi_interface.bcast_function_result(lambda: m.get_id())()
123
+ assert id_lst == [10, 11, 12, None], id_lst
124
+ id_lst = mpi_interface.bcast_function_result(lambda: m.return_input(['a', 'b', 'c']))()
125
+ assert id_lst == ["10 got: ('a',) and {}", "11 got: ('b',) and {}", "12 got: ('c',) and {}", None], id_lst
126
+
127
+ mpi_interface.barrier_print('hello world')
120
128
  try:
121
129
  assert m.get_id()[0] == rank + 10, m.get_id()
122
130
  main_id = mpi_interface.main_run(lambda: m.get_id())
@@ -137,7 +145,17 @@ def test_non_collective_mpi2():
137
145
  m.object.cls
138
146
 
139
147
 
140
- @mpirun
148
+ def test_non_collective_mpi2_no_mpi():
149
+ N = 3
150
+ with MultiClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
151
+
152
+ id_lst = mpi_interface.bcast_function_result(lambda: m.get_id())()
153
+ assert id_lst == [10, 11, 12], id_lst
154
+ id_lst = mpi_interface.bcast_function_result(lambda: m.return_input(['a', 'b', 'c']))()
155
+ assert id_lst == ["10 got: ('a',) and {}", "11 got: ('b',) and {}", "12 got: ('c',) and {}"], id_lst
156
+
157
+
158
+ @mpi_run_test(4)
141
159
  def test_mpi_non_collective_MyTest():
142
160
  mpi_interface.activate_mpi(collective_mpi=False)
143
161
  rank = mpi_interface.rank
@@ -185,7 +203,7 @@ def test_mpi_non_collective_MyTest():
185
203
  m[:3][1]
186
204
 
187
205
 
188
- @mpirun
206
+ @mpi_run_test(4)
189
207
  def test_multiprocessinterface_tests():
190
208
  from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
191
209
  mpi_interface.activate_mpi()
@@ -198,7 +216,7 @@ def test_multiprocessinterface_tests():
198
216
  pass
199
217
 
200
218
 
201
- @mpirun
219
+ @mpi_run_test(4)
202
220
  def test_multiprocessinterface_tests_diff_interface():
203
221
  from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
204
222
  mpi_interface.activate_mpi(terminate_on_close=False)
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.18'
22
+ __version_tuple__ = version_tuple = (1, 18)
23
+
24
+ __commit_id__ = commit_id = 'gb93401984'
@@ -1,21 +0,0 @@
1
- # file generated by setuptools-scm
2
- # don't change, don't track in version control
3
-
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
-
6
- TYPE_CHECKING = False
7
- if TYPE_CHECKING:
8
- from typing import Tuple
9
- from typing import Union
10
-
11
- VERSION_TUPLE = Tuple[Union[int, str], ...]
12
- else:
13
- VERSION_TUPLE = object
14
-
15
- version: str
16
- __version__: str
17
- __version_tuple__: VERSION_TUPLE
18
- version_tuple: VERSION_TUPLE
19
-
20
- __version__ = version = '1.17'
21
- __version_tuple__ = version_tuple = (1, 17)