The CCP4ErrorHandling module has two classes CErrorReport which holds a list of errors and CException which just subclasses CErrorReport and the Python Exception. I use CCP4ErrorReport where a function/method returns a list of errors (e.g. CData.validity()) andCException when raising an exception but I am not sure sure that this distinction is necessary. The error report is a list because many of the context likely to raise errors are looping over many items (e.g. reading a file, drawing widgets) and we usually do not want errors to stop the overall process but we do want a report of all errors.
Each library class that might raise an exception defines the error codes that it uses - for example
class CRange(CData): ERROR_CODES = { 101 : { 'description' : 'End of range less than start' }, 102 : { 'description' : 'End of range greater than start' } }
The description should ideally be helpful to a thoughtful user. The code which catches the exception can also add more context information to the error message. There is a slight complication in the numbering of ERROR_CODES in that errors may also come from a base class so for derived classes I start the code numbering at 101, 201 etc.. There are a lot of these ERROR_CODES definitions - this is an attempt to give helpful error messages.
The errors have a severity rating defined in the module:
SEVERITY_OK | 0 | No error |
SEVERITY_UNDEFINED | 1 | Data undefined for an object for which allowUndefined is True |
SEVERITY_WARNING | 2 | A warning |
SEVERITY_UNDEFINED_ERROR | 3 | Data undefined for an object for which allowUndefined is False |
SEVERITY_ERROR | 4 | An error |
By default the ERROR_CODES are assumed to be SEVERITY_ERROR but can be specified otherwise:
ERROR_CODES = { .... 7 : { 'severity' : SEVERITY_WARNING, 'description' : 'Unrecognised qualifier in data input' }, .... }
The CCP4ErrorHandling.CErrorReport methods are:
CErrorReport method | Argument | Description |
---|---|---|
__init__ | ||
append | Append an error to the report | |
cls | The class reporting the error | |
code | An error code number unique for the class | |
details | A Python string containing further information | |
name | A Python string with an identifier for the object. CData.objectPath() gives suitable value for this. | |
extend | Append errors from another CErrorReport object to this object | |
other | A CErrorReport or CException | |
count | Return the number of errors in list with the optional specified class or severity | |
cls | Optional. A class | |
severity | Optional. A minimum severity | |
maxSeverity | Return the maximum severity of the errors in the report | |
report | Return a text string summary or the errors. | |
__str__ | As report() | |
warningMessage | In graphical context presents a dialog box with warning message | |
windowTitle | Title for dialog box window | |
message | Additional message - should explain context of error |
CCP4ErrorHandling.CException has the same methods but can,optionally, be initiated with the information for one error:
CException method | Argument | Description |
---|---|---|
__init__ | ||
cls | The class reporting the error | |
code | An error code number unique for the class | |
details | A Python string containing further information | |
name | A Python string with an identifier for the object. CData.objectPath() gives suitable value for this. |
The individual items in the error list can be accessed by the usual list mechanism (square backets) and each item has the attributes: class, code, details, name.
CPluginScript maintains a list of errors and warnings that are reported in the diagnostic.xml file that is presented in the job report if the job fails. There is a convenience function to append errors to the list:
CPluginScript method | Argument | Description |
---|---|---|
appendErrorReport | Append an error to the report | |
code | An error code number unique for the class | |
details | A Python string containing further information | |
name | A Python string with an identifier for the object. CData.objectPath() gives suitable value for this. |
For a subclass can define its own error codes - for example for buccaneer-refmac:
class buccaneer_build_refine(CPluginScript): ... ERROR_CODES = { 200 : { 'description' : 'Buccaneer task failed' }, 201 : { 'description' : 'Refmac task failed' }, 202 : { 'description' : 'Buccaneer output coordinate file not found' }, 203 : { 'description' : 'Refmac output coordinate file not found' } } def process(self): ... # buccaneer run btask = self.makePluginObject('buccaneer') ... # run the buccaneer sub-task jobStatus = btask.process() # check sub-task has worked and produced output file if jobStatus == CPluginScript.FAILED: self.appendErrorReport(200) self.reportStatus(jobStatus) return if not btask.container.outputData.XYZOUT.exists(): self.appendErrorReport(202,'Expected file: '+str(btask.container.outputData.XYZOUT)) self.reportStatus(CPluginScript.FAILED) return
Note that the number codes start at 200 to avoid clashes with the base class error codes.
The three CPluginScript methods that a subclass is likely to reimplement (processInputFiles(), processOutputFiles() and makeCommandAndScript()) should return CPluginScript.FAILED if there is an error that will prevent the task continuing (and otherwise return CPluginScript.SUCCEEDED). Before returning a failed status the method should add an error report explaining the problem.