Contents

Error handling

CCP4I2 Developers Error Handling

Error handling

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 0No error
SEVERITY_UNDEFINED1Data undefined for an object for which allowUndefined is True
SEVERITY_WARNING2 A warning
SEVERITY_UNDEFINED_ERROR3Data undefined for an object for which allowUndefined is False
SEVERITY_ERROR4An 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 methodArgumentDescription
__init__
appendAppend an error to the report
clsThe class reporting the error
codeAn error code number unique for the class
detailsA Python string containing further information
nameA Python string with an identifier for the object. CData.objectPath() gives suitable value for this.
extendAppend errors from another CErrorReport object to this object
otherA CErrorReport or CException
countReturn the number of errors in list with the optional specified class or severity
clsOptional. A class
severityOptional. A minimum severity
maxSeverityReturn the maximum severity of the errors in the report
reportReturn a text string summary or the errors.
__str__As report()
warningMessageIn graphical context presents a dialog box with warning message
windowTitleTitle for dialog box window
messageAdditional message - should explain context of error

CCP4ErrorHandling.CException has the same methods but can,optionally, be initiated with the information for one error:
CException methodArgumentDescription
__init__
clsThe class reporting the error
codeAn error code number unique for the class
detailsA Python string containing further information
nameA 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.

Error handling in plugin scripts

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 methodArgumentDescription
appendErrorReportAppend an error to the report
codeAn error code number unique for the class
detailsA Python string containing further information
nameA 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.


Last modified: Thu May 8 15:01:31 BST 2014