scipion-pyworkflow 3.10.5__py3-none-any.whl → 3.11.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 (127) hide show
  1. pyworkflow/config.py +131 -67
  2. pyworkflow/constants.py +12 -2
  3. pyworkflow/object.py +3 -2
  4. pyworkflow/plugin.py +93 -44
  5. pyworkflow/project/scripts/fix_links.py +4 -1
  6. pyworkflow/resources/showj/arrowDown.png +0 -0
  7. pyworkflow/resources/showj/arrowUp.png +0 -0
  8. pyworkflow/resources/showj/background_section.png +0 -0
  9. pyworkflow/resources/showj/colRowModeOff.png +0 -0
  10. pyworkflow/resources/showj/colRowModeOn.png +0 -0
  11. pyworkflow/resources/showj/delete.png +0 -0
  12. pyworkflow/resources/showj/doc_icon.png +0 -0
  13. pyworkflow/resources/showj/download_icon.png +0 -0
  14. pyworkflow/resources/showj/enabled_gallery.png +0 -0
  15. pyworkflow/resources/showj/galleryViewOff.png +0 -0
  16. pyworkflow/resources/showj/galleryViewOn.png +0 -0
  17. pyworkflow/resources/showj/goto.png +0 -0
  18. pyworkflow/resources/showj/menu.png +0 -0
  19. pyworkflow/resources/showj/separator.png +0 -0
  20. pyworkflow/resources/showj/tableViewOff.png +0 -0
  21. pyworkflow/resources/showj/tableViewOn.png +0 -0
  22. pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  23. pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  24. pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  25. pyworkflow/resources/showj/volumeOff.png +0 -0
  26. pyworkflow/resources/showj/volumeOn.png +0 -0
  27. pyworkflow/viewer.py +23 -1
  28. pyworkflowtests/objects.py +2 -2
  29. pyworkflowtests/protocols.py +1 -3
  30. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +21 -25
  31. scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
  32. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
  33. scipion_pyworkflow-3.11.0.dist-info/entry_points.txt +2 -0
  34. pyworkflow/apps/__init__.py +0 -29
  35. pyworkflow/apps/pw_manager.py +0 -37
  36. pyworkflow/apps/pw_plot.py +0 -51
  37. pyworkflow/apps/pw_project.py +0 -113
  38. pyworkflow/apps/pw_protocol_list.py +0 -143
  39. pyworkflow/apps/pw_protocol_run.py +0 -51
  40. pyworkflow/apps/pw_run_tests.py +0 -267
  41. pyworkflow/apps/pw_schedule_run.py +0 -322
  42. pyworkflow/apps/pw_sleep.py +0 -37
  43. pyworkflow/apps/pw_sync_data.py +0 -439
  44. pyworkflow/apps/pw_viewer.py +0 -78
  45. pyworkflow/gui/__init__.py +0 -36
  46. pyworkflow/gui/browser.py +0 -726
  47. pyworkflow/gui/canvas.py +0 -1190
  48. pyworkflow/gui/dialog.py +0 -977
  49. pyworkflow/gui/form.py +0 -2637
  50. pyworkflow/gui/graph.py +0 -247
  51. pyworkflow/gui/graph_layout.py +0 -271
  52. pyworkflow/gui/gui.py +0 -566
  53. pyworkflow/gui/matplotlib_image.py +0 -233
  54. pyworkflow/gui/plotter.py +0 -247
  55. pyworkflow/gui/project/__init__.py +0 -25
  56. pyworkflow/gui/project/base.py +0 -192
  57. pyworkflow/gui/project/constants.py +0 -139
  58. pyworkflow/gui/project/labels.py +0 -205
  59. pyworkflow/gui/project/project.py +0 -492
  60. pyworkflow/gui/project/searchprotocol.py +0 -154
  61. pyworkflow/gui/project/searchrun.py +0 -181
  62. pyworkflow/gui/project/steps.py +0 -171
  63. pyworkflow/gui/project/utils.py +0 -332
  64. pyworkflow/gui/project/variables.py +0 -179
  65. pyworkflow/gui/project/viewdata.py +0 -472
  66. pyworkflow/gui/project/viewprojects.py +0 -510
  67. pyworkflow/gui/project/viewprotocols.py +0 -2093
  68. pyworkflow/gui/project/viewprotocols_extra.py +0 -560
  69. pyworkflow/gui/text.py +0 -771
  70. pyworkflow/gui/tooltip.py +0 -185
  71. pyworkflow/gui/tree.py +0 -684
  72. pyworkflow/gui/widgets.py +0 -307
  73. pyworkflow/mapper/__init__.py +0 -26
  74. pyworkflow/mapper/mapper.py +0 -222
  75. pyworkflow/mapper/sqlite.py +0 -1578
  76. pyworkflow/mapper/sqlite_db.py +0 -145
  77. pyworkflow/project/__init__.py +0 -31
  78. pyworkflow/project/config.py +0 -454
  79. pyworkflow/project/manager.py +0 -180
  80. pyworkflow/project/project.py +0 -2010
  81. pyworkflow/protocol/__init__.py +0 -38
  82. pyworkflow/protocol/bibtex.py +0 -48
  83. pyworkflow/protocol/constants.py +0 -87
  84. pyworkflow/protocol/executor.py +0 -455
  85. pyworkflow/protocol/hosts.py +0 -313
  86. pyworkflow/protocol/launch.py +0 -270
  87. pyworkflow/protocol/package.py +0 -42
  88. pyworkflow/protocol/params.py +0 -741
  89. pyworkflow/protocol/protocol.py +0 -2582
  90. pyworkflow/tests/__init__.py +0 -29
  91. pyworkflow/tests/test_utils.py +0 -25
  92. pyworkflow/tests/tests.py +0 -341
  93. pyworkflow/utils/__init__.py +0 -38
  94. pyworkflow/utils/dataset.py +0 -414
  95. pyworkflow/utils/echo.py +0 -104
  96. pyworkflow/utils/graph.py +0 -169
  97. pyworkflow/utils/log.py +0 -284
  98. pyworkflow/utils/path.py +0 -528
  99. pyworkflow/utils/process.py +0 -132
  100. pyworkflow/utils/profiler.py +0 -92
  101. pyworkflow/utils/progressbar.py +0 -154
  102. pyworkflow/utils/properties.py +0 -631
  103. pyworkflow/utils/reflection.py +0 -129
  104. pyworkflow/utils/utils.py +0 -879
  105. pyworkflow/utils/which.py +0 -229
  106. pyworkflow/webservices/__init__.py +0 -8
  107. pyworkflow/webservices/config.py +0 -11
  108. pyworkflow/webservices/notifier.py +0 -162
  109. pyworkflow/webservices/repository.py +0 -59
  110. pyworkflow/webservices/workflowhub.py +0 -74
  111. pyworkflowtests/tests/__init__.py +0 -0
  112. pyworkflowtests/tests/test_canvas.py +0 -72
  113. pyworkflowtests/tests/test_domain.py +0 -45
  114. pyworkflowtests/tests/test_logs.py +0 -74
  115. pyworkflowtests/tests/test_mappers.py +0 -392
  116. pyworkflowtests/tests/test_object.py +0 -507
  117. pyworkflowtests/tests/test_project.py +0 -42
  118. pyworkflowtests/tests/test_protocol_execution.py +0 -135
  119. pyworkflowtests/tests/test_protocol_export.py +0 -78
  120. pyworkflowtests/tests/test_protocol_output.py +0 -158
  121. pyworkflowtests/tests/test_streaming.py +0 -47
  122. pyworkflowtests/tests/test_utils.py +0 -210
  123. scipion_pyworkflow-3.10.5.dist-info/RECORD +0 -140
  124. scipion_pyworkflow-3.10.5.dist-info/dependency_links.txt +0 -1
  125. scipion_pyworkflow-3.10.5.dist-info/entry_points.txt +0 -5
  126. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info/licenses}/LICENSE.txt +0 -0
  127. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
