Action() is kw_only, abbr= is optional, closes #2571

This commit is contained in:
Simon Willison 2025-11-01 20:20:17 -07:00
commit 063bf7a96f
4 changed files with 10 additions and 9 deletions

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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,