-s/--setting x y gets merged into datasette.yml, refs #2143, #2156

This change updates the `-s/--setting` option to `datasette serve` to allow it to be used to set arbitrarily complex nested settings in a way that is compatible with the new `-c datasette.yml` work happening in:
- #2143

It will enable things like this:
```
datasette data.db --setting plugins.datasette-ripgrep.path "/home/simon/code"
```
For the moment though it just affects [settings](https://docs.datasette.io/en/1.0a4/settings.html) - so you can do this:
```
datasette data.db --setting settings.sql_time_limit_ms 3500
```
I've also implemented a backwards compatibility mechanism, so if you use it this way (the old way):
```
datasette data.db --setting sql_time_limit_ms 3500
```
It will notice that the setting you passed is one of Datasette's core settings, and will treat that as if you said `settings.sql_time_limit_ms` instead.
This commit is contained in:
Simon Willison 2023-08-28 13:06:14 -07:00 committed by GitHub
commit d9aad1fd04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 47 deletions

View file

@ -31,6 +31,7 @@ from .utils import (
ConnectionProblem,
SpatialiteConnectionProblem,
initial_path_for_datasette,
pairs_to_nested_config,
temporary_docker_directory,
value_as_boolean,
SpatialiteNotFound,
@ -56,35 +57,27 @@ class Setting(CompositeParamType):
def convert(self, config, param, ctx):
name, value = config
if name not in DEFAULT_SETTINGS:
msg = (
OBSOLETE_SETTINGS.get(name)
or f"{name} is not a valid option (--help-settings to see all)"
)
self.fail(
msg,
param,
ctx,
)
return
# Type checking
default = DEFAULT_SETTINGS[name]
if isinstance(default, bool):
try:
return name, value_as_boolean(value)
except ValueAsBooleanError:
self.fail(f'"{name}" should be on/off/true/false/1/0', param, ctx)
return
elif isinstance(default, int):
if not value.isdigit():
self.fail(f'"{name}" should be an integer', param, ctx)
return
return name, int(value)
elif isinstance(default, str):
return name, value
else:
# Should never happen:
self.fail("Invalid option")
if name in DEFAULT_SETTINGS:
# For backwards compatibility with how this worked prior to
# Datasette 1.0, we turn bare setting names into setting.name
# Type checking for those older settings
default = DEFAULT_SETTINGS[name]
name = "settings.{}".format(name)
if isinstance(default, bool):
try:
return name, "true" if value_as_boolean(value) else "false"
except ValueAsBooleanError:
self.fail(f'"{name}" should be on/off/true/false/1/0', param, ctx)
elif isinstance(default, int):
if not value.isdigit():
self.fail(f'"{name}" should be an integer', param, ctx)
return name, value
elif isinstance(default, str):
return name, value
else:
# Should never happen:
self.fail("Invalid option")
return name, value
def sqlite_extensions(fn):
@ -425,7 +418,7 @@ def uninstall(packages, yes):
"--setting",
"settings",
type=Setting(),
help="Setting, see docs.datasette.io/en/stable/settings.html",
help="nested.key, value setting to use in Datasette configuration",
multiple=True,
)
@click.option(
@ -547,6 +540,13 @@ def serve(
if config:
config_data = parse_metadata(config.read())
config_data = config_data or {}
# Merge in settings from -s/--setting
if settings:
settings_updates = pairs_to_nested_config(settings)
config_data.update(settings_updates)
kwargs = dict(
immutables=immutable,
cache_headers=not reload,
@ -558,7 +558,7 @@ def serve(
template_dir=template_dir,
plugins_dir=plugins_dir,
static_mounts=static,
settings=dict(settings),
settings=None, # These are passed in config= now
memory=memory,
secret=secret,
version_note=version_note,