wikitool: --set kann keine Array-Werte mit Komma ausdrücken #12

Closed
opened 2026-08-30 19:35:21 +00:00 by torben · 1 comment
Owner

Problem

_coerce_set_value() reicht jeden Wert eines Array-Feldes an commands/_util.parse_list() weiter, das hart an , splittet:

def parse_list(value: str | None) -> list[str]:
    if not value:
        return []
    return [v.strip() for v in value.split(",") if v.strip()]

Es gibt kein Escape und keine Alternative. Ein Listenelement, das selbst ein Komma enthält, ist damit nicht darstellbar — auch nicht mit Quoting, weil die Shell-Quotes vor dem Split verschwinden.

Reproduktion

Eine Rohdatei, deren Name ein Komma enthält:

$ tools/wikitool new source --name "..." \
    --set "raw_files=raw/notes/Conversation Transcript - Versioning, CI-CD and Content Migration Session 2026-08-30.md"
ERROR raw_files path does not exist: raw/notes/Conversation Transcript - 
Versioning

Der Pfad kommt als zwei nicht existierende Dateien an.

Warum das mehr als kosmetisch ist

raw_files ist ein Pfadfeld, und Dateinamen mit Komma sind normal. Betroffen sind ebenso entities, concepts und related, sobald ein Seitentitel ein Komma trägt.