pyworkflow/config.py CHANGED
@@ -97,12 +97,15 @@ class Variable:
97
97
  self.value = new_value
98
98
  self.isDefault= self._isValueDefault()
99
99
  def _isValueDefault(self):
100
- return self.value==self.default
100
+ return self.value == self.default
101
+
102
+
101
103
  class VariablesRegistry:
102
- _variables={}
104
+ _variables = {}
103
105
 
104
106
  def __init__(self):
105
107
  raise RuntimeError("Variables class doesn't need to be instantiated.")
108
+
106
109
  @classmethod
107
110
  def register(cls, variable: Variable):
108
111
  cls._variables[variable.name] = variable
@@ -146,7 +149,7 @@ class Config:
146
149
 
147
150
  if key in os.environ:
148
151
  value = os.environ.get(key)
149
- isDefault = (value==default)
152
+ isDefault = (value == default)
150
153
  else:
151
154
  isDefault = True
152
155
  value = default
@@ -154,10 +157,10 @@ class Config:
154
157
  # If the caster is passed do the casting, if fails go back to default
155
158
  if caster:
156
159
  try:
157
- value=caster(value)
160
+ value = caster(value)
158
161
  except:
159
162
  logger.warning("Variable %s has this value %s that can't be casted to the right type (%s). Using %s (default value)" %
160
- (key,value, caster, default))
163
+ (key, value, caster, default))
161
164
  value = default
162
165
  # If empty use default value
163
166
  if value == "" != default:
@@ -168,10 +171,23 @@ class Config:
168
171
  if isinstance(value, str):
169
172
  value = os.path.expandvars(os.path.expanduser(value))
170
173
 
171
- # Register the variable
174
+ # Register the variable. Boolean variables are converted to booleans after this call. May not be accurate.
175
+ # 1 turns into False in the Config
172
176
  VariablesRegistry.register(Variable(key,description, source, value, default, var_type=var_type, isDefault=isDefault))
173
177
  return value
174
178
 
179
+ @staticmethod
180
+ def __notFalse(value):
181
+ return value != FALSE_STR
182
+
183
+ @staticmethod
184
+ def __notTrue(value):
185
+ return value != TRUE_STR
186
+
187
+ @staticmethod
188
+ def __bool(value):
189
+ return value.lower() in TRUE_YES_ON_
190
+
175
191
  class Root:
176
192
  """ Simple helper to return path from a root. """
177
193
 
@@ -185,13 +201,17 @@ class Config:
185
201
  # join will not join if expanded is absolute
186
202
  return os.path.join(self._root, expanded)
187
203
 
188
- # Home for scipion
189
204
  _get = __get.__func__
190
- SCIPION_HOME = os.path.abspath(_get(SCIPION_HOME_VAR, '',
191
- "Path where Scipion is installed. Other paths are based on this like SCIPION_SOFTWARE, SCIPION_TESTS,... unless specified"))
205
+ _notFalse = __notFalse.__func__
206
+ __bool = __bool.__func__
207
+
208
+ # Home for scipion
209
+ _home_var = _get(SCIPION_HOME_VAR, '',
210
+ "Path where Scipion is installed. Other paths are based on this like SCIPION_SOFTWARE, SCIPION_TESTS,... unless specified")
211
+ SCIPION_HOME = os.path.abspath(_home_var)
192
212
 
193
213
  # False if SCIPION_HOME is not found in the environment. To distinguish API documentation generation execution.
