# Copyright 2011-2014 Biomedical Imaging Group Rotterdam, Departments of
# Medical Informatics and Radiology, Erasmus MC, Rotterdam, The Netherlands
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This module contains all Fastr-related Exceptions
"""
import inspect
import linecache
import os
import textwrap
# pylint: disable=too-many-ancestors
# Because fo inheriting from FastrError and a common exception causes this
# exception, even though this behaviour is desired
[docs]def get_message(exception):
"""
Extract the message from an exception is a safe manner
:param BaseException exception: exception to extract from
:return: message string
:rtype: str
"""
if isinstance(exception, FastrError):
return exception.message
if len(exception.args) == 0:
return ''
if not isinstance(exception.args, str):
return ''
return exception.args[0]
[docs]class FastrError(Exception):
"""
This is the base class for all fastr related exceptions. Catching this
class of exceptions should ensure a proper execution of fastr.
"""
[docs] def __init__(self, *args, **kwargs):
"""
Constructor for all exceptions. Saves the caller object fullid (if
found) and the file, function and line number where the object was
created.
"""
super(FastrError, self).__init__(*args, **kwargs)
# Save first argument as message
if args and isinstance(args[0], str):
self.message = args[0]
else:
self.message = ''
# Search the stack for a caller outside this file (to allow subclass
# constructors not to be assumed to be the caller)
frame = {}
for frame_info in inspect.stack():
info = inspect.getframeinfo(frame_info[0])
if info.filename != __file__:
frame = frame_info[0]
break
# Extract the caller info
call_object = frame.f_locals.get('self', None)
if call_object is not None and hasattr(call_object, 'fullid'):
self.fastr_object = call_object.fullid
else:
self.fastr_object = None
self.filename = info.filename
self.function = info.function
self.linenumber = info.lineno
# Add a formatted stack trace to the error itself, useful when re-raising the error
stack = []
for frame, filename, lino, name, code_contex, index in inspect.stack()[1:]:
source_line = linecache.getline(filename, lino).strip()
stack.append('File {f}, line {l} in {n}\n {s}'.format(f=filename, l=lino, n=name, s=source_line))
self.stack_trace = '\n'.join(reversed(stack))
[docs] def __repr__(self):
"""
String representation of the error
:return: error representation
:rtype: str
"""
if self.filename and self.linenumber:
error_string = '<{} (from {}:{}) {}>'.format(type(self).__name__,
self.filename,
self.linenumber,
self.message)
else:
error_string = '<{}: {}>'.format(type(self).__name__, self.message)
if self.fastr_object is not None:
return '[{}] {}'.format(self.fastr_object, error_string)
else:
return error_string
[docs] def __str__(self):
"""
String value of the error
:return: error string
:rtype: str
"""
if self.filename and self.linenumber:
error_string = '{} from {} line {}: {}'.format(type(self).__name__,
self.filename,
self.linenumber,
self.message)
else:
error_string = '{}: {}'.format(type(self).__name__, self.message)
if self.fastr_object is not None:
return '[{}] {}'.format(self.fastr_object, error_string)
else:
return error_string
[docs] def excerpt(self):
"""
Return a excerpt of the Error as a tuple.
"""
return type(self).__name__, self.message, self.filename, self.linenumber, self.stack_trace
[docs]class FastrNotImplementedError(FastrError, NotImplementedError):
"""
This function/method has not been implemented on purpose (e.g. should be
overwritten in a sub-class)
"""
pass
[docs]class FastrOptionalModuleNotAvailableError(FastrNotImplementedError):
"""
A optional modules for Fastr is needed for this function, but is not
available on the current python installation.
"""
pass
[docs]class FastrValueError(FastrError, ValueError):
"""
ValueError in the fastr system
"""
pass
[docs]class FastrCannotChangeAttributeError(FastrError):
"""
Attempting to change an attribute of an object that can be set only once.
"""
[docs]class FastrTypeError(FastrError, TypeError):
"""
TypeError in the fastr system
"""
pass
[docs]class FastrKeyError(FastrError, KeyError):
"""
KeyError in the fastr system
"""
pass
[docs]class FastrIndexError(FastrError, IndexError):
"""
IndexError in the fastr system
"""
pass
[docs]class FastrIndexNonexistent(FastrIndexError):
"""
This is an IndexError for samples requested from a sparse data array. The
sample is not there but is probably not there because of sparseness rather
than being a missing sample (e.g. out of bounds).
"""
pass
[docs]class FastrAttributeError(FastrError, AttributeError):
"""
AttributeError in the fastr system
"""
pass
[docs]class FastrIOError(FastrError, IOError):
"""
IOError in the fastr system
"""
pass
[docs]class FastrOSError(FastrError, OSError):
"""
OSError in the fastr system
"""
pass
[docs]class FastrImportError(FastrError, ImportError):
"""
ImportError in the fastr system
"""
pass
[docs]class FastrStateError(FastrError):
"""
An object is in an invalid/unexpected state.
"""
pass
[docs]class FastrSizeUnknownError(FastrError):
"""
The size of object is not (yet) known and only a theoretical estimate is
available at the moment.
"""
pass
[docs]class FastrSizeMismatchError(FastrError):
"""
The size of two object in fastr is not matching where it should.
"""
pass
[docs]class FastrSizeInvalidError(FastrError):
"""
The given size cannot be valid.
"""
pass
[docs]class FastrCardinalityError(FastrError):
"""
The description of the cardinality is not valid.
"""
pass
[docs]class FastrUnknownURLSchemeError(FastrKeyError):
"""
Fastr encountered a data URL with a scheme that was not recognised
by the IOPlugin manager.
"""
pass
[docs]class FastrDataTypeFileNotReadable(FastrError):
"""
Could not read the datatype file.
"""
pass
[docs]class FastrDataTypeNotAvailableError(FastrError):
"""
The DataType requested is not found by the fastr system. Typically this
means that no matching DataType is found in the DataTypeManager.
"""
pass
[docs]class FastrDataTypeNotInstantiableError(FastrError):
"""
The base classes for DataTypes cannot be instantiated and should always
be sub-classed.
"""
pass
[docs]class FastrDataTypeMismatchError(FastrError):
"""
When using a DataType as the key for the DataTypeManager, the
DataTypeManager found another DataType with the same name already
in the DataTypeManager. The means fastr has two version of the same
DataType in the system, which should never happen!
"""
pass
[docs]class FastrDataTypeValueError(FastrError):
"""
This value in fastr did not pass the validation specificied for its
DataType, typically means that the data is missing or corrupt.
"""
pass
[docs]class FastrObjectUnknownError(FastrKeyError):
"""
Reference to a Tool that is not recognised by the fastr system. This
typically means the specific id/version combination of the requested
tool has not been loaded by the ToolManager.
"""
pass
[docs]class FastrNetworkUnknownError(FastrKeyError):
"""
Reference to a Tool that is not recognised by the fastr system. This
typically means the specific id/version combination of the requested
tool has not been loaded by the ToolManager.
"""
pass
[docs]class FastrPluginNotAvailable(FastrKeyError):
"""
Indicates that a requested Plugin was not found on the system.
"""
pass
[docs]class FastrPluginNotLoaded(FastrStateError):
"""
The plugin was not successfully loaded. This means the plugin class cannot
be instantiated.
"""
pass
[docs]class FastrNodeNotValidError(FastrStateError):
"""
A NodeRun is not in a valid state where it should be, typically an invalid
NodeRun is passed to the executor causing trouble.
"""
pass
[docs]class FastrNodeNotPreparedError(FastrStateError):
"""
When trying to access executation data of a NodeRun, the NodeRun must be prepare.
The NodeRun has not been prepared by the execution, so the data is not
available!
"""
pass
[docs]class FastrNodeAreadyPreparedError(FastrStateError):
"""
A attempt is made at preparing a NodeRun for the second time. This is not
allowed as it would wipe the current execution data and cause data-loss.
"""
pass
[docs]class FastrLookupError(FastrError):
"""
Could not find specified object in the fastr environment.
"""
pass
[docs]class FastrNetworkMismatchError(FastrError):
"""
Two interacting objects belong to different fastr network.
"""
pass
[docs]class FastrParentMismatchError(FastrError):
"""
Two interactive objects have different parent where they should be the same
"""
pass
[docs]class FastrSerializationMethodError(FastrKeyError):
"""
The desired serialization method does not exist.
"""
pass
[docs]class FastrSerializationError(FastrError):
"""
The serialization encountered a serious problem
"""
[docs] def __init__(self, message, serializer, original_exception=None):
# Call superclass
super(FastrSerializationError, self).__init__(message)
self.data_path = serializer.data_path
self.schema_path = serializer.schema_path
self.object_type = serializer.current_type
self.current_object = serializer.current_object
self.current_schema = serializer.current_schema
if original_exception is not None:
self.original_exception = original_exception
else:
self.original_exception = ''
[docs] def __repr__(self):
"""
Simple string representation of the exception
"""
return '<{}: {}>'.format(self.__class__.__name__, self.message)
[docs] def __str__(self):
"""
Advanced string representation of the exception including the data
about where in the schema things went wrong.
"""
return self.message + textwrap.dedent('''
DATA PATH: {}
SCHEMA PATH: {}
CURRENT OBJECT TYPE: {}
CURRENT OBJECT: {}
CURRENT SCHEMA: {}
{}
'''.rstrip()).format(self.data_path,
self.schema_path,
self.object_type,
self.current_object,
self.current_schema,
self.original_exception)
[docs]class FastrSerializationIgnoreDefaultError(FastrSerializationError):
"""
The value and default are both None, so the value should not be serialized.
"""
pass
[docs]class FastrSerializationInvalidDataError(FastrSerializationError):
"""
Encountered data to serialize that is invalid given the serialization
schema.
"""
pass
[docs]class FastrVersionInvalidError(FastrValueError):
"""
The string representation of the version is malformatted.
"""
pass
[docs]class FastrVersionMismatchError(FastrValueError):
"""
There is a mismatch between different parts of the Fastr environment
and integrity is compromised.
"""
pass
[docs]class FastrMountUnknownError(FastrKeyError):
"""
Trying to access an undefined mount
"""
pass
[docs]class FastrSourceDataUnavailableError(FastrKeyError):
"""
Could not find the Source data for the desire source.
"""
pass
[docs]class FastrSinkDataUnavailableError(FastrKeyError):
"""
Could not find the Sink data for the desire sink.
"""
pass
[docs]class FastrExecutionError(FastrError):
"""
Base class for all fastr execution related errors
"""
pass
[docs]class FastrScriptNotFoundError(FastrExecutionError):
"""
Script could not be found
"""
[docs] def __init__(self, interpreter=None, script=None, paths=None, *args, **kwargs):
self.interpreter = interpreter
self.script = script
self.paths = paths
[docs] def __str__(self):
return "Could not find the '{}' script '{}' in paths {}".format(
self.interpreter,
self.script,
self.paths,
)
[docs]class FastrExecutableNotFoundError(FastrExecutionError):
"""
The executable could not be found!
"""
[docs] def __init__(self, executable=None, *args, **kwargs):
super(FastrExecutableNotFoundError, self).__init__(*args, **kwargs)
self.executable = executable
self.path = os.environ.get('PATH', '').split(os.pathsep)
[docs] def __str__(self):
"""String representation of the error"""
return 'Could not find executable "{}" on PATH: {}'.format(self.executable, self.path)
[docs]class FastrNotExecutableError(FastrExecutionError):
"""
The command invoked by subprocess is not executable on the system
"""
pass
[docs]class FastrErrorInSubprocess(FastrExecutionError):
"""
Encountered an error in the subprocess started by the execution script
"""
pass
[docs]class FastrSubprocessNotFinished(FastrExecutionError):
"""
Encountered an error before the subprocess call by the execution script was
properly finished.
"""
pass
[docs]class FastrFileNotFound(FastrError):
"""
Could not find an expected file
"""
[docs] def __init__(self, filepath, message=None):
super().__init__()
self.missing_file = filepath
if not message:
self.message = 'Could not find file {}'.format(filepath)
[docs]class FastrLockNotAcquired(FastrError):
"""
Could not lock a directory
"""
[docs] def __init__(self, directory, message=None):
super().__init__()
self.directory = directory
if not message:
self.message = 'Could not lock directory {}'.format(directory)
[docs]class FastrResultFileNotFound(FastrFileNotFound, FastrExecutionError):
"""
Could not found the result file of job that finished. This means the
executionscript process was killed during interruption. Generally this
means a scheduler killed it because of resource shortage.
"""
pass
[docs]class FastrOutputValidationError(FastrExecutionError):
"""
An output of a Job does not pass validation
"""
pass
[docs]class FastrNoValidTargetError(FastrKeyError):
"""
Cannot find a valid target for the tool
"""
pass
[docs]class FastrCollectorError(FastrError):
"""
Cannot collect the results from a Job because of an error
"""
pass
[docs]class FastrPluginCapabilityNotImplemented(FastrNotImplementedError):
"""
A plugin did not implement a capability that it advertised.
"""