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.
Files changed (140) hide show
  1. pyworkflow/__init__.py +33 -0
  2. pyworkflow/apps/__init__.py +29 -0
  3. pyworkflow/apps/pw_manager.py +37 -0
  4. pyworkflow/apps/pw_plot.py +51 -0
  5. pyworkflow/apps/pw_project.py +113 -0
  6. pyworkflow/apps/pw_protocol_list.py +143 -0
  7. pyworkflow/apps/pw_protocol_run.py +51 -0
  8. pyworkflow/apps/pw_run_tests.py +267 -0
  9. pyworkflow/apps/pw_schedule_run.py +322 -0
  10. pyworkflow/apps/pw_sleep.py +37 -0
  11. pyworkflow/apps/pw_sync_data.py +439 -0
  12. pyworkflow/apps/pw_viewer.py +78 -0
  13. pyworkflow/config.py +536 -0
  14. pyworkflow/constants.py +212 -0
  15. pyworkflow/exceptions.py +18 -0
  16. pyworkflow/gui/__init__.py +36 -0
  17. pyworkflow/gui/browser.py +726 -0
  18. pyworkflow/gui/canvas.py +1190 -0
  19. pyworkflow/gui/dialog.py +976 -0
  20. pyworkflow/gui/form.py +2627 -0
  21. pyworkflow/gui/graph.py +247 -0
  22. pyworkflow/gui/graph_layout.py +271 -0
  23. pyworkflow/gui/gui.py +566 -0
  24. pyworkflow/gui/matplotlib_image.py +233 -0
  25. pyworkflow/gui/plotter.py +247 -0
  26. pyworkflow/gui/project/__init__.py +25 -0
  27. pyworkflow/gui/project/base.py +192 -0
  28. pyworkflow/gui/project/constants.py +139 -0
  29. pyworkflow/gui/project/labels.py +205 -0
  30. pyworkflow/gui/project/project.py +484 -0
  31. pyworkflow/gui/project/searchprotocol.py +154 -0
  32. pyworkflow/gui/project/searchrun.py +181 -0
  33. pyworkflow/gui/project/steps.py +166 -0
  34. pyworkflow/gui/project/utils.py +332 -0
  35. pyworkflow/gui/project/variables.py +179 -0
  36. pyworkflow/gui/project/viewdata.py +472 -0
  37. pyworkflow/gui/project/viewprojects.py +510 -0
  38. pyworkflow/gui/project/viewprotocols.py +2093 -0
  39. pyworkflow/gui/project/viewprotocols_extra.py +560 -0
  40. pyworkflow/gui/text.py +771 -0
  41. pyworkflow/gui/tooltip.py +185 -0
  42. pyworkflow/gui/tree.py +684 -0
  43. pyworkflow/gui/widgets.py +307 -0
  44. pyworkflow/mapper/__init__.py +26 -0
  45. pyworkflow/mapper/mapper.py +222 -0
  46. pyworkflow/mapper/sqlite.py +1578 -0
  47. pyworkflow/mapper/sqlite_db.py +145 -0
  48. pyworkflow/object.py +1512 -0
  49. pyworkflow/plugin.py +712 -0
  50. pyworkflow/project/__init__.py +31 -0
  51. pyworkflow/project/config.py +451 -0
  52. pyworkflow/project/manager.py +179 -0
  53. pyworkflow/project/project.py +1990 -0
  54. pyworkflow/project/scripts/clean_projects.py +77 -0
  55. pyworkflow/project/scripts/config.py +92 -0
  56. pyworkflow/project/scripts/create.py +77 -0
  57. pyworkflow/project/scripts/edit_workflow.py +90 -0
  58. pyworkflow/project/scripts/fix_links.py +39 -0
  59. pyworkflow/project/scripts/load.py +87 -0
  60. pyworkflow/project/scripts/refresh.py +83 -0
  61. pyworkflow/project/scripts/schedule.py +111 -0
  62. pyworkflow/project/scripts/stack2volume.py +41 -0
  63. pyworkflow/project/scripts/stop.py +81 -0
  64. pyworkflow/protocol/__init__.py +38 -0
  65. pyworkflow/protocol/bibtex.py +48 -0
  66. pyworkflow/protocol/constants.py +86 -0
  67. pyworkflow/protocol/executor.py +334 -0
  68. pyworkflow/protocol/hosts.py +313 -0
  69. pyworkflow/protocol/launch.py +270 -0
  70. pyworkflow/protocol/package.py +42 -0
  71. pyworkflow/protocol/params.py +744 -0
  72. pyworkflow/protocol/protocol.py +2554 -0
  73. pyworkflow/resources/Imagej.png +0 -0
  74. pyworkflow/resources/chimera.png +0 -0
  75. pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
  76. pyworkflow/resources/fa-info-circle_alert.png +0 -0
  77. pyworkflow/resources/fa-search.png +0 -0
  78. pyworkflow/resources/fa-times-circle_alert.png +0 -0
  79. pyworkflow/resources/file_vol.png +0 -0
  80. pyworkflow/resources/loading.gif +0 -0
  81. pyworkflow/resources/no-image128.png +0 -0
  82. pyworkflow/resources/scipion_bn.png +0 -0
  83. pyworkflow/resources/scipion_icon.png +0 -0
  84. pyworkflow/resources/scipion_icon.svg +397 -0
  85. pyworkflow/resources/scipion_icon_proj.png +0 -0
  86. pyworkflow/resources/scipion_icon_projs.png +0 -0
  87. pyworkflow/resources/scipion_icon_prot.png +0 -0
  88. pyworkflow/resources/scipion_logo.png +0 -0
  89. pyworkflow/resources/scipion_logo_normal.png +0 -0
  90. pyworkflow/resources/scipion_logo_small.png +0 -0
  91. pyworkflow/resources/sprites.png +0 -0
  92. pyworkflow/resources/sprites.xcf +0 -0
  93. pyworkflow/resources/wait.gif +0 -0
  94. pyworkflow/template.py +322 -0
  95. pyworkflow/tests/__init__.py +29 -0
  96. pyworkflow/tests/test_utils.py +25 -0
  97. pyworkflow/tests/tests.py +341 -0
  98. pyworkflow/utils/__init__.py +38 -0
  99. pyworkflow/utils/dataset.py +414 -0
  100. pyworkflow/utils/echo.py +104 -0
  101. pyworkflow/utils/graph.py +196 -0
  102. pyworkflow/utils/log.py +284 -0
  103. pyworkflow/utils/path.py +527 -0
  104. pyworkflow/utils/process.py +132 -0
  105. pyworkflow/utils/profiler.py +92 -0
  106. pyworkflow/utils/progressbar.py +154 -0
  107. pyworkflow/utils/properties.py +627 -0
  108. pyworkflow/utils/reflection.py +129 -0
  109. pyworkflow/utils/utils.py +877 -0
  110. pyworkflow/utils/which.py +229 -0
  111. pyworkflow/viewer.py +328 -0
  112. pyworkflow/webservices/__init__.py +8 -0
  113. pyworkflow/webservices/config.py +11 -0
  114. pyworkflow/webservices/notifier.py +162 -0
  115. pyworkflow/webservices/repository.py +59 -0
  116. pyworkflow/webservices/workflowhub.py +74 -0
  117. pyworkflow/wizard.py +64 -0
  118. pyworkflowtests/__init__.py +51 -0
  119. pyworkflowtests/bibtex.py +51 -0
  120. pyworkflowtests/objects.py +830 -0
  121. pyworkflowtests/protocols.py +154 -0
  122. pyworkflowtests/tests/__init__.py +0 -0
  123. pyworkflowtests/tests/test_canvas.py +72 -0
  124. pyworkflowtests/tests/test_domain.py +45 -0
  125. pyworkflowtests/tests/test_logs.py +74 -0
  126. pyworkflowtests/tests/test_mappers.py +392 -0
  127. pyworkflowtests/tests/test_object.py +507 -0
  128. pyworkflowtests/tests/test_project.py +42 -0
  129. pyworkflowtests/tests/test_protocol_execution.py +72 -0
  130. pyworkflowtests/tests/test_protocol_export.py +78 -0
  131. pyworkflowtests/tests/test_protocol_output.py +158 -0
  132. pyworkflowtests/tests/test_streaming.py +47 -0
  133. pyworkflowtests/tests/test_utils.py +210 -0
  134. scipion_pyworkflow-3.7.0.dist-info/LICENSE.txt +674 -0
  135. scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
  136. scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
  137. scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
  138. scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
  139. scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
  140. scipion_pyworkflow-3.7.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,196 @@
