Home > Hacking Invenio > MiscUtil Internals > Error Library |
These are the functions and methodologies for error handling in Invenio.
This API handles two concepts: Errors and Warnings.
An error is an unexpected behavior that leads to the stopping of current process.
Discussing of web pages, errors should be displayed by instead of the
requested page. Errors are logged into invenio/var/log/invenio.err
.
Errors can be logged with client information and a tracestack.
A warning is an unexpected behavior that can be ignored. Warnings are logged into
invenio/var/log/invenio.log
with just the warning message.
Errors and warnings should be internationalized (see below).
Every module should create a file containing error definitions, warning definitions, variables avoiding "magic" number or strings, etc.
This file has to be named against a convention:
<module-name>_config.py
e.g. webmessage_config.py
for the WebMessage module.
Errors and warnings are eventually stored into dictionaries. The dictionaries are to be named against the following convention:
CFG_<MODULENAME>_ERROR_MESSAGES and CFG_<MODULENAME>_WARNING_MESSAGES
These two dictionaries (one can choose to implement only one if he doesn't need warnings, for example) contain an error-name -> displayable message association.
Errors are to be named against the following convention:
ERR_<MODULE-NAME>_ERROR_NAME
Please note the use of uppercase.
Warnings can also be named as errors if convenient, and so have to follow one of these rules:
WRN_<MODULE-NAME>_WARNING_NAME or ERR_<MODULE-NAME>_WARNING_NAME
The associated message can obviously contain substituted variables like %s
, %d
...
Errors should also be internationalized. As the config file cannot receive
parameters, this is done by the error handling library. The convenient way that has
been chosen is to nest the classical _()
function inside the string.
An internationalized error message should look like this:
'_("Internationalized error (%s) message")'
A complete example of correct dictionary is given below:
CFG_WEBCOMMENT_ERROR_MESSAGES = { 'ERR_WEBCOMMENT_RECID_INVALID' : '_("%i is an invalid record ID")', 'ERR_WEBCOMMENT_RECID_NAN' : '_("Record ID %i is not a number")', 'ERR_WEBCOMMENT_UID_INVALID' : '_("%i is an invalid user ID")' }
When displaying a page, the modules/webstyle/lib/webpage.py
python module should
be used. This module provides a page()
function, convenient for webpage output,
which can handle errors (display and log).
A call to this function should use the following arguments, assuming that language
information is stored in a variable called ln
, and request information
are stored in req (will be used for IP logging, for example):
page(..., req=req, language=ln, errors=error_list, warnings=warnings_list, ...)
list of errors and warnings are behaving the same way. They are lists of tuples:
[(error_name, param1, ..., paramN), ...]
The params are used to represent substitued values in messages. For example if you want to throw one of the errors above, error_list should look like this:
error_list = [('ERR_WEBCOMMENT_RECID_INVALID', 123456)]
Business logic should be separated from web interface. We consider three files in the following (real) example:
webmessage_webinterface.py
, which is the page as viewed by a browser,webmessage.py
, which contains the business logic,webmessage_config
, which contains error definitionsIn this example, a user tries to read a message. We must ensure he doesn't read another message, and that this message really exist in the system. For a more convenient reading, some (non error-related) parts of code have been suppressed.
# error messages. (should not happen, except in case of reload, or url altering) CFG_WEBMESSAGE_ERROR_MESSAGES = \ { 'ERR_WEBMESSAGE_NOTOWNER': '_("This message is not in your mailbox")', 'ERR_WEBMESSAGE_NONICKNAME':'_("No nickname or user for uid #%s")', 'ERR_WEBMESSAGE_NOMESSAGE': '_("This message doesn\'t exist")' }
from invenio.webmessage.webmessage_config import CFG_WEBMESSAGE_ERROR_MESSAGES def perform_request_display_msg(uid, msgid, ln=CFG_SITE_LANG): uid = wash_url_argument(uid, 'int') msgid = wash_url_argument(msgid, 'int') ln = wash_language(ln) errors = [] warnings = [] body = "" if (check_user_owns_message(uid, msgid) == 0): # The user doesn't own this message errors.append(('ERR_WEBMESSAGE_NOTOWNER',)) else: (msg_id, ...) = get_message(uid, msgid) if (msg_id == ""): # The message exists in table user_msgMESSAGE # but not in table msgMESSAGE => table inconsistency errors.append(('ERR_WEBMESSAGE_NOMESSAGE',)) else: body = webmessage_templates.tmpl_display_msg( ... ) return (body, errors, warnings)
from invenio.webstyle.webpage import page from invenio.webmessage.webmessage import perform_request_display_msg def display_msg(req, msgid=-1, ln=CFG_SITE_LANG): _ = gettext_set_language(ln) (body, errors, warnings) = perform_request_display_msg(uid, msgid, ln) title = _("Read a message") return page(title = title, body = body, navtrail = get_navtrail(ln, title), uid = uid, lastupdated = __lastupdated__, req = req, language = ln, errors = errors, warnings = warnings)
The following functions can be useful (see source code for other functions):
get_msgs_for_code_list(code_list, stream='error', ln=CFG_SITE_LANG) Returns formatted strings for the given errors @param code_list: list of tuples [(err_name, arg1, ..., argN), ...] @param stream: 'error' or 'warning' @return list of tuples of length 2 [('ERR_...', err_msg), ...] if code_list empty, will return None. if errors retrieving error messages, will append an error to the list register_errors(errors_or_warnings_list, stream, req=None) log errors to invenio.err and warnings to invenio.log errors will be logged with client information (if req is given) and a tracestack warnings will be logged with just the warning message @param errors_or_warnings_list: list of tuples (err_name, err_msg) @param stream: 'error' or 'warning' @param req = mod_python request @return integer 1 if successfully wrote to stream, integer 0 if not will append another error to errors_list if unsuccessful send_error_report_to_admin(header, url, time, browser, client, error, sys_error, traceback) Sends an email to the admin with client info and tracestack
In the following example, two files are used:
webmessage_config
, containing error messageswebmessage_example_bin.py
, containing business logicScenario: a function receives an error and wants to register it only if it is not a messaging error
# error messages. (should not happen, except in case of reload, or url altering) CFG_WEBMESSAGE_ERROR_MESSAGES = \ { 'ERR_WEBMESSAGE_NOTOWNER': '_("This message is not in your mailbox")', 'ERR_WEBMESSAGE_NONICKNAME':'_("No nickname or user for uid #%s")', 'ERR_WEBMESSAGE_NOMESSAGE': '_("This message doesn\'t exist")' }
from invenio.webmessage.webmessage_config import CFG_WEBMESSAGE_ERROR_MESSAGES from invenio.miscutil.errorlib import get_msgs_for_code_list, register_errors def handle_error(error): errorlist = get_msgs_for_code_list([error]) # error is a tuple of error name, arguments => we only need the name if CFG_WEBMESSAGE_ERROR_MESSAGES[error[0]]: print("Error in webmessage: %s" % errorlist[0][1]) else: for error in errorlist: print("Error: %s" % error[1]) register_errors(errorlist, 'error')
MiscUtil can generate errors. See miscutil_config.py for a complete list. One can see below some usual errors and their solutions:
ERR_MISCUTIL_IMPORT_ERROR
<module-name>_config.py
file has not been found. Check it
has the correct name and is deployed.WRN_<MODULE-NAME>_WARNING_NAME or ERR_<MODULE-NAME>_WARNING_NAME
ERR_MISCUTIL_NO_DICT
<module-name>_config.py
. Check
that your dictionary is correctly named:
CFG_<MODULENAME>_ERROR_MESSAGESYou could also have inverted errors and warnings if only one dictionary was provided.
stream
argument is misspelled.
ERR_MISCUTIL_NO_MESSAGE_IN_DICT
error_name
, or inverted errors and warnings dictionaries.
ERR_MISCUTIL_UNDEFINED_ERROR
ERR_MODULE-NAME_
or WRN_MODULE-NAME_
. This library uses
underscores as separators to guess module name.
ERR_MISCUTIL_TOO_MANY_ARGUMENT
'ERR_KNIGHTS': '_("We are the knights who say %s!")' errors = ('ERR_KNIGHTS', 'ni', 'ni')
ERR_MISCUTIL_TOO_FEW_ARGUMENT
'???'
:
'ERR_KNIGHTS': '_("We are the knights who say %s! We demand a %s")' errors = ('ERR_KNIGHTS', 'ni') # so, where is the shrubbery??
ERR_MISCUTIL_BAD_ARGUMENT_TYPE
%i
) and string (%s
)