I'm implementing a web socket server with tornado (currently version 3.1).
Inside the open() function I check GET argument, then based on it - I want to raise an error.
Something like this:
def open(self):
token = self.get_argument('token')
if ...:
??? # raise an error
How do I raise an error inside the open function? I did not find the way to do this.
Thanks
You can just raise an exception like you normally would:
class EchoWebSocket(websocket.WebSocketHandler):
def open(self):
if some_error:
raise Exception("Some error occurred")
Tornado will abort the connection when an unhandled exception occurs in open. Here's how open is scheduled to run in the tornado source:
self._run_callback(self.handler.open, *self.handler.open_args,
**self.handler.open_kwargs)
Here is _run_callback:
def _run_callback(self, callback, *args, **kwargs):
"""Runs the given callback with exception handling.
On error, aborts the websocket connection and returns False.
"""
try:
callback(*args, **kwargs)
except Exception:
app_log.error("Uncaught exception in %s",
self.request.path, exc_info=True)
self._abort()
def _abort(self):
"""Instantly aborts the WebSocket connection by closing the socket"""
self.client_terminated = True
self.server_terminated = True
self.stream.close() # forcibly tear down the connection
self.close() # let the subclass cleanup
As you can see, it aborts the connection when an exception occurs.
Related
So I have a Sinatra API containing this piece of code in a model:
def self.delete(account_id)
# using Sequel, not ActiveRecord:
if Account[account_id][:default] == true
abort("Impossible to delete a default account. Please first set another account to default.")
end
# rest of the code
end
then, in app.rb :
delete '/account/:id' do
if Account.delete(params[:id]) == 1
status 200
else
status 500
end
end
On the client side (vuejs app), I would like the error message to be displayed. Instead, when the error produces, I get a SystemExit with the error message.
How do I send that SystemExit message to the server?
In Ruby in general you want to break out of execution either by using return, exceptions or throw/catch. abort is rarely if ever used as it will immediately halt execution of the entire program - and prevent you from doing stuff like cleaning up or actually sending a meaningful response to the client besides whatever error page the web server will render if you just quit the job half way though.
You can easily implement this by creating your own exception class:
class AccountDeletionError < StandardError
end
def self.delete(account_id)
# using Sequel, not ActiveRecord:
if Account[account_id][:default] == true
raise AccountDeletionError.new, "Impossible to delete a default account. Please first set another account to default."
end
end
delete '/account/:id' do
if Account.delete(params[:id]) == 1
status 200
end
rescue AccountDeletionError => e
status 409 # not 500.
content_type :json
{ error: e.message }.to_json
end
end
Now that you know how to handle errors you should probally address the next possible one - when an account cannot be found.
I have a situation where on certain exception case, I should continue catch the error and continue the execution of the code. Currently my code looks like this:
s = TestSerializer(...)
if s.is_valid():
instance = s.save()
perform_some_query() # Django yells here
class TestSerializer(serializers.ModelSerializer):
def create(self, request, *args, **kwargs):
try:
return super().create(request, *args, **kwargs)
except SomeError as err:
if this_is_the_super_exotic_case:
return None # Don't shout, just continue silently and don't create an object
raise
The problem is that Django yells at me when executing perform_some_query() (see line 3 of the code snippet), saying that:
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block. What is the reason for this?
Is it possible to raise an exception if any, when calling an API, then exit the method in which exception happened and continue normally calling other methods? For example in case like this:
Class1.first_method
Class2.second_method -> failed because of an API error
Clas3.third_method - I want this one to continue
Class1.first_method
begin
Class2.second_method
rescue StandardError => e
$stderr << "An error occurred: #{e.message}"
end
Clas3.third_method
This is an example of a rescue block, which is part of the way ruby permits Exception handling
I am trying to improve my Ruby skills by catching exceptions. I want to know if it is common to reraise the same kind of exception when you have several method calls. So, would the following code make sense? Is it ok to reraise the same kind of exception, or should I not catch it on the process method?
class Logo
def process
begin
#processed_logo = LogoProcessor::create_image(self.src)
rescue CustomException
raise CustomException
end
end
end
module LogoProcessor
def self.create_image
raise CustomException if some_condition
end
end
Sometimes we just want to know an error happened, without having to actually handle the error.
It is often the case that the one responsible for handling errors is user of the object: the caller. What if we are interested in the error, but don't want to assume that responsibility? We rescue the error, do whatever we need to do and then propagate the signal up the stack as if nothing had happened.
For example, what if we wanted to log the error message and then let the caller deal with it?
begin
this_will_fail!
rescue Failure => error
log.error error.message
raise
end
Calling raise without any arguments will raise the last error. In our case, we are re-raising error.
In the example you presented in your question, re-raising the error is simply not necessary. You could simply let it propagate up the stack naturally. The only difference in your example is you're creating a new error object and raising it instead of re-raising the last one.
This will raise the same type of error as the original, but you can customize the message.
rescue StandardError => e
raise e.class, "Message: #{e.message}"
I had the same question as in the comment thread here, i.e. What if the line before (re)raise fails?
My understanding was limited by the missing knowledge that the global variable of $! is "kinda garbage collected" // "scoped to its functional context", which the below example demonstrates:
def func
begin
raise StandardError, 'func!'
rescue StandardError => err
puts "$! = #{$!.inspect}"
end
end
begin
raise StandardError, 'oh no!'
rescue StandardError => err
func
puts "$! = #{$!.inspect}"
raise
end
The output of the above is:
$! = #<StandardError: func!>
$! = #<StandardError: oh no!>
StandardError: oh no!
from (pry):47:in `__pry__'
This behavior is different than how Python's (re)raise works.
The documentation for Exception states:
When an exception has been raised but not yet handled (in rescue,
ensure, at_exit and END blocks), two global variables are set:
$! contains the current exception.
$# contains its backtrace.
So these variables aren't true global variables, they are only defined inside the block that's handling the error.
begin
raise
rescue
p $! # StandardError
end
p $! # nil
A slightly better way to do the same thing as FreePender is to use the exception method from the Exception class, which is the ancestor class to any error classes, like StandardError, so that the method is available to any error classes.
Here the method's documentation that you can find on ApiDock:
With no argument, or if the argument is the same as the receiver, return the receiver. Otherwise, create a new exception object of the same class as the receiver, but with a message equal to string.to_str.
Now let's see how it works:
begin
this_will_fail!
rescue Failure => error
raise error.exception("Message: #{error.message}")
end
Adding to above answers here:
In some applications you may need to log the error twice.
For example exception need to be notified to monitoring tools like
Nagios/Newrelic/Cloudwatch.
At the same time you may have your own kibana backed summary logging
tool, internally for your reference.
In those cases you might want to log & handle the errors multiple times.
Example:
begin
begin
nil.to_sym
rescue => e
puts "inner block error message: #{e.message}"
puts "inner block backtrace: #{e.backtrace.join("\n")}"
raise e
end
rescue => e
puts "outer block error message: #{e.message}"
puts "outer block backtrace: #{e.backtrace.join("\n")}"
end
I am using puts here, for your the ease of verifying this code in
rails console, in actual production you may need to use rails logger
I have a class called RemoteError with a self.fatal method on it. This methods job is basically to catch an exception, send the details of the exception to a server and then propagate the exception in order to kill the program.
class RemoteError
def initialize(label, error)
#label = label
#error = error
end
def self.fatal(label, error)
object = new(label, error)
object.send
raise error
end
def send
# send the error to the server
end
end
I'm trying to write tests for the RemoteError.fatal method. This is difficult because of the call to raise within the method. Every time I run my tests, raise obviously raises an exception and I can't test that send was called.
describe "fatal" do
it "should send a remote error" do
error = stub
RemoteError.stub(:new) { error }
error.should_receive(:send)
RemoteError.fatal(stub, stub)
end
end
Is there a way that I can stub or somehow circumvent raise for this specific test?
You could wrap the method that raises the error in a lambda...
it "should send a remote error" do
...
lambda { RemoteError.fatal(stub, stub) }.should raise_error(error)
end
This essentially allows the method to be called, and you get the return value or raised error from it, which you then assert against with .should raise_error(error). This also makes is so that if no error is raised from that call, the test will fail normally.
To say it another way, you don't need, nor want, to stub raise. Simply wrap it in a lambda and your code will continue to execute, you should be able to make sure that your message is sent, and your test won't exit/crash.
In your tests you are testing the method that raises an error and this is an expected result of method execution. You should write expectation for exception with this syntax:
lambda{ RemoteError.fatal(stub, stub) }.should raise_error
In this case your spec will fail if exception won't be raised and also will fail if all other expectations (like should_receive(:sen)) won't be met.