Skip to content

Commit

Permalink
Merge pull request #3525 from lonvia/project-dir-less-library
Browse files Browse the repository at this point in the history
Simplify handling of project directory for Nominatim library
  • Loading branch information
lonvia authored Aug 26, 2024
2 parents b5a6d7a + 7f11de0 commit 086116b
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 50 deletions.
7 changes: 4 additions & 3 deletions src/nominatim_api/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 7 additions & 5 deletions src/nominatim_db/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/nominatim_db/tools/convert_sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -21,7 +21,8 @@

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.
"""
Expand Down
13 changes: 5 additions & 8 deletions test/python/api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"""
Helper fixtures for API call tests.
"""
from pathlib import Path
import pytest
import pytest_asyncio
import time
Expand All @@ -24,7 +23,7 @@
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())


Expand Down Expand Up @@ -229,11 +228,9 @@ async def _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
Expand All @@ -249,5 +246,5 @@ def mkapi(apiobj, options=None):

@pytest_asyncio.fixture
async def api(temp_db):
async with napi.NominatimAPIAsync(Path('/invalid')) as api:
async with napi.NominatimAPIAsync() as api:
yield api
4 changes: 1 addition & 3 deletions test/python/api/search/test_icu_query_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
"""
Tests for query analyzer for ICU tokenizer.
"""
from pathlib import Path

import pytest
import pytest_asyncio

Expand Down Expand Up @@ -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

Expand Down
4 changes: 1 addition & 3 deletions test/python/api/search/test_legacy_query_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
"""
Tests for query analyzer for legacy tokenizer.
"""
from pathlib import Path

import pytest
import pytest_asyncio

Expand Down Expand Up @@ -74,7 +72,7 @@ class TEXT, type TEXT, country_code TEXT,
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

Expand Down
3 changes: 1 addition & 2 deletions test/python/api/test_api_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"""
Tests for the status API call.
"""
from pathlib import Path
import datetime as dt
import pytest

Expand Down Expand Up @@ -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()

Expand Down
47 changes: 23 additions & 24 deletions test/python/api/test_server_glue_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"""
import json
import xml.etree.ElementTree as ET
from pathlib import Path

import pytest

Expand Down Expand Up @@ -242,7 +241,7 @@ async def test_status_without_params(self):
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
Expand All @@ -254,7 +253,7 @@ async def test_status_with_error(self):
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
Expand All @@ -266,7 +265,7 @@ async def test_status_json_with_error(self):
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
Expand All @@ -279,7 +278,7 @@ async def test_status_bad_format(self):
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()
Expand All @@ -305,14 +304,14 @@ async def test_details_no_params(self):
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

Expand All @@ -321,7 +320,7 @@ async def test_details_by_place_id(self):
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
Expand All @@ -332,7 +331,7 @@ async def test_details_by_osm_id(self):
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'
Expand All @@ -345,7 +344,7 @@ async def test_details_no_result(self):
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()
Expand All @@ -370,7 +369,7 @@ async def test_reverse_no_params(self, params):
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
Expand All @@ -380,7 +379,7 @@ async def test_reverse_success(self, params):
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 == ''

Expand All @@ -391,7 +390,7 @@ async def test_reverse_success(self):
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
Expand All @@ -400,7 +399,7 @@ async def test_reverse_from_search(self):
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

Expand All @@ -425,7 +424,7 @@ async def test_lookup_no_params(self):
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 == '[]'

Expand All @@ -437,7 +436,7 @@ async def test_lookup_bad_params(self, param):
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

Expand All @@ -449,7 +448,7 @@ async def test_lookup_bad_osm_type(self, param):
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

Expand All @@ -460,7 +459,7 @@ async def test_lookup_working(self):
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

Expand All @@ -485,7 +484,7 @@ async def test_search_free_text(self):
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

Expand All @@ -496,7 +495,7 @@ async def test_search_free_text_xml(self):
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
Expand All @@ -509,7 +508,7 @@ async def test_search_free_and_structured(self):
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
Expand All @@ -521,7 +520,7 @@ async def test_search_dedupe(self, dedupe, numres):
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

Expand All @@ -544,7 +543,7 @@ async def test_search_structured(self):
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

Expand All @@ -567,6 +566,6 @@ async def test_search_category(self):
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

0 comments on commit 086116b

Please sign in to comment.