Reputation: 633
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
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
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
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
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