tools/CONTRACT.md schließt beim new <type>-Fehlerfall den Ausweg explizit aus („fix the argument and retry once. Never hand-craft the page instead"), also bleibt beim heutigen Stand nur, das Argument selbst zu ändern — im Ingest vom 2026-08-30 wurde die Rohdatei umbenannt, obwohl raw/CONTRACT.md die Rohschicht als unveränderlich führt.

Lösungsvorschläge

  1. Escape in parse_list: \, bleibt ein Literal-Komma. Minimal, rückwärtskompatibel.
  2. --set für Array-Felder wiederholbar machen, sodass jede Wiederholung genau ein Element anhängt (--set raw_files=a --set raw_files=b). Umgeht das Trennzeichen ganz.
  3. Beides — (2) als saubere Form, (1) für Einzeiler.

Zusätzlich: _check_raw_files_exist sollte den Fehlertext klar als Split-Ergebnis kennzeichnen, damit die Ursache nicht erst im Quelltext gesucht werden muss.

## Problem `_coerce_set_value()` reicht jeden Wert eines Array-Feldes an `commands/_util.parse_list()` weiter, das hart an `,` splittet: ```python def parse_list(value: str | None) -> list[str]: if not value: return [] return [v.strip() for v in value.split(",") if v.strip()] ``` Es gibt kein Escape und keine Alternative. Ein Listenelement, das selbst ein Komma enthält, ist damit nicht darstellbar — auch nicht mit Quoting, weil die Shell-Quotes vor dem Split verschwinden. ## Reproduktion Eine Rohdatei, deren Name ein Komma enthält: ``` $ tools/wikitool new source --name "..." \ --set "raw_files=raw/notes/Conversation Transcript - Versioning, CI-CD and Content Migration Session 2026-08-30.md" ERROR raw_files path does not exist: raw/notes/Conversation Transcript - Versioning ``` Der Pfad kommt als zwei nicht existierende Dateien an. ## Warum das mehr als kosmetisch ist `raw_files` ist ein Pfadfeld, und Dateinamen mit Komma sind normal. Betroffen sind ebenso `entities`, `concepts` und `related`, sobald ein Seitentitel ein Komma trägt. `tools/CONTRACT.md` schließt beim `new <type>`-Fehlerfall den Ausweg explizit aus („fix the argument and retry once. **Never hand-craft the page instead**"), also bleibt beim heutigen Stand nur, das Argument selbst zu ändern — im Ingest vom 2026-08-30 wurde die Rohdatei umbenannt, obwohl `raw/CONTRACT.md` die Rohschicht als unveränderlich führt. ## Lösungsvorschläge 1. Escape in `parse_list`: `\,` bleibt ein Literal-Komma. Minimal, rückwärtskompatibel. 2. `--set` für Array-Felder wiederholbar machen, sodass jede Wiederholung genau ein Element anhängt (`--set raw_files=a --set raw_files=b`). Umgeht das Trennzeichen ganz. 3. Beides — (2) als saubere Form, (1) für Einzeiler. Zusätzlich: `_check_raw_files_exist` sollte den Fehlertext klar als Split-Ergebnis kennzeichnen, damit die Ursache nicht erst im Quelltext gesucht werden muss.
Author
Owner

Behoben in 1.2.0 (40adbb7).

Umgesetzt wurden Vorschlag 1 und 2 — beides, weil sie verschiedene Fälle bedienen:

  • \, ist ein literales Komma in parse_list (commands/_util.py). Gilt damit auch für xref add --entities.
  • Wiederholtes --set auf ein Arrayfeld hängt an (--set raw_files=a --set raw_files=b). Skalare Felder behalten „last one wins" — es gibt nichts zum Anhängen. Das ist die Form ohne Trennzeichen und damit die richtige, wenn ein Element ein Komma trägt.
  • _check_raw_files_exist benennt den Split als Ursache und nennt beide Auswege, ausdrücklich einschließlich „never rename the raw file to fit the flag".

Ein zweiter Defekt, den erst der Test sichtbar gemacht hat

Mit Escape und Append allein war der Fall immer noch kaputt — nur eine Schicht tiefer. dump_frontmatter schreibt Listen in Flow-Syntax ([a, b]), quotete aber nach Dokument-Kontext, wo ein Komma ein gewöhnliches Zeichen ist. In [...] ist es ein Indikator:

raw_files: [raw/notes/Versioning, CI-CD.md]   # zwei Elemente beim Zurücklesen

--set hätte den Wert also korrekt geparst, und die Datei hätte ihn beim Schreiben wieder zerlegt. Gefixt in frontmatter_io.py:

  • _round_trips_as_string(text, flow=True) prüft den Rundlauf in dem Kontext, in dem tatsächlich geschrieben wird — es fragt weiterhin den Loader, statt Regeln aufzuzählen.
  • _quote() holt das Quoting für beide Kontexte aus der Bibliothek. Ein nackter Skalar aus safe_dump bringt sonst einen ...-Dokumentende-Marker mit, der in einer Liste Unsinn ist.

Bestehende Ausgabe ändert sich dadurch nicht (tags: [k8s, ci-cd] bleibt ungequotet) — nur Werte, die vorher stillschweigend kaputtgingen.

Tests

tools/wiki_tools/tests/test_util.py (neu), plus Fälle in test_new_page.py und test_frontmatter_io.py. Der aussagekräftigste ist der End-to-End-Fall: eine Rohdatei mit Komma im Namen wird angelegt, per \, referenziert, und die geschriebene Seite liest genau einen Pfad zurück. 658 Tests grün, auch mit leerem HOME und ohne globale git-Config.

Die Rohdatei ist zurückbenannt

5426a6e: raw/notes/Conversation Transcript - Versioning, CI-CD and Content Migration Session 2026-08-30.md trägt wieder ihren Originalnamen. raw_files: der Source-Seite korrigiert, sources rebuild-index, sources coverage 0/0, lint sauber über 255 Seiten.

Eine Lücke, die dabei aufgefallen ist

Kein wikitool-Kommando schreibt raw_files: einer bestehenden Seite. touch fasst nur die Felder an, die die Seite selbst beschreiben, xref nur die Page-Ref-Arrays. Ich musste das Frontmatter von Hand korrigieren — Invariante 1 verbietet das für raw_files: nicht (es steht nicht in ihrer Liste), aber es ist trotzdem der einzige Weg, und das sollte es nicht sein. Ist als eigenes Issue erfasst.

Behoben in **1.2.0** (`40adbb7`). Umgesetzt wurden Vorschlag 1 **und** 2 — beides, weil sie verschiedene Fälle bedienen: - **`\,` ist ein literales Komma** in `parse_list` (`commands/_util.py`). Gilt damit auch für `xref add --entities`. - **Wiederholtes `--set` auf ein Arrayfeld hängt an** (`--set raw_files=a --set raw_files=b`). Skalare Felder behalten „last one wins" — es gibt nichts zum Anhängen. Das ist die Form ohne Trennzeichen und damit die richtige, wenn ein Element ein Komma trägt. - **`_check_raw_files_exist` benennt den Split als Ursache** und nennt beide Auswege, ausdrücklich einschließlich „never rename the raw file to fit the flag". ## Ein zweiter Defekt, den erst der Test sichtbar gemacht hat Mit Escape und Append allein war der Fall **immer noch kaputt** — nur eine Schicht tiefer. `dump_frontmatter` schreibt Listen in Flow-Syntax (`[a, b]`), quotete aber nach Dokument-Kontext, wo ein Komma ein gewöhnliches Zeichen ist. In `[...]` ist es ein Indikator: ``` raw_files: [raw/notes/Versioning, CI-CD.md] # zwei Elemente beim Zurücklesen ``` `--set` hätte den Wert also korrekt geparst, und die Datei hätte ihn beim Schreiben wieder zerlegt. Gefixt in `frontmatter_io.py`: - `_round_trips_as_string(text, flow=True)` prüft den Rundlauf in dem Kontext, in dem tatsächlich geschrieben wird — es fragt weiterhin den Loader, statt Regeln aufzuzählen. - `_quote()` holt das Quoting für beide Kontexte aus der Bibliothek. Ein nackter Skalar aus `safe_dump` bringt sonst einen `...`-Dokumentende-Marker mit, der in einer Liste Unsinn ist. Bestehende Ausgabe ändert sich dadurch nicht (`tags: [k8s, ci-cd]` bleibt ungequotet) — nur Werte, die vorher stillschweigend kaputtgingen. ## Tests `tools/wiki_tools/tests/test_util.py` (neu), plus Fälle in `test_new_page.py` und `test_frontmatter_io.py`. Der aussagekräftigste ist der End-to-End-Fall: eine Rohdatei mit Komma im Namen wird angelegt, per `\,` referenziert, und die geschriebene Seite liest genau einen Pfad zurück. 658 Tests grün, auch mit leerem `HOME` und ohne globale git-Config. ## Die Rohdatei ist zurückbenannt `5426a6e`: `raw/notes/Conversation Transcript - Versioning, CI-CD and Content Migration Session 2026-08-30.md` trägt wieder ihren Originalnamen. `raw_files:` der Source-Seite korrigiert, `sources rebuild-index`, `sources coverage` 0/0, `lint` sauber über 255 Seiten. ## Eine Lücke, die dabei aufgefallen ist **Kein `wikitool`-Kommando schreibt `raw_files:` einer bestehenden Seite.** `touch` fasst nur die Felder an, die die Seite selbst beschreiben, `xref` nur die Page-Ref-Arrays. Ich musste das Frontmatter von Hand korrigieren — Invariante 1 verbietet das für `raw_files:` nicht (es steht nicht in ihrer Liste), aber es ist trotzdem der einzige Weg, und das sollte es nicht sein. Ist als eigenes Issue erfasst.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#12