add type annotations to config module

This commit is contained in:
Sarah Hoffmann
2022-06-30 15:43:18 +02:00
parent bf36f33e79
commit 95ed95c616

View File

@@ -7,7 +7,7 @@
""" """
Nominatim configuration accessor. Nominatim configuration accessor.
""" """
from typing import Dict, Any from typing import Dict, Any, List, Mapping, Optional, Union
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
@@ -18,10 +18,12 @@ from dotenv import dotenv_values
from nominatim.errors import UsageError from nominatim.errors import UsageError
PathOrStr = Union[str, os.PathLike[str]]
LOG = logging.getLogger() LOG = logging.getLogger()
CONFIG_CACHE : Dict[str, Any] = {} CONFIG_CACHE : Dict[str, Any] = {}
def flatten_config_list(content, section=''): def flatten_config_list(content: Any, section: str = '') -> List[Any]:
""" Flatten YAML configuration lists that contain include sections """ Flatten YAML configuration lists that contain include sections
which are lists themselves. which are lists themselves.
""" """
@@ -55,7 +57,8 @@ class Configuration:
avoid conflicts with other environment variables. avoid conflicts with other environment variables.
""" """
def __init__(self, project_dir, config_dir, environ=None): def __init__(self, project_dir: Path, config_dir: Path,
environ: Optional[Mapping[str, str]] = None) -> None:
self.environ = environ or os.environ self.environ = environ or os.environ
self.project_dir = project_dir self.project_dir = project_dir
self.config_dir = config_dir self.config_dir = config_dir
@@ -68,21 +71,24 @@ class Configuration:
self.lib_dir = _LibDirs() self.lib_dir = _LibDirs()
def set_libdirs(self, **kwargs):
def set_libdirs(self, **kwargs: PathOrStr) -> None:
""" Set paths to library functions and data. """ Set paths to library functions and data.
""" """
for key, value in kwargs.items(): for key, value in kwargs.items():
setattr(self.lib_dir, key, Path(value).resolve()) setattr(self.lib_dir, key, Path(value).resolve())
def __getattr__(self, name):
def __getattr__(self, name: str) -> str:
name = 'NOMINATIM_' + name name = 'NOMINATIM_' + name
if name in self.environ: if name in self.environ:
return self.environ[name] return self.environ[name]
return self._config[name] return self._config[name] or ''
def get_bool(self, name):
def get_bool(self, name: str) -> bool:
""" Return the given configuration parameter as a boolean. """ Return the given configuration parameter as a boolean.
Values of '1', 'yes' and 'true' are accepted as truthy values, Values of '1', 'yes' and 'true' are accepted as truthy values,
everything else is interpreted as false. everything else is interpreted as false.
@@ -90,7 +96,7 @@ class Configuration:
return getattr(self, name).lower() in ('1', 'yes', 'true') return getattr(self, name).lower() in ('1', 'yes', 'true')
def get_int(self, name): def get_int(self, name: str) -> int:
""" Return the given configuration parameter as an int. """ Return the given configuration parameter as an int.
""" """
try: try:
@@ -100,7 +106,7 @@ class Configuration:
raise UsageError("Configuration error.") from exp raise UsageError("Configuration error.") from exp
def get_str_list(self, name): def get_str_list(self, name: str) -> Optional[List[str]]:
""" Return the given configuration parameter as a list of strings. """ Return the given configuration parameter as a list of strings.
The values are assumed to be given as a comma-sparated list and The values are assumed to be given as a comma-sparated list and
will be stripped before returning them. On empty values None will be stripped before returning them. On empty values None
@@ -111,30 +117,31 @@ class Configuration:
return [v.strip() for v in raw.split(',')] if raw else None return [v.strip() for v in raw.split(',')] if raw else None
def get_path(self, name): def get_path(self, name: str) -> Optional[Path]:
""" Return the given configuration parameter as a Path. """ Return the given configuration parameter as a Path.
If a relative path is configured, then the function converts this If a relative path is configured, then the function converts this
into an absolute path with the project directory as root path. into an absolute path with the project directory as root path.
If the configuration is unset, a falsy value is returned. If the configuration is unset, None is returned.
""" """
value = getattr(self, name) value = getattr(self, name)
if value: if not value:
value = Path(value) return None
if not value.is_absolute(): cfgpath = Path(value)
value = self.project_dir / value
value = value.resolve() if not cfgpath.is_absolute():
cfgpath = self.project_dir / cfgpath
return value return cfgpath.resolve()
def get_libpq_dsn(self):
def get_libpq_dsn(self) -> str:
""" Get configured database DSN converted into the key/value format """ Get configured database DSN converted into the key/value format
understood by libpq and psycopg. understood by libpq and psycopg.
""" """
dsn = self.DATABASE_DSN dsn = self.DATABASE_DSN
def quote_param(param): def quote_param(param: str) -> str:
key, val = param.split('=') key, val = param.split('=')
val = val.replace('\\', '\\\\').replace("'", "\\'") val = val.replace('\\', '\\\\').replace("'", "\\'")
if ' ' in val: if ' ' in val:
@@ -148,7 +155,7 @@ class Configuration:
return dsn return dsn
def get_import_style_file(self): def get_import_style_file(self) -> Path:
""" Return the import style file as a path object. Translates the """ Return the import style file as a path object. Translates the
name of the standard styles automatically into a file in the name of the standard styles automatically into a file in the
config style. config style.
@@ -161,7 +168,7 @@ class Configuration:
return self.find_config_file('', 'IMPORT_STYLE') return self.find_config_file('', 'IMPORT_STYLE')
def get_os_env(self): def get_os_env(self) -> Dict[str, Optional[str]]:
""" Return a copy of the OS environment with the Nominatim configuration """ Return a copy of the OS environment with the Nominatim configuration
merged in. merged in.
""" """
@@ -171,7 +178,8 @@ class Configuration:
return env return env
def load_sub_configuration(self, filename, config=None): def load_sub_configuration(self, filename: PathOrStr,
config: Optional[str] = None) -> Any:
""" Load additional configuration from a file. `filename` is the name """ Load additional configuration from a file. `filename` is the name
of the configuration file. The file is first searched in the of the configuration file. The file is first searched in the
project directory and then in the global settings dirctory. project directory and then in the global settings dirctory.
@@ -208,16 +216,17 @@ class Configuration:
return result return result
def find_config_file(self, filename, config=None): def find_config_file(self, filename: PathOrStr,
config: Optional[str] = None) -> Path:
""" Resolve the location of a configuration file given a filename and """ Resolve the location of a configuration file given a filename and
an optional configuration option with the file name. an optional configuration option with the file name.
Raises a UsageError when the file cannot be found or is not Raises a UsageError when the file cannot be found or is not
a regular file. a regular file.
""" """
if config is not None: if config is not None:
cfg_filename = getattr(self, config) cfg_value = getattr(self, config)
if cfg_filename: if cfg_value:
cfg_filename = Path(cfg_filename) cfg_filename = Path(cfg_value)
if cfg_filename.is_absolute(): if cfg_filename.is_absolute():
cfg_filename = cfg_filename.resolve() cfg_filename = cfg_filename.resolve()
@@ -241,7 +250,7 @@ class Configuration:
raise UsageError("Config file not found.") raise UsageError("Config file not found.")
def _load_from_yaml(self, cfgfile): def _load_from_yaml(self, cfgfile: Path) -> Any:
""" Load a YAML configuration file. This installs a special handler that """ Load a YAML configuration file. This installs a special handler that
allows to include other YAML files using the '!include' operator. allows to include other YAML files using the '!include' operator.
""" """
@@ -250,7 +259,7 @@ class Configuration:
return yaml.safe_load(cfgfile.read_text(encoding='utf-8')) return yaml.safe_load(cfgfile.read_text(encoding='utf-8'))
def _yaml_include_representer(self, loader, node): def _yaml_include_representer(self, loader: Any, node: yaml.Node) -> Any:
""" Handler for the '!include' operator in YAML files. """ Handler for the '!include' operator in YAML files.
When the filename is relative, then the file is first searched in the When the filename is relative, then the file is first searched in the