194
- SCIPION_HOME_DEFINED = SCIPION_HOME != ''
214
+ SCIPION_HOME_DEFINED = _home_var != ''
195
215
 
196
216
  _root = Root(str(SCIPION_HOME))
197
217
  _join = _root.join
@@ -201,68 +221,75 @@ class Config:
201
221
  __defaultSpritesFile = _join(getResourcesPath(),'sprites.png')
202
222
 
203
223
  CONDA_ACTIVATION_CMD = _get(CONDA_ACTIVATION_CMD_VAR,'',
204
- "str: Command to activate/initialize conda itself. Do not confuse it with 'conda activate'. It should be defined at installation time. It looks like this: eval \"$(/extra/miniconda3/bin/conda shell.bash hook)\"")
224
+ "str: Command to activate/initialize conda itself. Do not confuse it with 'conda activate'. It should be defined at installation time. It looks like this: eval \"$(/extra/miniconda3/bin/conda shell.bash hook)\"")
205
225
 
206
226
  # SCIPION PATHS
207
227
  SCIPION_SOFTWARE = _get('SCIPION_SOFTWARE', _join('software'),
208
- "Path where Scipion will install the software. Defaults to SCIPION_HOME/software.", var_type=VarTypes.FOLDER)
228
+ "Path where Scipion will install the software. Defaults to SCIPION_HOME/software.", var_type=VarTypes.FOLDER)
209
229
 
210
230
  SCIPION_TESTS = _get('SCIPION_TESTS', _join('data', 'tests'),
211
- "Path where to find/download test data. Defaults to SCIPION_HOME/data/tests.", var_type=VarTypes.FOLDER)
231
+ "Path where to find/download test data. Defaults to SCIPION_HOME/data/tests.", var_type=VarTypes.FOLDER)
212
232
 
213
233
  # User dependent paths
214
234
  SCIPION_USER_DATA = _get('SCIPION_USER_DATA', '~/ScipionUserData',
215
- "Path where Scipion projects are or will be created. Defaults to ~/ScipionUserData", var_type=VarTypes.FOLDER)
235
+ "Path where Scipion projects are or will be created. Defaults to ~/ScipionUserData", var_type=VarTypes.FOLDER)
216
236
 
217
237
  # LOGGING variables
218
238
  SCIPION_LOGS = _get('SCIPION_LOGS', _join(SCIPION_USER_DATA, 'logs'),
219
- "Folder for Scipion logs used by the GUI. Defaults to SCIPION_USER_DATA/logs.", var_type=VarTypes.FOLDER)
239
+ "Folder for Scipion logs used by the GUI. Defaults to SCIPION_USER_DATA/logs.", var_type=VarTypes.FOLDER)
220
240
 
221
241
  SCIPION_LOG_CONFIG = _get('SCIPION_LOG_CONFIG', None,
222
- "Optional. Path to a python logging configuration file to fine tune the logging.", var_type=VarTypes.PATH)
242
+ "Optional. Path to a python logging configuration file to fine tune the logging.", var_type=VarTypes.PATH)
223
243
 
224
244
  SCIPION_LOG = _get('SCIPION_LOG', _join(SCIPION_LOGS, 'scipion.log'),
225
- "Path to the file where scipion will write GUI logging messages. Defaults to SCIPION_LOGS/scipion.log", var_type=VarTypes.PATH)
245
+ "Path to the file where scipion will write GUI logging messages. Defaults to SCIPION_LOGS/scipion.log", var_type=VarTypes.PATH)
226
246
 
227
247
  SCIPION_LOG_FORMAT = _get('SCIPION_LOG_FORMAT', "%(message)s",
228
- "str: Format for all the log lines, defaults to %(message)s. To compose the format see https://docs.python.org/3/library/logging.html#logrecord-attributes")
248
+ "str: Format for all the log lines, defaults to %(message)s. To compose the format see https://docs.python.org/3/library/logging.html#logrecord-attributes")
229
249
 
230
250
  SCIPION_LOG_LEVEL = _get(SCIPION_LOG_LEVEL, 'INFO',
231
- "Default logging level. String among CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. Default value is INFO.")
251
+ "Default logging level. String among CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. Default value is INFO.")
232
252
 
233
253
  NO_COLOR = _get('NO_COLOR', '',
234
- "str: Comply with https://no-color.org/ initiative. Set it to something different than '' to deactivate colors in the output.")
254
+ "str: Comply with https://no-color.org/ initiative. Set it to something different than '' to deactivate colors in the output.")
235
255
 
236
256
  SCIPION_SCRATCH = _get(SCIPION_SCRATCH, None,
237
- "Optional. Path to a location mounted in a scratch drive (SSD,...)")
257
+ "Optional. Path to a location mounted in a scratch drive (SSD,...)")
238
258
 
239
259
  SCIPION_TESTS_OUTPUT = _get('SCIPION_TESTS_OUTPUT', _join(SCIPION_USER_DATA, 'Tests'),
240
- "Path to a folder where the output of the tests will be written. Defaults to SCIPION_USER_DATA/Tests.", var_type=VarTypes.FOLDER)
260
+ "Path to a folder where the output of the tests will be written. Defaults to SCIPION_USER_DATA/Tests.", var_type=VarTypes.FOLDER)
241
261
 
242
262
  SCIPION_TEST_NOSYNC = _get('SCIPION_TEST_NOSYNC', FALSE_STR,
243
- "Set it to 1, True, Yes or y to cancel test dataset synchronization. Needed when updating files in a dataset.") != FALSE_STR
263
+ "Set it to any value except False to cancel test dataset synchronization."
264
+ " Needed when updating files in a dataset.", caster=_notFalse)
244
265
 
245
266
  SCIPION_SUPPORT_EMAIL = 'scipion@cnb.csic.es'
