From 6c70b3ef1d6b88851ce75db99b02b4d70768d57d Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sat, 7 Mar 2026 23:32:32 -0600 Subject: [PATCH 1/2] docs: add register_actions permission check example Fixes simonw/datasette#2469 --- docs/plugin_hooks.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst index b9701f7cf7..55182b9da5 100644 --- a/docs/plugin_hooks.rst +++ b/docs/plugin_hooks.rst @@ -957,6 +957,25 @@ The fields of the ``Action`` dataclass are as follows: - Implement a ``resources_sql()`` classmethod that returns SQL returning all resources as ``(parent, child)`` columns - Have an ``__init__`` method that accepts appropriate parameters and calls ``super().__init__(parent=..., child=...)`` +Checking a registered action +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To check one of these actions in your plugin code, call +``datasette.allowed()`` with both the action name and a resource instance. +For example, this checks if the current actor can view a specific +``DocumentResource``: + +.. code-block:: python + + async def can_view_document( + datasette, actor, collection, document_id + ): + return await datasette.allowed( + actor, + "view-document", + resource=DocumentResource(collection, document_id), + ) + The ``resources_sql()`` method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 5938ffdd61c0987d5c1b99a546b6a81a73fb495d Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sat, 7 Mar 2026 23:39:37 -0600 Subject: [PATCH 2/2] Fix table CSV exports to ignore max_csv_mb --- datasette/views/base.py | 6 +++++- tests/test_csv.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/datasette/views/base.py b/datasette/views/base.py index e4c1c7382d..3bc2cd37c1 100644 --- a/datasette/views/base.py +++ b/datasette/views/base.py @@ -472,7 +472,11 @@ async def stream_csv(datasette, fetch_data, request, database): async def stream_fn(r): nonlocal data, trace - limited_writer = LimitedWriter(r, datasette.setting("max_csv_mb")) + # Table exports should not be constrained by max_csv_mb - that limit + # is intended for arbitrary query exports. + is_table_export = request.url_vars.get("table") is not None + max_csv_mb = 0 if is_table_export else datasette.setting("max_csv_mb") + limited_writer = LimitedWriter(r, max_csv_mb) if trace: await limited_writer.write(preamble) writer = csv.writer(EscapeHtmlWriter(limited_writer)) diff --git a/tests/test_csv.py b/tests/test_csv.py index a2f03776b4..843f51488c 100644 --- a/tests/test_csv.py +++ b/tests/test_csv.py @@ -185,6 +185,23 @@ async def test_csv_with_non_ascii_characters(ds_client): assert response.text == "text,number\r\nšœš¢š­š¢šžš¬,1\r\nbob,2\r\n" +@pytest.mark.asyncio +async def test_table_csv_ignores_max_csv_mb(): + ds = Datasette(settings={"max_csv_mb": 1}) + await ds.invoke_startup() + db = ds.add_memory_database("max_csv") + await db.execute_write("create table t (id integer primary key, content text)") + await db.execute_write( + "insert into t (content) values (?)", + ["a" * (2 * 1024 * 1024)], + ) + + response = await ds.client.get("/max_csv/t.csv") + assert response.status_code == 200 + assert "CSV contains more than" not in response.text + assert len([line for line in response.text.split("\r\n") if line]) == 2 + + @pytest.mark.xfail(reason="Flaky, see https://github.com/simonw/datasette/issues/2355") def test_max_csv_mb(app_client_csv_max_mb_one): # This query deliberately generates a really long string