Multiclass-interface 1.12__tar.gz → 1.15__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 (24) hide show
  1. {multiclass_interface-1.12 → multiclass_interface-1.15}/.gitlab-ci.yml +4 -23
  2. {multiclass_interface-1.12 → multiclass_interface-1.15/Multiclass_interface.egg-info}/PKG-INFO +1 -1
  3. {multiclass_interface-1.12/Multiclass_interface.egg-info → multiclass_interface-1.15}/PKG-INFO +1 -1
  4. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/mpi_interface.py +77 -45
  5. multiclass_interface-1.15/multiclass_interface/tests/test_mpi.py +211 -0
  6. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/version.py +9 -4
  7. {multiclass_interface-1.12 → multiclass_interface-1.15}/pyproject.toml +0 -3
  8. multiclass_interface-1.12/multiclass_interface/tests/test_mpi.py +0 -97
  9. {multiclass_interface-1.12 → multiclass_interface-1.15}/.gitignore +0 -0
  10. {multiclass_interface-1.12 → multiclass_interface-1.15}/LICENSE +0 -0
  11. {multiclass_interface-1.12 → multiclass_interface-1.15}/Multiclass_interface.egg-info/SOURCES.txt +0 -0
  12. {multiclass_interface-1.12 → multiclass_interface-1.15}/Multiclass_interface.egg-info/dependency_links.txt +0 -0
  13. {multiclass_interface-1.12 → multiclass_interface-1.15}/Multiclass_interface.egg-info/requires.txt +0 -0
  14. {multiclass_interface-1.12 → multiclass_interface-1.15}/Multiclass_interface.egg-info/top_level.txt +0 -0
  15. {multiclass_interface-1.12 → multiclass_interface-1.15}/README.md +0 -0
  16. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/__init__.py +0 -0
  17. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/multi_object_list.py +0 -0
  18. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/multiprocess_interface.py +0 -0
  19. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/tests/__init__.py +0 -0
  20. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/tests/my_test_cls.py +0 -0
  21. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/tests/test_multiclass_list.py +0 -0
  22. {multiclass_interface-1.12 → multiclass_interface-1.15}/multiclass_interface/tests/test_multiprocessinterface.py +0 -0
  23. {multiclass_interface-1.12 → multiclass_interface-1.15}/sequence_diagrams.md +0 -0
  24. {multiclass_interface-1.12 → multiclass_interface-1.15}/setup.cfg +0 -0
@@ -2,39 +2,22 @@ image: continuumio/anaconda3
2
2
 
3
3
  stages:
4
4
  - test
5
- - build
6
5
  - deploy
7
6
 
8
- # ===== TEST Dynamiks ubuntu =====
9
- test_ubuntu: # name the job what we like
10
- image: continuumio/anaconda3
11
- stage: # build, test, deploy defined by default [2]
12
- test
13
- script:
14
- - pip install -e .[test]
15
- - pytest --cov-report term-missing:skip-covered --cov=multiclass_interface multiclass_interface/tests
16
- coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
17
- tags: # only runners with this tag can do the job [3]
18
- - ci-ubuntu
19
- rules:
20
- - if: $CI_COMMIT_BRANCH == "main"
21
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
22
-
23
-
24
- mpitest-linux_openmpi:
7
+ test_mpitest_openmpi:
25
8
  image: registry.windenergy.dtu.dk/hawc2/hawc2lib:mpi
26
9
  variables:
27
10
  GIT_SUBMODULE_STRATEGY: recursive # need to checkout hawc2 and utils
28
11
  script:
29
12
  - pip install .[test]
30
- - mpirun -n 4 python -c "from multiclass_interface.tests.test_mpi import mpitest_all; mpitest_all()"
31
- - python -c "from multiclass_interface import version; print (f'{version.__version__}')"
13
+ - pytest --cov-report term-missing:skip-covered --cov=multiclass_interface multiclass_interface/tests
32
14
  rules:
33
15
  - if: $CI_COMMIT_BRANCH == "main"
34
16
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
35
17
  tags:
36
18
  - ci-ubuntu
37
19
 
20
+
38
21
  # ===== Check code style =====
39
22
  check_code_style: # name the job what we like
40
23
  stage: # build, test, deploy defined by default [2]
@@ -49,8 +32,6 @@ check_code_style: # name the job what we like
49
32
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
50
33
 
51
34
 
52
-
53
-
54
35
  pypi_linux:
55
36
  stage:
56
37
  deploy
