alex-oleshkevich / starception

Beautiful exception page for Starlette apps.
MIT License
96 stars 6 forks source link

AttributeError: 'LoopContext' object has no attribute '_iterator' #2

Closed euri10 closed 1 year ago

euri10 commented 1 year ago

greetings, wanted to try this with an asyncpg error I was debugging, the below raise an exception and I have the classical Starlette template outputing fine,

backend-1  | ERROR:    Exception in ASGI application
backend-1  | Traceback (most recent call last):
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
backend-1  |     result = await app(self.scope, self.receive, self.send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
backend-1  |     return await self.app(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/message_logger.py", line 86, in __call__
backend-1  |     raise exc from None
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/message_logger.py", line 82, in __call__
backend-1  |     await self.app(scope, inner_receive, inner_send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/applications.py", line 269, in __call__
backend-1  |     await super().__call__(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__
backend-1  |     await self.middleware_stack(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
backend-1  |     raise exc
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
backend-1  |     await self.app(scope, receive, _send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starsessions/middleware.py", line 86, in __call__
backend-1  |     await self.app(scope, receive, send_wrapper)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/exceptions.py", line 93, in __call__
backend-1  |     raise exc
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/exceptions.py", line 82, in __call__
backend-1  |     await self.app(scope, receive, sender)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
backend-1  |     raise e
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
backend-1  |     await self.app(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/routing.py", line 670, in __call__
backend-1  |     await route.handle(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/routing.py", line 266, in handle
backend-1  |     await self.app(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/routing.py", line 65, in app
backend-1  |     response = await func(request)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/routing.py", line 227, in app
backend-1  |     raw_response = await run_endpoint_function(
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/routing.py", line 160, in run_endpoint_function
backend-1  |     return await dependant.call(**values)
backend-1  |   File "/opt/project/./abdul/routers/cart_router.py", line 110, in cart_get
backend-1  |     products, total_usd_decimal, total_xmr_decimal, order_now = await calculate_prices(
backend-1  |   File "/opt/project/./abdul/routers/cart_router.py", line 54, in calculate_prices
backend-1  |     xmr_price = await conn.fetchval_b(query_xmr_price, order_now=order_now)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/buildpg/asyncpg.py", line 69, in fetchval_b
backend-1  |     return await self.fetchval(query, *args, timeout=_timeout, column=_column)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/asyncpg/connection.py", line 645, in fetchval
backend-1  |     data = await self._execute(query, args, 1, timeout)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/asyncpg/connection.py", line 1659, in _execute
backend-1  |     result, _ = await self.__execute(
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/asyncpg/connection.py", line 1684, in __execute
backend-1  |     return await self._do_execute(
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/asyncpg/connection.py", line 1711, in _do_execute
backend-1  |     stmt = await self._get_statement(
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/asyncpg/connection.py", line 398, in _get_statement
backend-1  |     statement = await self._protocol.prepare(
backend-1  |   File "asyncpg/protocol/protocol.pyx", line 168, in prepare
backend-1  | asyncpg.exceptions.UndefinedFunctionError: operator does not exist: timestamp without time zone > interval
backend-1  | HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.

but as soon as I add the middleware with app.add_middleware(StarceptionMiddleware, debug=True) I get no template and a Internal Server Error 500 page, and this error, not sure the issue is here or in Starlette itself though

backend-1  | ERROR:    Exception in ASGI application
backend-1  | Traceback (most recent call last):
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
backend-1  |     result = await app(self.scope, self.receive, self.send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
backend-1  |     return await self.app(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/message_logger.py", line 86, in __call__
backend-1  |     raise exc from None
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/uvicorn/middleware/message_logger.py", line 82, in __call__
backend-1  |     await self.app(scope, inner_receive, inner_send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/fastapi/applications.py", line 269, in __call__
backend-1  |     await super().__call__(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__
backend-1  |     await self.middleware_stack(scope, receive, send)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 167, in __call__
backend-1  |     response = self.debug_response(request, exc)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 248, in debug_response
backend-1  |     content = self.generate_html(exc)
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 220, in generate_html
backend-1  |     traceback_obj = traceback.TracebackException.from_exception(
backend-1  |   File "/usr/local/lib/python3.10/traceback.py", line 572, in from_exception
backend-1  |     return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)
backend-1  |   File "/usr/local/lib/python3.10/traceback.py", line 502, in __init__
backend-1  |     self.stack = StackSummary.extract(
backend-1  |   File "/usr/local/lib/python3.10/traceback.py", line 376, in extract
backend-1  |     result.append(FrameSummary(
backend-1  |   File "/usr/local/lib/python3.10/traceback.py", line 276, in __init__
backend-1  |     self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
backend-1  |   File "/usr/local/lib/python3.10/traceback.py", line 276, in <dictcomp>
backend-1  |     self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/jinja2/runtime.py", line 581, in __repr__
backend-1  |     return f"<{type(self).__name__} {self.index}/{self.length}>"
backend-1  |   File "/opt/venv/lib/python3.10/site-packages/jinja2/runtime.py", line 440, in length
backend-1  |     iterable = list(self._iterator)
backend-1  | AttributeError: 'LoopContext' object has no attribute '_iterator'
backend-1  | INFO:     172.20.0.1:60112 - "GET /cart/ HTTP/1.1" 500
euri10 commented 1 year ago

if it helps and I'm correct debugging this, it seems to fail rendering the template with the last frame FrameInfo(frame=<frame at 0x55caef072920, file 'asyncpg/protocol/protocol.pyx', line 168, code prepare>, filename='asyncpg/protocol/protocol.pyx', lineno=168, function='prepare', code_context=None, index=None) it is as if in code_snippet.html here {% for line in frame.code_context|default([]) %} was not doing its job defaulting to an empty list

alex-oleshkevich commented 1 year ago

Can you share a code snippet to reproduce? Thanks

alex-oleshkevich commented 1 year ago

Okay, seems I found the error. Can you change that line to {% for line in frame.code_context|default([], true) %} and try again? It got that error fixed.

alex-oleshkevich commented 1 year ago

Try 0.2.0. Please see the README, the middleware was removed in favor of exception handler.

euri10 commented 1 year ago

Try 0.2.0. Please see the README, the middleware was removed in favor of exception handler.

I tried hard but cant run the exception handler now with that new api, let me expain, I'm using a probably weird setup but here it is with a simplified version.

I run uvicorn mm:app --reload

then mm.py contains:

from ma import get_app

app = get_app()

and ma.py contains

from fastapi import FastAPI
from starception import exception_handler
from starlette.requests import Request
from starlette.responses import Response

def get_app() -> FastAPI:
    app = FastAPI( debug=True)

#    def custom_exception_handler(request: Request, exc: Exception) -> Response:
#        return exception_handler(request, exc, debug=True)
#    app.add_exception_handler(Exception, custom_exception_handler)

    @app.exception_handler(Exception)
    def custom_exception_handler(request: Request, exc: Exception) -> Response:
        return exception_handler(request, exc, debug=True)

    from r import router as r
    app.include_router(r)
    return app

finally r.py contains

from fastapi import APIRouter
from starlette.requests import Request

router = APIRouter()
@router.get("/")
async def index(request: Request):
    raise TypeError("toto")

technically I could also run uvicorn ma:get_app --factory --reload

The issue is that the starception template is not used, I see the Starlette one. I'm not sure where it happens that I cannot get to it, without that factory the starception template is well displayed, if you have an idea I'm all ears ))

alex-oleshkevich commented 1 year ago

Okay, I see the problem. Starlette does not call custom error handlers when debug=True instead, it always uses default.

alex-oleshkevich commented 1 year ago

Published 0.3. Consult docs (you need to use middleware again until).

alex-oleshkevich commented 1 year ago

Upstream issue https://github.com/encode/starlette/issues/1802

euri10 commented 1 year ago

ok I think 0.3.0 fixed the asyncpg error frame not having a code_context however I have another problem, but I think I'll open another issue, it is because Im using starsessions, hey you write good stuff so I have to :) and there is sonehting in the template messing up with it, in {{ lib.details_row('Session', session) }}

I'll post a minimal example in another issue tomorrow, as for this one it's tested and fixed, thanks for the amazing look on the debug page !!