AE426082
AE426082

Reputation: 633

conversion of warnings to errors in MATLAB

In some of my functions I want to convert some warnings into errors. For example, if I want to throw an error when str2func yields a MATLAB:str2func:invalidFunctionName warning, I would do the following:

invalid_func_id = 'MATLAB:str2func:invalidFunctionName';
%hide warning of interest
warning('off', invalid_func_id);
%this might yield the warning of interest
predicate_func_try = str2func(predicate_func);
[~, warn_id] = lastwarn;
assert(~strcmp(warn_id, invalid_func_id)...
    , 'MyFunc:InvalidFunctionName'...
    , 'The predicate function %s does not have a valid name'...
    , predicate_func...
    );
warning on all

This works fine if I know that a particular block of code can give a small set of warnings. However it is verbose and probably doesn't scale to larger code blocks. Is there a better way of doing this? Ideally I would want a function which can turn certain warnings to errors in a whole block. It would allow me to modify my example to:

warnings2errors('MATLAB:str2func:invalidFunctionName');
predicate_func_try = str2func(predicate_func);
warnings2errors('off');

Upvotes: 10

Views: 3686

Answers (4)

JWCS
JWCS

Reputation: 1201

It was mentioned as an edit by jHops, but this technique is so simple, it deserves its own concise answer.

As (un)documented in https://undocumentedmatlab.com/articles/trapping-warnings-efficiently, to turn warnings to errors, simply do

s = warning('error', 'MATLAB:nearlySingularMatrix');

where the second string is the warning ID itself. It can be found by warning on verbose in cmdline, and inspecting the warning message. In my experience, it can be further debugged by turning on dbstop if error; dbstop if warning; dbstop if naninf, depending on your issue.

This is a full example for inverting a matrix and checking if it's singular. The last line turns the warning-errors back to regular warnings. s can be either a single warning state as above, or an array.

s = [warning('error', 'MATLAB:nearlySingularMatrix'), warning('error', 'MATLAB:singularMatrix')];
try
  Minv = inv(Mat);
  InvertFail = false;
catch
  InvertFail = true;
  return;
end
warning(s);

Upvotes: 2

jHops
jHops

Reputation: 339

Another method would be to overload warning itself. See implementation of warning.m and warning2error.m below. The biggest side effect I see from this is that you will see an extra "warning.m" displayed on the stack for all warning messages. Not sure if there's a way around that. Also, you would need to disable the MATLAB:dispatcher:nameConflict warning since we're overloading a builtin.

EDIT: Just noticed on matlabcentral.com an undocumented use of the builtin warning that accomplishes this: http://www.mathworks.com/matlabcentral/newsreader/view_thread/158768 http://undocumentedmatlab.com/blog/trapping-warnings-efficiently/

>> warning('error','MATLAB:str2func:invalidFunctionName')

USAGE FROM COMMAND LINE

warning2error('add','MATLAB:str2func:invalidFunctionName')

Warning.m:

% Overload of warning.m to enable conversion of certain warnings to errors
% via warning2error.m
%
% Will want to disable warning "MATLAB:dispatcher:nameConflict" via command
% warning('off','MATLAB:dispatcher:nameConflict');
%
% Jesse Hopkins
% Oct. 2 2012

function varargout = warning(varargin)
    %check for component:mnemonic syntax
    if nargin >= 2 && ischar(varargin{1}) && any(regexp(varargin{1},'^(\w+:\w+){1,}$','start','once'))
        %we've captured  <component>[:<component>]:<mnemonic>syntax

        %check if this is in the list
        if warning2error('query',varargin{1})
            %YES, convert to error
            ME = MException(varargin{1},varargin{2:end});
            ME = ME.addCause(MException('Warning2Error:ConvertedWarning','Converted warning "%s" to error becuase it was registered via warning2error.m.',varargin{1}));
            ME.throwAsCaller;
        end
    end

    %pass through to built-in warning
    varargout{1:nargout} = builtin('warning',varargin{:});
end

Warning2Error.m:

%warning2error.m
%USAGE:
%   Add warnings to convert to errors.
%  warning2error('add','<component>[:<component>]:<mnemonic>',...
%
%   Remove warnings to convert to errors
%   warning2error('remove','<component>[:<component>]:<mnemonic>',...
%
%   Query warnings to convert to errors 
%   tf = warning2error('query','<component>[:<component>]:<mnemonic>')
%
%   Get entire list of warnings converted to errors
%   list = warning2error('get')
%
% Jesse Hopkins
% Oct 2 2012

function varargout = warning2error(op,varargin)
    persistent list;
    if isempty(list)
        list = {};
    end
    varargout={};

    switch lower(op)
        case 'add'
            list = unique([list(:);varargin(:)]);
        case 'remove'
            for i = 1:length(varargin)
                [tf idx] = ismember(varargin{i},list);
                allidx = 1:length(list);
                newidx = setdiff(allidx,idx);
                list = list(newidx);
            end
        case 'clear'
            list = {};
        case 'get'
            varargout{1} = list;
        case 'query'
            out = false(1,length(varargin));
            for i = 1:length(varargin)
                out(i) = ismember(varargin{1},list);
            end
            varargout{1} = out;
    end
end

Upvotes: 6

AE426082
AE426082

Reputation: 633

I found a way to generalise this somewhat. It works the following way (similar to tic and toc):

warn_ids = setwarnings2errors('MATLAB:str2func:invalidFunctionName');
predicate_func_try = str2func(predicate_func);
getwarnings2errors(warn_ids);

Between setwarnings2errors and getwarnings2errors, all warnings set will throw an error if they are the last warning thrown. Therefore it shouldn't be used in large blocks where many different warnings can happen. I implemented the functions the following way:

setwarnings2errors:

function warning_ids = setwarnings2errors(varargin)
warning_ids = cell(nargin, 1);
for x_ = 1:nargin
    local_arg = varargin{x_};
    assert(ischar(local_arg));
    evalin('caller', sprintf('warning off %s', local_arg));
    warning_ids{x_} = local_arg;
end
end

getwarnings2errors:

function getwarnings2errors(warning_ids)
[lastwarn_str, lastwarn_id] = evalin('caller', 'lastwarn');
num_warns = numel(warning_ids);
try
    for x_ = 1:num_warns
        local_warn = warning_ids{x_};
        assert(~strcmp(local_warn, lastwarn_id)...
            , lastwarn_id...
            , lastwarn_str...
            );
    end
catch m_e
    evalin('caller', 'warning on all');
    throwAsCaller(m_e);
end
evalin('caller', 'warning on all');
end

Upvotes: 1

Pursuit
Pursuit

Reputation: 12345

I'm not aware of a clean way to do exactly what you want. Depending on your reason for wanting to turn errors into warnings, you may be able to get by with:

dbstop if warning

or

dbstop if warning MyFunc:InvalidFunctionName

You can also look at warning on stacktrace, to get more infomrat6ion on warnings as they appear.

If you need an actual error message (not just a way to break into the debugger) then I'm actually pretty impressed with the method you included in the question.

Upvotes: 3

Related Questions