Skip to content

Settings form logic and saving settings into a plugin block (M5 part 3a-1) - #252

Open
txoof-bot wants to merge 5 commits into
mainfrom
238-settings-form
Open

txoof-bot wants to merge 5 commits into
mainfrom
238-settings-form

Conversation

@txoof-bot

@txoof-bot txoof-bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #238. Merge order (top-down): merge #253 first (it goes into this branch), then this PR into main last. #251 is merged already; this branch is up to date with main.

What changed

The logic behind the plugin settings form. The page itself comes in 3a-2; this part has no new page yet (like 2b-1 was).

  • web/forms.py
    • fields() builds the form's fields from a plugin's settings description: choice list, check box, number (with its limits), text (with its length limit), secret (never put on the page), pick-several list. A setting of a kind the form can't show (a group of settings) is shown as it is in the file, to be changed there.
    • Groups as agreed: the name, then the plugin's own settings, then display time / refresh / layout / level, then the rest ("More settings").
    • Each field shows the value in the file, or the default (including the plugin's suggestions for refresh, layout and storage).
    • read() turns a sent form into the changes to save, or an error next to each field (then nothing is saved). The checks are the plugin's own settings description and the shared one, the same as the config check.
  • config_file.set_settings() writes those changes into one [[plugin]] block as text, keeping everything else.
    • A new value takes the place of its # key = ... comment line, so the help text above it stays.
    • A value changed back to its default becomes # key = default again.
    • name goes right below id.
    • A value written over several lines is never changed.
    • As in part 2, the result is read back and compared before saving.
  • PluginEditor.settings() / save_settings() join these under the config-file lock and apply the change at once. Saving the same form twice writes nothing the second time.
  • pytest-xdist as a dev dependency: uv run pytest -n auto runs the tests on all 4 cores (about 1.5 instead of 4.5 minutes on the Pi). CLAUDE.md says when to run which tests.
  • example.toml_value, choices and plain are now public (the form uses them). Notes added to docs/decisions/web-interface.md.

Choices made without asking:

  • A field missing from the form is left as it is. The page (3a-2) sends a hidden empty value for each check box and list, because browsers send nothing for an unticked box.
  • A value left as it is stays in the file, even one written there that equals the default. Only a value the user changes to its default is taken out.
  • Numbers must be plain digits (12, -1.5). 1_0, nan, 1e3 and other scripts' digits are refused.
  • A list picks each choice at most once.

Size: +937/−24 (about 960, of which 24 is uv.lock and about half is tests). That is over the guideline; it grew with the review fixes, which added tests for the cases they found.

Area

web (src/paperpi/web/forms.py, config_file.py, plugins.py), core (example.py: three helpers made public), ci (pyproject.toml, uv.lock: pytest-xdist), docs (CLAUDE.md, docs/decisions/web-interface.md).

Tests

New: tests/test_web_forms.py (fields, groups, kinds, reading, errors, secrets, lists, numbers, shown again after errors), the settings part of tests/test_web_config_file.py (comment lines, defaults, quoted keys, similar keys, values over several lines, CRLF), and save_settings in tests/test_web_plugins.py.

  • uv run pytest passes (1379, uv run pytest -n auto on the Pi)
  • uv run ruff check . passes
  • Hardware tests run on the Pi (only if display or driver code changed): not needed

Review

  • Review agents' comments answered: 2 local reviews (code + tests, security + docs) before the first push, all findings fixed in the second commit.
    • Blocking one found: a list from the file (a list) never matched the form's value (a tuple), so every save rewrote it.
    • Also fixed: a secret typed into a form with errors was kept to be shown again; repeated list values made a huge file; loose number parsing; a new setting could be put inside a value written over several lines; check boxes and lists shown wrongly after errors.

Only txoof approves and merges this PR.

🤖 Generated with Claude Code

txoof-bot and others added 4 commits October 8, 2026 09:40
Each [[plugin]] block now has a fixed `id` (letters, digits, _ and -, at most
40, unique; names the storage folder) and an optional friendly `name` shown
by the web interface. The Plugin Library makes the id (type + 8 random hex
characters); the name may be empty or the same as another plugin's.
`paperpi render --config` takes `--id`; `paperpi list` shows both.

Part of #238

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o a plugin block (M5 part 3a-1)

forms.fields() builds the settings form's fields from a plugin's settings
description (groups: name, own, common, more); forms.read() turns a sent form
into checked changes with an error per field. config_file.set_settings()
writes them into one [[plugin]] block (defaults back to comments, read-back
check). PluginEditor.save_settings() ties them together. pytest-xdist added
as a dev dependency (uv run pytest -n auto).

Part of #238

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et echoed, values over several lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@txoof-bot
txoof-bot requested a review from txoof as a code owner October 8, 2026 08:08
Base automatically changed from 238-plugin-id to main October 8, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant