Raise Custom Exception with Arguments

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?

  1. accept that the value passed to FooError#initialize may be either foo or a message.

  2. Don't use case A style. If you're not passing foo, write raise FooError.new(), 'argh'

  3. 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) {
}

Sample Image

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



Leave a reply



Submit