X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/4da4cbfe27a576ae011430b2de205c74435e241b..e1dc4379e0cd100200ac53752442143ca4846fc5:/src/nominatim_api/server/falcon/server.py?ds=sidebyside diff --git a/src/nominatim_api/server/falcon/server.py b/src/nominatim_api/server/falcon/server.py index bc9850b2..b58c1cfa 100644 --- a/src/nominatim_api/server/falcon/server.py +++ b/src/nominatim_api/server/falcon/server.py @@ -7,7 +7,7 @@ """ Server implementation using the falcon webserver framework. """ -from typing import Optional, Mapping, cast, Any, List +from typing import Optional, Mapping, Any, List from pathlib import Path import datetime as dt import asyncio @@ -17,7 +17,10 @@ from falcon.asgi import App, Request, Response from ...config import Configuration from ...core import NominatimAPIAsync from ... import v1 as api_impl +from ...result_formatting import FormatDispatcher, load_format_dispatcher from ... import logging as loglib +from ..asgi_adaptor import ASGIAdaptor, EndpointFunc + class HTTPNominatimError(Exception): """ A special exception class for errors raised during processing. @@ -28,7 +31,7 @@ class HTTPNominatimError(Exception): self.content_type = content_type -async def nominatim_error_handler(req: Request, resp: Response, #pylint: disable=unused-argument +async def nominatim_error_handler(req: Request, resp: Response, exception: HTTPNominatimError, _: Any) -> None: """ Special error handler that passes message and content type as @@ -39,8 +42,8 @@ async def nominatim_error_handler(req: Request, resp: Response, #pylint: disable resp.content_type = exception.content_type -async def timeout_error_handler(req: Request, resp: Response, #pylint: disable=unused-argument - exception: TimeoutError, #pylint: disable=unused-argument +async def timeout_error_handler(req: Request, resp: Response, + exception: TimeoutError, _: Any) -> None: """ Special error handler that passes message and content type as per exception info. @@ -57,57 +60,58 @@ async def timeout_error_handler(req: Request, resp: Response, #pylint: disable=u resp.content_type = 'text/plain; charset=utf-8' -class ParamWrapper(api_impl.ASGIAdaptor): +class ParamWrapper(ASGIAdaptor): """ Adaptor class for server glue to Falcon framework. """ def __init__(self, req: Request, resp: Response, - config: Configuration) -> None: + config: Configuration, formatter: FormatDispatcher) -> None: self.request = req self.response = resp self._config = config - + self._formatter = formatter def get(self, name: str, default: Optional[str] = None) -> Optional[str]: - return cast(Optional[str], self.request.get_param(name, default=default)) - + return self.request.get_param(name, default=default) def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]: - return cast(Optional[str], self.request.get_header(name, default=default)) - + return self.request.get_header(name, default=default) def error(self, msg: str, status: int = 400) -> HTTPNominatimError: return HTTPNominatimError(msg, status, self.content_type) - def create_response(self, status: int, output: str, num_results: int) -> None: self.response.context.num_results = num_results self.response.status = status self.response.text = output self.response.content_type = self.content_type - def base_uri(self) -> str: - return cast (str, self.request.forwarded_prefix) + return self.request.forwarded_prefix def config(self) -> Configuration: return self._config + def formatting(self) -> FormatDispatcher: + return self._formatter + class EndpointWrapper: """ Converter for server glue endpoint functions to Falcon request handlers. """ - def __init__(self, name: str, func: api_impl.EndpointFunc, api: NominatimAPIAsync) -> None: + def __init__(self, name: str, func: EndpointFunc, api: NominatimAPIAsync, + formatter: FormatDispatcher) -> None: self.name = name self.func = func self.api = api - + self.formatter = formatter async def on_get(self, req: Request, resp: Response) -> None: """ Implementation of the endpoint. """ - await self.func(self.api, ParamWrapper(req, resp, self.api.config)) + await self.func(self.api, ParamWrapper(req, resp, self.api.config, + self.formatter)) class FileLoggingMiddleware: @@ -115,15 +119,13 @@ class FileLoggingMiddleware: """ def __init__(self, file_name: str): - self.fd = open(file_name, 'a', buffering=1, encoding='utf8') # pylint: disable=R1732 - + self.fd = open(file_name, 'a', buffering=1, encoding='utf8') async def process_request(self, req: Request, _: Response) -> None: """ Callback before the request starts timing. """ req.context.start = dt.datetime.now(tz=dt.timezone.utc) - async def process_response(self, req: Request, resp: Response, resource: Optional[EndpointWrapper], req_succeeded: bool) -> None: @@ -131,7 +133,7 @@ class FileLoggingMiddleware: writes logs for successful requests for search, reverse and lookup. """ if not req_succeeded or resource is None or resp.status != 200\ - or resource.name not in ('reverse', 'search', 'lookup', 'details'): + or resource.name not in ('reverse', 'search', 'lookup', 'details'): return finish = dt.datetime.now(tz=dt.timezone.utc) @@ -174,11 +176,12 @@ def get_application(project_dir: Path, app.add_error_handler(HTTPNominatimError, nominatim_error_handler) app.add_error_handler(TimeoutError, timeout_error_handler) # different from TimeoutError in Python <= 3.10 - app.add_error_handler(asyncio.TimeoutError, timeout_error_handler) + app.add_error_handler(asyncio.TimeoutError, timeout_error_handler) # type: ignore[arg-type] legacy_urls = api.config.get_bool('SERVE_LEGACY_URLS') + formatter = load_format_dispatcher('v1', project_dir) for name, func in api_impl.ROUTES: - endpoint = EndpointWrapper(name, func, api) + endpoint = EndpointWrapper(name, func, api, formatter) app.add_route(f"/{name}", endpoint) if legacy_urls: app.add_route(f"/{name}.php", endpoint)