246
267
 
247
268
  # Config variables
248
269
  SCIPION_CONFIG = _get('SCIPION_CONFIG', _join('config','scipion.conf'),
249
- "Path to the scipion configuration file where all this variables could be defined.", var_type=VarTypes.PATH)
270
+ "Path to the scipion configuration file where all this variables could be defined.", var_type=VarTypes.PATH)
250
271
 
251
272
  SCIPION_LOCAL_CONFIG = _get('SCIPION_LOCAL_CONFIG', SCIPION_CONFIG,
252
- "Path to an optional/extra/user configuration file meant to overwrite default variables.", var_type=VarTypes.PATH)
273
+ "Path to an optional/extra/user configuration file meant to overwrite default variables.", var_type=VarTypes.PATH)
253
274
 
254
275
  SCIPION_HOSTS = _get('SCIPION_HOSTS', _join('config','hosts.conf'),
255
- "Path to the host.cof file to allow scipion to use queue engines and run in HPC environments.")
276
+ "Path to the host.cof file to allow scipion to use queue engines and run in HPC environments.")
256
277
 
257
278
  SCIPION_PROTOCOLS = _get('SCIPION_PROTOCOLS', _join('config','protocols.conf'),
258
- "Custom conf file to extend the protocols tree view panel (panel on the left)")
279
+ "Custom conf file to extend the protocols tree view panel (panel on the left)")
259
280
 
260
281
  SCIPION_PLUGIN_JSON = _get('SCIPION_PLUGIN_JSON', None,
261
- "Optional. Path to get the json file with all the plugins available for Scipion.")
282
+ "Optional. Path to get the json file with all the plugins available for Scipion.")
283
+
284
+ SCIPION_SITE = _get('SCIPION_SITE', 'https://scipion.i2pc.es',
285
+ "Scipion site URL.")
286
+ SCIPION_SITE_API = SCIPION_SITE + '/report_protocols/api/v2'
287
+ SCIPION_STATS_WORKFLOW_APP = SCIPION_SITE_API + '/workflow/'
288
+ SCIPION_STATS_SUGGESTION = SCIPION_SITE_API + '/nextprotocol/suggestion/%s'
262
289
 
263
290
  SCIPION_PLUGIN_REPO_URL = _get('SCIPION_PLUGIN_REPO_URL',
264
- 'https://scipion.i2pc.es/getplugins/',
265
- "Url from where to get the list of plugins.")
291
+ SCIPION_SITE + '/getplugins/',
292
+ "Url from where to get the list of plugins.")
266
293
 
267
294
  # REMOTE Section
268
295
  SCIPION_URL = 'https://scipion.cnb.csic.es/downloads/scipion'
@@ -271,65 +298,69 @@ class Config:
271
298
 
272
299
  # Scipion Notes
273
300
  SCIPION_NOTES_FILE = _get(SCIPION_NOTES_FILE, 'notes.txt',
274
- "Name of the file where to write per project notes.")
301
+ "Name of the file where to write per project notes.")
275
302
 
276
303
  SCIPION_NOTES_PROGRAM = _get(SCIPION_NOTES_PROGRAM, None,
277
- "Command or program to use to open the notes file. Otherwise system will extension association will take place.")
304
+ "Command or program to use to open the notes file. Otherwise system will extension association will take place.")
278
305
 
279
306
  SCIPION_NOTES_ARGS = _get(SCIPION_NOTES_ARGS, None)
280
307
 
281
308
  # External text editor:
282
309
  SCIPION_TEXT_EDITOR = _get(SCIPION_TEXT_EDITOR, '',
283
- "Preferred text editor executable.", caster=str)
310
+ "Preferred text editor executable.", caster=str)
284
311
 
285
312
  # Aspect
286
313
  SCIPION_FONT_NAME = _get('SCIPION_FONT_NAME', "Helvetica",
287
- "Name of the font to use in Scipion GUI. Defaults to Helvetica.")
314
+ "Name of the font to use in Scipion GUI. Defaults to Helvetica.")
288
315
 
289
- SCIPION_FONT_SIZE = _get('SCIPION_FONT_SIZE', SCIPION_DEFAULT_FONT_SIZE,
290
- "Size of the 'normal' font to be used in Scipion GUI. Defaults to 10.", caster=int)
316
+ SCIPION_FONT_SIZE = _get('SCIPION_FONT_SIZE', str(SCIPION_DEFAULT_FONT_SIZE),
317
+ "Size of the 'normal' font to be used in Scipion GUI. "
318
+ "Defaults to 10.", caster=int)
291
319
 
292
320
  SCIPION_MAIN_COLOR = _get('SCIPION_MAIN_COLOR', Color.MAIN_COLOR,
293
- "str: Main color of the GUI. Background will be white, so for better contrast choose a dark color. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
321
+ "str: Main color of the GUI. Background will be white, so for better contrast choose a dark color. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
294
322
  caster=validColor)
295
323
 
296
324
  SCIPION_BG_COLOR = _get('SCIPION_BG_COLOR', Color.BG_COLOR,
297
- "str: Main background color of the GUI. Default is white, chose a light one. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
325
+ "str: Main background color of the GUI. Default is white, chose a light one. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
298
326
  validColor)
299
327
 
300
- SCIPION_CONTRAST_COLOR = _get('SCIPION_CONTRAST_COLOR', 'cyan',
301
- "Color used to highlight features over grayscaled images.", caster=validColor)
328
+ SCIPION_CONTRAST_COLOR = _get('SCIPION_CONTRAST_COLOR', 'cyan', "Color used to highlight features over "
329
+ "gray-scaled images.", caster=validColor)
302
330
 
