add sanic development server implementation

This commit is contained in:
Sarah Hoffmann
2022-12-01 17:58:22 +01:00
parent 45c675bd78
commit 23dabad0b0
7 changed files with 98 additions and 3 deletions

View File

@@ -197,10 +197,15 @@ class AdminServe:
"""\
Start a simple web server for serving the API.
This command starts the built-in PHP webserver to serve the website
This command starts a built-in webserver to serve the website
from the current project directory. This webserver is only suitable
for testing and development. Do not use it in production setups!
There are different webservers available. The default 'php' engine
runs the classic PHP frontend. 'sanic' and 'falcon' are Python servers
which run the new Python frontend code. This is highly experimental
at the moment and may not include the full API.
By the default, the webserver can be accessed at: http://127.0.0.1:8088
"""
@@ -208,10 +213,31 @@ class AdminServe:
group = parser.add_argument_group('Server arguments')
group.add_argument('--server', default='127.0.0.1:8088',
help='The address the server will listen to.')
group.add_argument('--engine', default='php',
choices=('php', 'sanic', 'falcon'),
help='Webserver framework to run. (default: php)')
def run(self, args: NominatimArgs) -> int:
run_php_server(args.server, args.project_dir / 'website')
if args.engine == 'php':
run_php_server(args.server, args.project_dir / 'website')
elif args.engine == 'sanic':
import nominatim.server.sanic.server
app = nominatim.server.sanic.server.get_application(args.project_dir)
server_info = args.server.split(':', 1)
host = server_info[0]
if len(server_info) > 1:
if not server_info[1].isdigit():
raise UsageError('Invalid format for --server parameter. Use <host>:<port>')
port = int(server_info[1])
else:
port = 8088
app.run(host=host, port=port, debug=True)
elif args.engine == 'falcon':
raise NotImplementedError('Support for falcon not yet available.')
return 0

View File

@@ -127,6 +127,7 @@ class NominatimArgs:
# Arguments to 'serve'
server: str
engine: str
# Arguments to 'special-phrases
import_from_wiki: bool

View File

@@ -28,6 +28,12 @@ class ResultFormatter(Generic[T]):
return list(self.functions.keys())
def supports_format(self, fmt: str) -> bool:
""" Check if the given format is supported by this formatter.
"""
return fmt in self.functions
def format(self, result: T, fmt: str) -> str:
""" Convert the given result into a string using the given format.

View File

@@ -28,7 +28,7 @@ def _format_status_json(result: StatusResult) -> str:
out['status'] = result.status
out['message'] = result.message
if result.data_updated is not None:
out['data_updated'] = result.data_updated
out['data_updated'] = result.data_updated.isoformat()
out['software_version'] = result.software_version
if result.database_version is not None:
out['database_version'] = result.database_version

View File

View File

View File

@@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2022 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Server implementation using the sanic webserver framework.
"""
from pathlib import Path
import sanic
from nominatim.api import NominatimAPIAsync
from nominatim.apicmd.status import StatusResult
import nominatim.result_formatter.v1 as formatting
api = sanic.Blueprint('NominatimAPI')
CONTENT_TYPE = {
'text': 'text/plain; charset=utf-8',
'xml': 'text/xml; charset=utf-8'
}
def usage_error(msg):
return sanic.response.text(msg, status=400)
def api_response(request, result):
body = request.ctx.formatter.format(result, request.ctx.format)
return sanic.response.text(body,
content_type=CONTENT_TYPE.get(request.ctx.format, 'application/json'))
@api.on_request
async def extract_format(request):
request.ctx.formatter = request.app.ctx.formatters[request.route.ctx.result_type]
request.ctx.format = request.args.get('format', request.route.ctx.default_format)
if not request.ctx.formatter.supports_format(request.ctx.format):
return usage_error("Parameter 'format' must be one of: " +
', '.join(request.ctx.formatter.list_formats()))
@api.get('/status', ctx_result_type=StatusResult, ctx_default_format='text')
async def status(request):
return api_response(request,await request.app.ctx.api.status())
def get_application(project_dir: Path) -> sanic.Sanic:
app = sanic.Sanic("NominatimInstance")
app.ctx.api = NominatimAPIAsync(project_dir)
app.ctx.formatters = {}
for rtype in (StatusResult, ):
app.ctx.formatters[rtype] = formatting.create(rtype)
app.blueprint(api)
return app