Raise custom Exception with arguments
Solution:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
end
This is the best way if you follow the Rubocop Style Guide and always pass your message as the second argument to raise
:
raise FooError.new(foo), 'argh'
You can get foo
like this:
rescue FooError => error
error.foo # => 1234
error.message # => 'argh'
If you want to customize the error message then write:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
def message
"The foo is: #{foo}"
end
end
This works great if foo
is required. If you want foo
to be an optional argument, then keep reading.
If you don't follow the Rubocop Style Guide
And want this to work:
raise FooError.new('argh', foo)
You need to pass the message to super
as the only argument:
class FooError < StandardError
attr_reader :foo
def initialize(message, foo)
super(message)
@foo = foo
end
end
Explanation:
Pass your message as the second argument to raise
As the Rubocop Style Guide says, the message and the exception should be passed separately. If you write:
raise FooError.new('argh')
And want to pass a backtrace, there is no way to do it without passing the message twice:
raise FooError.new('argh'), 'argh', other_error.backtrace
You need to pass a backtrace if you want to re-raise an exception as a new instance with the same backtrace and a different message or data. Sometimes this is very useful.
Why is this so complicated?
The crux of the problem is a design flaw in Ruby: exception messages get set in two different ways.
raise StandardError, 'argh' # case 1
raise StandardError.new('argh') # case 2
In case 1, raise
just calls StandardError.new('argh')
, so these are the same. But what if you pass an exception instance and a message to raise
?
raise FooError.new(foo), 'argh', backtrace
raise
will set 'argh'
as the message on the FooError
instance, so it behaves as if you called super('argh')
in FooError#initialize
.
We want to be able to use this syntax, because otherwise, we'll have to pass the message twice anytime we want to pass a backtrace:
raise FooError.new(foo, 'argh'), 'argh', backtrace
raise FooError.new('argh', foo), 'argh', backtrace
But what if foo
is optional? Then FooError#initialize
is overloaded.
raise FooError, 'argh' # case A
raise FooError.new(foo), 'argh' # case B
In case A, raise
will call FooError.new('argh')
, but your code expects an optional foo
, not a message. This is bad. What are your options?
accept that the value passed to
FooError#initialize
may be eitherfoo
or a message.Don't use case A style. If you're not passing
foo
, writeraise FooError.new(), 'argh'
Make foo a keyword argument
IMO, don't do 2. The code's not self-documenting, so you have to remember all of this. Too complicated.
If you don't want to use a keyword argument, my implementation of FooError
way at the top of this answer actually works great with 1. This is why FooError#initialize
has to call super
and not super()
. Because when you write raise FooError, 'argh'
, foo
will be 'argh'
, and you have to pass it to the parent class to set the message. The code doesn't break if you call super
with something that isn't a string; nothing happens.
3 is the simplest option, if you're ok with a keyword argument - h/t Lemon Cat. Here's the code for that:
class FooError < StandardError
attr_reader :foo
def initialize(message, foo: nil)
super(message)
@foo = foo
end
end
raise FooError, 'message', backtrace
raise FooError(foo: foo), 'message', backtrace
Proper way to declare custom exceptions in modern Python?
Maybe I missed the question, but why not:
class MyException(Exception):
pass
To override something (or pass extra args), do this:
class ValidationError(Exception):
def __init__(self, message, errors):
# Call the base class constructor with the parameters it needs
super().__init__(message)
# Now for your custom code...
self.errors = errors
That way you could pass dict of error messages to the second param, and get to it later with e.errors
.
In Python 2, you have to use this slightly more complex form of super()
:
super(ValidationError, self).__init__(message)
Passing arguments into a custom exception
You are initializing your custom exception with two arguments, item
and the string 'not an int'
. When you initialize the Exception with multiple arguments, the *args will be displayed as a tuple:
>>> raise NonIntError('hi', 1, [1,2,3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.NonIntError: ('hi', 1, [1, 2, 3])
To get your desired result, pass exactly one string, i.e.:
>>> item = 'apple'
>>> raise NonIntError(item + ' not an int')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.NonIntError: apple not an int
Making a custom Exception with parameters
you would use it like
throw new ServiceDireccionException(TypeOfService.Provincia);
but therefore you need to change the constructor parameter type to
public ServiceDireccionException(TypeOfService type) : base(GetMessage(type)) { }
To keep information it should contain inner exception
catch (ServiceDireccionException ex)
{
throw new ServiceDireccionException(ex, TypeOfService.Provincia);
}
therefore the constructor will be something like
public ServiceDireccionException(Exception innerException, TypeOfService type)
: base(GetMessage(type), innerException)
{
}
to get the message use
private static string GetMessage(TypeOfService type)
{
switch (type)
{
case TypeOfService.Provincia:
return $"Sucedio un error al buscar la/s {type}/s";
case TypeOfService.Municipio:
return $"Sucedio un error al buscar lo/s {type}/s";
case TypeOfService.Localidad:
return $"Sucedio un error al buscar la/s {type}/es";
}
return $"Unknown TypeOfService: {type}";
}
how to raise a custom exception in main method proper?
It seems that you are printing an object so it will not print your message for that you have to change in your main()
.
def main():
try:
tryerror(-1)
except pdv_error_response as e:
if pdv_error_response:
print(e)
Now you can get your expected output.
You can refer User Defined Exception for more.
Raising exception with two arguments
There's definitely benefit to doing this. You chain exceptions and provide that chain as information to the user rather than providing the most recent exception created.
Of course, your colleague could of done it in a better way by using syntax Python provides for this. The raise exc from raised_exc
syntax is used to raise an exception after another exception has been raised:
except CustomException as e:
raise DifferentExceptionClass("Could not do something") from e
and causes e
(the raised exception, CustomException
here) to be stored as the __cause__
attribute of the most recent exception (DifferentExceptionClass
here) if you need to peek at it.
In the case where a raise
is used inside an except
handler (as happens in your code snippet), the previous exception (e
) is already implicitly stored as a __context__
attribute for it. So, passing it as an argument too, doesn't do anything else but also store the exception in the args
tuple.
Laravel create custom exception with parameters & return as JSON
In your case you are returning the exception as a response instead of throwing it. That's why it's displayed like this.
You could just have to throw the exception without the try/catch:
$check_api_key = $attendance_libraries->check_api_key($this->request);
if ($check_api_key == null) {
throw new NullException(false, 'API Key Not Found', null, 500);
}
The laravel error handler will catch the exception & render it.
EDIT: Or as @miken32 pointed out you could re throw the exception to handle other exceptions:
try {
//...
} catch (NullException $e) {
throw $e;
} catch (// other exceptions) {
}
C++: How to pass parameters to custom exceptions?
You have correctly passed the parameters to the constructor of your exception class.
But, the function MyEmptyImageException::what()
is incorrect because it returns msg.c_str()
, where msg
is allocated on the stack. When the function what()
returns, them msg
object is destroyed, and the char*
pointer points to the buffer managed by a deallocated object. To fix that, you can construct the message in the constructor of MyEmptyImageException
itself:
class MyEmptyImageException : public MyIOException
{
std::string m_msg;
public:
MyEmptyImageException(const std::string& bn, const std::string& on)
: m_msg(std::string("Empty Image : ") + bn + on)
{}
virtual const char* what() const throw()
{
return m_msg.c_str();
}
};
Manually raising (throwing) an exception in Python
How do I manually throw/raise an exception in Python?
Use the most specific Exception constructor that semantically fits your issue.
Be specific in your message, e.g.:
raise ValueError('A very specific bad thing happened.')
Don't raise generic exceptions
Avoid raising a generic Exception
. To catch it, you'll have to catch all other more specific exceptions that subclass it.
Problem 1: Hiding bugs
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
For example:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
Problem 2: Won't catch
And more specific catches won't catch the general exception:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
Best Practices: raise
statement
Instead, use the most specific Exception constructor that semantically fits your issue.
raise ValueError('A very specific bad thing happened')
which also handily allows an arbitrary number of arguments to be passed to the constructor:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
These arguments are accessed by the args
attribute on the Exception
object. For example:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
prints
('message', 'foo', 'bar', 'baz')
In Python 2.5, an actual message
attribute was added to BaseException
in favor of encouraging users to subclass Exceptions and stop using args
, but the introduction of message
and the original deprecation of args has been retracted.
Best Practices: except
clause
When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. The best way to do this while preserving the stack trace is to use a bare raise statement. For example:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Don't modify your errors... but if you insist.
You can preserve the stacktrace (and error value) with sys.exc_info()
, but this is way more error prone and has compatibility problems between Python 2 and 3, prefer to use a bare raise
to re-raise.
To explain - the sys.exc_info()
returns the type, value, and traceback.
type, value, traceback = sys.exc_info()
This is the syntax in Python 2 - note this is not compatible with Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
If you want to, you can modify what happens with your new raise - e.g. setting new args
for the instance:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
And we have preserved the whole traceback while modifying the args. Note that this is not a best practice and it is invalid syntax in Python 3 (making keeping compatibility much harder to work around).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
In Python 3:
raise error.with_traceback(sys.exc_info()[2])
Again: avoid manually manipulating tracebacks. It's less efficient and more error prone. And if you're using threading and sys.exc_info
you may even get the wrong traceback (especially if you're using exception handling for control flow - which I'd personally tend to avoid.)
Python 3, Exception chaining
In Python 3, you can chain Exceptions, which preserve tracebacks:
raise RuntimeError('specific message') from error
Be aware:
- this does allow changing the error type raised, and
- this is not compatible with Python 2.
Deprecated Methods:
These can easily hide and even get into production code. You want to raise an exception, and doing them will raise an exception, but not the one intended!
Valid in Python 2, but not in Python 3 is the following:
raise ValueError, 'message' # Don't do this, it's deprecated!
Only valid in much older versions of Python (2.4 and lower), you may still see people raising strings:
raise 'message' # really really wrong. don't do this.
In all modern versions, this will actually raise a TypeError
, because you're not raising a BaseException
type. If you're not checking for the right exception and don't have a reviewer that's aware of the issue, it could get into production.
Example Usage
I raise Exceptions to warn consumers of my API if they're using it incorrectly:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
Create your own error types when apropos
"I want to make an error on purpose, so that it would go into the except"
You can create your own error types, if you want to indicate something specific is wrong with your application, just subclass the appropriate point in the exception hierarchy:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
and usage:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
How do I raise the same Exception with a custom message in Python?
Update: For Python 3, check Ben's answer
To attach a message to the current exception and re-raise it:
(the outer try/except is just to show the effect)
For python 2.x where x>=6:
try:
try:
raise ValueError # something bad...
except ValueError as err:
err.message=err.message+" hello"
raise # re-raise current exception
except ValueError as e:
print(" got error of type "+ str(type(e))+" with message " +e.message)
This will also do the right thing if err
is derived from ValueError
. For example UnicodeDecodeError
.
Note that you can add whatever you like to err
. For example err.problematic_array=[1,2,3]
.
Edit: @Ducan points in a comment the above does not work with python 3 since .message
is not a member of ValueError
. Instead you could use this (valid python 2.6 or later or 3.x):
try:
try:
raise ValueError
except ValueError as err:
if not err.args:
err.args=('',)
err.args = err.args + ("hello",)
raise
except ValueError as e:
print(" error was "+ str(type(e))+str(e.args))
Edit2:
Depending on what the purpose is, you can also opt for adding the extra information under your own variable name. For both python2 and python3:
try:
try:
raise ValueError
except ValueError as err:
err.extra_info = "hello"
raise
except ValueError as e:
print(" error was "+ str(type(e))+str(e))
if 'extra_info' in dir(e):
print e.extra_info
Related Topics
How to Run Ruby on Rails with Ruby 1.9X
Optimization for Finding Perfect-Square Algorithm
File.Open, Open and Io.Foreach in Ruby, What Is the Difference
Measure and Benchmark Time for Ruby Methods
Ruby: Most Concise Way to Use an Env Variable If It Exists, Otherwise Use Default Value
Accessing Config from Application.Rb in Controller (Rails 3)
How to Change Hash Keys from 'Symbol's to 'String'S
Deploying Sinatra App (With Config.Ru) on Heroku Cedar Stack
Ruby on Rails Gem for Google Map Integration
Invalid Multibyte Char (Utf-8) Error, Ruby
Open_Id_Authentication - "Openidauthentication.Store Is Nil. Using In-Memory Store." Problem
Will Uuid as Primary Key in Postgresql Give Bad Index Performance
Rails 4: How to Reset Test Database
When We Import CSV Data, How Eliminate "Invalid Byte Sequence in Utf-8"
Ruby's <=> Operator and Sort Method