X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/6e89310a9285f1ad15d8002bf68f578eada367a0..06683edaae8bfa517e488375d710ca6532336725:/src/nominatim_api/core.py diff --git a/src/nominatim_api/core.py b/src/nominatim_api/core.py index 6f91e7a8..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 +from typing import Mapping, Optional, Any, AsyncIterator, Dict, Sequence, List,\ + Union, Tuple, cast import asyncio import sys import contextlib @@ -16,10 +17,10 @@ from pathlib import Path import sqlalchemy as sa import sqlalchemy.ext.asyncio as sa_asyncio -from nominatim_core.errors import UsageError -from nominatim_core.db.sqlalchemy_schema import SearchTables -from nominatim_core.db.async_core_library import PGCORE_LIB, PGCORE_ERROR -from nominatim_core.config import Configuration +from .errors import UsageError +from .sql.sqlalchemy_schema import SearchTables +from .sql.async_core_library import PGCORE_LIB, PGCORE_ERROR +from .config import Configuration from .sql import sqlite_functions, sqlalchemy_functions #pylint: disable=unused-import from .connection import SearchConnection from .status import get_status, StatusResult @@ -38,8 +39,10 @@ class NominatimAPIAsync: #pylint: disable=too-many-instance-attributes This class shares most of the functions with its synchronous version. There are some additional functions or parameters, which are documented below. + + 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. @@ -107,16 +110,16 @@ class NominatimAPIAsync: #pylint: disable=too-many-instance-attributes raise UsageError(f"SQlite database '{params.get('dbname')}' does not exist.") else: dsn = self.config.get_database_params() - query = {k: v for k, v in dsn.items() + query = {k: str(v) for k, v in dsn.items() if k not in ('user', 'password', 'dbname', 'host', 'port')} dburl = sa.engine.URL.create( f'postgresql+{PGCORE_LIB}', - database=dsn.get('dbname'), - username=dsn.get('user'), - password=dsn.get('password'), - host=dsn.get('host'), - port=int(dsn['port']) if 'port' in dsn else None, + database=cast(str, dsn.get('dbname')), + username=cast(str, dsn.get('user')), + password=cast(str, dsn.get('password')), + host=cast(str, dsn.get('host')), + port=int(cast(str, dsn['port'])) if 'port' in dsn else None, query=query) engine = sa_asyncio.create_async_engine(dburl, **extra_args) @@ -166,6 +169,14 @@ class NominatimAPIAsync: #pylint: disable=too-many-instance-attributes await self._engine.dispose() + async def __aenter__(self) -> 'NominatimAPIAsync': + return self + + + async def __aexit__(self, *_: Any) -> None: + await self.close() + + @contextlib.asynccontextmanager async def begin(self) -> AsyncIterator[SearchConnection]: """ Create a new connection with automatic transaction handling. @@ -351,9 +362,11 @@ class NominatimAPI: """ This class provides a thin synchronous wrapper around the asynchronous Nominatim functions. It creates its own event loop and runs each synchronous function call to completion using that loop. + + 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. @@ -376,13 +389,22 @@ class NominatimAPI: This function also closes the asynchronous worker loop making the NominatimAPI object unusable. """ - self._loop.run_until_complete(self._async_api.close()) - self._loop.close() + if not self._loop.is_closed(): + self._loop.run_until_complete(self._async_api.close()) + self._loop.close() + + + def __enter__(self) -> 'NominatimAPI': + return self + + + def __exit__(self, *_: Any) -> None: + self.close() @property def config(self) -> Configuration: - """ Provide read-only access to the [configuration](#Configuration) + """ Provide read-only access to the [configuration](Configuration.md) used by the API. """ return self._async_api.config