]> git.openstreetmap.org Git - nominatim.git/commitdiff
add sanic development server implementation
authorSarah Hoffmann <lonvia@denofr.de>
Thu, 1 Dec 2022 16:58:22 +0000 (17:58 +0100)
committerSarah Hoffmann <lonvia@denofr.de>
Tue, 3 Jan 2023 09:02:53 +0000 (10:02 +0100)
nominatim/cli.py
nominatim/clicmd/args.py
nominatim/result_formatter/base.py
nominatim/result_formatter/v1.py
nominatim/server/__init__.py [new file with mode: 0644]
nominatim/server/sanic/__init__.py [new file with mode: 0644]
nominatim/server/sanic/server.py [new file with mode: 0644]

index 56ed6a078eb3a082f9430564b279b1470348c89e..c134ca1808c2a2fe7267fc26947c54931bb23ab7 100644 (file)
@@ -197,10 +197,15 @@ class AdminServe:
     """\
     Start a simple web server for serving the API.
 
     """\
     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!
 
     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
     """
 
     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 = 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:
 
 
     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
 
 
         return 0
 
 
index 15de72a5bc7c61c797b3a3fd8cd005a2cd1153f2..e47287b33dff17c7f62c335ad5dc9ed08b6236e8 100644 (file)
@@ -127,6 +127,7 @@ class NominatimArgs:
 
     # Arguments to 'serve'
     server: str
 
     # Arguments to 'serve'
     server: str
+    engine: str
 
     # Arguments to 'special-phrases
     import_from_wiki: bool
 
     # Arguments to 'special-phrases
     import_from_wiki: bool
index 88f4d9183a3ff172fbe856741d88423d65312c2f..d77f4db883d33171d152570c461050f1438cbb4b 100644 (file)
@@ -28,6 +28,12 @@ class ResultFormatter(Generic[T]):
         return list(self.functions.keys())
 
 
         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.
 
     def format(self, result: T, fmt: str) -> str:
         """ Convert the given result into a string using the given format.
 
index d14e3f6c4105209faf387f0446a14bc2f4a981a2..081f451e5f01ad5b2ea74e5d5022eb2a76d2d37b 100644 (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['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
     out['software_version'] = result.software_version
     if result.database_version is not None:
         out['database_version'] = result.database_version
diff --git a/nominatim/server/__init__.py b/nominatim/server/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/nominatim/server/sanic/__init__.py b/nominatim/server/sanic/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/nominatim/server/sanic/server.py b/nominatim/server/sanic/server.py
new file mode 100644 (file)
index 0000000..9c75327
--- /dev/null
@@ -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
+
+