Skip to content

Commit b108e06

Browse files
committed
Refine spec: preserve source order and provenance comments in uv constraint-dependencies
1 parent b194245 commit b108e06

1 file changed

Lines changed: 51 additions & 23 deletions

File tree

‎docs/superpowers/specs/2026-05-31-uv-constraint-dependencies-design.md‎

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,36 +47,60 @@ write_hooks() → UvPyprojectUpdater.write()
4747

4848
### 1. Filter-/Transform-Funktion (rein, testbar)
4949

50-
`_constraints_to_uv(constraints: list[str]) -> list[str]` im uv-Modul:
51-
52-
1. Über jede Zeile aus `state.constraints` iterieren.
53-
2. Überspringen: Leerzeilen, `####`-Trennlinien, alle mit `#` beginnenden Kommentarzeilen
54-
(das schließt die `# begin/end constraints from ...`-Header **und** die
55-
`# <pkg> -> mxdev disabled (...)`-Zeilen ein). Dadurch fallen aus-Source-entwickelte und
56-
per Override ersetzte Pakete automatisch raus — korrekt, da diese über `[tool.uv.sources]`
57-
bzw. `override-dependencies` kommen.
58-
3. Verbleibende Zeilen mit `packaging.requirements.Requirement` parsen. Nicht-parsebare
59-
Zeilen (z.B. `--hash`, `--index-url`, `-e ...`) verwerfen und auf DEBUG loggen.
60-
4. **Kein Dedupe, keine Konfliktauflösung** — konsistent mit dem klassischen Pfad
50+
Leitidee: Das `constraint-dependencies`-Array wird ein **TOML-Spiegel von
51+
`constraints-mxdev.txt`** — gleiche **Quell-Reihenfolge**, Herkunfts-Kommentare erhalten.
52+
Specifier-Zeilen werden zu Array-Einträgen, Kommentarzeilen zu Array-internen Kommentaren.
53+
54+
`_constraints_to_uv(constraints: list[str]) -> list[ConstraintItem]` im uv-Modul, wobei
55+
`ConstraintItem` ein kleines, geordnetes Element ist, das **entweder** einen Kommentar
56+
**oder** einen Specifier trägt (z.B. `("comment", text)` / `("entry", specifier)`). So bleibt
57+
die Funktion rein und testbar und der Writer kann Kommentare + Einträge in Reihenfolge
58+
emittieren.
59+
60+
Pro Zeile aus `state.constraints`, in Original-Reihenfolge:
61+
62+
1. **Specifier-Zeilen** mit `packaging.requirements.Requirement` parsen → als `entry`
63+
übernehmen. Environment-Markers (`; python_version >= "3.9"`) bleiben erhalten.
64+
Nicht-parsebare „Zeilen, die wie Specifier aussehen" (z.B. `--hash`, `--index-url`,
65+
`-e ...`) verwerfen und auf DEBUG loggen.
66+
2. **Herkunfts-Kommentare** (`# begin constraints from: <url>` / `# end constraints from: <url>`)
67+
als `comment` erhalten — sie liefern die Nachvollziehbarkeit, die der txt-Pfad bietet.
68+
3. **`# <pkg> -> mxdev disabled (...)`-Zeilen** als `comment` erhalten (1:1 wie in
69+
`constraints-mxdev.txt`). Sie dokumentieren, *warum* ein Pin hier fehlt (das Paket kommt
70+
über `[tool.uv.sources]` bzw. `override-dependencies`). → **Auf Review verifizieren**, ob
71+
gewünscht; Alternative wäre, sie wegzulassen.
72+
4. **Dekorative `####…`-Trennlinien und Leerzeilen** verwerfen (reines visuelles Rauschen;
73+
die begin/end-Header liefern die Gruppierung).
74+
5. **Kein Dedupe, keine Konfliktauflösung** — konsistent mit dem klassischen Pfad
6175
(`constraints-mxdev.txt` ist reiner Passthrough; pip/uv behandelt Konflikte). Konfligierende
6276
Versionen desselben Pakets bleiben beide erhalten.
63-
5. **Alphabetisch sortieren**, case-insensitive nach Paketname (diff-freundlich).
64-
Environment-Markers (`; python_version >= "3.9"`) bleiben am Specifier erhalten.
65-
6. Rückgabe: Liste sortierter Specifier-Strings.
77+
6. **Keine Sortierung** — Quell-Reihenfolge wird beibehalten (Gruppen-Kohärenz vor
78+
Diff-Minimierung).
6679

6780
### 2. Schreiben nach pyproject.toml
6881

6982
Erweiterung von `_update_pyproject` in [src/mxdev/uv.py](../../../src/mxdev/uv.py):
7083