@@ -61,7 +42,7 @@ pypi_linux:
61
42
  #- pip install pypandoc
62
43
  - pip install -e . --upgrade
63
44
  - pip install --upgrade build
64
- - pip install --upgrade twine
45
+ - pip install twine==6.0.1 --upgrade
65
46
  - python3 -m build
66
47
  - twine upload dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD
67
48
  rules:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Multiclass_interface
3
- Version: 1.12
3
+ Version: 1.15
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,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Multiclass_interface
3
- Version: 1.12
3
+ Version: 1.15
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,7 +1,7 @@
1
1
  import numpy as np
2
2
  import sys
3
3
  import traceback
4
- import inspect
4
+ from contextlib import contextmanager
5
5
 
6
6
  comm = None
7
7
  size = 1
@@ -11,8 +11,8 @@ mpi = False
11
11
  main = True
12
12
 
13
13
 
14
- def activate_mpi(collective_mpi=True, loop_until_close=True, terminate_on_close=True):
15
- global comm, size, rank, name, main, mpi, LOOP_UNTIL_CLOSE, TERMINATE_ON_CLOSE, COLLECTIVE_MPI
14
+ def activate_mpi(collective_mpi=True, terminate_on_close=True):
15
+ global comm, size, rank, name, main, mpi, TERMINATE_ON_CLOSE, COLLECTIVE_MPI
16
16
  from mpi4py import MPI
17
17
  comm = MPI.COMM_WORLD
18
18
  size = MPI.COMM_WORLD.Get_size()