303
331
  SCIPION_SPRITES_FILE = _get('SCIPION_SPRITES_FILE', __defaultSpritesFile,
304
- "File (png) with the icons in a collage. Default is found at pyworkflow/resources/sprites.png. And a GIMP file could be found at the same folder in the github repo.")
332
+ "File (png) with the icons in a collage. Default is found at "
333
+ "pyworkflow/resources/sprites.png. And a GIMP file could be found at the same "
334
+ "folder in the github repo.")
305
335
 
306
- SCIPION_SHOW_TEXT_IN_TOOLBAR = _get('SCIPION_SHOW_TEXT_IN_TOOLBAR', TRUE_STR,
307
- "Define it to anything else except False to show the label of the icons. It will take more space.") == TRUE_STR
336
+ SCIPION_SHOW_TEXT_IN_TOOLBAR = _get('SCIPION_SHOW_TEXT_IN_TOOLBAR', TRUE_STR, "Define it to anything else except "
337
+ "%s to hide labels. It will take less space." % TRUE_YES_ON_, caster=__bool)
308
338
 
309
- SCIPION_ICON_ZOOM = _get('SCIPION_ICON_ZOOM', 50,
310
- "Define it to anything else except False to show the label of the icons. It will take more space.", var_type=VarTypes.INTEGER, caster=int)
339
+ SCIPION_ICON_ZOOM = _get('SCIPION_ICON_ZOOM', "50",
340
+ "Define it to any integer value(percentage) to increase/decrease the size of the icons.",
341
+ var_type=VarTypes.INTEGER, caster=int)
311
342
 
312
343
  # Notification
313
- SCIPION_NOTIFY = _get('SCIPION_NOTIFY', TRUE_STR,
314
- "If set to False, Scipion developers will know almost nothing about Scipion usage and will have less information to improve it.") == TRUE_STR
344
+ SCIPION_NOTIFY = _get('SCIPION_NOTIFY', TRUE_STR, "If set to anything except %s Scipion developers will know "
345
+ "nothing abut Scipion usage and will have less information to improve it." % TRUE_YES_ON_,
346
+ caster=__bool)
315
347
 
316
348
  # *** Execution variables ***
317
- SCIPION_CWD = _get('SCIPION_CWD', os.path.abspath(os.getcwd()),
318
- "Directory when scipion was launched")
349
+ SCIPION_CWD = _get('SCIPION_CWD', os.path.abspath(os.getcwd()), "Directory when scipion was launched")
319
350
 
320
351
  SCIPION_GUI_REFRESH_IN_THREAD = _get('SCIPION_GUI_REFRESH_IN_THREAD', FALSE_STR,
321
- "True to refresh the runs graph with a thread. Unstable.") != FALSE_STR
352
+ "True to refresh the runs graph with a thread. Unstable.") != FALSE_STR
322
353
 
323
354
  SCIPION_GUI_REFRESH_INITIAL_WAIT = _get("SCIPION_GUI_REFRESH_INITIAL_WAIT", 5,
324
- "Seconds to wait after a manual refresh", caster=int)
355
+ "Seconds to wait after a manual refresh", caster=int)
325
356
 
326
357
  SCIPION_GUI_CANCEL_AUTO_REFRESH = _get("SCIPION_GUI_CANCEL_AUTO_REFRESH",FALSE_STR,
327
- "Set it to True to cancel automatic refresh of the runs.") != FALSE_STR
358
+ "Set it to True to cancel automatic refresh of the runs.") != FALSE_STR
328
359
 
329
360
  # Cancel shutil fast copy. In GPFS, shutil.copy does fail when trying a fastcopy and does not
330
361
  # fall back on the slow copy. For legacy reasons None is also False.
331
362
  SCIPION_CANCEL_FASTCOPY = _get('SCIPION_CANCEL_FASTCOPY', FALSE_STR,
332
- "Cancel fast copy done by shutil (copying files) when it fails. Has happened in GPFS environments. Defaults to False. None is also False otherwise fastcopy is cancelled."
363
+ "Cancel fast copy done by shutil (copying files) when it fails. Has happened in GPFS environments. Defaults to False. None is also False otherwise fastcopy is cancelled."
333
364
  ) not in [NONE_STR, FALSE_STR]
334
365
 
335
366
  # Priority package list: This variable is used in the view protocols in
@@ -339,26 +370,40 @@ class Config:
339
370
  SCIPION_PRIORITY_PACKAGE_LIST = _get('SCIPION_PRIORITY_PACKAGE_LIST', EMPTY_STR)
340
371
 
341
372
  SCIPION_STEPS_CHECK_SEC = _get('SCIPION_STEPS_CHECK_SEC', 5,
342
- "Number of seconds to wait before checking if new input is available in streamified protocols.", caster=int)
373
+ "Number of seconds to wait before checking if new input is available in "
374
+ "streamified protocols.", caster=int)
343
375
 
344
376
  SCIPION_UPDATE_SET_ATTEMPTS = _get('SCIPION_UPDATE_SET_ATTEMPTS', 3,
345
- "Number of attempts to modify the protocol output before failing. The default value is 3", caster=int)
377
+ "Number of attempts to modify the protocol output before failing. "
378
+ "The default value is 3", caster=int)
346
379
 
347
380
  SCIPION_UPDATE_SET_ATTEMPT_WAIT = _get('SCIPION_UPDATE_SET_ATTEMPT_WAIT', 2,
348
- "Time in seconds to wait until the next attempt when checking new outputs. The default value is 2 seconds", caster=int)
381
+ "Time in seconds to wait until the next attempt when checking new outputs. "
382
+ "The default value is 2 seconds", caster=int)
383
+
384
+ SCIPION_UNLOAD_FORM_ON_SAVE = _get('SCIPION_UNLOAD_FORM_ON_SAVE', TRUE_STR,
385
+ "When a protocol is saved it is unloaded if all goes fine. Set it to false "
386
+ "otherwise to rescue old behaviour.", caster=__bool)
349
387
 
