"""
if parameterString is None: return ( None, None )
l = parameterString.split( '.' )
node = self
for nodeName in l[ : -1 ]:
node = node.child( nodeName )
parameterized = node._parameterized
if parameterized is not None: parameterized = parameterized()
name = l[ -1 ]
if parameterized is None or not parameterized.signature.has_key( name ):
raise KeyError( name )
return ( parameterized, name )
def saveStateInDictionary( self, result=None ):
if result is None:
result = {}
result[ 'name' ] = self._name
result[ 'selected' ] = self._selected
if self._parameterized is not None:
Parameterized.saveStateInDictionary( self._parameterized(), result )
eNodesState = {}
for eNodeKey in self.childrenNames():
eNode = self.child( eNodeKey )
eNodesState[ eNodeKey ] = eNode.saveStateInDictionary()
result[ 'executionNodes' ] = eNodesState
return result
def addExecutionDependencies( self, deps ):
'''Adds to the execution node dependencies on the execution of other nodes.
This allows to build a dependencies structure which is not forced to be a
tree, but can be a grapĥ. Dependencies are used to build Soma-Workflow
workflows with correct dependencies.
'''
if type( deps ) not in ( types.ListType, types.TupleType ):
deps = [ deps ]
self._dependencies += [ weakref.ref(x) for x in deps ]
#-------------------------------------------------------------------------------
class ProcessExecutionNode( ExecutionNode ):
'''
An execution node that has no children and run one process
'''
def __init__( self, process, optional = False, selected = True,
guiOnly = False, expandedInGui = False, altname = None ):
process = getProcessInstance( process )
#print 'ProcessExecutionNode.__init__:', self, process.name
ExecutionNode.__init__( self, process.name,
optional = optional,
selected = selected,
guiOnly = guiOnly,
parameterized = process,
expandedInGui = expandedInGui )
self.__dict__[ '_process' ] = process
if altname is not None:
self.__dict__[ '_name' ] = altname
reloadNotifier = getattr( process, 'processReloadNotifier', None )
if reloadNotifier is not None:
reloadNotifier.add( ExecutionNode.MethodCallbackProxy( \
self.processReloaded ) )
def __del__( self ):
#print 'del ProcessExecutionNode', self
if not hasattr( self, '_deleted' ) or self._deleted:
# print '*** already deleted !***'
return
if hasattr( self, '_process' ):
#print ' del proc:', self._process.name
reloadNotifier = getattr( self._process, 'processReloadNotifier', None )
if reloadNotifier is not None:
try:
l = len( reloadNotifier._listeners )
z = ExecutionNode.MethodCallbackProxy( self.processReloaded )
# bidouille: hack z so as to contain a weakref to None
# since we are in __del__ and existing weakrefs to self have already
# been neutralized
class A(object): pass
w = weakref.ref(A()) # w points to None immediately
z.object = w
x = reloadNotifier.remove( z )
except AttributeError:
# this try..except is here to prevent an error when quitting
# BrainVisa:
# ProcessExecutionNode class is set to None during module destruction
pass
else:
# print 'del ProcessExecutionNode', self
print 'no _process in ProcessExecutionNode !'
try:
ExecutionNode.__del__( self )
except:
# same as above
pass
def addChild( self, name, node, index = None ):
raise RuntimeError( _t_( 'A ProcessExecutionNode cannot have children' ) )
def _run( self, context ):
return context.runProcess( self._process )
def gui( self, parent, processView = None ):
if processView is not None:
return ProcessView( self._process, parent,
externalInfo = processView.info,
read_only=processView.read_only)
else:
return ProcessView( self._process, parent )
def children( self ):
eNode = getattr( self._process, '_executionNode', None )
if eNode is not None:
return eNode._children.itervalues()
else:
return []
def childrenNames( self ):
eNode = getattr( self._process, '_executionNode', None )
if eNode is not None:
return eNode._children.keys()
else:
return []
def __setattr__( self, attribute, value ):
if self._parameterized is not None and \
self._parameterized().signature.has_key( attribute ):
setattr( self._parameterized(), attribute, value )
else:
eNode = getattr( self._process, '_executionNode', None )
if eNode is not None and eNode._children.has_key( attribute ):
raise RuntimeError( HTMLMessage(_t_( 'Direct modification of execution node %s is not allowed.' ) % ( attribute, )) )
self.__dict__[ attribute ] = value
def __getattr__( self, attribute ):
p = self.__dict__.get( '_parameterized' )()
if p is not None and hasattr( p, attribute ):
return getattr( p, attribute )
eNode = getattr( self._process, '_executionNode', None )
if eNode is not None:
c = eNode.child( attribute )
if c is not None:
return c
raise AttributeError( attribute )
def child( self, name, default=None ):
eNode = getattr( self._process, '_executionNode', None )
if eNode is not None:
return eNode.child( name, default )
return default
def processReloaded( self, newProcess ):
"""
If the associated process has an attribute *processReloadNotifier*, this callback is attached to the notifier.
So, the node is reloaded when the process is reloaded.
"""
event = ProcessExecutionEvent()
event.setProcess( self._process )
self._process.processReloadNotifier.remove( ExecutionNode.MethodCallbackProxy( self.processReloaded ) )
self.__dict__[ '_process' ] = getProcessInstanceFromProcessEvent( event )
self._process.processReloadNotifier.add( ExecutionNode.MethodCallbackProxy( self.processReloaded ) )
def addExecutionDependencies( self, deps ):
ExecutionNode.addExecutionDependencies( self, deps )
eNode = self._process._executionNode
if eNode:
eNode.addExecutionDependencies( deps )
#-------------------------------------------------------------------------------
class SerialExecutionNode( ExecutionNode ):
'''An execution node that run all its children sequentially'''
def __init__(self, name='', optional = False, selected = True,
guiOnly = False, parameterized = None, stopOnError=True,
expandedInGui = False, possibleChildrenProcesses = None, notify = False ):
#print 'SerialExecutionNode.__init__', self
ExecutionNode.__init__(self, name, optional, selected, guiOnly, parameterized, expandedInGui=expandedInGui )
self.stopOnError=stopOnError
self.notify = notify
if possibleChildrenProcesses :
if not isinstance( possibleChildrenProcesses, dict ) :
if not isinstance( possibleChildrenProcesses, list ) \
and not isinstance( possibleChildrenProcesses, tuple ) :
possibleChildrenProcesses = [ possibleChildrenProcesses ]
r = {}
for i in xrange(len(possibleChildrenProcesses)) :
r[possibleChildrenProcesses[i]] = { 'optional' : True,
'selected' : True,
'expandedInGui' : False }
possibleChildrenProcesses = r
self._internalIndex = 0
self.possibleChildrenProcesses = possibleChildrenProcesses
if self.notify :
# Add child changes notifiers
self.beforeChildRemoved = Notifier( 4 )
self.afterChildRemoved = Notifier( 4 )
self.beforeChildAdded = Notifier( 4 )
self.afterChildAdded = Notifier( 4 )
def _run( self, context ):
result = []
pi, p = context.getProgressInfo( self )
pi.children = [ None ] * len( self._children )
if self.stopOnError:
for node in self._children.values():
npi, proc = context.getProgressInfo( node, parent=pi )
context.progress()
result.append( node.run( context ) )
del npi
else:
for node in self._children.values():
npi, proc = context.getProgressInfo( node, parent=pi )
context.progress()
try:
result.append( node.run( context ) )
del npi
except ExecutionContext.UserInterruptionStep, e:
context.error(unicode(e))
except ExecutionContext.UserInterruption:
raise
except Exception, e:
context.error("Error in execution node : "+unicode(e))
context.progress()
return result
def addChild(self, name = None, node = None, index = None):
if self.possibleChildrenProcesses :
if not name :
if isinstance( node, ExecutionNode ):
name = node.name() + '_' + str(self._internalIndex)
else :
raise RuntimeError( HTMLMessage('node argument must be an execution node') )
else:
name += '_' + str(self._internalIndex)
if not node:
node = self.possibleChildrenProcesses
self._internalIndex += 1
if self.notify :
self.beforeChildAdded.notify( weakref.proxy( self ), name, weakref.proxy( node ) )
super( SerialExecutionNode, self ).addChild(name, node, index)
if self.notify :
self.afterChildAdded.notify( weakref.proxy( self ), name, weakref.proxy( node ) )
def removeChild(self, name):
if self.possibleChildrenProcesses :
if not self._children.has_key( name ):
raise KeyError( HTMLMessage(_t_( '%s not defined' ) % ( name, )) )
if self.notify :
node = self._children[ name ]
self.beforeChildRemoved.notify( weakref.proxy( self ), name, weakref.proxy( node ) )
super( SerialExecutionNode, self ).removeChild(name)
if self.notify :
self.afterChildRemoved.notify( weakref.proxy( self ), name, weakref.proxy( node ) )
#-------------------------------------------------------------------------------
class ParallelExecutionNode( SerialExecutionNode ):
"""
An execution node that run all its children in any order (and in parallel
if possible)
"""
def _run( self, context ):
pi, p = context.getProgressInfo( self )
# do as for serial node
return super( ParallelExecutionNode, self )._run( context )
#-------------------------------------------------------------------------------
class SelectionExecutionNode( ExecutionNode ):
'''An execution node that run one of its children'''
def __init__( self, *args, **kwargs ):
#print 'SelectionExecutionNode.__init__', self
ExecutionNode.__init__( self, *args, **kwargs )
def __del__( self ):
#print 'SelectionExecutionNode.__del__', self
if not hasattr( self, '_deleted' ) or self._deleted:
# print '*** SelectionExecutionNode already deleted'
return
for node in self._children.values():
node._selectionChange.remove( ExecutionNode.MethodCallbackProxy( \
self.childSelectionChange ) )
#print '__del__ finished'
def _run( self, context ):
'Run the selected child'
if self._selected is None:
raise RuntimeError( _t_( 'No children selected' ) )
pi, p = context.getProgressInfo( self )
pi.children = [ None ]
for node in self._children.values():
if node._selected:
npi, proc = context.getProgressInfo( node, parent=pi )
context.progress()
res = node.run( context )
del npi
context.progress()
return res
context.progress()
def addChild( self, name, node, index = None ):
'Add a new child execution node'
ExecutionNode.addChild(self, name, node, index)
node._selectionChange.add( ExecutionNode.MethodCallbackProxy( \
self.childSelectionChange ) )
node._dependencies += self._dependencies
def childSelectionChange(self, node):
'''This callback is called when the selection state of a child has changed.
If the child is selected, all the other children must be unselected
because this node is a selectionNode.'''
if node._selected:
for child in self.children():
if child != node:
child.setSelected(False)
def addExecutionDependencies( self, deps ):
ExecutionNode.addExecutionDependencies( self, deps )
for node in self._children.values():
node.addExecutionDependencies( deps )
#-------------------------------------------------------------------------------
class ExecutionContext( object ):
"""
This object represents the execution context of the processes.
Indeed, a process can be started in different contexts :
* The user starts the process by clicking on the Run button in the graphical interface.
* The process is started via a script. It is possible to run brainvisa in batch mode (without any graphical interface) and to run a process via a python function : brainvisa.processes.defaultContext().runProcess(...).
* The process is a converter, so it can be run automatically by BrainVISA when a conversion is needed for another process parameters.
* The process is a viewer or an editor, it is run when the user clicks on the corresponding icon to view or edit another process parameter.
The interactions with the user are different according to the context. That's why the context object offers several useful functions to interact with BrainVISA and to call system commands.
Here are these functions :
* :py:meth:`write`, :py:meth:`warning`, :py:meth:`error` : prints a message, either in the graphical process window (in GUI mode) or in the terminal (in batch mode).
* :py:meth:`log` : writes a message in the BrainVISA log file.
* :py:meth:`ask`, :py:meth:`dialog` : asks a question to the user.
* :py:meth:`temporary` : creates a temporary file.
* :py:meth:`system`: calls a system command.
* :py:meth:`runProcess` : runs a BrainVISA process.
* :py:meth:`checkInterruption` : defines a breakpoint.
"""
remote = None
class UserInterruption( Exception ):
def __init__( self ):
Exception.__init__( self, _t_( 'user interruption' ) )
class UserInterruptionStep( Exception ):
def __init__( self ):
Exception.__init__( self, _t_( 'user interruption of current step' ) )
class StackInfo:
def __init__( self, process ):
self.process = process
self.processCount = {}
self.thread = None
self.debug = None
self.log = None
self.time = time.localtime()
def __init__( self, userLevel = None, debug = None ):
if userLevel is None:
self.userLevel = neuroConfig.userLevel
else:
self.userLevel = userLevel
#self._processStack = []
self._lock = threading.RLock()
self._processStackThread = {}
self._processStackHead = None
self.manageExceptions = 1
self._systemOutputLevel = 0
self._systemLog = None
self._systemLogFile = None
self._interruptionRequest = None
self._interruptionActions = {}
self._interruptionActionsId = 0
self._interruptionLock = threading.RLock()
self._allowHistory = True
def _processStack( self ):
self._lock.acquire()
try:
stack = self._processStackThread[ threading.currentThread() ]
except:
stack = []
self._processStackThread[ threading.currentThread() ] = stack
self._lock.release()
return stack
def _popStack( self ):
self._lock.acquire()
stack = self._processStackThread[ threading.currentThread() ]
stackinfo = stack.pop()
if len( stack ) == 0:
del self._processStackThread[ threading.currentThread() ]
if stackinfo is self._processStackHead:
self._processStackHead = None
self._lock.release()
return stackinfo
def _pushStack( self, stackinfo ):
self._lock.acquire()
stack = self._processStack()
stack.append( stackinfo )
if self._processStackHead is None:
self._processStackHead = stackinfo
self._lock.release()
def _stackTop( self ):
stack = self._processStack()
if len( stack ) == 0:
return None
return stack[-1]
def _processStackParent( self ):
stack = self._processStack()
if len( stack ) == 0:
return self._processStackHead
return stack[-1]
def _setArguments( self, _process, *args, **kwargs ):
# Set arguments
for i, v in enumerate( args ):
n = _process.signature.keys()[ i ]
_process._setImmutable( n, True )
# performing this 2 pass loop allows to set parameters with
# a forced value to immutable (ie non-linked) before actually
# setting values and running links. This avoids a bunch of unnecessary
# links to work (often several times)
for ( n, v ) in kwargs.items():
_process._setImmutable( n, True )
for i, v in enumerate( args ):
n = _process.signature.keys()[ i ]
_process.setDefault( n, 0 )
if v is not None:
_process.setValue( n, v )
else:
setattr( _process, n, None )
for ( n, v ) in kwargs.items():
_process.setDefault( n, 0 )
if v is not None:
_process.setValue( n, v )
else:
setattr( _process, n, None )
_process._clearImmutableParameters()
_process.checkArguments()
def _startProcess( self, _process, executionFunction, *args, **kwargs ):
if not isinstance( _process, Process ):
_process = getProcessInstance( _process )
apply( self._setArguments, (_process,)+args, kwargs )
# Launch process
t = threading.Thread( target = self._processExecutionThread,
args = ( _process, executionFunction ) )
t.start()
return _process
def runProcess( self, _process, *args, **kwargs ):
"""
It is possible to call a sub-process in the current process by calling context.runProcess.
The first argument is the process identifier, which is either the filename wihtout extension of the process or its english name.
The other arguments are the values of the process parameters. All mandatory argument must have a value.
The function returns the value returned by the sub-process execution method.
*Example*
>>> context.runProcess( 'do_something', self.input, self.output, value = 3.14 )
In this example, the process do_something is called with self.input as the first paramter value, self.ouput as the second parameter value and 3.14 to the parameter named value.
"""
_process = getProcessInstance( _process )
self.checkInterruption()
apply( self._setArguments, (_process,)+args, kwargs )
result = self._processExecution( _process, None )
self.checkInterruption()
if self._lastProcessRaisedException:
e = self._lastProcessRaisedException
self._lastProcessRaisedException = None # reset exception once used.
if isinstance( e, tuple ) and len( e ) == 2:
# e = ( exception, traceback )
raise e[0], None, e[1]
else:
raise e
return result
@staticmethod
def createContext():
return ExecutionContext()
def runInteractiveProcess( self, callMeAtTheEnd, process, *args, **kwargs ):
"""
Runs a process in a new thread and calls a callback function when the execution is finished.
:param function callMeAtTheEnd: callback function which will be called the process execution is finished.
:param process: id of the process which will be run.
"""
context = self.createContext()
process = getProcessInstance( process )
self.checkInterruption()
apply( self._setArguments, (process,)+args, kwargs )
thread = threading.Thread( target = self._runInteractiveProcessThread,
args = ( context, process, callMeAtTheEnd ) )
thread.start()
def _runInteractiveProcessThread( self, context, process, callMeAtTheEnd ):
try:
result = context.runProcess( process )
except Exception, e:
result = e
callMeAtTheEnd( result )
def _processExecutionThread( self, *args, **kwargs ):
self._processExecution( *args, **kwargs )
neuroHierarchy.databases.currentThreadCleanup()
def _processExecution( self, process, executionFunction=None ):
'''Execute the process "process". The value return is stored to avoid
the garbage-collection of some of the objects created by the process
itself (GUI for example).
'''
result = None
stackTop = None
process = getProcessInstance( process )
stack = self._processStack()
stackTop = self._processStackParent()
if stackTop:
## if neuroConfig.userLevel > 0:
## self.write( '
' \
## + _t_(process.name) + ' '\
## + str(process.instance) + '' )
# Count process execution
count = stackTop.processCount.get( process._id, 0 )
stackTop.processCount[ process._id ] = count + 1
newStackTop = self.StackInfo( process )
self._pushStack( newStackTop )
ishead = not stackTop
# Logging process start
if not stackTop:
process.isMainProcess = True
try: # finally -> processFinished
try: # show exception
# check write parameters if the process is the main process (check all parameters in child nodes if it is a pipeline)
# or if it has a parent which is not a pipeline that is to say, the current process is run throught context.runProcess
if ishead:
self._allWriteDiskItems = {}
if ishead or (stackTop and stackTop.process._executionNode is None):
writeParameters = []
for parameterized, attribute, type in process.getAllParameters():
if isinstance( type, WriteDiskItem ):
item = getattr( parameterized, attribute )
if item is not None:
writeParameters.append(item)
elif isinstance( type, ListOf ) and isinstance( type.contentType, WriteDiskItem ):
itemList = getattr( parameterized, attribute )
if itemList:
writeParameters.extend(itemList)
for item in writeParameters:
dirname = os.path.dirname( item.fullPath() )
if not os.path.exists( dirname ):
safemkdir.makedirs( dirname )
uuid=item.uuid()
self._allWriteDiskItems[uuid] = [ item, item.modificationHash() ]
if ishead:
log = neuroConfig.mainLog
else:
if len( stack ) >= 2:
log = stack[ -2 ].log
else:
# FIXME:
# attaching to head log is not always the right solution
# if a sub-process has parallel sub-nodes, then a new thread
# and a new stack will be created, but the logs will not be
# appended to the correct parent
log = self._processStackHead.log
if log is not None:
#print "Create subLog for process ", process.name
newStackTop.log = log.subLog()
process._log = newStackTop.log
content = '
' + _t_(process.name) + '
' + _t_('Process identifier') + '
' + process._id
content += '' + _t_('Execution platform') +'
'
if(hasattr(process, 'executionWorkflow')):
content += 'Soma-Workflow'
else :
content += 'BrainVISA'
content += '' + _t_('Parameters') +'
'
for n in process.signature.keys():
content += '' + n + ' = ' + htmlEscape( str( getattr( process, n, None ) ) ) + ''
content += '
' + _t_( 'Output' ) + '
'
try:
#print "Create subTextLog for process ", process.name
process._outputLog = log.subTextLog()
process._outputLogFile = open( process._outputLog.fileName, 'w' )
print >> process._outputLogFile, content
process._outputLogFile.flush()
content = process._outputLog
except:
content += '' + _t_('Unabled to open log file') + ''
process._outputLog = None
process._outputLogFile = None
if stackTop:
self._lastStartProcessLogItem = log.append( _t_(process.name), html=content,
children=newStackTop.log, icon='icon_process.png' )
else:
self._lastStartProcessLogItem = log.append( _t_(process.name) + ' ' + str(process.instance), html=content,
children=newStackTop.log, icon='icon_process.png' )
else:
newStackTop.log = None
if ishead and self._allowHistory:
self._historyBookEvent, self._historyBooksContext = HistoryBook.storeProcessStart( self, process )
self._processStarted()
newStackTop.thread = threading.currentThread()
self._lastProcessRaisedException = None
# Check arguments and conversions
def _getConvertedValue( v, p ):
# v: value
# p: parameter (Read/WriteDiskItem)
if v and getattr(v, "type", None) and ( ( not isSameDiskItemType( v.type, p.type ) ) or v.format not in p.formats ):
c = None
formats = [ p.preferredFormat ] \
+ [ f for f in p.formats if f is not p.preferredFormat ]
for destinationFormat in formats:
converter = getConverter( (v.type, v.format), (p.type, destinationFormat), checkUpdate=False )
if converter:
tmp = self.temporary( destinationFormat )
tmp.type = v.type
tmp.copyAttributes( v )
convargs = { 'read' : v, 'write' : tmp }
c = getProcessInstance( converter.name )
if c is not None:
try:
apply( self._setArguments, (c,), convargs )
if c.write is not None:
break
except:
pass
## if not converter: raise Exception( _t_('Cannot convert format %s to format %s for parameter %s') % ( _t_( v.format.name ), _t_( destinationFormat.name ), n ) )
## tmp = self.temporary( destinationFormat )
## tmp.type = v.type
## tmp.copyAttributes( v )
## self.runProcess( converter.name, read = v, write = tmp )
if not c: raise Exception( HTMLMessage(_t_('Cannot convert format %s to format %s for parameter %s') % ( _t_( v.format.name ), _t_( destinationFormat.name ), n )) )
self.runProcess( c )
return tmp
converter = None
for ( n, p ) in process.signature.items():
if isinstance( p, ReadDiskItem ) and p.enableConversion:
v = getattr( process, n )
tmp = _getConvertedValue( v, p )
if tmp is not None:
process.setConvertedValue( n, tmp )
elif isinstance( p, WriteDiskItem ):
v = getattr( process, n )
#test if data is locked
if (v is not None ):
if v.isLockData() and (process.execution.im_func != super(process.__class__, process).execution.im_func ) :
# raise an error if the diskitem is an output of a process which has an execution function (not the default execution function of the Process class)
raise IOError ( HTMLMessage(_t_('The file: %s is locked. It cannot be opened for writing. You can unlock it if necessary using the contextual menu of the parameter %s') % ( str(v), n ) ))
#end test if data is locked
if v is not None:
v.createParentDirectory()
elif isinstance( p, ListOf ):
needsconv = False
converted = []
lv = getattr( process, n )
for v in lv:
tmp = _getConvertedValue( v, p.contentType )
if tmp is not None:
converted.append( tmp )
needsconv = True
else:
converted.append( v )
if needsconv:
process.setConvertedValue( n, converted )
if executionFunction is None:
if(hasattr(process, 'executionWorkflow')) :
from soma_workflow.client import WorkflowController, Workflow, Helper
jobs, dependencies, root_group = process.executionWorkflow( self )
workflow = Workflow(jobs = jobs, dependencies = dependencies, root_group = root_group)
controller = WorkflowController()
wid = controller.submit_workflow(workflow=workflow, name=process.name)
Helper.wait_workflow(wid, controller)
list_failed_jobs = Helper.list_failed_jobs(wid, controller)
result = workflow
if (len(list_failed_jobs) > 0):
raise Exception('run through soma workflow failed, see details with soma-workflow-gui')
else :
# Delete the submitted workflow
controller.delete_workflow(wid, True)
else :
result = process.execution( self )
else:
result = executionFunction( self )
except Exception, e:
self._lastProcessRaisedException = ( e, sys.exc_info()[2] )
try:
self._showException()
except SystemExit, e:
neuroConfig.exitValue = e.args[0]
except:
import traceback
info = sys.exc_info()
sys.stderr.write('\n%s: %s\n' % (info[0].__name__, unicode(info[1])))
traceback.print_tb(info[2], None, sys.stderr)
logException( context=self )
if self._depth() != 1 or not self.manageExceptions:
raise
finally:
self._processFinished( result )
process.restoreConvertedValues()
# update history
if self._allowHistory and ishead and hasattr( self, '_historyBookEvent' ) and self._historyBookEvent is not None: # one of these conditions may be false when an exception occurs during execution
HistoryBook.storeProcessFinished( self, process, self._historyBookEvent, self._historyBooksContext )
self._historyBookEvent = None
self._historyBooksContext = None
for item_hash in self._allWriteDiskItems.values():
item, hash = item_hash
if item.isReadable():
if item.modificationHash() != hash:
try:
# do not try to insert in the database an item that doesn't have any reference to a database
# or which is temporary
if item.get("_database", None) and \
( not hasattr( item, '_isTemporary' ) \
or not item._isTemporary ):
neuroHierarchy.databases.insertDiskItem( item, update=True )
except NotInDatabaseError:
pass
except:
showException()
item_hash[ 1 ] = item.modificationHash()
elif (process.isMainProcess): # clear unused minfs only when the main process is finished to avoid clearing minf that will be used in next steps
item.clearMinf()
# Close output log file
if process._outputLogFile is not None:
print >> process._outputLogFile, '