scipion-pyworkflow 3.7.0__py3-none-any.whl → 3.9.0__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.
Files changed (38) hide show
  1. pyworkflow/config.py +26 -1
  2. pyworkflow/constants.py +7 -1
  3. pyworkflow/gui/canvas.py +6 -6
  4. pyworkflow/gui/dialog.py +3 -2
  5. pyworkflow/gui/form.py +12 -2
  6. pyworkflow/gui/graph.py +6 -6
  7. pyworkflow/gui/graph_layout.py +2 -2
  8. pyworkflow/gui/project/base.py +7 -7
  9. pyworkflow/gui/project/project.py +36 -28
  10. pyworkflow/gui/project/steps.py +8 -3
  11. pyworkflow/gui/project/viewdata.py +1 -1
  12. pyworkflow/gui/project/viewprotocols.py +9 -9
  13. pyworkflow/gui/project/viewprotocols_extra.py +3 -3
  14. pyworkflow/gui/widgets.py +2 -2
  15. pyworkflow/mapper/sqlite.py +5 -5
  16. pyworkflow/object.py +12 -4
  17. pyworkflow/plugin.py +8 -3
  18. pyworkflow/project/config.py +4 -1
  19. pyworkflow/project/manager.py +4 -3
  20. pyworkflow/project/project.py +36 -20
  21. pyworkflow/project/scripts/create.py +14 -4
  22. pyworkflow/project/scripts/schedule.py +1 -1
  23. pyworkflow/protocol/constants.py +1 -0
  24. pyworkflow/protocol/executor.py +133 -19
  25. pyworkflow/protocol/params.py +5 -8
  26. pyworkflow/protocol/protocol.py +57 -37
  27. pyworkflow/template.py +1 -1
  28. pyworkflow/utils/graph.py +24 -51
  29. pyworkflow/utils/properties.py +16 -12
  30. pyworkflowtests/protocols.py +3 -3
  31. pyworkflowtests/tests/test_protocol_execution.py +63 -0
  32. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/METADATA +12 -12
  33. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/RECORD +38 -38
  34. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/WHEEL +1 -1
  35. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/LICENSE.txt +0 -0
  36. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/dependency_links.txt +0 -0
  37. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/entry_points.txt +0 -0
  38. {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/top_level.txt +0 -0
@@ -25,8 +25,7 @@
25
25
  This modules contains classes required for the workflow
26
26
  execution and tracking like: Step and Protocol
27
27
  """
28
- import contextlib
29
- import sys, os
28
+ import os
30
29
  import json
31
30
  import threading
32
31
  import time
@@ -36,7 +35,7 @@ import pyworkflow as pw
36
35
  from pyworkflow.exceptions import ValidationException, PyworkflowException
37
36
  from pyworkflow.object import *
38
37
  import pyworkflow.utils as pwutils
39
- from pyworkflow.utils.log import LoggingConfigurator, getExtraLogInfo, STATUS, setDefaultLoggingContext
38
+ from pyworkflow.utils.log import getExtraLogInfo, STATUS, setDefaultLoggingContext
40
39
  from .executor import StepExecutor, ThreadStepExecutor, QueueStepExecutor
41
40
  from .constants import *
42
41
  from .params import Form
@@ -50,21 +49,24 @@ logger = logging.getLogger(__name__)
50
49
 
51
50
  class Step(Object):
52
51
  """ Basic execution unit.
53
- It should defines its Input, Output
52
+ It should define its Input, Output
54
53
  and define a run method.
55
54
  """
56
55
 
57
- def __init__(self, **kwargs):
58
- Object.__init__(self, **kwargs)
56
+ def __init__(self, interactive=False, needsGPU=True, **kwargs):
57
+ super().__init__()
59
58
  self._prerequisites = CsvList() # which steps needs to be done first
60
59
  self.status = String()
61
60
  self.initTime = String()
62
61
  self.endTime = String()
63
62
  self._error = String()
64
- self.interactive = Boolean(False)
63
+ self.interactive = Boolean(interactive)
65
64
  self._resultFiles = String()
65
+ self._needsGPU = Boolean(needsGPU)
66
66
  self._index = None
67
67
 
68
+ def needsGPU(self) -> bool:
69
+ return self._needsGPU.get()
68
70
  def getIndex(self):
69
71
  return self._index
70
72
 
@@ -214,7 +216,7 @@ class Step(Object):
214
216
  self.status.set(status)
215
217
 
216
218
  except PyworkflowException as e:
217
- print(pwutils.redStr(str(e)))
219
+ logger.info(pwutils.redStr(str(e)))
218
220
  self.setFailed(str(e))
219
221
  except Exception as e:
220
222
  self.setFailed(str(e))
@@ -230,7 +232,7 @@ class FunctionStep(Step):
230
232
  This class will ease the insertion of Protocol function steps
231
233
  through the function _insertFunctionStep"""
232
234
 
233
- def __init__(self, func=None, funcName=None, *funcArgs, **kwargs):
235
+ def __init__(self, func=None, funcName=None, *funcArgs, wait=False, interactive=False, needsGPU=True):
234
236
  """
235
237
  Params:
236
238
  func: the function that will be executed.
@@ -238,13 +240,12 @@ class FunctionStep(Step):
238
240
  *funcArgs: argument list passed to the function (serialized and stored)
239
241
  **kwargs: extra parameters.
240
242
  """
241
- Step.__init__(self)
243
+ super().__init__(interactive=interactive, needsGPU=needsGPU)
242
244
  self._func = func # Function should be set before run
243
245
  self._args = funcArgs
244
246
  self.funcName = String(funcName)
245
247
  self.argsStr = String(json.dumps(funcArgs, default=lambda x: None))
246
- self.setInteractive(kwargs.get('interactive', False))
247
- if kwargs.get('wait', False):
248
+ if wait:
248
249
  self.setStatus(STATUS_WAITING)
249
250
 
250
251
  def _runFunc(self):
@@ -280,7 +281,7 @@ class FunctionStep(Step):
280
281
  return not self.__eq__(other)
281
282
 
282
283
  def __str__(self):
283
- return self.funcName.get()
284
+ return "%s - %s" % (self._objId ,self.funcName.get())
284
285
 
285
286
 
286
287
  class RunJobStep(FunctionStep):
@@ -328,7 +329,6 @@ class Protocol(Step):
328
329
  """
329
330
 
330
331
  # Version where protocol appeared first time
331
- _lastUpdateVersion = pw.VERSION_1
332
332
  _stepsCheckSecs = pw.Config.getStepsCheckSeconds()
333
333
  # Protocol develop status: PROD, BETA, NEW
334
334
  _devStatus = pw.PROD
@@ -916,6 +916,27 @@ class Protocol(Step):
916
916
 
917
917
  return s
918
918
 
919
+ def getOutputSuffix(self, outputPrefix):
920
+ """ Return the suffix to be used for a new output.
921
+ For example: output3DCoordinates7.
922
+ It should take into account previous outputs
923
+ and number with a higher value.
924
+ """
925
+ maxCounter = -1
926
+ for attrName, _ in self.iterOutputAttributes():
927
+ suffix = attrName.replace(outputPrefix, '')
928
+ try:
929
+ counter = int(suffix)
930
+ except:
931
+ counter = 1 # when there is not number assume 1
932
+ maxCounter = max(counter, maxCounter)
933
+
934
+ return str(maxCounter + 1) if maxCounter > 0 else '' # empty if not output
935
+
936
+ def getNextOutputName(self, outputPrefix):
937
+ """Return the name to be used for a new output."""
938
+ return outputPrefix + self.getOutputSuffix(outputPrefix)
939
+
919
940
  def copyDefinitionAttributes(self, other):
920
941
  """ Copy definition attributes to other protocol. """
921
942
  for paramName, _ in self.iterDefinitionAttributes():
@@ -996,12 +1017,11 @@ class Protocol(Step):
996
1017
  """
997
1018
  pass
998
1019
 
999
- def __insertStep(self, step, **kwargs):
1020
+ def __insertStep(self, step, prerequisites=None):
1000
1021
  """ Insert a new step in the list.
1001
1022
 
1002
1023
  :param prerequisites: a single integer or a list with the steps index that need to be done
1003
1024
  previous to the current one."""
1004
- prerequisites = kwargs.get('prerequisites', None)
1005
1025
 
1006
1026
  if prerequisites is None:
1007
1027
  if len(self._steps):
@@ -1097,7 +1117,7 @@ class Protocol(Step):
1097
1117
  """
1098
1118
  return self._getPath(os.path.basename(path))
1099
1119
 
1100
- def _insertFunctionStep(self, func, *funcArgs, **kwargs):
1120
+ def _insertFunctionStep(self, func, *funcArgs, prerequisites=None, wait=False, interactive=False, needsGPU=True):
1101
1121
  """
1102
1122
  Params:
1103
1123
  func: the function itself or, optionally, the name (string) of the function to be run in the Step.
@@ -1115,24 +1135,24 @@ class Protocol(Step):
1115
1135
  if not callable(func):
1116
1136
  raise Exception("Protocol._insertFunctionStep: '%s' is not callable"
1117
1137
  % func)
1118
- step = FunctionStep(func, func.__name__, *funcArgs, **kwargs)
1119
-
1120
- return self.__insertStep(step, **kwargs)
1121
-
1122
- def _insertRunJobStep(self, progName, progArguments, resultFiles=[],
1123
- **kwargs):
1124
- """ Insert an Step that will simple call runJob function
1125
- **args: see __insertStep
1126
- """
1127
- return self._insertFunctionStep('runJob', progName, progArguments,
1128
- **kwargs)
1129
-
1130
- def _insertCopyFileStep(self, sourceFile, targetFile, **kwargs):
1131
- """ Shortcut function to insert an step for copying a file to a destiny. """
1132
- step = FunctionStep(pwutils.copyFile, 'copyFile', sourceFile,
1133
- targetFile,
1134
- **kwargs)
1135
- return self.__insertStep(step, **kwargs)
1138
+ step = FunctionStep(func, func.__name__, *funcArgs, wait=wait, interactive=interactive, needsGPU=needsGPU)
1139
+
1140
+ return self.__insertStep(step,prerequisites)
1141
+
1142
+ # def _insertRunJobStep(self, progName, progArguments, resultFiles=[],
1143
+ # **kwargs):
1144
+ # """ Insert an Step that will simple call runJob function
1145
+ # **args: see __insertStep
1146
+ # """
1147
+ # return self._insertFunctionStep('runJob', progName, progArguments,
1148
+ # **kwargs)
1149
+ #
1150
+ # def _insertCopyFileStep(self, sourceFile, targetFile, **kwargs):
1151
+ # """ Shortcut function to insert a step for copying a file to a destiny. """
1152
+ # step = FunctionStep(pwutils.copyFile, 'copyFile', sourceFile,
1153
+ # targetFile,
1154
+ # **kwargs)
1155
+ # return self.__insertStep(step, **kwargs)
1136
1156
 
1137
1157
  def _enterDir(self, path):
1138
1158
  """ Enter into a new directory path and store the current path.
@@ -1196,7 +1216,7 @@ class Protocol(Step):
1196
1216
  protocol that allow some kind of continue (such as ctf estimation).
1197
1217
  """
1198
1218
  for step in self.loadSteps():
1199
- self.__insertStep(step)
1219
+ self.__insertStep(step, )
1200
1220
 
1201
1221
  def __findStartingStep(self):
1202
1222
  """ From a previous run, compare self._steps and self._prevSteps
@@ -2518,7 +2538,7 @@ class ProtStreamingBase(Protocol):
2518
2538
  self.stepsExecutionMode = STEPS_PARALLEL
2519
2539
  def _insertAllSteps(self):
2520
2540
  # Insert the step that generates the steps
2521
- self._insertFunctionStep(self.resumableStepGeneratorStep, str(datetime.now()))
2541
+ self._insertFunctionStep(self.resumableStepGeneratorStep, str(datetime.now()), needsGPU=False)
2522
2542
 
2523
2543
  def resumableStepGeneratorStep(self, ts):
2524
2544
  """ This allow to resume protocols. ts is the time stamp so this stap is alway different form previous exceution"""
pyworkflow/template.py CHANGED
@@ -10,7 +10,7 @@ import logging
10
10
  logger = logging.getLogger(__name__)
11
11
 
12
12
  class Template:
13
- def __init__(self, source, name, description):
13
+ def __init__(self, source, name, description=""):
14
14
  self.source = source
15
15
  # Tidy up templates names: removing .json.template and .json (when passed as parameter)
16
16
  self.name = name
pyworkflow/utils/graph.py CHANGED
@@ -26,6 +26,8 @@
26
26
  """
27
27
  This module define a Graph class and some utilities
28
28
  """
29
+ import logging
30
+ logger = logging.getLogger(__name__)
29
31
 
30
32
 
31
33
  class Node(object):
@@ -33,7 +35,7 @@ class Node(object):
33
35
  _count = 1
34
36
 
35
37
  def __init__(self, name=None, label=None):
36
- self._childs = []
38
+ self._children = []
37
39
  self._parents = []
38
40
 
39
41
  if name is None:
@@ -44,6 +46,7 @@ class Node(object):
44
46
  if label is None:
45
47
  label = name
46
48
  self._label = label
49
+ self._fixed = False
47
50
 
48
51
  def getName(self):
49
52
  return self._name
@@ -57,13 +60,13 @@ class Node(object):
57
60
  def isRoot(self):
58
61
  return len(self._parents) == 0
59
62
 
60
- def getChilds(self):
61
- return self._childs
63
+ def getChildren(self):
64
+ return self._children
62
65
 
63
66
  def addChild(self, *nodes):
64
67
  for n in nodes:
65
- if n not in self._childs:
66
- self._childs.append(n)
68
+ if n not in self._children:
69
+ self._children.append(n)
67
70
  n._parents.append(self)
68
71
 
69
72
  def getParent(self):
@@ -78,36 +81,36 @@ class Node(object):
78
81
  def getParents(self):
79
82
  return self._parents
80
83
 
81
- def iterChilds(self):
82
- """ Iterate over all childs and subchilds.
83
- Nodes can be visited more than once if have
84
+ def iterChildren(self):
85
+ """ Iterate over all children and sub-children.
86
+ Nodes can be visited more than once if it has
84
87
  more than one parent.
85
88
  """
86
- for child in self._childs:
87
- for c in child.iterChilds():
89
+ for child in self._children:
90
+ for c in child.iterChildren():
88
91
  yield c
89
92
 
90
93
  yield self
91
94
 
92
- def countChilds(self, visitedNode=None, count=0):
95
+ def countChildren(self, visitedNode=None, count=0):
93
96
  """ Iterate over all childs and subchilds.
94
97
  Nodes can be visited once
95
98
  """
96
- for child in self._childs:
99
+ for child in self._children:
97
100
  if child._name not in visitedNode:
98
101
  visitedNode[child._name] = True
99
- child.countChilds(visitedNode)
102
+ child.countChildren(visitedNode)
100
103
  return len(visitedNode)
101
104
 
102
105
 
103
- def iterChildsBreadth(self):
106
+ def iterChildrenBreadth(self):
104
107
  """ Iter child nodes in a breadth-first order
105
108
  """
106
- for child in self._childs:
109
+ for child in self._children:
107
110
  yield child
108
111
 
109
- for child in self._childs:
110
- for child2 in child.iterChildsBreadth():
112
+ for child in self._children:
113
+ for child2 in child.iterChildrenBreadth():
111
114
  yield child2
112
115
 
113
116
  def __str__(self):
@@ -133,13 +136,13 @@ class Graph(object):
133
136
  def _registerNode(self, node):
134
137
  self._nodes.append(node)
135
138
  self._nodesDict[node.getName()] = node
136
- for child in node.getChilds():
139
+ for child in node.getChildren():
137
140
  self._registerNode(child)
138
141
 
139
- def getRoot(self):
142
+ def getRoot(self) -> Node:
140
143
  return self._root
141
144
 
142
- def createNode(self, nodeName, nodeLabel=None):
145
+ def createNode(self, nodeName, nodeLabel=None) -> Node:
143
146
  """ Add a node to the graph """
144
147
  node = Node(nodeName, nodeLabel)
145
148
  self._registerNode(node)
@@ -150,7 +153,7 @@ class Graph(object):
150
153
  """ Register an alias name for the node. """
151
154
  self._nodesDict[aliasName] = node
152
155
 
153
- def getNode(self, nodeName):
156
+ def getNode(self, nodeName) -> Node:
154
157
  return self._nodesDict.get(nodeName, None)
155
158
 
156
159
  def getNodeNames(self):
@@ -164,33 +167,3 @@ class Graph(object):
164
167
  """ Return all nodes that have no parent. """
165
168
  return [n for n in self._nodes if n.isRoot()]
166
169
 
167
- def printNodes(self):
168
- for node in self.getNodes():
169
- print("Node: ", node)
170
- print(" Childs: ", ','.join([c.getLabel()
171
- for c in node.getChilds()]))
172
-
173
- def _escape(self, label):
174
- return label.replace('.', '_').replace(' ', '_').replace('-', '_').replace('___', '_')
175
-
176
- def printDot(self, useId=True):
177
- """ If useId is True, use the node id for label the graph.
178
- If not, use the run name.
179
- """
180
-
181
- def getLabel(node):
182
- if useId:
183
- return node.getName()
184
- else:
185
- return node.getLabel()
186
-
187
- dotStr = "\ndigraph {\n"
188
-
189
- for node in self.getNodes():
190
- for child in node.getChilds():
191
- nodeLabel = self._escape(getLabel(node))
192
- childLabel = self._escape(getLabel(child))
193
- dotStr += " %s -> %s;\n" % (nodeLabel, childLabel)
194
- dotStr += "}"
195
- print(dotStr)
196
- return dotStr
@@ -29,7 +29,7 @@
29
29
  This module defines the text used in the application.
30
30
  """
31
31
  # NOTE: DO NOT REMOVE UNTIL plugin manager uses Config.SCIPION_MAIN_COLOR and is released
32
- from pyworkflow.constants import Color
32
+ from pyworkflow.constants import Color, DOCSITEURLS
33
33
  from PIL import Image
34
34
 
35
35
  class Message:
@@ -124,6 +124,7 @@ class Message:
124
124
  LABEL_PARALLEL = 'Parallel'
125
125
  LABEL_HOST = 'Host'
126
126
  LABEL_THREADS = 'Threads'
127
+ LABEL_SCIPION_THREADS = 'Scipion threads'
127
128
  LABEL_MPI = 'MPI'
128
129
  LABEL_QUEUE = 'Use a queue engine?'
129
130
 
@@ -140,18 +141,21 @@ Each step could be computationally intensive, that's why
140
141
  the *Continue* execution mode will try to continue from the
141
142
  last completed step. On the other hand, the *Restart* mode
142
143
  will clean the whole run directory and start from scratch.
143
- """
144
-
145
- HELP_MPI_THREADS = """
146
- Define the number of processors to be used in the execution.
147
- *MPI*: This is a number of independent processes
148
- that communicate through message passing
149
- over the network (or the same computer).
150
- *Threads*: This refers to different execution threads
151
- in the same process that can share memory. They run in
152
- the same computer.
153
144
  """
154
145
 
146
+ HELP_PARALLEL_HEADER = 'Define the number of processors to be used in the execution.\nCheck %s for more detailed info.\n\n' % DOCSITEURLS.THREADS_MPIS_AND_GPUS
147
+ HELP_PARALLEL_MPI = ("*MPI*:\nThis is a number of independent processes"
148
+ " that communicate through message passing "
149
+ "over the network (or the same computer).\n")
150
+ HELP_PARALLEL_THREADS = ("*Threads*:\nThis refers to different execution threads in the same process that "
151
+ "can share memory. They run in the same computer. This value is an argument"
152
+ " passed to the program integrated")
153
+
154
+ HELP_SCIPION_THREADS = ("*Scipion threads*:\n threads created by Scipion to run the steps."
155
+ " 1 thread is always used by the master/main process. Then extra threads will allow"
156
+ " this protocol to run several steps at the same time, taking always into account "
157
+ "restrictions to previous steps and 'theoretical GPU availability'")
158
+
155
159
  HELP_USEQUEUE = """
156
160
  Click Yes if you want to send this execution to a queue engine like Slurm, Torque, ...
157
161
  The queue commands to launch and stop jobs should be configured at
@@ -446,7 +450,7 @@ class Sprite:
446
450
  @classmethod
447
451
  def getSpritesFile(cls):
448
452
  from pyworkflow import Config
449
- return Config.SCIPION_SPRITES_FILE
453
+ return Config.getSpritesFile()
450
454
  @classmethod
451
455
  def loadSprites(cls):
452
456
  """ Loads the image of the sprite"""
@@ -55,16 +55,16 @@ class SleepingProtocol(pwprot.Protocol):
55
55
  def _insertAllSteps(self):
56
56
  print("Inserting all steps...")
57
57
  for i in range(self.numberOfSleeps.get()):
58
- self._insertFunctionStep('sleepStep', i + 1)
58
+ self._insertFunctionStep(self.sleepStep, i + 1)
59
59
 
60
60
 
61
61
  class ParallelSleepingProtocol(SleepingProtocol):
62
62
  def _insertAllSteps(self):
63
- step1 = self._insertFunctionStep('sleepStep', 1)
63
+ step1 = self._insertFunctionStep(self.sleepStep, 1)
64
64
  n = 2
65
65
  deps = [step1]
66
66
  for i in range(n):
67
- self._insertFunctionStep('sleepStep')
67
+ self._insertFunctionStep(self.sleepStep)
68
68
 
69
69
  class ConcurrencyProtocol(SleepingProtocol):
70
70
  """ Protocol to test concurrency access to sets"""
@@ -22,11 +22,13 @@
22
22
  # * e-mail address 'scipion@cnb.csic.es'
23
23
  # *
24
24
  # **************************************************************************
25
+ import threading
25
26
 
26
27
  import pyworkflow.tests as pwtests
27
28
  import pyworkflow.mapper as pwmapper
28
29
  import pyworkflow.protocol as pwprot
29
30
  from pyworkflow.project import Project
31
+ from pyworkflow.protocol.constants import VOID_GPU
30
32
 
31
33
 
32
34
  # TODO: this test seems not to be finished.
@@ -70,3 +72,64 @@ class TestProtocolExecution(pwtests.BaseTest):
70
72
  prot2 = mapper2.selectById(prot.getObjId())
71
73
 
72
74
  self.assertEqual(prot.endTime.get(), prot2.endTime.get())
75
+
76
+ def test_gpuSlots(self):
77
+ """ Test gpu slots are properly composed in combination of threads"""
78
+
79
+ # Test basic GPU setu methods
80
+ stepExecutor = pwprot.ThreadStepExecutor(None, 1, gpuList=None)
81
+
82
+
83
+ self.assertEqual(stepExecutor.cleanVoidGPUs([0,1]), [0,1],
84
+ "CleanVoidGpus does not work in absence of void GPUS")
85
+
86
+ self.assertEqual(stepExecutor.cleanVoidGPUs([0, VOID_GPU]), [0],
87
+ "CleanVoidGpus does not work with a void GPU")
88
+
89
+ self.assertEqual(stepExecutor.cleanVoidGPUs([VOID_GPU, VOID_GPU]), [],
90
+ "CleanVoidGpus does not work with all void GPU")
91
+
92
+
93
+ currThread = threading.currentThread()
94
+ currThread.thId = 1
95
+ self.assertEqual(stepExecutor.getGpuList(),[], "Gpu list should be empty")
96
+
97
+ # 2 threads 1 GPU
98
+ stepExecutor = pwprot.ThreadStepExecutor(None, 2, gpuList=[1])
99
+ self.assertEqual(stepExecutor.getGpuList(),[1], "Gpu list should be [1]")
100
+
101
+ currThread.thId = 2
102
+ self.assertEqual(stepExecutor.getGpuList(),[], "Gpu list should be empty after a second request")
103
+
104
+
105
+ # 2 threads 3 GPUs
106
+ stepExecutor = pwprot.ThreadStepExecutor(None, 2, gpuList=[0,1,2])
107
+ self.assertEqual(stepExecutor.getGpuList(),[0,1], "Gpu list should be [0,1]")
108
+
109
+ currThread.thId = 1
110
+ self.assertEqual(stepExecutor.getGpuList(),[2], "Gpu list should be [2] after a second request")
111
+
112
+
113
+ # 2 threads 4 GPUs with void gpus
114
+ stepExecutor = pwprot.ThreadStepExecutor(None, 2, gpuList=[0,1,2, VOID_GPU])
115
+ self.assertEqual(stepExecutor.getGpuList(),[0,1], "Gpu list should be [0,1]")
116
+
117
+ currThread.thId = 2
118
+ self.assertEqual(stepExecutor.getGpuList(),[2], "Gpu list should be [2] after a second request without the void gpu")
119
+
120
+ # less GPUs than threads. No extension should happen
121
+ stepExecutor = pwprot.ThreadStepExecutor(None, 4, gpuList=[0, VOID_GPU, 2])
122
+ self.assertEqual(stepExecutor.getGpuList(), [0], "Gpu list should not be extended")
123
+
124
+ currThread.thId = 1
125
+ self.assertEqual(stepExecutor.getGpuList(), [2],
126
+ "Gpu list should be [2] after a second request, skipping the VOID gpu")
127
+
128
+ currThread.thId = 3
129
+ self.assertEqual(stepExecutor.getGpuList(), [], "Gpu list should be empty ather all GPU slots are busy")
130
+
131
+
132
+
133
+
134
+
135
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scipion-pyworkflow
3
- Version: 3.7.0
3
+ Version: 3.9.0
4
4
  Summary: Simple workflow platform used in scientific applications, initially developed within the Scipion framework for image processing in Electron Microscopy.
5
5
  Home-page: https://github.com/scipion-em/scipion-pyworkflow
6
6
  Author: J.M. De la Rosa Trevin, Roberto Marabini, Grigory Sharov, Josue Gomez Blanco, Pablo Conesa, Yunior Fonseca Reyna
@@ -18,18 +18,18 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Topic :: Scientific/Engineering
20
20
  License-File: LICENSE.txt
21
- Requires-Dist: bibtexparser <=1.4.1
22
- Requires-Dist: psutil <=5.9.6
21
+ Requires-Dist: bibtexparser<=1.4.1
22
+ Requires-Dist: psutil<=5.9.6
23
23
  Requires-Dist: tkcolorpicker
24
- Requires-Dist: distro <=1.8
25
- Requires-Dist: importlib-metadata <=6.8.0
26
- Requires-Dist: matplotlib ==3.7.3 ; python_version == "3.8"
27
- Requires-Dist: numpy ==1.24.4 ; python_version == "3.8"
28
- Requires-Dist: configparser ==6.0.0 ; python_version >= "3.8"
29
- Requires-Dist: pillow ==10.1.0 ; python_version >= "3.8"
30
- Requires-Dist: requests ==2.31.0 ; python_version >= "3.8"
31
- Requires-Dist: matplotlib ==3.8.1 ; python_version >= "3.9"
32
- Requires-Dist: numpy ==1.26.1 ; python_version >= "3.9"
24
+ Requires-Dist: distro<=1.8
25
+ Requires-Dist: importlib-metadata<=6.8.0
26
+ Requires-Dist: matplotlib==3.7.3; python_version == "3.8"
27
+ Requires-Dist: numpy==1.24.4; python_version == "3.8"
28
+ Requires-Dist: configparser==6.0.0; python_version >= "3.8"
29
+ Requires-Dist: pillow==10.1.0; python_version >= "3.8"
30
+ Requires-Dist: requests==2.31.0; python_version >= "3.8"
31
+ Requires-Dist: matplotlib==3.8.1; python_version >= "3.9"
32
+ Requires-Dist: numpy==1.26.1; python_version >= "3.9"
33
33
 
34
34
  .. image:: https://img.shields.io/pypi/v/scipion-pyworkflow.svg
35
35
  :target: https://pypi.python.org/pypi/scipion-pyworkflow