350
388
  SCIPION_USE_QUEUE = _get("SCIPION_USE_QUEUE", FALSE_STR,
351
- "Default value for using the queue. By default is False. ANY value will be True except and empty value. \"False\" or \"0\" will be True too.")!= FALSE_STR
389
+ "Default value for using the queue. By default is False. "
390
+ "ANY value will be True except and empty value. \"False\" or \"0\" "
391
+ "will be True too.") != FALSE_STR
352
392
 
353
393
  SCIPION_DEFAULT_EXECUTION_ACTION = _get('SCIPION_DEFAULT_EXECUTION_ACTION', DEFAULT_EXECUTION_ACTION_ASK,
354
- """Ask if you want to launch a single protocol or a sub-workflow. The default value is 1
355
- 1: Scipion always ask
356
- 2: Run a single protocol
357
- 3: Run a sub-workflow """, caster=int)
394
+ """Ask if you want to launch a single protocol or a sub-workflow. The default value is 1
395
+ 1: Scipion always ask
396
+ 2: Run a single protocol
397
+ 3: Run a sub-workflow """, caster=int)
358
398
 
359
399
  SCIPION_MAPPER_USE_TEMPLATE = _get('SCIPION_MAPPER_USE_TEMPLATE', TRUE_STR,
360
- "Set it to False to force instantiation for each item during sets iterations. Experimental. This penalize the iteration but avoids"
361
- "the use of .clone() ot the items.") == TRUE_STR
400
+ "Set it to False to force instantiation for each item during sets iterations. Experimental. This penalize the iteration but avoids"
401
+ "the use of .clone() ot the items.") == TRUE_STR
402
+
403
+ CUDA_DEVICE_ORDER = _get('CUDA_DEVICE_ORDER', 'PCI_BUS_ID',
404
+ "To make GPU ID match what you see in nvidia-smi, that is the PCI ID."
405
+ " Use FASTEST_FIRST for default behaviour but this may not match what is returned by nvidia-smi."
406
+ " See https://docs.nvidia.com/cuda/cuda-c-programming-guide/#:~:text=in%20device%20memory.-,CUDA_DEVICE_ORDER,-FASTEST_FIRST%2C%20PCI_BUS_ID%2C%20(default ")
362
407
 
363
408
  try:
364
409
  VIEWERS = ast.literal_eval(_get('VIEWERS', "{}", "Json string to define which viewer are the default ones per output type."))
@@ -377,7 +422,8 @@ class Config:
377
422
  :return: Folder where libraries must be placed in case a binding needs them
378
423
  """
379
424
  lib = cls._join(cls.SCIPION_SOFTWARE, 'lib')
380
- os.makedirs(lib, exist_ok=True)
425
+ if cls.SCIPION_HOME_DEFINED:
426
+ os.makedirs(lib, exist_ok=True)
381
427
  return lib
382
428
 
383
429
  @classmethod
@@ -388,7 +434,8 @@ class Config:
388
434
  :return: The bindings folder
389
435
  """
390
436
  bindings = cls._join(cls.SCIPION_SOFTWARE, 'bindings')
391
- os.makedirs(bindings, exist_ok=True)
437
+ if cls.SCIPION_HOME_DEFINED:
438
+ os.makedirs(bindings, exist_ok=True)
392
439
  return bindings
393
440
 
394
441
  @classmethod
@@ -397,7 +444,8 @@ class Config:
397
444
  Folder where scipion logs must be placed. The folder is created
398
445
  """
399
446
  logsFolder = cls.SCIPION_LOGS
400
- os.makedirs(logsFolder, exist_ok=True)
447
+ if cls.SCIPION_HOME_DEFINED:
448
+ os.makedirs(logsFolder, exist_ok=True)
401
449
  return logsFolder
402
450
 
403
451
  @classmethod
@@ -455,8 +503,13 @@ class Config:
455
503
  debugOn = not Config.debugOn()
456
504
  os.environ[SCIPION_DEBUG] = str(debugOn)
457
505
  os.environ[SCIPION_DEBUG_NOCLEAN] = str(debugOn)
458
- os.environ[SCIPION_LOG_LEVEL] = "INFO" if not debugOn else "DEBUG"
459
506
 
507
+ newLevel = "DEBUG" if debugOn else "INFO"
508
+ os.environ[SCIPION_LOG_LEVEL] = newLevel
509
+
510
+ from pyworkflow.utils import changeLogLevel
511
+ changeLogLevel(newLevel)
512
+ logger.info("Log level set to %s" % newLevel)
460
513
  @staticmethod
461
514
  def debugSQLOn():
462
515
  from .utils import envVarOn
@@ -545,6 +598,17 @@ class Config:
545
598
  condaExe = os.path.join(envFolder, "bin", "python")
546
599
  return condaExe == getPython()
547
600
 
601
+ @classmethod
602
+ def getEnvName(cls):
603
+ """ Returns Scipion's environment name(conda or venv)"""
604
+ if cls.isCondaInstallation():
605
+ envPath = os.environ['CONDA_PREFIX']
606
+ else: # Virtualenv
607
+ envPath = os.environ['VIRTUAL_ENV']
608
+
609
+ return os.path.basename(envPath)
610
+
611
+
548
612
  @classmethod
549
613
  def getSpritesFile(cls):
550
614
  if not os.path.exists(Config.SCIPION_SPRITES_FILE):
pyworkflow/constants.py CHANGED
@@ -43,7 +43,7 @@ VERSION_1 = '1.0.0'
43
43
  VERSION_1_1 = '1.1.0'
44
44
  VERSION_1_2 = '1.2.0'
45
45
  VERSION_2_0 = '2.0.0'
46
- VERSION_3_0 = '3.10.5'
46
+ VERSION_3_0 = '3.11.0'
47
47
 
48
48
  # For a new release, define a new constant and assign it to LAST_VERSION
49
49
  # The existing one has to be added to OLD_VERSIONS list.
@@ -73,6 +73,16 @@ SCIPION_HOME_VAR = 'SCIPION_HOME'
73
73
  SCIPION_TESTS = 'SCIPION_TESTS'
74
74
  SCIPION_SCRATCH = 'SCIPION_SCRATCH'
75
75
 
76
+ # VARIABLE names in host.conf
77
+ PARALLEL_COMMAND_VAR = 'PARALLEL_COMMAND'
78
+ PLUGIN_MODULE_VAR = 'PLUGIN_MODULE'
79
+ QUEUE_FOR_JOBS = 'QUEUE_FOR_JOBS'
80
+
81
+
82
+ # Launching constants
83
+ RUN_JOB_GPU_PARAM = 'GPU' # Param name to "place" GPU ids used to build a run command.
84
+ RUN_JOB_GPU_PARAM_SEARCH = "%("+ RUN_JOB_GPU_PARAM +")s"
85
+
76
86
  # FONT
77
87
  SCIPION_DEFAULT_FONT_SIZE = 10
78
88
 
@@ -131,6 +141,7 @@ PROJECT_SETTINGS = 'settings.sqlite'
131
141
  FALSE_STR = str(False)
132
142
  TRUE_STR = str(True)
133
143
  NONE_STR = str(None)
144
+ TRUE_YES_ON_ = ['true', 'yes', 'on', '1']
134
145
  EMPTY_STR = ""
135
146
 
136
147
  # GUI colors
@@ -215,4 +226,3 @@ DEFAULT_EXECUTION_ACTION_ALL = 3
215
226
  # Id field/attribute constants
216
227
  ID_COLUMN='id'
217
228
  ID_ATTRIBUTE='_objId'
218
-
pyworkflow/object.py CHANGED
@@ -1233,12 +1233,13 @@ class Set(Object):
1233
1233
  return self._getMapper().exists(itemId)
1234
1234
 
1235
1235
  def iterItems(self, orderBy='id', direction='ASC', where=None,
1236
- limit=None, iterate=True):
1236
+ limit=None, iterate=True, rowFilter=None):
1237
1237
  return self._getMapper().selectAll(orderBy=orderBy,
1238
1238
  direction=direction,
1239
1239
  where=where,
1240
1240
  limit=limit,
1241
- iterate=iterate) # has flat mapper, iterate is true
1241
+ iterate=iterate,
1242
+ rowFilter=rowFilter) # has flat mapper, iterate is true
1242
1243
 
1243
1244
  def getFirstItem(self):
1244
1245
  """ Return the first item in the Set. """
pyworkflow/plugin.py CHANGED
@@ -28,6 +28,7 @@
28
28
  # **************************************************************************
29
29
  import logging
30
30
  import sys
31
+ from functools import lru_cache
31
32
 
32
33
  from pyworkflow import Variable, VariablesRegistry, VarTypes
33
34
  from .protocol import Protocol
@@ -79,6 +80,9 @@ class Domain:
79
80
  _viewers = {}
80
81
  _wizards = {}
81
82
 
83
+ # Preferred viewers:
84
+ _preferred_viewers = None
85
+
82
86
  @classmethod
83
87
  def registerPlugin(cls, name):
