Skip to content

Commit

Permalink
New --setting to replace --config, closes #992
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Nov 24, 2020
1 parent 4bac9f1 commit 3159263
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 4 deletions.
56 changes: 54 additions & 2 deletions datasette/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import uvicorn
import click
from click import formatting
from click.types import CompositeParamType
from click_default_group import DefaultGroup
import json
import os
Expand Down Expand Up @@ -29,6 +30,7 @@


class Config(click.ParamType):
# This will be removed in Datasette 1.0 in favour of class Setting
name = "config"

def convert(self, config, param, ctx):
Expand Down Expand Up @@ -63,6 +65,39 @@ def convert(self, config, param, ctx):
self.fail("Invalid option")


class Setting(CompositeParamType):
name = "setting"
arity = 2

def convert(self, config, param, ctx):
name, value = config
if name not in DEFAULT_CONFIG:
self.fail(
f"{name} is not a valid option (--help-config to see all)",
param,
ctx,
)
return
# Type checking
default = DEFAULT_CONFIG[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")


@click.group(cls=DefaultGroup, default="serve", default_if_no_args=True)
@click.version_option(version=__version__)
def cli():
Expand Down Expand Up @@ -330,7 +365,14 @@ def uninstall(packages, yes):
@click.option(
"--config",
type=Config(),
help="Set config option using configname:value docs.datasette.io/en/stable/config.html",
help="Deprecated: set config option using configname:value. Use --setting instead.",
multiple=True,
)
@click.option(
"--setting",
"settings",
type=Setting(),
help="Setting, see docs.datasette.io/en/stable/config.html",
multiple=True,
)
@click.option(
Expand Down Expand Up @@ -372,6 +414,7 @@ def serve(
static,
memory,
config,
settings,
secret,
root,
get,
Expand Down Expand Up @@ -410,6 +453,15 @@ def serve(
if metadata:
metadata_data = parse_metadata(metadata.read())

combined_config = {}
if config:
click.echo(
"--config name:value will be deprecated in Datasette 1.0, use --setting name value instead",
err=True,
)
combined_config.update(config)
combined_config.update(settings)

kwargs = dict(
immutables=immutable,
cache_headers=not reload,
Expand All @@ -420,7 +472,7 @@ def serve(
template_dir=template_dir,
plugins_dir=plugins_dir,
static_mounts=static,
config=dict(config),
config=combined_config,
memory=memory,
secret=secret,
version_note=version_note,
Expand Down
5 changes: 3 additions & 2 deletions docs/datasette-serve-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ Options:
--plugins-dir DIRECTORY Path to directory containing custom plugins
--static MOUNT:DIRECTORY Serve static files from this directory at /MOUNT/...
--memory Make :memory: database available
--config CONFIG Set config option using configname:value
docs.datasette.io/en/stable/config.html
--config CONFIG Deprecated: set config option using configname:value. Use
--setting instead.

--setting SETTING... Setting, see docs.datasette.io/en/stable/config.html
--secret TEXT Secret used for signing secure values, such as signed
cookies

Expand Down
36 changes: 36 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
TestClient as _TestClient,
EXPECTED_PLUGINS,
)
import asyncio
from datasette.plugins import DEFAULT_PLUGINS
from datasette.cli import cli, serve
from datasette.version import __version__
Expand All @@ -17,6 +18,13 @@
from unittest import mock


@pytest.fixture
def ensure_eventloop():
# Workaround for "Event loop is closed" error
if asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop())


def test_inspect_cli(app_client):
runner = CliRunner()
result = runner.invoke(cli, ["inspect", "fixtures.db"])
Expand Down Expand Up @@ -115,6 +123,7 @@ def test_metadata_yaml():
static=[],
memory=False,
config=[],
settings=[],
secret=None,
root=False,
version_note=None,
Expand Down Expand Up @@ -163,3 +172,30 @@ def test_version():
runner = CliRunner()
result = runner.invoke(cli, ["--version"])
assert result.output == f"cli, version {__version__}\n"


def test_setting(ensure_eventloop):
runner = CliRunner()
result = runner.invoke(
cli, ["--setting", "default_page_size", "5", "--get", "/-/config.json"]
)
assert result.exit_code == 0, result.output
assert json.loads(result.output)["default_page_size"] == 5


def test_setting_type_validation(ensure_eventloop):
runner = CliRunner(mix_stderr=False)
result = runner.invoke(cli, ["--setting", "default_page_size", "dog"])
assert result.exit_code == 2
assert '"default_page_size" should be an integer' in result.stderr


def test_config_deprecated(ensure_eventloop):
# The --config option should show a deprecation message
runner = CliRunner(mix_stderr=False)
result = runner.invoke(
cli, ["--config", "allow_download:off", "--get", "/-/config.json"]
)
assert result.exit_code == 0
assert not json.loads(result.output)["allow_download"]
assert "will be deprecated in" in result.stderr

0 comments on commit 3159263

Please sign in to comment.