1
+ # **************************************************************************
2
+ # *
3
+ # * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
4
+ # *
5
+ # * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6
+ # *
7
+ # * This program is free software; you can redistribute it and/or modify
8
+ # * it under the terms of the GNU General Public License as published by
9
+ # * the Free Software Foundation; either version 3 of the License, or
10
+ # * (at your option) any later version.
11
+ # *
12
+ # * This program is distributed in the hope that it will be useful,
13
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # * GNU General Public License for more details.
16
+ # *
17
+ # * You should have received a copy of the GNU General Public License
18
+ # * along with this program; if not, write to the Free Software
19
+ # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20
+ # * 02111-1307 USA
21
+ # *
22
+ # * All comments concerning this program package may be sent to the
23
+ # * e-mail address 'scipion@cnb.csic.es'
24
+ # *
25
+ # **************************************************************************
26
+ """
27
+ This module define a Graph class and some utilities
28
+ """
29
+
30
+
31
+ class Node(object):
32
+ """ A node inside the graph. """
33
+ _count = 1
34
+
35
+ def __init__(self, name=None, label=None):
36
+ self._childs = []
37
+ self._parents = []
38
+
39
+ if name is None:
40
+ name = str(self._count)
41
+ self._count += 1
42
+ self._name = name
43
+
44
+ if label is None:
45
+ label = name
46
+ self._label = label
47
+
48
+ def getName(self):
49
+ return self._name
50
+
51
+ def getLabel(self):
52
+ return self._label
53
+
54
+ def setLabel(self, newLabel):
55
+ self._label = newLabel
56
+
57
+ def isRoot(self):
58
+ return len(self._parents) == 0
59
+
60
+ def getChilds(self):
61
+ return self._childs
62
+
63
+ def addChild(self, *nodes):
64
+ for n in nodes:
65
+ if n not in self._childs:
66
+ self._childs.append(n)
67
+ n._parents.append(self)
68
+
69
+ def getParent(self):
70
+ """ Return the first parent in the list,
71
+ if the node isRoot, None is returned.
72
+ """
73
+ if self.isRoot():
74
+ return None
75
+
76
+ return self._parents[0]
77
+
78
+ def getParents(self):
79
+ return self._parents
80
+
81
+ def iterChilds(self):
82
+ """ Iterate over all childs and subchilds.
83
+ Nodes can be visited more than once if have
84
+ more than one parent.
85
+ """
86
+ for child in self._childs:
87
+ for c in child.iterChilds():
88
+ yield c
89
+
90
+ yield self
91
+
92
+ def countChilds(self, visitedNode=None, count=0):
93
+ """ Iterate over all childs and subchilds.
94
+ Nodes can be visited once
95
+ """
96
+ for child in self._childs:
97
+ if child._name not in visitedNode:
98
+ visitedNode[child._name] = True
99
+ child.countChilds(visitedNode)
100
+ return len(visitedNode)
101
+
102
+
103
+ def iterChildsBreadth(self):
104
+ """ Iter child nodes in a breadth-first order
105
+ """
106
+ for child in self._childs:
107
+ yield child
108
+
109
+ for child in self._childs:
110
+ for child2 in child.iterChildsBreadth():
111
+ yield child2
112
+
113
+ def __str__(self):
114
+ return "Node (id=%s, label=%s, root=%s)" % (self._name,
115
+ self.getLabel(),
116
+ self.isRoot())
117
+
118
+
119
+ class Graph(object):
120
+ """Simple directed Graph class.
121
+ Implemented using adjacency lists.
122
+ """
123
+
124
+ def __init__(self, rootName='ROOT', root=None):
125
+ self._nodes = []
126
+ self._nodesDict = {} # To retrieve nodes from name
127
+ if root is None:
128
+ self._root = self.createNode(rootName)
129
+ else:
130
+ self._root = root
131
+ self._registerNode(root)
132
+
133
+ def _registerNode(self, node):
134
+ self._nodes.append(node)
135
+ self._nodesDict[node.getName()] = node
136
+ for child in node.getChilds():
137
+ self._registerNode(child)
138
+
139
+ def getRoot(self):
140
+ return self._root
141
+
142
+ def createNode(self, nodeName, nodeLabel=None):
143
+ """ Add a node to the graph """
144
+ node = Node(nodeName, nodeLabel)
145
+ self._registerNode(node)
146
+
147
+ return node
148
+
149
+ def aliasNode(self, node, aliasName):
150
+ """ Register an alias name for the node. """
151
+ self._nodesDict[aliasName] = node
152
+
153
+ def getNode(self, nodeName):
154
+ return self._nodesDict.get(nodeName, None)
155
+
156
+ def getNodeNames(self):
157
+ """ Returns all the keys in the node dictionary"""
158
+ return self._nodesDict.keys()
159
+
160
+ def getNodes(self):
161
+ return self._nodes
162
+
163
+ def getRootNodes(self):
164
+ """ Return all nodes that have no parent. """
165
+ return [n for n in self._nodes if n.isRoot()]
166
+
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
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env python
2
+ # **************************************************************************
3
+ # *
4
+ # * Authors: Antonio Poza (Apr 30, 2013)
5
+ # *
6
+ # * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
7
+ # *
8
+ # * This program is free software; you can redistribute it and/or modify
9
+ # * it under the terms of the GNU General Public License as published by
10
+ # * the Free Software Foundation; either version 3 of the License, or
11
+ # * (at your option) any later version.
12
+ # *
13
+ # * This program is distributed in the hope that it will be useful,
14
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # * GNU General Public License for more details.
17
+ # *
18
+ # * You should have received a copy of the GNU General Public License
19
+ # * along with this program; if not, write to the Free Software
20
+ # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21
+ # * 02111-1307 USA
22
+ # *
23
+ # * All comments concerning this program package may be sent to the
24
+ # * e-mail address 'scipion@cnb.csic.es'
25
+ # *
26
+ # **************************************************************************
27
+ """
28
+ Module to handle default logging configuration and custom one. Default logging configuration
29
+ is defined here but optionally, it can be configured with an external json file
30
+ containing a standard python logging configuration content as documented here:
31
+ https://docs.python.org/3/howto/logging-cookbook.html#an-example-dictionary-based-configuration
32
+ To specify a custom logging config file use SCIPION_LOG_CONFIG variable to a json logging configuration file
33
+ If you just want to change the logger devel use SCIPION_LOG_LEVEL variable (defaults to INFO)
34
+ See https://docs.python.org/3/howto/logging.html#logging-levels for available levels. Use the literal! not de value.
35
+ """
36
+ import os
37
+ import sys
38
+ import logging
39
+ import logging.config
40
+ import json
41
+ from logging import FileHandler
42
+
43
+ from pyworkflow.constants import PROJECT_SETTINGS, PROJECT_DBNAME
44
+ from pyworkflow.utils import Config
45
+
46
+ CONSOLE_HANDLER = 'consoleHandler'
47
+
48
+ SCIPION_PROT_ID = "SCIPION_PROT_ID"
49
+ SCIPION_PROJ_ID = "SCIPION_PROJ_ID"
50
+
51
+
52
+ # Constant for extra logging data
53
+ class STATUS:
54
+ START = "START"
55
+ STOP = "STOP"
56
+ INTERVAL = "INTERVAL"
57
+ EVENT = "EVENT"
58
+
59
+
60
+ class LevelFilter(object):
61
+ """ Logging handler filter to filter some levels. e.g.: ERROR """
62
+ def __init__(self, level):
63
+ """
64
+
65
+ :param level: Level integer value,from which include messages. Value is compared to record.levelno.
66
+ """
67
+
68
+ self.level = level
69
+
70
+ def filter(self, record):
71
+ return record.levelno <= self.level
72
+
73
+ class LoggingConfigurator:
74
+ """ Class to configure logging scenarios:
75
+
76
+ 1.- GUI logging
77
+
78
+ 2.- Protocol run logging
79
+
80
+ 3.- Other loggings: tests, sync data"""
81
+
82
+ customLoggingActive = False # Holds if a custom logging configuration has taken place.
83
+
84
+ @classmethod
85
+ def setupLogging(cls, logFile=None, console=True):
86
+ if not cls.loadCustomLoggingConfig():
87
+ cls.setupDefaultLogging(logFile=logFile, console=console)
88
+
89
+ @classmethod
90
+ def loadCustomLoggingConfig(cls):
91
+ """ Loads the custom logging configuration file"""
92
+ from pyworkflow import Config
93
+
94
+ if Config.SCIPION_LOG_CONFIG:
95
+ if os.path.exists(Config.SCIPION_LOG_CONFIG):
96
+ with open(Config.SCIPION_LOG_CONFIG, 'r') as stream:
97
+ config = json.load(stream)
98
+
99
+ logging.config.dictConfig(config)
100
+ cls.customLoggingActive = True
101
+ return True
102
+ else:
103
+ print("SCIPION_LOG_CONFIG variable points to a non existing file: %s." % Config.SCIPION_LOG_CONFIG)
104
+ return False
105
+
106
+ @staticmethod
107
+ def setupDefaultLogging(logFile=None, console=True):
108
+ """ Configures logging in a default way that is to file (rotating) and console
109
+
110
+ :param logFile: Optional, path to the log file. Defaults to SCIPION_LOG variable value. If folder
111
+ does not exists it will be created.
112
+ :param console: Optional, defaults to True, so log messages are sent to the terminal as well
113
+
114
+ """
115
+ from pyworkflow import Config
116
+
117
+ logFile = logFile or Config.SCIPION_LOG
118
+
119
+ # Log configuration
120
+ config = {
121
+ 'version': 1,
122
+ 'disable_existing_loggers': False,
123
+ 'formatters': {
124
+ 'standard': {
125
+ 'format': Config.SCIPION_LOG_FORMAT
126
+ }
127
+ },
128
+ 'handlers': {
129
+ 'fileHandler': {
130
+ 'level': Config.SCIPION_LOG_LEVEL,
131
+ 'class': 'logging.handlers.RotatingFileHandler',
132
+ 'formatter': 'standard',
133
+ 'filename': logFile,
134
+ 'maxBytes': 1000000,
135
+ 'backupCount': 10
136
+ },
137
+ },
138
+ 'loggers': {
139
+ '': {
140
+ 'handlers': ['fileHandler'],
141
+ 'level': Config.SCIPION_LOG_LEVEL,
142
+ 'propagate': False,
143
+ 'qualname': 'pyworkflow',
144
+ },
145
+ }
146
+ }
147
+
148
+ if console:
149
+ config["handlers"][CONSOLE_HANDLER] = {
150
+ 'level': 'ERROR', #Config.SCIPION_LOG_LEVEL,
151
+ 'class': 'logging.StreamHandler',
152
+ 'formatter': 'standard',
153
+ }
154
+
155
+ config['loggers']['']['handlers'].append(CONSOLE_HANDLER)
156
+
157
+ # Create the log folder
158
+ os.makedirs(os.path.dirname(os.path.abspath(logFile)), exist_ok=True)
159
+
160
+ logging.config.dictConfig(config)
161
+
162
+ @classmethod
163
+ def setUpGUILogging(cls, logFile=None):
164
+ """Sets up the logging library for the GUI processes: By default all goes to SCIPION_LOG file and console."""
165
+ cls.setupLogging(logFile=logFile)
166
+
167
+ @classmethod
168
+ def setUpProtocolSchedulingLog(cls, scheduleLogFile):
169
+ """ Sets up the logging for the scheduling process"""
170
+
171
+ # Load custom logging
172
+ cls.loadCustomLoggingConfig()
173
+
174
+ # File handler to the scheduling log file
175
+ scheduleHandler = FileHandler(scheduleLogFile)
176
+
177
+ # Get the root logger
178
+ rootLogger = logging.getLogger()
179
+
180
+ # If there wasn't any custom logging
181
+ if not cls.customLoggingActive:
182
+ # Remove the default handler that goes to the terminal
183
+ rootLogger.handlers.clear()
184
+
185
+ # Add the handler
186
+ rootLogger.addHandler(scheduleHandler)
187
+ rootLogger.setLevel(Config.SCIPION_LOG_LEVEL)
188
+
189
+ # create formatter and add it to the handlers
190
+ formatter = logging.Formatter(Config.SCIPION_LOG_FORMAT)
191
+ scheduleHandler.setFormatter(formatter)
192
+
193
+
194
+ @classmethod
195
+ def setUpProtocolRunLogging(cls, stdoutLogFile, stderrLogFile):
196
+ """ Sets up the logging library for the protocols run processes, loads the custom configuration plus
197
+ 2 FileHandlers for stdout and stderr"""
198
+
199
+ cls.loadCustomLoggingConfig()
200
+
201
+ # std out: Only warning, info and debug. Error and critical should go exclusively to stderr handler
202
+ stdoutHandler = FileHandler(stdoutLogFile)
203
+ stdoutHandler.addFilter(LevelFilter(logging.WARNING))
204
+
205
+ # std err: just errors and critical
206
+ stderrHandler = FileHandler(stderrLogFile)
207
+ stderrHandler.setLevel(logging.ERROR)
208
+
209
+ # Get the root logger
210
+ rootLogger = logging.getLogger()
211
+
212
+ # If there wasn't any custom logging
213
+ if not cls.customLoggingActive:
214
+ # Remove the default handler that goes to the terminal
215
+ rootLogger.handlers.clear()
216
+
217
+ # Add the 2 handlers, remove the
218
+ rootLogger.addHandler(stderrHandler)
219
+ rootLogger.addHandler(stdoutHandler)
220
+ rootLogger.setLevel(Config.SCIPION_LOG_LEVEL)
221
+
222
+ # create formatter and add it to the handlers
223
+ formatter = logging.Formatter(Config.SCIPION_LOG_FORMAT)
224
+ stdoutHandler.setFormatter(formatter)
225
+ stderrHandler.setFormatter(formatter)
226
+
227
+ # Capture std out and std err and send it to the file handlers
228
+ rootLogger.info("Logging configured. STDOUT --> %s , STDERR --> %s" % (stdoutLogFile, stderrLogFile))
229
+
230
+ # TO IMPROVE: This redirects the std out and stderr to the stream contained by the FileHandlers.
231
+ # The problem with this is that output stderr and stdout from subprocesses is written directly to the file
232
+ # and therefore not being formatted or propagated to other loggers in case of a custom logging configuration.
233
+ # I've (Pablo) have attempted what is described here: https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python
234
+ # but didn't work--> check_call(command, shell=True, stdout=sys.stdout, stderr=sys.stderr ) . This leads to an error cause deep in the code python does this: c2pwrite = stdout.fileno()
235
+ sys.stderr = stderrHandler.stream
236
+ sys.stdout = stdoutHandler.stream
237
+
238
+ return rootLogger
239
+
240
+
241
+ def restoreStdoutAndErr():
242
+ sys.stdout = sys.__stdout__
243
+ sys.stderr = sys.__stderr__
244
+
245
+
246
+ # ******** Extra code to send some log lines to an external performance analysis tool ***********
247
+ def setDefaultLoggingContext(protId, projId):
248
+ os.environ[SCIPION_PROT_ID] = str(protId)
249
+ os.environ[SCIPION_PROJ_ID] = projId
250
+
251
+
252
+ def getFinalProtId(protId):
253
+ return protId if protId is not None else int(os.environ.get(SCIPION_PROT_ID, "-1"))
254
+
255
+
256
+ def getFinalProjId(projId):
257
+ return projId if projId is not None else os.environ.get(SCIPION_PROJ_ID, "unknown")
258
+
259
+
260
+ def getExtraLogInfo(measurement, status, project_name=None, prot_id=None, prot_name=None, step_id=None, duration=None,
261
+ dbfilename=None):
262
+ try:
263
+ # Add TS!! optionally
264
+ if dbfilename:
265
+ splitDb = dbfilename.split("/")
266
+ dbName = splitDb[-1]
267
+ runName = ""
268
+ # project.sqlite and settings.sqlite may not have elements
269
+ if dbName not in [PROJECT_SETTINGS, PROJECT_DBNAME, ":memory:"]:
270
+ runName = splitDb[1]
271
+ dbfilename = os.path.join(runName, dbName)
272
+
273
+ return {"measurement": measurement,
274
+ "status": status,
275
+ "project_name": getFinalProjId(project_name),
276
+ "prot_id": getFinalProtId(prot_id),
277
+ "prot_name": prot_name,
278
+ "step_id": step_id,
279
+ "duration": duration,
280
+ "dbfilename": dbfilename
281
+ }
282
+
283
+ except Exception as e:
284
+ print("getExtraLogInfo failed: %s.Params were: dbFilename %s" % (e, dbfilename))