]> git.openstreetmap.org Git - nominatim.git/blob - src/nominatim_db/tokenizer/factory.py
Merge pull request #3519 from lonvia/api-error-handling
[nominatim.git] / src / nominatim_db / tokenizer / factory.py
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2024 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Functions for creating a tokenizer or initialising the right one for an
9 existing database.
10
11 A tokenizer is something that is bound to the lifetime of a database. It
12 can be chosen and configured before the initial import but then needs to
13 be used consistently when querying and updating the database.
14
15 This module provides the functions to create and configure a new tokenizer
16 as well as instantiating the appropriate tokenizer for updating an existing
17 database.
18
19 A tokenizer usually also includes PHP code for querying. The appropriate PHP
20 normalizer module is installed, when the tokenizer is created.
21 """
22 from typing import Optional
23 import logging
24 import importlib
25 from pathlib import Path
26
27 from ..errors import UsageError
28 from ..db import properties
29 from ..db.connection import connect
30 from ..config import Configuration
31 from ..tokenizer.base import AbstractTokenizer, TokenizerModule
32
33 LOG = logging.getLogger()
34
35 def _import_tokenizer(name: str) -> TokenizerModule:
36     """ Load the tokenizer.py module from project directory.
37     """
38     src_file = Path(__file__).parent / (name + '_tokenizer.py')
39     if not src_file.is_file():
40         LOG.fatal("No tokenizer named '%s' available. "
41                   "Check the setting of NOMINATIM_TOKENIZER.", name)
42         raise UsageError('Tokenizer not found')
43
44     return importlib.import_module('nominatim_db.tokenizer.' + name + '_tokenizer')
45
46
47 def create_tokenizer(config: Configuration, init_db: bool = True,
48                      module_name: Optional[str] = None) -> AbstractTokenizer:
49     """ Create a new tokenizer as defined by the given configuration.
50
51         The tokenizer data and code is copied into the 'tokenizer' directory
52         of the project directory and the tokenizer loaded from its new location.
53     """
54     if module_name is None:
55         module_name = config.TOKENIZER
56
57     # Create the directory for the tokenizer data
58     assert config.project_dir is not None
59     basedir = config.project_dir / 'tokenizer'
60     if not basedir.exists():
61         basedir.mkdir()
62     elif not basedir.is_dir():
63         LOG.fatal("Tokenizer directory '%s' cannot be created.", basedir)
64         raise UsageError("Tokenizer setup failed.")
65
66     # Import and initialize the tokenizer.
67     tokenizer_module = _import_tokenizer(module_name)
68
69     tokenizer = tokenizer_module.create(config.get_libpq_dsn(), basedir)
70     tokenizer.init_new_db(config, init_db=init_db)
71
72     with connect(config.get_libpq_dsn()) as conn:
73         properties.set_property(conn, 'tokenizer', module_name)
74
75     return tokenizer
76
77
78 def get_tokenizer_for_db(config: Configuration) -> AbstractTokenizer:
79     """ Instantiate a tokenizer for an existing database.
80
81         The function looks up the appropriate tokenizer in the database
82         and initialises it.
83     """
84     assert config.project_dir is not None
85     basedir = config.project_dir / 'tokenizer'
86     if not basedir.is_dir():
87         # Directory will be repopulated by tokenizer below.
88         basedir.mkdir()
89
90     with connect(config.get_libpq_dsn()) as conn:
91         name = properties.get_property(conn, 'tokenizer')
92
93     if name is None:
94         LOG.fatal("Tokenizer was not set up properly. Database property missing.")
95         raise UsageError('Cannot initialize tokenizer.')
96
97     tokenizer_module = _import_tokenizer(name)
98
99     tokenizer = tokenizer_module.create(config.get_libpq_dsn(), basedir)
100     tokenizer.init_from_project(config)
101
102     return tokenizer