from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.responses import HTMLResponse from starlette.routing import NoMatchFound from otree import errorpage from otree.database import save_sqlite_db from . import middleware from . import settings from .errorpage import OTreeServerErrorMiddleware from .patch import ExceptionMiddleware from .urls import routes class OTreeStarlette(Starlette): def build_middleware_stack(self): debug = self.debug error_handler = None exception_handlers = {} for key, value in self.exception_handlers.items(): if key in (500, Exception): error_handler = value else: exception_handlers[key] = value # By default Starlette puts ServerErrorMiddleware outside of all user middleware, # but I need to reverse that, because if we roll back the transaction before the error page # is displayed, it will show incorrect field values for a model instance's __repr__. middlewares = [ Middleware(middleware.CommitTransactionMiddleware), Middleware(OTreeServerErrorMiddleware, handler=error_handler, debug=debug), Middleware(middleware.PerfMiddleware), Middleware(middleware.SessionMiddleware, secret_key=middleware._SECRET), Middleware(ExceptionMiddleware, handlers=exception_handlers, debug=debug), ] app = self.router for cls, options in reversed(middlewares): app = cls(app=app, **options) return app ERR_500 = 500 async def server_error(request, exc): return HTMLResponse(content=HTML_500_PAGE, status_code=ERR_500) app = OTreeStarlette( debug=settings.DEBUG, routes=routes, exception_handlers={ERR_500: server_error}, on_shutdown=[save_sqlite_db], ) # alias like django reverse() def reverse(name, **path_params): try: return app.url_path_for(name, **path_params) except NoMatchFound as exc: raise NoMatchFound(f'{name}, {path_params}') from None ERR_500_EXPLANATION = """

For security reasons, the error is not displayed here. You can view it with one of the below techniques:

""" # 500 page should look like the debug 500 page so that people make the connection HTML_500_PAGE = errorpage.TEMPLATE.format( styles=errorpage.STYLES, otree_styles=errorpage.OTREE_STYLES, tab_title="Application error (500)", error="", ibis_html='', exc_html=ERR_500_EXPLANATION, js='', )