@@ -21,12 +21,12 @@ def activate_mpi(collective_mpi=True, loop_until_close=True, terminate_on_close=
21
21
  mpi = True
22
22
  main = rank == 0
23
23
  COLLECTIVE_MPI = collective_mpi
24
- LOOP_UNTIL_CLOSE = loop_until_close
25
24
  TERMINATE_ON_CLOSE = terminate_on_close
26
25
 
27
26
 
28
27
  MPIClassInterfaceAttributes = {
29
28
  'close',
29
+ 'release_mpi_workers',
30
30
  'use_rank',
31
31
  'cls',
32
32
  'work_loop',
@@ -38,13 +38,27 @@ MPIClassInterfaceAttributes = {
38
38
  'closed'}
39
39
 
40
40
 
41
- LOOP_UNTIL_CLOSE = True # MPI workers (rank>0) will enter the work loop and wait for commands from rank 0
42
41
  TERMINATE_ON_CLOSE = True # MPI workers (rank>0) will terminate (sys.exit) when the close command is called via rank0
43
42
  COLLECTIVE_MPI = True # rank0 acts as a single point of contact (scatters command+arg and gathers results)
44
43
 
45
44
 
45
+ def main_run(f, *args, **kwargs):
46
+ if main:
47
+ r = f(*args, **kwargs)
48
+ if COLLECTIVE_MPI:
49
+ return r[0]
50
+ else:
51
+ try:
52
+ f(*args, **kwargs)
53
+ except BaseException:
54
+ pass
55
+ r = None
56
+ return comm.bcast(r)
57
+
58
+
46
59
  class MPIClassInterface():
47
60
  def __init__(self, cls, args_lst):
61
+ self.closed = False
48
62
  if len(args_lst) > size:
49
63
  if rank == 0:
50
64
  raise Exception(f"Not enough mpi slots. Slots: {size}, Requested: {len(args_lst)}")
@@ -59,13 +73,12 @@ class MPIClassInterface():
59
73
 
60
74
  def __getattr__(self, name):
61
75
  raise AttributeError(
62
- f"rank {rank} trying to access {name}, but only {len(args_lst)} instances of {cls.__name__} exists")
76
+ f"Rank {rank} trying to access {name}, but only {len(args_lst)} instances of {cls.__name__} exists")
63
77
  self.object = Dummy()
64
- self.closed = False
65
78
 
66
79
  self.use_rank = np.array([True] * size)
67
80
  self.use_rank[len(args_lst):] = False
68
- if rank > 0 and COLLECTIVE_MPI and LOOP_UNTIL_CLOSE:
81
+ if rank > 0 and COLLECTIVE_MPI:
69
82
  self.work_loop()
70
83
 
71
84
  def __enter__(self):
@@ -78,20 +91,20 @@ class MPIClassInterface():
78
91
  while True:
79
92
  method, args, kwargs = comm.scatter(None)
80
93
  comm.gather(self.do_task(method, args, kwargs))
81
- if LOOP_UNTIL_CLOSE:
82
- if method == 'close':
83
- if TERMINATE_ON_CLOSE:
84
- # comm.gather(f'Exit, rank {rank}')
85
- print("sys.exit", rank, flush=True)
86
- sys.exit(0)
87
- else:
88
- raise ChildProcessError('MPI worker done')
89
- else:
94
+ if method == 'exit_work_loop':
90
95
  break
96
+ if method == 'close':
97
+ if TERMINATE_ON_CLOSE:
98
+ # comm.gather(f'Exit, rank {rank}')
99
+ print("sys.exit", rank, flush=True)
100
+ self.closed = True
101
+ sys.exit(0)
102
+ else:
103
+ raise ChildProcessError('MPI worker done')
91
104
 
92
105
  def do_task(self, method, args, kwargs):
93
106
  try:
94
- if method == 'skip':
107
+ if method == 'skip' or method == 'exit_work_loop':
95
108
  res = None
96
109
  elif method == 'setattr':
97
110
  name, value = args
@@ -105,29 +118,28 @@ class MPIClassInterface():
105
118
  return res
106
119
 
107
120
  def run_task(self, input_lst):
121
+ assert rank == 0, "other ranks should be in work_loop when invoking run_task. For non-collective mpi, do_task should be used"
108
122
  comm.scatter(input_lst, root=0)
109
- if rank == 0:
110
- method, args, kwargs = input_lst[0]
111
- res = self.do_task(method, args, kwargs)
112
- else:
113
- res = None
123
+ method, args, kwargs = input_lst[0]
124
+ res = self.do_task(method, args, kwargs)
125
+
114
126
  use_rank = self.use_rank
115
127
 
116
128
  res = [res for i, res in enumerate(comm.gather(res, root=0)) if use_rank[i]]
117
- if rank == 0:
118
- for r in res:
119
- if isinstance(r, tuple) and len(r) > 1 and isinstance(r[0], BaseException):
120
- raise r[0].__class__(r[1])
129
+ for r in res:
130
+ if isinstance(r, tuple) and len(r) > 1 and isinstance(r[0], BaseException):
131
+ raise r[0].__class__(r[1])
121
132
  return res
122
133
 
123
134
  def get_input(self, name, i, args, kwargs):
124
135
  use_rank = self.use_rank
125
- if COLLECTIVE_MPI:
126
- N = np.sum(use_rank)
127
- j = sum(use_rank[:i])
128
- else:
129
- N = 1
130
- j = 0
136
+ N = np.sum(use_rank)
137
+ j = sum(use_rank[:i]) # needed for subsets
138
+
139
+ # if COLLECTIVE_MPI:
140
+ # j = sum(use_rank[:i])
141
+ # else:
142
+ # j = 0
131
143
 
132
144
  if use_rank[i]:
133
145
  def get_arg(arg):
@@ -135,6 +147,7 @@ class MPIClassInterface():
135
147
  return arg[j]
136
148
  else:
137
149
  return arg
150
+
138
151
  return (name, [get_arg(arg) for arg in args], {k: get_arg(v) for k, v in kwargs.items()})
139
152
  else:
140
153
  return ('skip', [], {})
@@ -142,10 +155,7 @@ class MPIClassInterface():
142
155
  def __getattribute__(self, name):
143
156
  if name in MPIClassInterfaceAttributes:
144
157
  return object.__getattribute__(self, name)
145
- elif COLLECTIVE_MPI and rank > 0:
146
- self.work_loop()
147
- return lambda *args, **kwargs: 1
148
- elif rank > 0 and not self.use_rank[rank]:
158
+ elif not self.use_rank[rank]:
149
159
  raise ChildProcessError(f'MPI worker {rank} not used')
150
160
 
151
161
  def wrap(*args, **kwargs):
@@ -153,20 +163,28 @@ class MPIClassInterface():
153
163
  inp = [self.get_input(name, i, args, kwargs) for i in range(size)]
154
164
  return self.run_task(inp)
155
165
  else:
156
- method, args, kwargs = self.get_input(name, 0, args, kwargs)
157
- return [self.do_task(name, args, kwargs)]
158
- if hasattr(getattr(self.object, name), '__call__'):
166
+ method, args, kwargs = self.get_input(name, rank, args, kwargs)
167
+ return [self.do_task(method, args, kwargs)]
168
+ try:
169
+ att = getattr(self.object, name)
170
+ except AttributeError as e:
171
+ raise type(e)(f'Rank {rank}: {e}').with_traceback(sys.exc_info()[2])
172
+
173
+ if hasattr(att, '__call__'):
159
174
  res = wrap
160
175
  else:
161
176
  res = wrap()
162
177
  return res
163
178
 
164
179
  def __setattr__(self, name, value):
165
- if rank > 0 or name in MPIClassInterfaceAttributes:
180
+ if (COLLECTIVE_MPI and rank > 0) or name in MPIClassInterfaceAttributes:
166
181
  return object.__setattr__(self, name, value)
167
182
  else:
168
183
  inp = [self.get_input('setattr', i, (name, value), {}) for i in range(size)]
169
- return self.run_task(inp)
184
+ if COLLECTIVE_MPI:
185
+ return self.run_task(inp)
186
+ else:
187
+ return self.do_task(*inp[rank])
170
188
 
171
189
  def __getitem__(self, slice):
172
190
  use_rank = np.full_like(self.use_rank, False)
@@ -175,14 +193,28 @@ class MPIClassInterface():
175
193
  return self
176
194
  return SubsetMPIClassInterface(self.cls, self.object, use_rank)
177
195
 
196
+ @contextmanager
197
+ def release_mpi_workers(self):
198
+ global COLLECTIVE_MPI
199
+ """Use to release mpi_workers. All object statements including 'release_mpi_workers' must be protected by 'if mpi_interface.main:' """
200
+ if COLLECTIVE_MPI:
201
+ if rank == 0:
202
+ self.run_task([('exit_work_loop', [], {}) for _ in range(size)])
203
+ COLLECTIVE_MPI = False
204
+ try:
205
+ yield self
206
+ finally:
207
+ COLLECTIVE_MPI = True
208
+ if rank > 0 and not self.closed:
209
+ self.work_loop()
210
+
178
211
  def close(self):
212
+
179
213
  if not self.closed:
214
+
180
215
  if COLLECTIVE_MPI:
181
216
  if rank == 0:
182
217
  res = self.run_task([('close', [], {}) for _ in range(size)])
183
- else:
184
- self.work_loop()
185
- res = None
186
218
  else:
187
219
  res = self.do_task('close', [], {})
188
220
  self.closed = True
@@ -0,0 +1,211 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import traceback
5
+ from numpy import testing as npt
6
+ import pytest
7
+
8
+ from multiclass_interface.mpi_interface import MPIClassInterface
9
+ from multiclass_interface.multiprocess_interface import ProcessClass
10
+ import numpy as np
11
+ from multiclass_interface.tests.my_test_cls import MyTest
12
+ from multiclass_interface import mpi_interface
13
+
14
+
15
+ def mpirun(f):
16
+ if 'main()' in traceback.format_stack()[0]:
17
+ n = f.__name__
18
+
19
+ def wrap():
20
+ exe = sys.executable.replace("\\", "/")
21
+ cmd = f'''mpirun -n 4 {exe} -c "from multiclass_interface.tests.test_mpi import {n}; {n}()"'''
22
+ # print(cmd)
23
+ assert os.system(cmd) == 0, n
24
+ wrap.__name__ = n
25
+ return wrap
26
+ else:
27
+ return f
28
+
29
+
30
+ @mpirun
31
+ def test_mpi_collective_too_few_slots():
32
+ mpi_interface.activate_mpi()
33
+ N = 4
34
+ rank = mpi_interface.rank
35
+
36
+ try:
37
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N + 1)]) as m:
38
+ pass
39
+ assert rank in [1, 2, 3]
40
+ except Exception as e:
41
+ assert rank == 0
42
+ assert str(e) == 'Not enough mpi slots. Slots: 4, Requested: 5'
43
+
44
+
45
+ @mpirun
46
+ def test_mpi_collective_MyTest():
47
+ mpi_interface.activate_mpi()
48
+ N = 4
49
+ rank = mpi_interface.rank
50
+
51
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
52
+ if mpi_interface.main:
53
+ i = m.get_id()
54
+ npt.assert_array_equal(i, np.arange(N) + 1)
55
+
56
+ with m.release_mpi_workers():
57
+ assert m.get_id() == [rank + 1]
58
+
59
+ assert m.get_id() == [1, 2, 3, 4]
60
+ i = mpi_interface.main_run(lambda: m.get_id())
61
+ npt.assert_array_equal(i, 1)
62
+
63
+ try:
64
+ npt.assert_array_equal(m[1:3].get_id(), np.arange(1, 3) + 1)
65
+ except ChildProcessError:
66
+ pass
67
+
68
+ m.x = "hello"
69
+ assert m.x == ['hello'] * 4
70
+
71
+ assert m.return_input([101, 102, 103, 104])[0] == f'1 got: ({101},) and ' + '{}'
72
+ m.x = 'hello'
73
+ assert m.x == ['hello'] * 4
74
+ m[1::2].x = 'world'
75
+ assert m[:].x == ['hello', 'world', 'hello', 'world']
76
+
77
+ m.release_mpi_workers()
78
+ m.x = [1, 2, 3, 4]
79
+ assert m.x == [1, 2, 3, 4]
80
+ m.y = list(np.array(m.x) + 10)
81
+ assert m.y == [11, 12, 13, 14]
82
+ t = time.time()
83
+ m.work(1)
84
+ t = time.time() - t
85
+ assert t < 1.1
86
+
87
+ with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
88
+ m[:3].close()
89
+ with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
90
+ m[:3][1]
91
+
92
+
93
+ @mpirun
94
+ def test_mpi_collective_close_released_mpi_workers():
95
+ mpi_interface.activate_mpi()
96
+ N = 4
97
+ rank = mpi_interface.rank
98
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
99
+ with m.release_mpi_workers():
100
+ m.close()
101
+
102
+
103
+ @mpirun
104
+ def test_mpi_ProcessClass():
105
+ mpi_interface.activate_mpi()
106
+ with ProcessClass(MyTest) as cls:
107
+ myTest = cls(1)
108
+ assert myTest.get_id() == 1
109
+
110
+
111
+ @mpirun
112
+ def test_non_collective_mpi2():
113
+ mpi_interface.activate_mpi(collective_mpi=False)
114
+ rank = mpi_interface.rank
115
+ N = 3
116
+ with MPIClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
117
+
118
+ try:
119
+ assert m.get_id()[0] == rank + 10, m.get_id()
120
+ main_id = mpi_interface.main_run(lambda: m.get_id())
121
+ assert main_id == [10], (rank, main_id)
122
+ assert rank < N # rank > N will fail with ChildProcessError
123
+ assert m.return_input([101, 102, 103])[0] == f'{rank + 10} got: ({rank + 101},) and ' + '{}'
124
+ m.x = 'hello'
125
+ assert m.x[0] == 'hello', m.x[0]
126
+ m.x = [1, 2, 3]
127
+ assert m.x[0] == rank + 1
128
+ m.y = m.x[0] + 10
129
+ assert m.y[0] == rank + 11
130
+ except ChildProcessError:
131
+ pass
132
+
133
+ if rank == 3:
134
+ with pytest.raises(AttributeError, match='Rank 3 trying to access cls, but only 3 instances of MyTest exists'):
135
+ m.object.cls
136
+
137
+
138
+ @mpirun
139
+ def test_mpi_non_collective_MyTest():
140
+ mpi_interface.activate_mpi(collective_mpi=False)
141
+ rank = mpi_interface.rank
142
+ N = 4
143
+
144
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
145
+ i = m.get_id()[0]
146
+ assert i == rank + 1
147
+ i = mpi_interface.main_run(lambda: m.get_id())
148
+ npt.assert_array_equal(i, 1) # id of rank0 broadcast to all
149
+ try:
150
+ assert m[1:3].get_id()[0] == rank + 1
151
+ except ChildProcessError:
152
+ assert rank in [0, 3]
153
+
154
+ # rank>0 fails, but only rank=0 is needed, so should not fail
155
+ mpi_interface.main_run(lambda rank=rank: 1 / [0, 1][rank == 0])
156
+
157
+ assert m.return_input([101, 102, 103, 104])[0] == f'{rank + 1} got: ({rank + 101},) and ' + '{}'
158
+ m.x = "hello"
159
+ assert m.x[0] == 'hello'
160
+
161
+ m[1::2].x = 'world'
162
+ assert m.x[0] == ['hello', 'world'][rank % 2]
163
+
164
+ try:
165
+ assert m[1::2].x[0] == 'world'
166
+ assert m[1::2][:].x[0] == 'world'
167
+ except ChildProcessError:
168
+ pass
169
+
170
+ m.x = [1, 2, 3, 4]
171
+ assert m.x[0] == rank + 1
172
+
173
+ m.y = m.x[0] + 10
174
+ assert m.y[0] == rank + 11
175
+ t = time.time()
176
+ m.work(1)
177
+ t = time.time() - t
178
+ assert t < 1.1
179
+
180
+ with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
181
+ m[:3].close()
182
+ with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
183
+ m[:3][1]
184
+
185
+
186
+ @mpirun
187
+ def test_multiprocessinterface_tests():
188
+ from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
189
+ mpi_interface.activate_mpi()
190
+
191
+ try:
192
+ with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
193
+ for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
194
+ f(mpici)
195
+ except ChildProcessError:
196
+ pass
197
+
198
+
199
+ @mpirun
200
+ def test_multiprocessinterface_tests_diff_interface():
201
+ from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
202
+ mpi_interface.activate_mpi(terminate_on_close=False)
203
+
204
+ for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
205
+ # print (mpi_interface.rank, f.__name__,flush=True)
206
+ try:
207
+ with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
208
+ f(mpici)
209
+ except ChildProcessError:
210
+ pass
211
+ sys.exit(0)
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '1.12'
16
- __version_tuple__ = version_tuple = (1, 12)
20
+ __version__ = version = '1.15'
21
+ __version_tuple__ = version_tuple = (1, 15)
@@ -41,9 +41,6 @@ version_file = "multiclass_interface/version.py"
41
41
  [tool.coverage.run]
42
42
  omit = [
43
43
  "multiclass_interface/version.py",
44
- "multiclass_interface/mpi_utils.py",
45
- "multiclass_interface/mpi_interface.py",
46
- "multiclass_interface/tests/my_test_cls.py",
47
44
  "multiclass_interface/tests/*",
48
45
  ]
49
46
 
@@ -1,97 +0,0 @@
1
- import os
2
- import time
3
-
4
- from numpy import testing as npt
5
- import pytest
6
-
7
- from multiclass_interface.mpi_interface import MPIClassInterface
8
- from multiclass_interface.multiprocess_interface import ProcessClass, MultiProcessClassInterface
9
- import numpy as np
10
- from multiclass_interface.tests.my_test_cls import MyTest
11
- from multiclass_interface import mpi_interface
12
-
13
-
14
- def mpitest_mpi_MyTest():
15
-
16
- N = 4
17
- try:
18
- with MPIClassInterface(MyTest, [(i,) for i in range(N)]) as m:
19
-
20
- time.sleep(.1)
21
- i = m.get_id()
22
- npt.assert_array_equal(i, np.arange(N))
23
-
24
- npt.assert_array_equal(m[1:3].get_id(), np.arange(1, 3))
25
- t = time.time()
26
- m.work(1)
27
- t = time.time() - t
28
- assert t < 1.1
29
-
30
- with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
31
- m[:3].close()
32
- with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
33
- m[:3][1]
34
-
35
- print("done, test_mpi_MyTest")
36
- except ChildProcessError:
37
- pass
38
-
39
-
40
- def mpitest_mpi_ProcessClass():
41
-
42
- with ProcessClass(MyTest) as cls:
43
- myTest = cls(1)
44
- assert myTest.get_id() == 1
45
- print("done, test_mpi_ProcessClass", flush=True)
46
-
47
-
48
- def mpitest_non_collective_mpi():
49
- mpi_interface.COLLECTIVE_MPI = False
50
- rank = mpi_interface.rank
51
- import time
52
- N = 3
53
- with MPIClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
54
-
55
- try:
56
- assert m.get_id()[0] == rank + 10, m.get_id()
57
- assert rank < N # rank > N will fail with ChildProcessError
58
- except ChildProcessError:
59
- pass
60
-
61
- mpi_interface.COLLECTIVE_MPI = True
62
-
63
-
64
- def mpitest_all():
65
- mpi_interface.activate_mpi()
66
-
67
- mpi_interface.exit_mpi_on_close = False
68
- rank = mpi_interface.rank
69
- from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
70
- mpi_interface.TERMINATE_ON_CLOSE = False
71
-
72
- def run(f, *args):
73
- try:
74
- if rank == 0:
75
- print(f"Rank {rank} start {f.__name__}", flush=True)
76
- f(*args)
77
- except ChildProcessError:
78
- pass
79
- finally:
80
- if rank == 0:
81
- print(f"Rank {rank} done {f.__name__}", flush=True)
82
- for f in [mpitest_mpi_MyTest,
83
- mpitest_mpi_ProcessClass]:
84
- run(f)
85
-
86
- try:
87
- with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
88
- for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
89
-
90
- run(f, mpici)
91
-
92
- except ChildProcessError:
93
- pass
94
-
95
- run(mpitest_non_collective_mpi)
96
-
97
- print(f"Rank {rank} Done test_all")