def error_handler(exception: Exception) -> Response: LOGGER.exception(exception) http_exception = InternalServerError(str(exception)) http_exception.__cause__ = exception return _handle_http_error(http_exception)
async def fetch_all_user_pages(ctx: HTTPRequestContext, battletag: str, *, platform="pc"): """ Fetches all user pages for a specified user. Returns a dictionary in the format of `{"any": etree._Element | None}`. """ if platform != "pc": coro = get_user_page(ctx, battletag, platform=platform, cache_404=True) result = await coro if isinstance(result, etree._Element): return {"any": result} else: # Raise a 404. raise NotFound() futures = [] coro = get_user_page(ctx, battletag, platform=platform, cache_404=True) futures.append(coro) # Gather all the futures to download in parallel. results = await asyncio.gather(*futures, return_exceptions=True) user_data = {"any": None} error = None for key, result in zip(["any"], results): # Make sure it's either a None or an element. if isinstance(result, etree._Element): user_data[key] = result elif isinstance(result, Exception): logger.error("Failed to fetch user page!\n{}".format( ''.join(traceback.format_exception(type(result), result, result.__traceback__)) )) error = result user_data[key] = None else: user_data[key] = None # Check if we should raise or return. if user_data["any"] is None: if error is not None: e = InternalServerError() e.__cause__ = error e.__context__ = error raise e raise NotFound() return user_data
async def fetch_all_user_pages(ctx: HTTPRequestContext, battletag: str, *, platform="pc"): """ Fetches all user pages for a specified user. Returns a dictionary in the format of `{region: etree._Element | None}`. """ if platform != "pc": coro = get_user_page(ctx, battletag, region="", platform=platform, cache_404=True) result = await coro if isinstance(result, etree._Element): return {"any": result, "eu": None, "us": None, "kr": None} else: # Raise a 404. raise NotFound() futures = [] for region in AVAILABLE_REGIONS: # Add the get_user_page coroutine. coro = get_user_page(ctx, battletag, region=region, platform=platform, cache_404=True) futures.append(coro) # Gather all the futures to download paralellely. results = await asyncio.gather(*futures, return_exceptions=True) d = {"any": None} error = None for region, result in zip(AVAILABLE_REGIONS, results): # Remove the `/` from the front of the region. # This is used internally to make building the URL to get simpler. region = region[1:] # Make sure it's either a None or an element. if isinstance(result, etree._Element): d[region] = result elif isinstance(result, Exception): logger.error("Failed to fetch user page!\n{}".format(''.join( traceback.format_exception(type(result), result, result.__traceback__)))) error = result d[region] = None else: d[region] = None # Check if we should raise or return. if not any(d[i[1:]] is not None for i in AVAILABLE_REGIONS): if error is not None: e = InternalServerError() e.__cause__ = error e.__context__ = error raise e raise NotFound() return d
async def process_request(self, request: Request, parent_context: Context) -> Response: """ Processes a Request and returns a Response object. This is the main processing method of Kyoukai, and is meant to be used by one of the HTTP server backends, and not by client code. :param request: \ The :class:`werkzeug.wrappers.Request` object to process. A new :class:`~.HTTPRequestContext` will be provided to wrap this request inside of \ to client code. :param parent_context: \ The :class:`asphalt.core.Context` that is the parent context for this particular app. It will be used as the parent for the HTTPRequestContext. :return: A :class:`werkzeug.wrappers.Response` object that can be written to the client \ as a response. """ if not self.root.finalized: raise RuntimeError("App was not finalized") # Create a new HTTPRequestContext. ctx = self.context_class(parent_context, request) ctx.app = self async with ctx: # Call match on our Blueprint to find the request. try: matched, params, rule = self.root.match(request.environ) ctx.rule = rule except NotFound as e: # No route matched. self.log_route(ctx.request, 404) logger.debug("Could not resolve route for {request.path}." .format(request=request)) return await self.handle_httpexception(ctx, e, request.environ) except MethodNotAllowed as e: # 405 method not allowed self.log_route(ctx.request, 405) logger.debug("Could not resolve valid method for " "{request.path} ({request.method})".format(request=request)) return await self.handle_httpexception(ctx, e, request.environ) except RequestRedirect as e: # slashes etc # user code is not allowed to handle this self.log_route(ctx.request, 301) return e.get_response(request.environ) else: ctx.route_matched.dispatch(ctx=ctx) ctx.route = matched ctx.bp = ctx.route.bp ctx.route_args = params result = None # Invoke the route. try: ctx.route_invoked.dispatch(ctx=ctx) # INTERCEPT if ctx.request.method.upper() == "OPTIONS": # NO USER CODE HERE HEHEHEHEHE # instead, we need to return an Allow: header # kyoukai autocalcs this result = Response(status=200) result.headers["Allow"] = ",".join(x for x in ctx.rule.methods if x != "OPTIONS") else: result = await matched.invoke(ctx, params=params) except BadRequestKeyError as e: logger.info("BadRequestKeyError: {}".format(' '.join(e.args)), exc_info=True) result = await self.handle_httpexception(ctx, e, request.environ) except HTTPException as e: fmtted = traceback.format_exception(type(e), e, e.__traceback__) logger.debug(''.join(fmtted)) logger.info( "Hit HTTPException ({}) inside function, delegating.".format(str(e)) ) result = await self.handle_httpexception(ctx, e, request.environ) except Exception as e: logger.exception("Unhandled exception in route function") new_e = InternalServerError() new_e.__cause__ = e result = await self.handle_httpexception(ctx, new_e, request.environ) else: ctx.route_completed.dispatch(ctx=ctx, result=result) finally: # result = wrap_response(result, self.response_class) if result: # edge cases self.log_route(ctx.request, result.status_code) # Update the Server header. result.headers["Server"] = "Kyoukai/{}".format(__version__) # list means wsgi response probably if not isinstance(result.response, (bytes, str, list)): result.set_data(str(result.response)) result.headers["X-Powered-By"] = "Kyoukai/{}".format(__version__) # Return the new Response. return result