84
88
  """ Register a new plugin. This function should only be called when
@@ -113,15 +117,17 @@ class Domain:
113
117
 
114
118
  # Catch any import exception, warn about it but continue.
115
119
  except ModuleNotFoundError as e:
120
+
121
+ logger.debug("Module %s not found: %s" %(name, e))
116
122
  if e.name == name:
117
123
  # This is probably due to a priority package like pwchem not being installed
118
- pass
124
+ logger.debug("Name is different!!: e.name='%s', name='%s'" %( e.name , name))
119
125
  else:
120
126
  logger.warning("Plugin '%s' has import errors: %s. Maybe a missing dependency?. "
121
127
  "Is it devel mode and need to be reinstalled?. Ignoring it and continuing." % (name, str(e)))
122
128
  except Exception as e:
123
129
 
124
- (pwutils.yellow("WARNING!!: Plugin containing module %s does not import properly. "
130
+ logger.info(pwutils.yellow("WARNING!!: Plugin containing module %s does not import properly. "
125
131
  "All its content will be missing in this execution." % name))
126
132
  logger.info("Please, contact developers at %s and send this ugly information below. They'll understand it!." % DOCSITEURLS.CONTACTUS)
127
133
  logger.info("Error message: %s"% str(e))
@@ -390,56 +396,99 @@ class Domain:
390
396
  @classmethod
391
397
  def getPreferredViewers(cls, className):
392
398
  """ Find and import the preferred viewers for this class. """
393
- viewerNames = pw.Config.VIEWERS.get(className, [])
394
- if not isinstance(viewerNames, list):
395
- viewerNames = [viewerNames]
396
- viewers = [] # we will try to import them and store here
397
- for prefViewerStr in viewerNames:
398
- try:
399
- viewerModule, viewerClassName = prefViewerStr.rsplit('.', 1)
400
- prefViewer = cls.importFromPlugin(viewerModule,
401
- viewerClassName,
402
- doRaise=True)
403
- viewers.append(prefViewer)
404
- except Exception as e:
405
- logger.error("Couldn't load \"%s\" as preferred viewer for %s.\n"
406
- "There might be a typo in your VIEWERS variable "
407
- "or an error in the viewer's plugin installation"
408
- % (prefViewerStr, className), exc_info=e)
409
- return viewers
399
+
400
+ if cls._preferred_viewers is None:
401
+ logger.info("Caching preferred viewers from VIEWERS config variable.")
402
+ cls._preferred_viewers = dict()
403
+
404
+ for target, viewerNames in pw.Config.VIEWERS.items():
405
+ viewers = []
406
+ for prefViewerStr in viewerNames:
407
+ try:
408
+ viewerModule, viewerClassName = prefViewerStr.rsplit('.', 1)
409
+ prefViewer = cls.importFromPlugin(viewerModule,
410
+ viewerClassName,
411
+ doRaise=True)
412
+ viewers.append(prefViewer)
413
+ except Exception as e:
414
+ logger.error("Couldn't load \"%s\" as preferred viewer for %s.\n"
415
+ "There might be a typo in your VIEWERS variable "
416
+ "or an error in the viewer's plugin installation"
417
+ % (prefViewerStr, className), exc_info=e)
418
+
419
+ cls._preferred_viewers[target] = viewers
420
+
421
+ return cls._preferred_viewers.get(className, [])
410
422
 
411
423
  @classmethod
412
- def findViewers(cls, className, environment):
413
- """ Find the available viewers in this Domain for this class. """
424
+ @lru_cache
425
+ def getViewersSorted(cls):
426
+ """ Returns all viewers sorted by its class name"""
427
+
428
+ viewers = cls.getViewers()
429
+ return [viewers[key] for key in sorted(viewers)]
430
+
431
+ @classmethod
432
+ def findViewers(cls, target, environment):
433
+ """ Find the available viewers in this Domain for this target.
434
+
435
+ Sorting criteria:
436
+
437
+ 1st Will appear those viewers in VIEWERS variable (preferred viewers) in appearance order
438
+ 2nd Viewers targeting specifically the target and not any super class of it
439
+ 3rd rest.
440
+
441
+ In 2nd and 3rd case the order viewers are added depends on the alphabetical order the viewer when discovered.
442
+ This usually matches the viewer class name, but could be fine tune using import aliases like:
443
+
444
+ from my_viewer import MyViewer as AAAMyViewer in the viewers folder of the plugin.
445
+
446
+ In case viewers is a file and not a folder with an __init__, I'm afraid class name is what is taken into account
447
+
448
+ The import order is not possible to use since python sort them automatically.
449
+
450
+ """
414
451
  viewers = []
415
452
  try:
416
- clazz = cls.findClass(className)
453
+ instance = None
454
+ if isinstance(target, str):
455
+ logger.warning("DEVELOPERS: pass the instance/object instead of the class. This mode will be deprecated soon")
456
+ className = target
457
+ clazz = cls.findClass(className)
458
+ else:
459
+ className = target.__class__.__name__
460
+ clazz = target.__class__
461
+ instance = target
462
+
417
463
  baseClasses = clazz.mro()
418
- preferredViewers = cls.getPreferredViewers(className)
419
- preferedFlag = 0
420
464
 
421
- for viewer in cls.getViewers().values():
422
- viewerAdded=False
465
+ # Add preferred viewers
466
+ preferred_viewers = []
467
+ available_preferred_viewers = cls.getPreferredViewers(className)
468
+ for prefViewer in available_preferred_viewers:
469
+ if prefViewer.can_handle_this(baseClasses, instance=instance):
470
+ preferred_viewers.append(prefViewer)
471
+
472
+
473
+ specific_viewers = []
474
+ other_viewers=[]
475
+ # get all the viewers available
476
+
477
+ for viewer in cls.getViewersSorted():
478
+
423
479
  if environment in viewer._environments:
424
- for t in viewer._targets:
425
- if t in baseClasses:
426
- for prefViewer in preferredViewers:
427
- if viewer is prefViewer:
428
- viewers.insert(0, viewer)
429
- viewerAdded=True
430
- preferedFlag = 1
431
- break
480
+
481
+ if viewer not in preferred_viewers:
482
+
483
+ t = viewer.can_handle_this(baseClasses, target)
484
+ if t is not None:
485
+ if t == clazz:
486
+ specific_viewers.append(viewer)
487
+
432
488
  else:
433
- if t == clazz:
434
- viewers.insert(preferedFlag, viewer)
435
- viewerAdded = True
436
- else:
437
- viewers.append(viewer)
438
- viewerAdded = True
439
- break
489
+ other_viewers.append(viewer)
440
490
 
441
- if viewerAdded:
442
- break
491
+ viewers = preferred_viewers + specific_viewers + other_viewers
443
492
 
444
493
  except Exception as e:
445
494
  # Catch if there is a missing plugin, we will get Legacy which triggers and Exception
@@ -508,7 +557,7 @@ class Domain:
508
557
 
509
558
  raiseMsg = "%s\n %s\n%s\n" % (msgStr, calling, hint)
510
559
  if doRaise:
511
- raise Exception("\n\n" + raiseMsg)
560
+ raise ImportError("\n\n" + raiseMsg)
512
561
  else:
513
562
  logger.info(raiseMsg)
514
563
 
@@ -3,12 +3,15 @@
3
3
  import sys
4
4
  import os
5
5
  import logging
6
- logging.basicConfig(level="INFO")
7
6
 
7
+ from pyworkflow.config import Config
8
8
  from pyworkflow.project import Manager
9
9
  import pyworkflow.utils as pwutils
10
10
 
11
11
 
12
+ logging.basicConfig(level=Config.SCIPION_LOG_LEVEL, format=Config.SCIPION_LOG_FORMAT)
13
+
14
+
12
15
  def usage(error):
13
16
  print("""
14
17
  ERROR: %s