7184
- Ziel: `[tool.uv] constraint-dependencies` als multiline-Array (analog zum bestehenden
72-
`override-dependencies`-Block, `uv.py:152-156`).
85+
`override-dependencies`-Block, `uv.py:152-156`), aber mit **Array-internen Kommentaren**
86+
in Quell-Reihenfolge.
7387
- **Ersetzen + Marker:** Das Array wird vollständig von mxdev verwaltet und bei jedem Lauf
74-
überschrieben. Kennzeichnung „managed by mxdev“ — bevorzugt als Array-interner Kommentar
75-
via tomlkit; falls tomlkit das nicht stabil serialisiert, als Kommentarzeile direkt über
76-
dem Array (`# managed by mxdev — do not edit`). **Im Plan zu verifizieren.**
88+
überschrieben. Eine `# managed by mxdev — do not edit`-Kommentarzeile als erstes Element.
7789
- Leeres Filterergebnis → Key nicht schreiben; ein bestehender, mxdev-verwalteter Key wird in
7890
dem Fall geleert/entfernt, damit kein veralteter Stand stehen bleibt.
7991

92+
### ⚠️ Technisches Hauptrisiko: tomlkit & Array-interne Kommentare
93+
94+
Array-interne Kommentare über viele Zeilen sind die heikelste tomlkit-Stelle. **Früh im Plan
95+
verifizieren** (kleiner Spike), dass tomlkit interspersed comments in einem multiline-Array
96+
korrekt **und idempotent** serialisiert (zweiter Lauf = byte-identisch).
97+
98+
Falls tomlkit das nicht sauber round-trippt, **Fallback** (Reihenfolge bleibt erhalten):
99+
Herkunft als **Inline-Kommentar pro Eintrag** statt als eigene Kommentarzeile, z.B.
100+
`"Zope==6.0", # from <url>`. Das unterstützt tomlkit zuverlässig. Die begin/end-Blockheader
101+
werden dann zu einem Inline-`# from <url>`-Suffix pro Eintrag verdichtet; die disabled-Marker
102+
entfallen in diesem Fallback.
103+
80104
### 3. Opt-out
81105

82106
- Default **an**, sobald `[tool.uv] managed = true` (konsistent mit `sources`/`override-dependencies`).
@@ -96,9 +120,11 @@ Erweiterung von `_update_pyproject` in [src/mxdev/uv.py](../../../src/mxdev/uv.p
96120
## Tests (TDD)
97121

98122
Reine Funktion `_constraints_to_uv`:
99-
- Kommentare, `####`-Trenner, `# ... -> mxdev disabled`-Zeilen werden gefiltert.
123+
- Specifier-Zeilen → `entry`, in **Quell-Reihenfolge** (keine Sortierung).
124+
- `# begin/end constraints from`-Header → als `comment` erhalten.
125+
- `# ... -> mxdev disabled`-Zeilen → als `comment` erhalten.
126+
- `####`-Trenner und Leerzeilen werden verworfen.
100127
- Nicht-parsebare Zeilen (`--hash`, `-e`, `--index-url`) werden verworfen.
101-
- Alphabetische Sortierung (case-insensitive).
102128
- Environment-Markers bleiben erhalten.
103129
- Konfligierende/doppelte Versionen bleiben beide erhalten (Passthrough).
104130

@@ -107,12 +133,13 @@ Hook-Integration:
107133
- `managed=false`/fehlend → nicht geschrieben.
108134
- Opt-out-Setting `uv-constraint-dependencies = false` greift.
109135
- Bestehendes Array wird ersetzt (nicht gemerged).
110-
- Idempotenz: zweiter Lauf erzeugt identisches Ergebnis.
136+
- Herkunfts-Kommentare landen in Quell-Reihenfolge im Array.
137+
- Idempotenz: zweiter Lauf erzeugt **byte-identisches** Ergebnis (deckt das tomlkit-Risiko ab).
111138
- Leeres Ergebnis → kein/leerer Key.
112139

113140
Ende-zu-Ende:
114-
- `-c URL`-Kette via httpretty-Fixture (wie in bestehenden Tests) → korrekt aufgelöste,
115-
sortierte `constraint-dependencies`.
141+
- `-c URL`-Kette via httpretty-Fixture (wie in bestehenden Tests) → korrekt aufgelöste
142+
`constraint-dependencies` in Quell-Reihenfolge mit Herkunfts-Kommentaren.
116143

117144
## Bewusst ausgeklammert (YAGNI)
118145

@@ -121,3 +148,4 @@ Ende-zu-Ende:
121148
- Keine separate `uv-constraints = URL`-Konfiguration (die bestehende `requirements-in`-Kette
122149
ist die einzige Quelle der Wahrheit).
123150
- Keine Konflikt-/Dedupe-Logik (Konsistenz mit dem klassischen Pfad).
151+
- Keine alphabetische Sortierung (Quell-Reihenfolge + Gruppen-Header gewählt).

0 commit comments

Comments
 (0)