1 # SPDX-License-Identifier: GPL-2.0-only
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2022 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Command-line interface to the Nominatim functions for import, update,
9 database administration and querying.
15 from pathlib import Path
17 from nominatim.config import Configuration
18 from nominatim.tools.exec_utils import run_legacy_script, run_php_server
19 from nominatim.errors import UsageError
20 from nominatim import clicmd
21 from nominatim import version
22 from nominatim.clicmd.args import NominatimArgs
24 LOG = logging.getLogger()
27 class CommandlineParser:
28 """ Wraps some of the common functions for parsing the command line
29 and setting up subcommands.
31 def __init__(self, prog, description):
32 self.parser = argparse.ArgumentParser(
34 description=description,
35 formatter_class=argparse.RawDescriptionHelpFormatter)
37 self.subs = self.parser.add_subparsers(title='available commands',
40 # Global arguments that only work if no sub-command given
41 self.parser.add_argument('--version', action='version',
42 version=CommandlineParser.nominatim_version_text(),
43 help='Print Nominatim version and exit')
45 # Arguments added to every sub-command
46 self.default_args = argparse.ArgumentParser(add_help=False)
47 group = self.default_args.add_argument_group('Default arguments')
48 group.add_argument('-h', '--help', action='help',
49 help='Show this help message and exit')
50 group.add_argument('-q', '--quiet', action='store_const', const=0,
51 dest='verbose', default=1,
52 help='Print only error messages')
53 group.add_argument('-v', '--verbose', action='count', default=1,
54 help='Increase verboseness of output')
55 group.add_argument('--project-dir', metavar='DIR', default='.',
56 help='Base directory of the Nominatim installation (default:.)')
57 group.add_argument('-j', '--threads', metavar='NUM', type=int,
58 help='Number of parallel threads to use')
61 def nominatim_version_text():
62 """ Program name and version number as string
64 return "Nominatim version %s.%s.%s.%s\n" % version.NOMINATIM_VERSION
66 def add_subcommand(self, name, cmd):
67 """ Add a subcommand to the parser. The subcommand must be a class
68 with a function add_args() that adds the parameters for the
69 subcommand and a run() function that executes the command.
71 parser = self.subs.add_parser(name, parents=[self.default_args],
72 help=cmd.__doc__.split('\n', 1)[0],
73 description=cmd.__doc__,
74 formatter_class=argparse.RawDescriptionHelpFormatter,
76 parser.set_defaults(command=cmd)
79 def run(self, **kwargs):
80 """ Parse the command line arguments of the program and execute the
81 appropriate subcommand.
83 args = NominatimArgs()
85 self.parser.parse_args(args=kwargs.get('cli_args'), namespace=args)
89 if args.subcommand is None:
90 self.parser.print_help()
93 for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'sqllib_dir',
94 'data_dir', 'config_dir', 'phpcgi_path'):
95 setattr(args, arg, Path(kwargs[arg]))
96 args.project_dir = Path(args.project_dir).resolve()
98 if 'cli_args' not in kwargs:
99 logging.basicConfig(stream=sys.stderr,
100 format='%(asctime)s: %(message)s',
101 datefmt='%Y-%m-%d %H:%M:%S',
102 level=max(4 - args.verbose, 1) * 10)
104 args.config = Configuration(args.project_dir, args.config_dir,
105 environ=kwargs.get('environ', os.environ))
106 args.config.set_libdirs(module=args.module_dir,
107 osm2pgsql=args.osm2pgsql_path,
112 log = logging.getLogger()
113 log.warning('Using project directory: %s', str(args.project_dir))
116 return args.command.run(args)
117 except UsageError as exception:
118 if log.isEnabledFor(logging.DEBUG):
119 raise # use Python's exception printing
120 log.fatal('FATAL: %s', exception)
122 # If we get here, then execution has failed in some way.
128 # Each class needs to implement two functions: add_args() adds the CLI parameters
129 # for the subfunction, run() executes the subcommand.
131 # The class documentation doubles as the help text for the command. The
132 # first line is also used in the summary when calling the program without
135 # No need to document the functions each time.
136 # pylint: disable=C0111
139 Export addresses as CSV file from the database.
143 def add_args(parser):
144 group = parser.add_argument_group('Output arguments')
145 group.add_argument('--output-type', default='street',
146 choices=('continent', 'country', 'state', 'county',
147 'city', 'suburb', 'street', 'path'),
148 help='Type of places to output (default: street)')
149 group.add_argument('--output-format',
150 default='street;suburb;city;county;state;country',
151 help=("Semicolon-separated list of address types "
152 "(see --output-type). Multiple ranks can be "
153 "merged into one column by simply using a "
154 "comma-separated list."))
155 group.add_argument('--output-all-postcodes', action='store_true',
156 help=("List all postcodes for address instead of "
157 "just the most likely one"))
158 group.add_argument('--language',
159 help=("Preferred language for output "
160 "(use local name, if omitted)"))
161 group = parser.add_argument_group('Filter arguments')
162 group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
163 help='Export only objects within country')
164 group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
165 help='Export only children of this OSM node')
166 group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
167 help='Export only children of this OSM way')
168 group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
169 help='Export only children of this OSM relation')
174 params = ['export.php',
175 '--output-type', args.output_type,
176 '--output-format', args.output_format]
177 if args.output_all_postcodes:
178 params.append('--output-all-postcodes')
180 params.extend(('--language', args.language))
181 if args.restrict_to_country:
182 params.extend(('--restrict-to-country', args.restrict_to_country))
183 if args.restrict_to_osm_node:
184 params.extend(('--restrict-to-osm-node', args.restrict_to_osm_node))
185 if args.restrict_to_osm_way:
186 params.extend(('--restrict-to-osm-way', args.restrict_to_osm_way))
187 if args.restrict_to_osm_relation:
188 params.extend(('--restrict-to-osm-relation', args.restrict_to_osm_relation))
190 return run_legacy_script(*params, nominatim_env=args)
195 Start a simple web server for serving the API.
197 This command starts the built-in PHP webserver to serve the website
198 from the current project directory. This webserver is only suitable
199 for testing and development. Do not use it in production setups!
201 By the default, the webserver can be accessed at: http://127.0.0.1:8088
205 def add_args(parser):
206 group = parser.add_argument_group('Server arguments')
207 group.add_argument('--server', default='127.0.0.1:8088',
208 help='The address the server will listen to.')
212 run_php_server(args.server, args.project_dir / 'website')
214 def get_set_parser(**kwargs):
216 Initializes the parser and adds various subcommands for
219 parser = CommandlineParser('nominatim', nominatim.__doc__)
221 parser.add_subcommand('import', clicmd.SetupAll)
222 parser.add_subcommand('freeze', clicmd.SetupFreeze)
223 parser.add_subcommand('replication', clicmd.UpdateReplication)
225 parser.add_subcommand('special-phrases', clicmd.ImportSpecialPhrases)
227 parser.add_subcommand('add-data', clicmd.UpdateAddData)
228 parser.add_subcommand('index', clicmd.UpdateIndex)
229 parser.add_subcommand('refresh', clicmd.UpdateRefresh())
231 parser.add_subcommand('admin', clicmd.AdminFuncs)
233 parser.add_subcommand('export', QueryExport)
234 parser.add_subcommand('serve', AdminServe)
236 if kwargs.get('phpcgi_path'):
237 parser.add_subcommand('search', clicmd.APISearch)
238 parser.add_subcommand('reverse', clicmd.APIReverse)
239 parser.add_subcommand('lookup', clicmd.APILookup)
240 parser.add_subcommand('details', clicmd.APIDetails)
241 parser.add_subcommand('status', clicmd.APIStatus)
243 parser.parser.epilog = 'php-cgi not found. Query commands not available.'
248 def nominatim(**kwargs):
250 Command-line tools for importing, updating, administrating and
251 querying the Nominatim database.
253 parser = get_set_parser(**kwargs)
255 return parser.run(**kwargs)