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.
- pyworkflow/config.py +26 -1
- pyworkflow/constants.py +7 -1
- pyworkflow/gui/canvas.py +6 -6
- pyworkflow/gui/dialog.py +3 -2
- pyworkflow/gui/form.py +12 -2
- pyworkflow/gui/graph.py +6 -6
- pyworkflow/gui/graph_layout.py +2 -2
- pyworkflow/gui/project/base.py +7 -7
- pyworkflow/gui/project/project.py +36 -28
- pyworkflow/gui/project/steps.py +8 -3
- pyworkflow/gui/project/viewdata.py +1 -1
- pyworkflow/gui/project/viewprotocols.py +9 -9
- pyworkflow/gui/project/viewprotocols_extra.py +3 -3
- pyworkflow/gui/widgets.py +2 -2
- pyworkflow/mapper/sqlite.py +5 -5
- pyworkflow/object.py +12 -4
- pyworkflow/plugin.py +8 -3
- pyworkflow/project/config.py +4 -1
- pyworkflow/project/manager.py +4 -3
- pyworkflow/project/project.py +36 -20
- pyworkflow/project/scripts/create.py +14 -4
- pyworkflow/project/scripts/schedule.py +1 -1
- pyworkflow/protocol/constants.py +1 -0
- pyworkflow/protocol/executor.py +133 -19
- pyworkflow/protocol/params.py +5 -8
- pyworkflow/protocol/protocol.py +57 -37
- pyworkflow/template.py +1 -1
- pyworkflow/utils/graph.py +24 -51
- pyworkflow/utils/properties.py +16 -12
- pyworkflowtests/protocols.py +3 -3
- pyworkflowtests/tests/test_protocol_execution.py +63 -0
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/METADATA +12 -12
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/RECORD +38 -38
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/WHEEL +1 -1
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/dependency_links.txt +0 -0
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.7.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/top_level.txt +0 -0
pyworkflow/protocol/protocol.py
CHANGED
@@ -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
|
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
|
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
|
52
|
+
It should define its Input, Output
|
54
53
|
and define a run method.
|
55
54
|
"""
|
56
55
|
|
57
|
-
def __init__(self, **kwargs):
|
58
|
-
|
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(
|
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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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,
|
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,
|
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,
|
1119
|
-
|
1120
|
-
return self.__insertStep(step,
|
1121
|
-
|
1122
|
-
def _insertRunJobStep(self, progName, progArguments, resultFiles=[],
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
def _insertCopyFileStep(self, sourceFile, targetFile, **kwargs):
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
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.
|
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
|
61
|
-
return self.
|
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.
|
66
|
-
self.
|
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
|
82
|
-
""" Iterate over all
|
83
|
-
Nodes can be visited more than once if
|
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.
|
87
|
-
for c in child.
|
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
|
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.
|
99
|
+
for child in self._children:
|
97
100
|
if child._name not in visitedNode:
|
98
101
|
visitedNode[child._name] = True
|
99
|
-
child.
|
102
|
+
child.countChildren(visitedNode)
|
100
103
|
return len(visitedNode)
|
101
104
|
|
102
105
|
|
103
|
-
def
|
106
|
+
def iterChildrenBreadth(self):
|
104
107
|
""" Iter child nodes in a breadth-first order
|
105
108
|
"""
|
106
|
-
for child in self.
|
109
|
+
for child in self._children:
|
107
110
|
yield child
|
108
111
|
|
109
|
-
for child in self.
|
110
|
-
for child2 in child.
|
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.
|
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
|
pyworkflow/utils/properties.py
CHANGED
@@ -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.
|
453
|
+
return Config.getSpritesFile()
|
450
454
|
@classmethod
|
451
455
|
def loadSprites(cls):
|
452
456
|
""" Loads the image of the sprite"""
|
pyworkflowtests/protocols.py
CHANGED
@@ -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(
|
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(
|
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(
|
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.
|
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
|
22
|
-
Requires-Dist: psutil
|
21
|
+
Requires-Dist: bibtexparser<=1.4.1
|
22
|
+
Requires-Dist: psutil<=5.9.6
|
23
23
|
Requires-Dist: tkcolorpicker
|
24
|
-
Requires-Dist: distro
|
25
|
-
Requires-Dist: importlib-metadata
|
26
|
-
Requires-Dist: matplotlib
|
27
|
-
Requires-Dist: numpy
|
28
|
-
Requires-Dist: configparser
|
29
|
-
Requires-Dist: pillow
|
30
|
-
Requires-Dist: requests
|
31
|
-
Requires-Dist: matplotlib
|
32
|
-
Requires-Dist: numpy
|
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
|