scipion-pyworkflow 3.7.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/__init__.py +33 -0
- pyworkflow/apps/__init__.py +29 -0
- pyworkflow/apps/pw_manager.py +37 -0
- pyworkflow/apps/pw_plot.py +51 -0
- pyworkflow/apps/pw_project.py +113 -0
- pyworkflow/apps/pw_protocol_list.py +143 -0
- pyworkflow/apps/pw_protocol_run.py +51 -0
- pyworkflow/apps/pw_run_tests.py +267 -0
- pyworkflow/apps/pw_schedule_run.py +322 -0
- pyworkflow/apps/pw_sleep.py +37 -0
- pyworkflow/apps/pw_sync_data.py +439 -0
- pyworkflow/apps/pw_viewer.py +78 -0
- pyworkflow/config.py +536 -0
- pyworkflow/constants.py +212 -0
- pyworkflow/exceptions.py +18 -0
- pyworkflow/gui/__init__.py +36 -0
- pyworkflow/gui/browser.py +726 -0
- pyworkflow/gui/canvas.py +1190 -0
- pyworkflow/gui/dialog.py +976 -0
- pyworkflow/gui/form.py +2627 -0
- pyworkflow/gui/graph.py +247 -0
- pyworkflow/gui/graph_layout.py +271 -0
- pyworkflow/gui/gui.py +566 -0
- pyworkflow/gui/matplotlib_image.py +233 -0
- pyworkflow/gui/plotter.py +247 -0
- pyworkflow/gui/project/__init__.py +25 -0
- pyworkflow/gui/project/base.py +192 -0
- pyworkflow/gui/project/constants.py +139 -0
- pyworkflow/gui/project/labels.py +205 -0
- pyworkflow/gui/project/project.py +484 -0
- pyworkflow/gui/project/searchprotocol.py +154 -0
- pyworkflow/gui/project/searchrun.py +181 -0
- pyworkflow/gui/project/steps.py +166 -0
- pyworkflow/gui/project/utils.py +332 -0
- pyworkflow/gui/project/variables.py +179 -0
- pyworkflow/gui/project/viewdata.py +472 -0
- pyworkflow/gui/project/viewprojects.py +510 -0
- pyworkflow/gui/project/viewprotocols.py +2093 -0
- pyworkflow/gui/project/viewprotocols_extra.py +560 -0
- pyworkflow/gui/text.py +771 -0
- pyworkflow/gui/tooltip.py +185 -0
- pyworkflow/gui/tree.py +684 -0
- pyworkflow/gui/widgets.py +307 -0
- pyworkflow/mapper/__init__.py +26 -0
- pyworkflow/mapper/mapper.py +222 -0
- pyworkflow/mapper/sqlite.py +1578 -0
- pyworkflow/mapper/sqlite_db.py +145 -0
- pyworkflow/object.py +1512 -0
- pyworkflow/plugin.py +712 -0
- pyworkflow/project/__init__.py +31 -0
- pyworkflow/project/config.py +451 -0
- pyworkflow/project/manager.py +179 -0
- pyworkflow/project/project.py +1990 -0
- pyworkflow/project/scripts/clean_projects.py +77 -0
- pyworkflow/project/scripts/config.py +92 -0
- pyworkflow/project/scripts/create.py +77 -0
- pyworkflow/project/scripts/edit_workflow.py +90 -0
- pyworkflow/project/scripts/fix_links.py +39 -0
- pyworkflow/project/scripts/load.py +87 -0
- pyworkflow/project/scripts/refresh.py +83 -0
- pyworkflow/project/scripts/schedule.py +111 -0
- pyworkflow/project/scripts/stack2volume.py +41 -0
- pyworkflow/project/scripts/stop.py +81 -0
- pyworkflow/protocol/__init__.py +38 -0
- pyworkflow/protocol/bibtex.py +48 -0
- pyworkflow/protocol/constants.py +86 -0
- pyworkflow/protocol/executor.py +334 -0
- pyworkflow/protocol/hosts.py +313 -0
- pyworkflow/protocol/launch.py +270 -0
- pyworkflow/protocol/package.py +42 -0
- pyworkflow/protocol/params.py +744 -0
- pyworkflow/protocol/protocol.py +2554 -0
- pyworkflow/resources/Imagej.png +0 -0
- pyworkflow/resources/chimera.png +0 -0
- pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
- pyworkflow/resources/fa-info-circle_alert.png +0 -0
- pyworkflow/resources/fa-search.png +0 -0
- pyworkflow/resources/fa-times-circle_alert.png +0 -0
- pyworkflow/resources/file_vol.png +0 -0
- pyworkflow/resources/loading.gif +0 -0
- pyworkflow/resources/no-image128.png +0 -0
- pyworkflow/resources/scipion_bn.png +0 -0
- pyworkflow/resources/scipion_icon.png +0 -0
- pyworkflow/resources/scipion_icon.svg +397 -0
- pyworkflow/resources/scipion_icon_proj.png +0 -0
- pyworkflow/resources/scipion_icon_projs.png +0 -0
- pyworkflow/resources/scipion_icon_prot.png +0 -0
- pyworkflow/resources/scipion_logo.png +0 -0
- pyworkflow/resources/scipion_logo_normal.png +0 -0
- pyworkflow/resources/scipion_logo_small.png +0 -0
- pyworkflow/resources/sprites.png +0 -0
- pyworkflow/resources/sprites.xcf +0 -0
- pyworkflow/resources/wait.gif +0 -0
- pyworkflow/template.py +322 -0
- pyworkflow/tests/__init__.py +29 -0
- pyworkflow/tests/test_utils.py +25 -0
- pyworkflow/tests/tests.py +341 -0
- pyworkflow/utils/__init__.py +38 -0
- pyworkflow/utils/dataset.py +414 -0
- pyworkflow/utils/echo.py +104 -0
- pyworkflow/utils/graph.py +196 -0
- pyworkflow/utils/log.py +284 -0
- pyworkflow/utils/path.py +527 -0
- pyworkflow/utils/process.py +132 -0
- pyworkflow/utils/profiler.py +92 -0
- pyworkflow/utils/progressbar.py +154 -0
- pyworkflow/utils/properties.py +627 -0
- pyworkflow/utils/reflection.py +129 -0
- pyworkflow/utils/utils.py +877 -0
- pyworkflow/utils/which.py +229 -0
- pyworkflow/viewer.py +328 -0
- pyworkflow/webservices/__init__.py +8 -0
- pyworkflow/webservices/config.py +11 -0
- pyworkflow/webservices/notifier.py +162 -0
- pyworkflow/webservices/repository.py +59 -0
- pyworkflow/webservices/workflowhub.py +74 -0
- pyworkflow/wizard.py +64 -0
- pyworkflowtests/__init__.py +51 -0
- pyworkflowtests/bibtex.py +51 -0
- pyworkflowtests/objects.py +830 -0
- pyworkflowtests/protocols.py +154 -0
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +72 -0
- pyworkflowtests/tests/test_domain.py +45 -0
- pyworkflowtests/tests/test_logs.py +74 -0
- pyworkflowtests/tests/test_mappers.py +392 -0
- pyworkflowtests/tests/test_object.py +507 -0
- pyworkflowtests/tests/test_project.py +42 -0
- pyworkflowtests/tests/test_protocol_execution.py +72 -0
- pyworkflowtests/tests/test_protocol_export.py +78 -0
- pyworkflowtests/tests/test_protocol_output.py +158 -0
- pyworkflowtests/tests/test_streaming.py +47 -0
- pyworkflowtests/tests/test_utils.py +210 -0
- scipion_pyworkflow-3.7.0.dist-info/LICENSE.txt +674 -0
- scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
- scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
- scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
- scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
- scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
- scipion_pyworkflow-3.7.0.dist-info/top_level.txt +2 -0
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
pyworkflow/template.py
ADDED
@@ -0,0 +1,322 @@
|
|
1
|
+
""" Module to host templates classes"""
|
2
|
+
import collections
|
3
|
+
import glob
|
4
|
+
import os
|
5
|
+
import tempfile
|
6
|
+
from datetime import datetime
|
7
|
+
from pyworkflow import SCIPION_JSON_TEMPLATES, Config, VarTypes
|
8
|
+
from pyworkflow.utils import greenStr
|
9
|
+
import logging
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
class Template:
|
13
|
+
def __init__(self, source, name, description):
|
14
|
+
self.source = source
|
15
|
+
# Tidy up templates names: removing .json.template and .json (when passed as parameter)
|
16
|
+
self.name = name
|
17
|
+
self.description = description
|
18
|
+
self.content = None
|
19
|
+
self.params = None
|
20
|
+
self.projectName = None
|
21
|
+
|
22
|
+
def __str__(self):
|
23
|
+
return self.name
|
24
|
+
|
25
|
+
def getContent(self):
|
26
|
+
""" Returns the content of the template if not present it calls , loadContent"""
|
27
|
+
|
28
|
+
if self.content is None:
|
29
|
+
self.content = self.loadContent()
|
30
|
+
|
31
|
+
return self.content
|
32
|
+
|
33
|
+
def loadContent(self):
|
34
|
+
""" Method to load into self.content the content of a template"""
|
35
|
+
pass
|
36
|
+
|
37
|
+
def getObjId(self):
|
38
|
+
return self.source + '-' + self.name
|
39
|
+
|
40
|
+
def genProjectName(self):
|
41
|
+
self.projectName = self.getObjId() + '-' + datetime.now().strftime("%y%m%d-%H%M%S")
|
42
|
+
|
43
|
+
def replaceEnvVariables(self):
|
44
|
+
self.content = (self.getContent() % os.environ).split('~')
|
45
|
+
|
46
|
+
def parseContent(self):
|
47
|
+
|
48
|
+
content = self.getContent()
|
49
|
+
|
50
|
+
def paramStr2Param(fieldIndex, fieldString):
|
51
|
+
fieldLst = fieldString.split('|')
|
52
|
+
|
53
|
+
title = fieldLst[0]
|
54
|
+
defaultValue = fieldLst[1] if len(fieldLst) >= 2 else None
|
55
|
+
varType = fieldLst[2] if len(fieldLst) >= 3 else None
|
56
|
+
alias = fieldLst[3] if len(fieldLst) >= 4 else None
|
57
|
+
|
58
|
+
return TemplateParam(fieldIndex, title, defaultValue, varType, alias)
|
59
|
+
|
60
|
+
# Fill each field in the template in order to prevent spreading in the form
|
61
|
+
self.params = collections.OrderedDict()
|
62
|
+
for index in range(1, len(content), 2):
|
63
|
+
param = paramStr2Param(index, content[index])
|
64
|
+
self.params[param.getTitle()] = param
|
65
|
+
|
66
|
+
def createTemplateFile(self):
|
67
|
+
|
68
|
+
# Where to write the json file.
|
69
|
+
(fileHandle, path) = tempfile.mkstemp()
|
70
|
+
|
71
|
+
self._replaceFields()
|
72
|
+
|
73
|
+
finalJson = "".join(self.getContent())
|
74
|
+
|
75
|
+
os.write(fileHandle, finalJson.encode())
|
76
|
+
os.close(fileHandle)
|
77
|
+
|
78
|
+
print("New workflow saved at " + path)
|
79
|
+
|
80
|
+
return path
|
81
|
+
|
82
|
+
def _replaceFields(self):
|
83
|
+
|
84
|
+
for field in self.params.values():
|
85
|
+
self.content[field.getIndex()] = field.getValue()
|
86
|
+
|
87
|
+
def getParams(self):
|
88
|
+
return self.params
|
89
|
+
|
90
|
+
def setParamValue(self, alias, newValue):
|
91
|
+
paramsSetted = 0
|
92
|
+
for field in self.params.values():
|
93
|
+
if field.getAlias() == alias:
|
94
|
+
oldValue = field.getValue()
|
95
|
+
field.setValue(newValue)
|
96
|
+
if field.validate() is None:
|
97
|
+
paramsSetted += 1
|
98
|
+
print(greenStr("%s set to %s") %
|
99
|
+
(field.getTitle(), str(newValue)))
|
100
|
+
else:
|
101
|
+
field.setValue(oldValue)
|
102
|
+
raise Exception("%s is not compatible with %s(%s) parameter." % (newValue, field.getTitle(), alias))
|
103
|
+
if not paramsSetted:
|
104
|
+
raise Exception("Alias %s not recognized." % alias)
|
105
|
+
return paramsSetted
|
106
|
+
|
107
|
+
class LocalTemplate(Template):
|
108
|
+
""" Local template representing a json file in the file system"""
|
109
|
+
|
110
|
+
def __init__(self, source, tempPath):
|
111
|
+
# Tidy up templates names: removing .json.template and .json (when passed as parameter)
|
112
|
+
name = os.path.basename(tempPath).replace(SCIPION_JSON_TEMPLATES, "").replace(".json", "")
|
113
|
+
super().__init__(source,name, "")
|
114
|
+
self.templatePath = os.path.abspath(tempPath)
|
115
|
+
self.description, self.content = self._parseTemplate()
|
116
|
+
|
117
|
+
def _parseTemplate(self):
|
118
|
+
with open(self.templatePath, 'r') as myFile:
|
119
|
+
allContents = myFile.read().splitlines()
|
120
|
+
description, index = LocalTemplate.getDescription(allContents)
|
121
|
+
|
122
|
+
if not description:
|
123
|
+
description = 'Not provided'
|
124
|
+
content = ''.join(allContents[index:])
|
125
|
+
return description, content
|
126
|
+
|
127
|
+
|
128
|
+
@staticmethod
|
129
|
+
def getDescription(strList):
|
130
|
+
# Example of json.template file with description:
|
131
|
+
# -----------------------------------------------
|
132
|
+
# Here goes the description
|
133
|
+
# Description
|
134
|
+
# Another description line...
|
135
|
+
# [
|
136
|
+
# {
|
137
|
+
# "object.className": "ProtImportMovies",
|
138
|
+
# "object.id": "2",...
|
139
|
+
# -----------------------------------------------
|
140
|
+
|
141
|
+
contents_start_1 = '['
|
142
|
+
contents_start_2 = '{'
|
143
|
+
description = []
|
144
|
+
counter = 0
|
145
|
+
nLines = len(strList)
|
146
|
+
|
147
|
+
while counter + 1 < nLines:
|
148
|
+
currentLine = strList[counter]
|
149
|
+
nextLine = strList[counter + 1]
|
150
|
+
if contents_start_1 not in currentLine:
|
151
|
+
description.append(currentLine)
|
152
|
+
else:
|
153
|
+
if contents_start_2 in nextLine:
|
154
|
+
break
|
155
|
+
else:
|
156
|
+
description.append(currentLine)
|
157
|
+
counter += 1
|
158
|
+
|
159
|
+
return ''.join(description), counter
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
class TemplateParam(object):
|
164
|
+
def __init__(self, index, title, value=None, varType=None, alias=None):
|
165
|
+
self._index = index
|
166
|
+
self._title = title
|
167
|
+
self._value = value
|
168
|
+
self._type = int(varType)
|
169
|
+
self._alias = alias
|
170
|
+
|
171
|
+
def getTitle(self):
|
172
|
+
return self._title
|
173
|
+
|
174
|
+
def getIndex(self):
|
175
|
+
return self._index
|
176
|
+
|
177
|
+
def getType(self):
|
178
|
+
return self._type
|
179
|
+
|
180
|
+
def getValue(self):
|
181
|
+
return self._value
|
182
|
+
|
183
|
+
def setValue(self, value):
|
184
|
+
self._value = value
|
185
|
+
|
186
|
+
def getAlias(self):
|
187
|
+
return self._alias
|
188
|
+
|
189
|
+
def validate(self):
|
190
|
+
return Validations.check(self._value, self._type)
|
191
|
+
|
192
|
+
|
193
|
+
class Validations:
|
194
|
+
|
195
|
+
""" FIELDS VALIDATION """
|
196
|
+
""" FIELDS TYPES"""
|
197
|
+
@classmethod
|
198
|
+
def check(cls, value, fieldType):
|
199
|
+
if fieldType == VarTypes.BOOLEAN.value:
|
200
|
+
return cls.validBoolean(value)
|
201
|
+
elif fieldType == VarTypes.DECIMAL.value:
|
202
|
+
return cls.validDecimal(value)
|
203
|
+
elif fieldType == VarTypes.INTEGER.value:
|
204
|
+
return cls.validInteger(value)
|
205
|
+
elif fieldType in (VarTypes.PATH.value, VarTypes.FOLDER.value):
|
206
|
+
return cls.validPath(value)
|
207
|
+
elif fieldType == VarTypes.STRING.value:
|
208
|
+
return cls.validString(value)
|
209
|
+
|
210
|
+
else:
|
211
|
+
return "Type %s for %s not recognized. Review the template." % (fieldType, value)
|
212
|
+
|
213
|
+
@staticmethod
|
214
|
+
def validString(value):
|
215
|
+
if value is None:
|
216
|
+
return "String does not accept None/empty values."
|
217
|
+
|
218
|
+
@staticmethod
|
219
|
+
def validInteger(value):
|
220
|
+
if not value.isdigit():
|
221
|
+
return "Value does not seem to be an integer number."
|
222
|
+
|
223
|
+
@staticmethod
|
224
|
+
def validPath(value):
|
225
|
+
if not os.path.exists(value):
|
226
|
+
return "Path does not exists."
|
227
|
+
|
228
|
+
@staticmethod
|
229
|
+
def validDecimal(value):
|
230
|
+
|
231
|
+
try:
|
232
|
+
float(value)
|
233
|
+
return None
|
234
|
+
except Exception as e:
|
235
|
+
return "Value can't be converted to a float (%s)" % str(e)
|
236
|
+
|
237
|
+
@staticmethod
|
238
|
+
def validBoolean(value):
|
239
|
+
validValues = ["true", "1", "false", "0"]
|
240
|
+
|
241
|
+
valueL = value.lower()
|
242
|
+
|
243
|
+
if valueL not in validValues:
|
244
|
+
return "Only valid values for a boolean type are: %s" % validValues
|
245
|
+
|
246
|
+
|
247
|
+
class TemplateList:
|
248
|
+
def __init__(self, templates=None):
|
249
|
+
self.templates = templates if templates else []
|
250
|
+
|
251
|
+
def addTemplate(self, t):
|
252
|
+
self.templates.append(t)
|
253
|
+
|
254
|
+
def genFromStrList(self, templateList):
|
255
|
+
for t in templateList:
|
256
|
+
parsedPath = t.split(os.path.sep)
|
257
|
+
pluginName = parsedPath[parsedPath.index('templates') - 1]
|
258
|
+
self.addTemplate(LocalTemplate(pluginName, t))
|
259
|
+
|
260
|
+
def sortListByPluginName(self):
|
261
|
+
# Create a identifier with both plugin and template names to sort by both
|
262
|
+
self.templates = sorted(self.templates, key=lambda template: '.' + template.getObjId()
|
263
|
+
if template.getObjId().startswith('local') else template.getObjId())
|
264
|
+
|
265
|
+
return self
|
266
|
+
|
267
|
+
def addScipionTemplates(self, tempId=None):
|
268
|
+
""" Adds scipion templates from local file system or from workflow hub.
|
269
|
+
:param tempId: identifier of the template to look up for. If fount only this template is chosen
|
270
|
+
"""
|
271
|
+
|
272
|
+
self.addLocalTemplates(tempId)
|
273
|
+
|
274
|
+
if tempId is None or len(self.templates) == 0:
|
275
|
+
self.addWHTemplates(tempId)
|
276
|
+
|
277
|
+
|
278
|
+
def addLocalTemplates(self, tempId=None):
|
279
|
+
# Check if there is any .json.template in the template folder
|
280
|
+
# get the template folder (we only want it to be included once)
|
281
|
+
templateFolder = Config.getExternalJsonTemplates()
|
282
|
+
for templateName in glob.glob1(templateFolder,
|
283
|
+
"*" + SCIPION_JSON_TEMPLATES):
|
284
|
+
t = LocalTemplate("local", os.path.join(templateFolder, templateName))
|
285
|
+
if tempId is not None:
|
286
|
+
if t.getObjId() == tempId:
|
287
|
+
self.addTemplate(t)
|
288
|
+
break
|
289
|
+
else:
|
290
|
+
self.addTemplate(t)
|
291
|
+
|
292
|
+
def addWHTemplates(self, tempId=None):
|
293
|
+
|
294
|
+
try:
|
295
|
+
|
296
|
+
from pyworkflow.webservices.workflowhub import get_wh_templates
|
297
|
+
|
298
|
+
templates = get_wh_templates(tempId)
|
299
|
+
|
300
|
+
self.templates.extend(templates)
|
301
|
+
except Exception as e:
|
302
|
+
logger.warning("Can't get templates from workflow hub: %s" % e)
|
303
|
+
|
304
|
+
def addPluginTemplates(self, tempId=None):
|
305
|
+
"""
|
306
|
+
Get the templates provided by all plugins.
|
307
|
+
:return: a list of templates
|
308
|
+
"""
|
309
|
+
# Check if other plugins have json.templates
|
310
|
+
domain = Config.getDomain()
|
311
|
+
# Check if there is any .json.template in the template folder
|
312
|
+
# get the template folder (we only want it to be included once)
|
313
|
+
for pluginName, pluginModule in domain.getPlugins().items():
|
314
|
+
tempListPlugin = pluginModule._pluginInstance.getTemplates()
|
315
|
+
for t in tempListPlugin:
|
316
|
+
if tempId is not None:
|
317
|
+
if t.getObjId() == tempId:
|
318
|
+
self.addTemplate(t)
|
319
|
+
break
|
320
|
+
else:
|
321
|
+
self.addTemplate(t)
|
322
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# **************************************************************************
|
3
|
+
# *
|
4
|
+
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
5
|
+
# Laura del Cano (ldelcano@cnb.csic.es)
|
6
|
+
# *
|
7
|
+
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
8
|
+
# *
|
9
|
+
# * This program is free software; you can redistribute it and/or modify
|
10
|
+
# * it under the terms of the GNU General Public License as published by
|
11
|
+
# * the Free Software Foundation; either version 3 of the License, or
|
12
|
+
# * (at your option) any later version.
|
13
|
+
# *
|
14
|
+
# * This program is distributed in the hope that it will be useful,
|
15
|
+
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# * GNU General Public License for more details.
|
18
|
+
# *
|
19
|
+
# * You should have received a copy of the GNU General Public License
|
20
|
+
# * along with this program; if not, write to the Free Software
|
21
|
+
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
22
|
+
# * 02111-1307 USA
|
23
|
+
# *
|
24
|
+
# * All comments concerning this program package may be sent to the
|
25
|
+
# * e-mail address 'scipion@cnb.csic.es'
|
26
|
+
# *
|
27
|
+
# **************************************************************************
|
28
|
+
|
29
|
+
from .tests import *
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# coding: latin-1
|
3
|
+
"""
|
4
|
+
Created on Mar 25, 2014
|
5
|
+
|
6
|
+
@author: airen
|
7
|
+
@author: roberto.marabini
|
8
|
+
"""
|
9
|
+
import time
|
10
|
+
|
11
|
+
|
12
|
+
def wait(condition, timeout=30):
|
13
|
+
""" Wait until "condition" returns False or return after timeout (seconds)
|
14
|
+
param"""
|
15
|
+
t0 = time.time()
|
16
|
+
|
17
|
+
while condition():
|
18
|
+
time.sleep(1)
|
19
|
+
|
20
|
+
# Check timeout
|
21
|
+
tDelta = time.time()
|
22
|
+
|
23
|
+
if tDelta - t0 >= timeout:
|
24
|
+
print("Wait timed out after ", timeout, " seconds")
|
25
|
+
return
|