]> git.openstreetmap.org Git - nominatim.git/commitdiff
allow None and str for project_dir in NominatimAPI init
authorSarah Hoffmann <lonvia@denofr.de>
Thu, 22 Aug 2024 20:49:12 +0000 (22:49 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Thu, 22 Aug 2024 20:49:12 +0000 (22:49 +0200)
src/nominatim_api/core.py
src/nominatim_db/config.py
src/nominatim_db/tools/convert_sqlite.py
test/python/api/conftest.py
test/python/api/search/test_icu_query_analyzer.py
test/python/api/search/test_legacy_query_analyzer.py
test/python/api/test_api_status.py
test/python/api/test_server_glue_v1.py

index ac5798625cc4900c8de1227892ce67da0716a0bd..ff0db39f820b51585abd3fd4723ee886a455027b 100644 (file)
@@ -7,7 +7,8 @@
 """
 Implementation of classes for API access via libraries.
 """
 """
 Implementation of classes for API access via libraries.
 """
-from typing import Mapping, Optional, Any, AsyncIterator, Dict, Sequence, List, Tuple, cast
+from typing import Mapping, Optional, Any, AsyncIterator, Dict, Sequence, List,\
+                   Union, Tuple, cast
 import asyncio
 import sys
 import contextlib
 import asyncio
 import sys
 import contextlib
@@ -41,7 +42,7 @@ class NominatimAPIAsync: #pylint: disable=too-many-instance-attributes
 
         This class should usually be used as a context manager in 'with' context.
     """
 
         This class should usually be used as a context manager in 'with' context.
     """
-    def __init__(self, project_dir: Path,
+    def __init__(self, project_dir: Optional[Union[str, Path]] = None,
                  environ: Optional[Mapping[str, str]] = None,
                  loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
         """ Initiate a new frontend object with synchronous API functions.
                  environ: Optional[Mapping[str, str]] = None,
                  loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
         """ Initiate a new frontend object with synchronous API functions.
@@ -365,7 +366,7 @@ class NominatimAPI:
         This class should usually be used as a context manager in 'with' context.
     """
 
         This class should usually be used as a context manager in 'with' context.
     """
 
-    def __init__(self, project_dir: Path,
+    def __init__(self, project_dir: Optional[Union[str, Path]] = None,
                  environ: Optional[Mapping[str, str]] = None) -> None:
         """ Initiate a new frontend object with synchronous API functions.
 
                  environ: Optional[Mapping[str, str]] = None) -> None:
         """ Initiate a new frontend object with synchronous API functions.
 
index 5ae3dea3b3ea8b14dfef655c0edf45e31aab6bdf..2cab02377593a6ce71286199105faf49e12732ff 100644 (file)
@@ -59,15 +59,17 @@ class Configuration:
         other than string.
     """
 
         other than string.
     """
 
-    def __init__(self, project_dir: Optional[Path],
+    def __init__(self, project_dir: Optional[Union[Path, str]],
                  environ: Optional[Mapping[str, str]] = None) -> None:
         self.environ = environ or os.environ
                  environ: Optional[Mapping[str, str]] = None) -> None:
         self.environ = environ or os.environ
-        self.project_dir = project_dir
         self.config_dir = paths.CONFIG_DIR
         self._config = dotenv_values(str(self.config_dir / 'env.defaults'))
         self.config_dir = paths.CONFIG_DIR
         self._config = dotenv_values(str(self.config_dir / 'env.defaults'))
-        if self.project_dir is not None and (self.project_dir / '.env').is_file():
-            self.project_dir = self.project_dir.resolve()
-            self._config.update(dotenv_values(str(self.project_dir / '.env')))
+        if project_dir is not None:
+            self.project_dir: Optional[Path] = Path(project_dir).resolve()
+            if (self.project_dir / '.env').is_file():
+                self._config.update(dotenv_values(str(self.project_dir / '.env')))
+        else:
+            self.project_dir = None
 
         class _LibDirs:
             module: Path
 
         class _LibDirs:
             module: Path
index 2377abc0b2be5dde5913a742eb5d5af7eac118f1..d53527d181f4f670e9028b0cf27869751e3a9bfa 100644 (file)
@@ -7,7 +7,7 @@
 """
 Exporting a Nominatim database to SQlite.
 """
 """
 Exporting a Nominatim database to SQlite.
 """
-from typing import Set, Any
+from typing import Set, Any, Optional, Union
 import datetime as dt
 import logging
 from pathlib import Path
 import datetime as dt
 import logging
 from pathlib import Path
@@ -21,7 +21,8 @@ from nominatim_api.sql.sqlalchemy_types import Geometry, IntArray
 
 LOG = logging.getLogger()
 
 
 LOG = logging.getLogger()
 
-async def convert(project_dir: Path, outfile: Path, options: Set[str]) -> None:
+async def convert(project_dir: Optional[Union[str, Path]],
+                  outfile: Path, options: Set[str]) -> None:
     """ Export an existing database to sqlite. The resulting database
         will be usable against the Python frontend of Nominatim.
     """
     """ Export an existing database to sqlite. The resulting database
         will be usable against the Python frontend of Nominatim.
     """
index 0c770980acdada423eb2e8879503c53b744a1b01..3ca0720b4da24f76a76b0f30e3084af5e87d2802 100644 (file)
@@ -7,7 +7,6 @@
 """
 Helper fixtures for API call tests.
 """
 """
 Helper fixtures for API call tests.
 """
-from pathlib import Path
 import pytest
 import pytest_asyncio
 import time
 import pytest
 import pytest_asyncio
 import time
@@ -24,7 +23,7 @@ import nominatim_api.logging as loglib
 class APITester:
 
     def __init__(self):
 class APITester:
 
     def __init__(self):
-        self.api = napi.NominatimAPI(Path('/invalid'))
+        self.api = napi.NominatimAPI()
         self.async_to_sync(self.api._async_api.setup_database())
 
 
         self.async_to_sync(self.api._async_api.setup_database())
 
 
@@ -229,11 +228,9 @@ def frontend(request, event_loop, tmp_path):
 
             apiobj.async_to_sync(_do_sql())
 
 
             apiobj.async_to_sync(_do_sql())
 
-            event_loop.run_until_complete(convert_sqlite.convert(Path('/invalid'),
-                                                                 db, options))
-            outapi = napi.NominatimAPI(Path('/invalid'),
-                                       {'NOMINATIM_DATABASE_DSN': f"sqlite:dbname={db}",
-                                        'NOMINATIM_USE_US_TIGER_DATA': 'yes'})
+            event_loop.run_until_complete(convert_sqlite.convert(None, db, options))
+            outapi = napi.NominatimAPI(environ={'NOMINATIM_DATABASE_DSN': f"sqlite:dbname={db}",
+                                                'NOMINATIM_USE_US_TIGER_DATA': 'yes'})
             testapis.append(outapi)
 
             return outapi
             testapis.append(outapi)
 
             return outapi
@@ -249,5 +246,5 @@ def frontend(request, event_loop, tmp_path):
 
 @pytest_asyncio.fixture
 async def api(temp_db):
 
 @pytest_asyncio.fixture
 async def api(temp_db):
-    async with napi.NominatimAPIAsync(Path('/invalid')) as api:
+    async with napi.NominatimAPIAsync() as api:
         yield api
         yield api
index 7f88879c14fd7d8c0a856997432bc64b007b2d96..473bc149de1fbf16959b64f7d2f4dd1b1c226a6e 100644 (file)
@@ -7,8 +7,6 @@
 """
 Tests for query analyzer for ICU tokenizer.
 """
 """
 Tests for query analyzer for ICU tokenizer.
 """
-from pathlib import Path
-
 import pytest
 import pytest_asyncio
 
 import pytest
 import pytest_asyncio
 
@@ -40,7 +38,7 @@ async def conn(table_factory):
     table_factory('word',
                   definition='word_id INT, word_token TEXT, type TEXT, word TEXT, info JSONB')
 
     table_factory('word',
                   definition='word_id INT, word_token TEXT, type TEXT, word TEXT, info JSONB')
 
-    async with NominatimAPIAsync(Path('/invalid'), {}) as api:
+    async with NominatimAPIAsync(environ={}) as api:
         async with api.begin() as conn:
             yield conn
 
         async with api.begin() as conn:
             yield conn
 
index 0e967c10fa5f8e062825fc19e491352eee087bb1..a99485ead17c95071ec7a280611b96c1bfbe2bf8 100644 (file)
@@ -7,8 +7,6 @@
 """
 Tests for query analyzer for legacy tokenizer.
 """
 """
 Tests for query analyzer for legacy tokenizer.
 """
-from pathlib import Path
-
 import pytest
 import pytest_asyncio
 
 import pytest
 import pytest_asyncio
 
@@ -74,7 +72,7 @@ async def conn(table_factory, temp_db_cursor):
     temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION make_standard_name(name TEXT)
                               RETURNS TEXT AS $$ SELECT lower(name); $$ LANGUAGE SQL;""")
 
     temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION make_standard_name(name TEXT)
                               RETURNS TEXT AS $$ SELECT lower(name); $$ LANGUAGE SQL;""")
 
-    async with NominatimAPIAsync(Path('/invalid'), {}) as api:
+    async with NominatimAPIAsync(environ={}) as api:
         async with api.begin() as conn:
             yield conn
 
         async with api.begin() as conn:
             yield conn
 
index 5412ca6e334fad5f8932379a274a7f65284182d2..29eb34d0c9a8b82a7ab6d66e92b5199e7d3c7569 100644 (file)
@@ -7,7 +7,6 @@
 """
 Tests for the status API call.
 """
 """
 Tests for the status API call.
 """
-from pathlib import Path
 import datetime as dt
 import pytest
 
 import datetime as dt
 import pytest
 
@@ -46,7 +45,7 @@ def test_status_full(apiobj, frontend):
 def test_status_database_not_found(monkeypatch):
     monkeypatch.setenv('NOMINATIM_DATABASE_DSN', 'dbname=rgjdfkgjedkrgdfkngdfkg')
 
 def test_status_database_not_found(monkeypatch):
     monkeypatch.setenv('NOMINATIM_DATABASE_DSN', 'dbname=rgjdfkgjedkrgdfkngdfkg')
 
-    api = napi.NominatimAPI(Path('/invalid'), {})
+    api = napi.NominatimAPI(environ={})
 
     result = api.status()
 
 
     result = api.status()
 
index 5ef169045e0a4712c256bf38d2634a4bbae6e146..6ea790c060b21c740c3ff2e985adeb4bf51ba3b5 100644 (file)
@@ -9,7 +9,6 @@ Tests for the Python web frameworks adaptor, v1 API.
 """
 import json
 import xml.etree.ElementTree as ET
 """
 import json
 import xml.etree.ElementTree as ET
-from pathlib import Path
 
 import pytest
 
 
 import pytest
 
@@ -242,7 +241,7 @@ class TestStatusEndpoint:
         a = FakeAdaptor()
         self.status = napi.StatusResult(0, 'foo')
 
         a = FakeAdaptor()
         self.status = napi.StatusResult(0, 'foo')
 
-        resp = await glue.status_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        resp = await glue.status_endpoint(napi.NominatimAPIAsync(), a)
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
@@ -254,7 +253,7 @@ class TestStatusEndpoint:
         a = FakeAdaptor()
         self.status = napi.StatusResult(405, 'foo')
 
         a = FakeAdaptor()
         self.status = napi.StatusResult(405, 'foo')
 
-        resp = await glue.status_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        resp = await glue.status_endpoint(napi.NominatimAPIAsync(), a)
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 500
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 500
@@ -266,7 +265,7 @@ class TestStatusEndpoint:
         a = FakeAdaptor(params={'format': 'json'})
         self.status = napi.StatusResult(405, 'foo')
 
         a = FakeAdaptor(params={'format': 'json'})
         self.status = napi.StatusResult(405, 'foo')
 
-        resp = await glue.status_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        resp = await glue.status_endpoint(napi.NominatimAPIAsync(), a)
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
@@ -279,7 +278,7 @@ class TestStatusEndpoint:
         self.status = napi.StatusResult(0, 'foo')
 
         with pytest.raises(FakeError):
         self.status = napi.StatusResult(0, 'foo')
 
         with pytest.raises(FakeError):
-            await glue.status_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+            await glue.status_endpoint(napi.NominatimAPIAsync(), a)
 
 
 # details_endpoint()
 
 
 # details_endpoint()
@@ -305,14 +304,14 @@ class TestDetailsEndpoint:
         a = FakeAdaptor()
 
         with pytest.raises(FakeError, match='^400 -- .*Missing'):
         a = FakeAdaptor()
 
         with pytest.raises(FakeError, match='^400 -- .*Missing'):
-            await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+            await glue.details_endpoint(napi.NominatimAPIAsync(), a)
 
 
     @pytest.mark.asyncio
     async def test_details_by_place_id(self):
         a = FakeAdaptor(params={'place_id': '4573'})
 
 
 
     @pytest.mark.asyncio
     async def test_details_by_place_id(self):
         a = FakeAdaptor(params={'place_id': '4573'})
 
-        await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        await glue.details_endpoint(napi.NominatimAPIAsync(), a)
 
         assert self.lookup_args[0].place_id == 4573
 
 
         assert self.lookup_args[0].place_id == 4573
 
@@ -321,7 +320,7 @@ class TestDetailsEndpoint:
     async def test_details_by_osm_id(self):
         a = FakeAdaptor(params={'osmtype': 'N', 'osmid': '45'})
 
     async def test_details_by_osm_id(self):
         a = FakeAdaptor(params={'osmtype': 'N', 'osmid': '45'})
 
-        await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        await glue.details_endpoint(napi.NominatimAPIAsync(), a)
 
         assert self.lookup_args[0].osm_type == 'N'
         assert self.lookup_args[0].osm_id == 45
 
         assert self.lookup_args[0].osm_type == 'N'
         assert self.lookup_args[0].osm_id == 45
@@ -332,7 +331,7 @@ class TestDetailsEndpoint:
     async def test_details_with_debugging(self):
         a = FakeAdaptor(params={'osmtype': 'N', 'osmid': '45', 'debug': '1'})
 
     async def test_details_with_debugging(self):
         a = FakeAdaptor(params={'osmtype': 'N', 'osmid': '45', 'debug': '1'})
 
-        resp = await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        resp = await glue.details_endpoint(napi.NominatimAPIAsync(), a)
         content = ET.fromstring(resp.output)
 
         assert resp.content_type == 'text/html; charset=utf-8'
         content = ET.fromstring(resp.output)
 
         assert resp.content_type == 'text/html; charset=utf-8'
@@ -345,7 +344,7 @@ class TestDetailsEndpoint:
         self.result = None
 
         with pytest.raises(FakeError, match='^404 -- .*found'):
         self.result = None
 
         with pytest.raises(FakeError, match='^404 -- .*found'):
-            await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+            await glue.details_endpoint(napi.NominatimAPIAsync(), a)
 
 
 # reverse_endpoint()
 
 
 # reverse_endpoint()
@@ -370,7 +369,7 @@ class TestReverseEndPoint:
         a.params['format'] = 'xml'
 
         with pytest.raises(FakeError, match='^400 -- (?s:.*)missing'):
         a.params['format'] = 'xml'
 
         with pytest.raises(FakeError, match='^400 -- (?s:.*)missing'):
-            await glue.reverse_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+            await glue.reverse_endpoint(napi.NominatimAPIAsync(), a)
 
 
     @pytest.mark.asyncio
 
 
     @pytest.mark.asyncio
@@ -380,7 +379,7 @@ class TestReverseEndPoint:
         a.params = params
         a.params['format'] = 'json'
 
         a.params = params
         a.params['format'] = 'json'
 
-        res = await glue.reverse_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.reverse_endpoint(napi.NominatimAPIAsync(), a)
 
         assert res == ''
 
 
         assert res == ''
 
@@ -391,7 +390,7 @@ class TestReverseEndPoint:
         a.params['lat'] = '56.3'
         a.params['lon'] = '6.8'
 
         a.params['lat'] = '56.3'
         a.params['lon'] = '6.8'
 
-        assert await glue.reverse_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        assert await glue.reverse_endpoint(napi.NominatimAPIAsync(), a)
 
 
     @pytest.mark.asyncio
 
 
     @pytest.mark.asyncio
@@ -400,7 +399,7 @@ class TestReverseEndPoint:
         a.params['q'] = '34.6 2.56'
         a.params['format'] = 'json'
 
         a.params['q'] = '34.6 2.56'
         a.params['format'] = 'json'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -425,7 +424,7 @@ class TestLookupEndpoint:
         a = FakeAdaptor()
         a.params['format'] = 'json'
 
         a = FakeAdaptor()
         a.params['format'] = 'json'
 
-        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(), a)
 
         assert res.output == '[]'
 
 
         assert res.output == '[]'
 
@@ -437,7 +436,7 @@ class TestLookupEndpoint:
         a.params['format'] = 'json'
         a.params['osm_ids'] = f'W34,{param},N33333'
 
         a.params['format'] = 'json'
         a.params['osm_ids'] = f'W34,{param},N33333'
 
-        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -449,7 +448,7 @@ class TestLookupEndpoint:
         a.params['format'] = 'json'
         a.params['osm_ids'] = f'W34,{param},N33333'
 
         a.params['format'] = 'json'
         a.params['osm_ids'] = f'W34,{param},N33333'
 
-        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -460,7 +459,7 @@ class TestLookupEndpoint:
         a.params['format'] = 'json'
         a.params['osm_ids'] = 'N23,W34'
 
         a.params['format'] = 'json'
         a.params['osm_ids'] = 'N23,W34'
 
-        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.lookup_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -485,7 +484,7 @@ class TestSearchEndPointSearch:
         a = FakeAdaptor()
         a.params['q'] = 'something'
 
         a = FakeAdaptor()
         a.params['q'] = 'something'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -496,7 +495,7 @@ class TestSearchEndPointSearch:
         a.params['q'] = 'something'
         a.params['format'] = 'xml'
 
         a.params['q'] = 'something'
         a.params['format'] = 'xml'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert res.status == 200
         assert res.output.index('something') > 0
 
         assert res.status == 200
         assert res.output.index('something') > 0
@@ -509,7 +508,7 @@ class TestSearchEndPointSearch:
         a.params['city'] = 'ignored'
 
         with pytest.raises(FakeError, match='^400 -- .*cannot be used together'):
         a.params['city'] = 'ignored'
 
         with pytest.raises(FakeError, match='^400 -- .*cannot be used together'):
-            res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+            res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
 
     @pytest.mark.asyncio
 
 
     @pytest.mark.asyncio
@@ -521,7 +520,7 @@ class TestSearchEndPointSearch:
         if not dedupe:
             a.params['dedupe'] = '0'
 
         if not dedupe:
             a.params['dedupe'] = '0'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == numres
 
 
         assert len(json.loads(res.output)) == numres
 
@@ -544,7 +543,7 @@ class TestSearchEndPointSearchAddress:
         a = FakeAdaptor()
         a.params['street'] = 'something'
 
         a = FakeAdaptor()
         a.params['street'] = 'something'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
 
         assert len(json.loads(res.output)) == 1
 
@@ -567,6 +566,6 @@ class TestSearchEndPointSearchCategory:
         a = FakeAdaptor()
         a.params['q'] = '[shop=fog]'
 
         a = FakeAdaptor()
         a.params['q'] = '[shop=fog]'
 
-        res = await glue.search_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a)
+        res = await glue.search_endpoint(napi.NominatimAPIAsync(), a)
 
         assert len(json.loads(res.output)) == 1
 
         assert len(json.loads(res.output)) == 1