From 063bf7a96f42a62df6253128b48f230946e5450f Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 1 Nov 2025 20:20:17 -0700 Subject: [PATCH] Action() is kw_only, abbr= is optional, closes #2571 --- datasette/permissions.py | 4 ++-- docs/plugin_hooks.rst | 2 +- docs/upgrade-1.0a20.md | 4 +--- tests/test_plugins.py | 9 ++++++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/datasette/permissions.py b/datasette/permissions.py index 8e0d0fc1..7b1fc90c 100644 --- a/datasette/permissions.py +++ b/datasette/permissions.py @@ -95,11 +95,11 @@ class AllowedResource(NamedTuple): reason: str -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class Action: name: str - abbr: str | None description: str | None + abbr: str | None = None resource_class: type[Resource] | None = None also_requires: str | None = None # Optional action name that must also be allowed diff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst index 3156aa7d..51e4a69f 100644 --- a/docs/plugin_hooks.rst +++ b/docs/plugin_hooks.rst @@ -905,7 +905,7 @@ The fields of the ``Action`` dataclass are as follows: The name of the action, e.g. ``view-document``. This should be unique across all plugins. ``abbr`` - string or None - An abbreviation of the action, e.g. ``vdoc``. This is optional. Since this needs to be unique across all installed plugins it's best to choose carefully or use ``None``. + An abbreviation of the action, e.g. ``vdoc``. This is optional. Since this needs to be unique across all installed plugins it's best to choose carefully or omit it entirely (same as setting it to ``None``.) ``description`` - string or None A human-readable description of what the action allows you to do. diff --git a/docs/upgrade-1.0a20.md b/docs/upgrade-1.0a20.md index 339ab588..0dbb9626 100644 --- a/docs/upgrade-1.0a20.md +++ b/docs/upgrade-1.0a20.md @@ -63,23 +63,21 @@ def register_actions(datasette): return [ Action( name="explain-sql", - abbr=None, description="Explain SQL queries", resource_class=DatabaseResource, ), Action( name="annotate-rows", - abbr=None, description="Annotate rows", resource_class=TableResource, ), Action( name="view-debug-info", - abbr=None, description="View debug information", ), ] ``` +The `abbr=` is now optional and defaults to `None`. For actions that apply to specific resources (like databases or tables), specify the `resource_class` instead of `takes_parent` and `takes_child`. Note that `view-debug-info` does not specify a `resource_class` because it applies globally. diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 1c601b27..5a530b25 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1600,7 +1600,6 @@ async def test_hook_register_actions_with_custom_resources(): # Parent-level action - collection only Action( name="view-document-collection", - abbr="vdc", description="View a document collection", resource_class=DocumentCollectionResource, ), @@ -1651,7 +1650,7 @@ async def test_hook_register_actions_with_custom_resources(): # Test parent-level action view_collection = datasette.actions["view-document-collection"] assert view_collection.name == "view-document-collection" - assert view_collection.abbr == "vdc" + assert view_collection.abbr is None assert view_collection.resource_class is DocumentCollectionResource assert view_collection.takes_parent is True assert view_collection.takes_child is False @@ -1705,7 +1704,11 @@ async def test_hook_register_actions_with_custom_resources(): # Test 4: Collection-level action - allowed for specific collection collection_resource = DocumentCollectionResource(collection="collection1") - restricted_collection = {"id": "user4", "_r": {"d": {"collection1": ["vdc"]}}} + # This one does not have an abbreviation: + restricted_collection = { + "id": "user4", + "_r": {"d": {"collection1": ["view-document-collection"]}}, + } allowed = await datasette.allowed( action="view-document-collection", resource=collection_resource,