User messaging: result records vs exceptions vs logging

Motivation

This specification delineates the applicable contexts for using result records, exceptions, progress reporting, specific log levels, or other types of user messaging processes.

Specification

Result records

Result records are the only return value format for all DataLad interfaces.

Contrasting with classic Python interfaces that return specific non-annotated values, DataLad interfaces (i.e. subclasses of datalad.interface.base.Interface) implement message passing by yielding result records that are associated with individual operations. Result records are routinely inspected throughout the code base and their annotations are used to inform general program flow and error handling.

DataLad interface calls can include an on_failure parameterization to specify how to proceed with a particular operation if a returned result record is classified as a failure result. DataLad interface calls can also include a result_renderer parameterization to explicitly enable or disable the rendering of result records.

Developers should be aware that external callers will use DataLad interface call parameterizations that can selectively ignore or act on result records, and that the process should therefore yield meaningful result records. If, in turn, the process itself receives a set of result records from a sub-process, these should be inspected individually in order to identify result values that could require re-annotation or status re-classification.

For user messaging purposes, result records can also be enriched with additional human-readable information on the nature of the result, via the message key, and human-readable hints to the user, via the hints key. Both of these are rendered via the UI Module.

Exception handling

In general, exceptions should be raised when there is no way to ignore or recover from the offending action.

More specifically, raise an exception when:

  1. A DataLad interface’s parameter specifications are violated

  2. An additional requirement (beyond parameters) for the meaningful continuation of a DataLad interface, function, or process is not met

It must be made clear to the user/caller what the exact cause of the exception is, given the context within which the user/caller triggered the action. This is achieved directly via a (re)raised exception, as opposed to logging messages or results records which could be ignored or unseen by the user.

Note

In the case of a complex set of dependent actions it could be expensive to confirm parameter violations. In such cases, initial sub-routines might already generate result records that have to be inspected by the caller, and it could be practically better to yield a result record (with status=[error|impossible]) to communicate the failure. It would then be up to the upstream caller to decide whether to specify on_failure='ignore' or whether to inspect individual result records and turn them into exceptions or not.

Logging

Logging provides developers with additional means to describe steps in a process, so as to allow insight into the program flow during debugging or analysis of e.g. usage patterns. Logging can be turned off externally, filtered, and redirected. Apart from the log-level and message, it is not inspectable and cannot be used to control the logic or flow of a program.

Importantly, logging should not be the primary user messaging method for command outcomes, Therefore:

  1. No interface should rely solely on logging for user communication

  2. Use logging for in-progress user communication via the mechanism for progress reporting

  3. Use logging to inform debugging processes

UI Module

The ui module provides the means to communicate information to the user in a user-interface-specific manner, e.g. via a console, dialog, or an iPython interface. Internally, all DataLad results processed by the result renderer are passed through the UI module.

Therefore: unless the criteria for logging apply, and unless the message to be delivered to the user is specified via the message key of a result record, developers should let explicit user communication happen through the UI module as it provides the flexibility to adjust to the present UI. Specifically, datalad.ui.message() allows passing a simple message via the UI module.

Examples

The following links point to actual code implementations of the respective user messaging methods: