-
-
Notifications
You must be signed in to change notification settings - Fork 906
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"raise exc from None" interferes with exception chaining #1114
Comments
Here is some more context my use case. Let's say I have a template containing this:
Now, let's say that |
When using |
Proposed changes to WebSocketEndpoint...
async def dispatch(self) -> None:
websocket = WebSocket(self.scope, receive=self.receive, send=self.send)
await self.on_connect(websocket)
close_code = status.WS_1000_NORMAL_CLOSURE
try:
while True:
message = await websocket.receive()
if message["type"] == "websocket.receive":
data = await self.decode(websocket, message)
await self.on_receive(websocket, data)
elif message["type"] == "websocket.disconnect":
close_code = int(message.get("code", status.WS_1000_NORMAL_CLOSURE))
break
except Exception as exc:
close_code = status.WS_1011_INTERNAL_ERROR
# raise exc from None
# call self.on_exception(...) rather than raising Exception immediately
await self.on_exception(websocket, exc)
finally:
await self.on_disconnect(websocket, close_code)
async def decode(self, websocket: WebSocket, message: Message) -> typing.Any:
if self.encoding == "text":
if "text" not in message:
await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA)
# raise RuntimeError("Expected text websocket messages, but got bytes")
# call self.on_exception(...) rather than raising Exception immediately
await self.on_exception(websocket, RuntimeError("Expected text websocket messages, but got bytes"))
return message["text"]
elif self.encoding == "bytes":
if "bytes" not in message:
await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA)
# raise RuntimeError("Expected bytes websocket messages, but got text")
# call self.on_exception(...) rather than raising Exception immediately
await self.on_exception(websocket, RuntimeError("Expected bytes websocket messages, but got text"))
return message["bytes"]
elif self.encoding == "json":
if message.get("text") is not None:
text = message["text"]
else:
text = message["bytes"].decode("utf-8")
try:
return json.loads(text)
except json.decoder.JSONDecodeError:
await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA)
# raise RuntimeError("Malformed JSON data received.")
# call self.on_exception(...) rather than raising Exception immediately
await self.on_exception(websocket, RuntimeError("Malformed JSON data received."))
assert (
self.encoding is None
), f"Unsupported 'encoding' attribute {self.encoding}"
return message["text"] if message.get("text") else message["bytes"]
...
# New overridable method to handle exceptions internally/by user
async def on_exception(self, websocket: WebSocket, exception: Exception) -> None:
"""Override to handle an error in websocket connection"""
raise exception my_app.py (Additional Method)import logging
from starlette.endpoints import WebSocketEndpoint
logger = logging.getLogger("my_app")
class MessagesEndpoint(WebSocketEndpoint):
async def on_connect(self, websocket):
await websocket.accept()
async def on_receive(self, websocket, data):
pass
async def on_disconnect(self, websocket, close_code):
pass
# Override `on_exception` method to handling Exception properly
async def on_exception(self, websocket, exception):
logger.error("Exception caught")
|
Another fix for the issue would be to remove the
|
It's possible that there might be a good reason for one or more of those cases to be a |
This required bumping the Starlette version because of this issue: encode/starlette#1114
Checklist
master
.Describe the bug
Even though I use exception chaining (
raise ... from
) in my app, the original exception info gets deleted because Starlette handles exceptions withraise .. from None
at the outer scope.Here is a MWE:
Run the server, load
/
, then look at the console output.What I expect to get is a chained exception:
What I get is only the final exception:
Starlette uses
raise exc from None
in 4 places:ExceptionMiddleware.__call__
ServerErrorMiddleware.__call__
WebSocketEndpoint.dispatch
_ASGIAdapter.send
It seems to me that in all 4 cases the
from None
could hide intentional context about users' errors.Having the chained traceback would be very valuable both for reading the console output and for programmatic use
(I want to make a middleware that looks at the
.__cause__
attribute of certain exceptions to deliver a more relevant error message). I'm happy to provide more details on my use case.The text was updated successfully, but these errors were encountered: