diff --git a/src/nominatim_api/core.py b/src/nominatim_api/core.py index ac579862..ff0db39f 100644 --- a/src/nominatim_api/core.py +++ b/src/nominatim_api/core.py @@ -7,7 +7,8 @@ """ 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 @@ -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. """ - 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. @@ -365,7 +366,7 @@ class NominatimAPI: 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. diff --git a/src/nominatim_db/config.py b/src/nominatim_db/config.py index 5ae3dea3..2cab0237 100644 --- a/src/nominatim_db/config.py +++ b/src/nominatim_db/config.py @@ -59,15 +59,17 @@ class Configuration: 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 - self.project_dir = project_dir 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 diff --git a/src/nominatim_db/tools/convert_sqlite.py b/src/nominatim_db/tools/convert_sqlite.py index 2377abc0..d53527d1 100644 --- a/src/nominatim_db/tools/convert_sqlite.py +++ b/src/nominatim_db/tools/convert_sqlite.py @@ -7,7 +7,7 @@ """ 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 @@ -21,7 +21,8 @@ from nominatim_api.sql.sqlalchemy_types import Geometry, IntArray 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. """ diff --git a/test/python/api/conftest.py b/test/python/api/conftest.py index 0c770980..3ca0720b 100644 --- a/test/python/api/conftest.py +++ b/test/python/api/conftest.py @@ -7,7 +7,6 @@ """ Helper fixtures for API call tests. """ -from pathlib import Path import pytest import pytest_asyncio import time @@ -24,7 +23,7 @@ import nominatim_api.logging as loglib 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()) @@ -229,11 +228,9 @@ def frontend(request, event_loop, tmp_path): 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 @@ -249,5 +246,5 @@ def frontend(request, event_loop, tmp_path): @pytest_asyncio.fixture async def api(temp_db): - async with napi.NominatimAPIAsync(Path('/invalid')) as api: + async with napi.NominatimAPIAsync() as api: yield api diff --git a/test/python/api/search/test_icu_query_analyzer.py b/test/python/api/search/test_icu_query_analyzer.py index 7f88879c..473bc149 100644 --- a/test/python/api/search/test_icu_query_analyzer.py +++ b/test/python/api/search/test_icu_query_analyzer.py @@ -7,8 +7,6 @@ """ Tests for query analyzer for ICU tokenizer. """ -from pathlib import Path - 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') - async with NominatimAPIAsync(Path('/invalid'), {}) as api: + async with NominatimAPIAsync(environ={}) as api: async with api.begin() as conn: yield conn diff --git a/test/python/api/search/test_legacy_query_analyzer.py b/test/python/api/search/test_legacy_query_analyzer.py index 0e967c10..a99485ea 100644 --- a/test/python/api/search/test_legacy_query_analyzer.py +++ b/test/python/api/search/test_legacy_query_analyzer.py @@ -7,8 +7,6 @@ """ Tests for query analyzer for legacy tokenizer. """ -from pathlib import Path - 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;""") - async with NominatimAPIAsync(Path('/invalid'), {}) as api: + async with NominatimAPIAsync(environ={}) as api: async with api.begin() as conn: yield conn diff --git a/test/python/api/test_api_status.py b/test/python/api/test_api_status.py index 5412ca6e..29eb34d0 100644 --- a/test/python/api/test_api_status.py +++ b/test/python/api/test_api_status.py @@ -7,7 +7,6 @@ """ Tests for the status API call. """ -from pathlib import Path 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') - api = napi.NominatimAPI(Path('/invalid'), {}) + api = napi.NominatimAPI(environ={}) result = api.status() diff --git a/test/python/api/test_server_glue_v1.py b/test/python/api/test_server_glue_v1.py index 5ef16904..6ea790c0 100644 --- a/test/python/api/test_server_glue_v1.py +++ b/test/python/api/test_server_glue_v1.py @@ -9,7 +9,6 @@ Tests for the Python web frameworks adaptor, v1 API. """ import json import xml.etree.ElementTree as ET -from pathlib import Path import pytest @@ -242,7 +241,7 @@ class TestStatusEndpoint: 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 @@ -254,7 +253,7 @@ class TestStatusEndpoint: 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 @@ -266,7 +265,7 @@ class TestStatusEndpoint: 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 @@ -279,7 +278,7 @@ class TestStatusEndpoint: 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() @@ -305,14 +304,14 @@ class TestDetailsEndpoint: 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'}) - await glue.details_endpoint(napi.NominatimAPIAsync(Path('/invalid')), a) + await glue.details_endpoint(napi.NominatimAPIAsync(), a) 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'}) - 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 @@ -332,7 +331,7 @@ class TestDetailsEndpoint: 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' @@ -345,7 +344,7 @@ class TestDetailsEndpoint: 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() @@ -370,7 +369,7 @@ class TestReverseEndPoint: 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 @@ -380,7 +379,7 @@ class TestReverseEndPoint: 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 == '' @@ -391,7 +390,7 @@ class TestReverseEndPoint: 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 @@ -400,7 +399,7 @@ class TestReverseEndPoint: 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 @@ -425,7 +424,7 @@ class TestLookupEndpoint: 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 == '[]' @@ -437,7 +436,7 @@ class TestLookupEndpoint: 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 @@ -449,7 +448,7 @@ class TestLookupEndpoint: 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 @@ -460,7 +459,7 @@ class TestLookupEndpoint: 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 @@ -485,7 +484,7 @@ class TestSearchEndPointSearch: 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 @@ -496,7 +495,7 @@ class TestSearchEndPointSearch: 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 @@ -509,7 +508,7 @@ class TestSearchEndPointSearch: 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 @@ -521,7 +520,7 @@ class TestSearchEndPointSearch: 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 @@ -544,7 +543,7 @@ class TestSearchEndPointSearchAddress: 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 @@ -567,6 +566,6 @@ class TestSearchEndPointSearchCategory: 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