Structured error handling: the :f:mod:`dictionaries` and :f:mod:`exception_callbacks` modules ============================================================================================= Futile provides a single, structured error-handling API. In ordinary client code the public routines are reached by writing:: use dictionaries which re-exports the raising, inspection, try/catch and query routines together with the dictionary types they operate on. The callback and severe-error policy routines are public through the companion module :f:mod:`exception_callbacks`; when they are needed the explicit ``use exception_callbacks`` line can be added. The two modules are documented separately below only to keep the source locations visible, but they describe the same error pipeline and are meant to be used together. Raising and inspecting errors from :f:mod:`dictionaries` --------------------------------------------------------- The routines below are implemented in the ``error_handling.f90`` include file and made public through :f:mod:`dictionaries`. Client code obtains them with ``use dictionaries``; a separate ``use error_handling`` is never needed. .. f:automodule:: dictionaries :members: f_err_define, f_err_check, f_err_raise, f_err_throw, f_err_clean, f_err_pop, f_err_open_try, f_err_close_try, f_err_trying, f_get_last_error, f_get_no_of_errors, f_get_past_error, f_get_error_dict, f_get_error_definitions Defining a new error ~~~~~~~~~~~~~~~~~~~~ New error families are registered with :f:subr:`f_err_define`, which returns a stable integer code (``err_id``) that can be reused for subsequent raises and checks. An optional per-error callback and action string may be attached at definition time. Errors should be defined once, at library startup, together with the dictionary errors registered by :f:subr:`dictionaries_errors`. Raising and checking ~~~~~~~~~~~~~~~~~~~~~ :f:func:`f_err_raise` is the primary idiom for conditional errors: it returns ``.true.`` when the condition holds so it can be used inline as:: if (f_err_raise(condition, 'message', err_id=MY_ERR)) return :f:subr:`f_err_throw` raises an error unconditionally (for example inside a routine that has already detected the failure). The raised error is then inspected with :f:func:`f_err_check`, which accepts an optional ``err_id`` or ``err_name`` to query a specific family, or no argument to ask whether *any* error is pending. Pending errors are cleared with :f:subr:`f_err_clean`. Try/catch environment ~~~~~~~~~~~~~~~~~~~~~ :f:subr:`f_err_open_try` and :f:subr:`f_err_close_try` delimit a nested try/catch region. Inside such a region callbacks are suppressed (errors are simply accumulated) and a stack of pending-error dictionaries is kept so that regions can nest. :f:func:`f_err_check` should be called *before* :f:subr:`f_err_close_try`, and :f:subr:`f_err_close_try` optionally returns the pending exceptions as a dictionary for inspection. :f:func:`f_err_trying` reports whether the code is currently inside a try environment. Inspecting the error record ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each raised error is stored as a dictionary entry, so the introspection API is dictionary-based: - :f:func:`f_get_last_error` returns the id of the most recent error and optionally its additional message; - :f:func:`f_get_no_of_errors` returns how many errors are currently pending; - :f:func:`f_get_past_error` returns the id of a pending error by its position in the stack; - :f:func:`f_get_error_dict` returns the dictionary describing a given error (by default the last one); - :f:func:`f_get_error_definitions` returns the global dictionary of all defined error families, useful for diagnostics. Callback and severe-error policy from :f:mod:`exception_callbacks` ------------------------------------------------------------------ These routines govern what happens when an error is raised outside a try environment. They live in :f:mod:`exception_callbacks` (source file ``callbacks.f90``) and are made available alongside the dictionaries API; client code that overrides the default behaviour adds ``use exception_callbacks`` explicitly. .. f:automodule:: exception_callbacks :members: f_err_set_callback, f_err_unset_callback, f_err_severe, f_err_severe_override, f_err_severe_restore, f_err_ignore, f_dump_last_error, f_dump_possible_errors, f_err_set_last_error_callback, f_err_unset_last_error_callback, f_err_set_all_errors_callback, f_err_unset_all_errors_callback Global callbacks ~~~~~~~~~~~~~~~~ :f:subr:`f_err_set_callback` installs a routine invoked each time an error is raised outside a try environment; the advanced overload also accepts a data address to forward to the callback. The callback is cleared with :f:subr:`f_err_unset_callback`, which restores the default behaviour. A convenience no-op callback :f:subr:`f_err_ignore` is provided to silently swallow errors. Severe-error policy ~~~~~~~~~~~~~~~~~~~ :f:subr:`f_err_severe` is the routine of last resort, called internally when the library cannot proceed (for example when the error pipeline is used before :f:subr:`f_lib_initialize`). Its behaviour can be redirected with :f:subr:`f_err_severe_override` (useful to turn a hard ``stop`` into a custom cleanup path) and restored with :f:subr:`f_err_severe_restore`. Error-dump callbacks ~~~~~~~~~~~~~~~~~~~ Two separate callback hooks control how errors are reported: - :f:subr:`f_err_set_last_error_callback` / :f:subr:`f_err_unset_last_error_callback` drive :f:subr:`f_dump_last_error`, which prints information about the most recent error; - :f:subr:`f_err_set_all_errors_callback` / :f:subr:`f_err_unset_all_errors_callback` drive :f:subr:`f_dump_possible_errors`, which prints the full catalogue of defined errors and is used internally to help diagnose unknown error names or ids. Examples --------- The examples below are rendered from ``fortranliteral`` directives placed in the Fortran source comments next to each public routine (see the policy in the :doc:`sphinxfortran-documentation ` skill). They are drawn from the CI-compiled program ``tests/flib/errors_examples.f90``, so the rendered documentation and the tested behaviour stay synchronized. A larger, integration-style example exercising error definition, conditional raising, nested try/catch and callbacks together is available in the regression test. The ``Use`` block shows the module imports needed by client code: .. literalinclude:: /../tests/flib/errs.f90 :language: fortran :start-after: Use :end-before: Use The ``Error Define`` block shows how to register error families with :f:subr:`f_err_define` and attach per-error callbacks: .. literalinclude:: /../tests/flib/errs.f90 :language: fortran :start-after: Error Define :end-before: Error Define Documentation coverage ----------------------- The CI documentation contract for this page mirrors the one described for :f:mod:`f_precisions` and checks three complementary notions of coverage: ``symbol coverage`` Every expected public error-handling entry must appear in the rendered HTML page. This catches missing raise/throw/check/try/callback symbols when the parser or the source changes. ``topic coverage`` Required explanatory topics must also appear in the rendered page. For the error-handling page these cover the ``use dictionaries`` entry point, the ``error_handling.f90`` source location, error definition, raising and checking, the try/catch environment, error-record inspection, the callback and severe-error policy from :f:mod:`exception_callbacks`, and the ``fortranliteral`` example machinery. ``example coverage`` Public helpers include examples with ``fortranliteral`` directives taken from executable code under ``tests/``. The same code is compiled and run by CI, so the rendered documentation cannot silently drift away from tested API behaviour. For parsed helpers and interfaces the directive lives in the Fortran source comment next to the public API. If a public routine has no runnable example the page and source comment should explain why, and the coverage check should make that exception explicit. The coverage check lives in ``doc/check_errors_public_api.py`` and should be extended whenever a new public error-handling routine is added. .. f:currentmodule::