From facdb041b25de9ee5db26960d03fe8618f13f948 Mon Sep 17 00:00:00 2001 From: Steven Hernandez Date: Sat, 24 Oct 2026 03:25:39 +0200 Subject: [PATCH 01/10] =?UTF-8?q?openpyxl=20im=20Browser,=20Excel-=C3=9Cbu?= =?UTF-8?q?ngen=20in=20Datenformate=20(LRN-12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openpyxl 3.1.5 und et_xmlfile 2.0.0 liegen als reine Python-Wheels in vendor/pyodide und laden wie pytest per Dateiname, sobald Code openpyxl importiert. pnpm verify installiert dieselben Versionen in seine uv-Umgebung. datenformate trägt jetzt place: unterwegs. Zwei neue Abschnitte zeigen Arbeitsmappen, Blätter über Namen, Kopfzeilen als Vertrag, Formeln ohne Neuberechnung, data_only, Zahlenformate und fremden Text, der mit "=" beginnt. Neu: blattreihenfolge (output), excel-bericht und excel-nach-kopf (code). Alle Codezeilen passen auf 360 px; dafür ändern sich der geprüfte Code von pipeline, csv-quote, json-typen, id-verlust und excel-cache. Co-Authored-By: Claude Opus 5.5 --- HANDBUCH.md | 4 +- README.md | 4 +- content/automation/modules/datenformate.yaml | 515 +++++++++++++++--- src/python/packages.ts | 14 +- src/python/worker.ts | 5 +- tooling/verify-code.ts | 12 +- tooling/vite-plugins.ts | 4 +- .../pyodide/et_xmlfile-2.0.0-py3-none-any.whl | Bin 0 -> 18059 bytes .../openpyxl-3.1.5-py2.py3-none-any.whl | Bin 0 -> 250910 bytes 9 files changed, 472 insertions(+), 86 deletions(-) create mode 100644 vendor/pyodide/et_xmlfile-2.0.0-py3-none-any.whl create mode 100644 vendor/pyodide/openpyxl-3.1.5-py2.py3-none-any.whl diff --git a/HANDBUCH.md b/HANDBUCH.md index aea57cea..fecf19fb 100644 --- a/HANDBUCH.md +++ b/HANDBUCH.md @@ -445,7 +445,7 @@ Alles andere (Langoptionen wie `--all`, `sudo`, andere Pfade, Reihenfolge von Ar Laufzeitumgebung (Browser und `pnpm verify` identisch, siehe `src/python/harness.py`): -- Pyodide mit **Python 3.14** und der Standardbibliothek (inklusive `sqlite3`, `json`, `csv`, `re`, `pathlib`, `shutil`, `zipfile`, `hashlib`, `email`, `html.parser`, `xml.etree`, `tomllib`, `unittest.mock`). Zusätzlich verfügbar: `bs4` (BeautifulSoup), `yaml` (PyYAML), `zoneinfo` mit Zeitzonendaten, **pandas 3.0 mit numpy 2.4** sowie **pytest 9.0 und Hypothesis 6.168** (siehe unten). Beim ersten `import pandas` lädt der Browser einmalig etwa 8 MB; das zählt nicht zum Zeitlimit. pandas 3 hat Copy-on-Write und einen eigenen `str`-Datentyp für Text – Beispiele und erwartete Ausgaben müssen dazu passen. `pnpm verify` führt pandas-Code in einer virtuellen Umgebung mit denselben Versionen aus (angelegt mit `uv`). Nicht verfügbar: matplotlib, openpyxl, pyarrow – Code dafür als `python` ohne `run` oder als `practice`. +- Pyodide mit **Python 3.14** und der Standardbibliothek (inklusive `sqlite3`, `json`, `csv`, `re`, `pathlib`, `shutil`, `zipfile`, `hashlib`, `hmac`, `email`, `html.parser`, `xml.etree`, `tomllib`, `logging`, `contextvars`, `unittest.mock`). Zusätzlich verfügbar: `bs4` (BeautifulSoup), `yaml` (PyYAML), `zoneinfo` mit Zeitzonendaten, **pandas 3.0 mit numpy 2.4**, **pytest 9.0 und Hypothesis 6.168** (siehe unten) sowie **openpyxl 3.1** für Excel-Dateien. Beim ersten `import pandas` lädt der Browser einmalig etwa 8 MB, bei openpyxl rund 270 KB; das zählt nicht zum Zeitlimit. pandas 3 hat Copy-on-Write und einen eigenen `str`-Datentyp für Text – Beispiele und erwartete Ausgaben müssen dazu passen. openpyxl rechnet Formeln nicht aus: Eine Formel bleibt Text, `data_only=True` liefert nur Werte, die eine Tabellenkalkulation gespeichert hat. `pnpm verify` führt pandas-, pytest- und openpyxl-Code in einer virtuellen Umgebung mit denselben Versionen aus (angelegt mit `uv`). Nicht verfügbar: matplotlib, pyarrow – Code dafür als `python` ohne `run` oder als `practice`. - **Nicht verfügbar:** Netzwerk, `subprocess`, Threads (`threading`, `ThreadPoolExecutor`), `multiprocessing`-Prozesse, `input()`. Für diese Themen `explain`, `output` (nur deterministisch), `bug` oder `practice` verwenden – oder die Aufgabe so schneiden, dass die Logik als reine Funktion testbar ist (zum Beispiel eine Funktion, die die Befehlsliste für `subprocess.run` *baut*, oder ein Abrufer, der als Parameter übergeben wird). - **Async:** Tests dürfen `await` auf oberster Ebene verwenden (`assert await hole(1) == …`). `asyncio.gather`, `TaskGroup`, `asyncio.timeout` und `asyncio.sleep` funktionieren. Niemals `asyncio.run()` im Lernenden-Code oder in Tests aufrufen – im Browser läuft bereits eine Event-Loop. - Jeder Lauf startet in einem frischen, leeren Arbeitsverzeichnis. `setup` kann dort Dateien anlegen, auch Module: Das Verzeichnis ist importierbar, `from tarif import mietpreis` findet also eine Datei `tarif.py` aus `setup`. @@ -782,7 +782,7 @@ pnpm test:e2e # Browser-Tests (Playwright) pnpm build # TypeScript + Produktionsbuild ``` -`pnpm verify` braucht lokal `python3` und `rustc` (Edition 2024), für pandas- und pytest-Code zusätzlich `uv`: Beim ersten Lauf entsteht unter `node_modules/.cache/verify-python/` eine virtuelle Umgebung mit genau den pandas- und numpy-Versionen des Browsers (aus `pyodide-lock.json`) und den pytest- und Hypothesis-Versionen aus `src/python/packages.ts`. TypeScript- und JavaScript-`output` laufen mit dem Node, das auch `pnpm verify` ausführt. SQL prüft `verify` in Pyodide unter Node, also mit derselben SQLite wie im Browser, PostgreSQL mit derselben PGlite-Version wie im Browser. Kotlin wird nicht kompiliert; Kotlin-Aufgaben daher besonders sorgfältig gegen die offizielle Dokumentation prüfen. Excel-Formeln und Power-Query-Code laufen nirgends; sie brauchen besonders sorgfältige Prüfung gegen die Microsoft-Dokumentation. +`pnpm verify` braucht lokal `python3` und `rustc` (Edition 2024), für pandas-, pytest- und openpyxl-Code zusätzlich `uv`: Beim ersten Lauf entsteht unter `node_modules/.cache/verify-python/` eine virtuelle Umgebung mit genau den pandas- und numpy-Versionen des Browsers (aus `pyodide-lock.json`) und den pytest-, Hypothesis- und openpyxl-Versionen aus `src/python/packages.ts`. TypeScript- und JavaScript-`output` laufen mit dem Node, das auch `pnpm verify` ausführt. SQL prüft `verify` in Pyodide unter Node, also mit derselben SQLite wie im Browser, PostgreSQL mit derselben PGlite-Version wie im Browser. Kotlin wird nicht kompiliert; Kotlin-Aufgaben daher besonders sorgfältig gegen die offizielle Dokumentation prüfen. Excel-Formeln und Power-Query-Code laufen nirgends; sie brauchen besonders sorgfältige Prüfung gegen die Microsoft-Dokumentation. `content:check` und `verify` nehmen auch einzelne Module: `pnpm verify data/sql-joins`. Dann zählen nur Fehler dieses Moduls und der `area.yaml` – praktisch, solange andere Module desselben Bereichs noch entstehen. diff --git a/README.md b/README.md index 04f272a7..d415c11a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Die Plattform ist kein Nachschlagewerk. Jedes Modul besteht aus einer Lektion, d ## Start -Node.js 24, pnpm 11. Für `pnpm verify` zusätzlich `python3` (3.14), `rustc` (Edition 2024) und `uv` (legt für pandas- und pytest-Übungen eine Umgebung mit den pandas-, numpy-, pytest- und Hypothesis-Versionen des Browsers an), für den Bear-Generator Python 3. +Node.js 24, pnpm 11. Für `pnpm verify` zusätzlich `python3` (3.14), `rustc` (Edition 2024) und `uv` (legt für pandas-, pytest- und Excel-Übungen eine Umgebung mit den pandas-, numpy-, pytest-, Hypothesis- und openpyxl-Versionen des Browsers an), für den Bear-Generator Python 3. ```sh pnpm install @@ -20,7 +20,7 @@ pnpm dev # http://127.0.0.1:5180 | Befehl | Zweck | | --- | --- | | `pnpm content:check [bereich[/modul]]` | Inhalte validieren (Pflichtfelder, IDs, Lücken, Richtwerte) | -| `pnpm verify [bereich[/modul]]` | Python-Übungen und -Beispiele ausführen (auch pandas, pytest und Hypothesis), SQL mit der SQLite und dem PostgreSQL des Browsers prüfen, Rust kompilieren, TypeScript-Ausgaben mit Node prüfen, Ausgaben vergleichen, Git-Labore durchspielen | +| `pnpm verify [bereich[/modul]]` | Python-Übungen und -Beispiele ausführen (auch pandas, pytest, Hypothesis und openpyxl), SQL mit der SQLite und dem PostgreSQL des Browsers prüfen, Rust kompilieren, TypeScript-Ausgaben mit Node prüfen, Ausgaben vergleichen, Git-Labore durchspielen | | `pnpm links [bereich]` | Alle externen Links und Anker prüfen | | `pnpm test` | Unit-Tests (Prüflogik, Speicher, Bear-Rekonstruktion, Inhalte) | | `pnpm lint`, `pnpm typecheck` | ESLint und strikte TypeScript-Prüfung einschließlich Backend | diff --git a/content/automation/modules/datenformate.yaml b/content/automation/modules/datenformate.yaml index 7d924da9..d78d6522 100644 --- a/content/automation/modules/datenformate.yaml +++ b/content/automation/modules/datenformate.yaml @@ -1,14 +1,16 @@ id: datenformate title: Datenformate zuverlässig verarbeiten level: 2 -minutes: 170 -summary: CSV, JSON, YAML, TOML und Excel lesen, prüfen und exportieren – mit klaren Typen, Encoding und nachvollziehbaren - Fehlern. +minutes: 210 +summary: CSV, JSON, YAML, TOML und Excel lesen, prüfen und exportieren – mit klaren Typen, Encoding, echten + Arbeitsmappen und nachvollziehbaren Fehlern. goals: - Du trennst Parsen, fachliche Validierung und Transformation. - Du verarbeitest CSV mit dem csv-Modul statt durch split. - Du behandelst JSON-Typen, Dezimalzahlen und fehlende Werte ausdrücklich. -- Du wählst sichere YAML-Loader und prüfst Excel-Arbeitsmappen mit ihren Grenzen. +- Du wählst sichere YAML-Loader für Konfiguration. +- Du schreibst Excel-Arbeitsmappen mit mehreren Blättern, Formaten und Formeln und liest sie über Spaltennamen + wieder ein. resources: - &id001 title: Python · csv @@ -17,8 +19,13 @@ resources: url: https://docs.python.org/3/library/json.html - title: PyYAML · Documentation url: https://pyyaml.org/wiki/PyYAMLDocumentation -- title: openpyxl · Tutorial +- &xl + title: openpyxl · Tutorial url: https://openpyxl.readthedocs.io/en/stable/tutorial.html +- &xlstyle + title: openpyxl · Working with styles + url: https://openpyxl.readthedocs.io/en/stable/styles.html +place: unterwegs lesson: | Eine monatliche Auswertung bekommt Verkäufe aus CSV, Einstellungen aus YAML und eine Excel-Vorlage von einem Kollegen. Die Dateien sehen wie Text oder Tabellen aus, haben aber unterschiedliche Typregeln. Die Zeichenfolge `0012` kann eine Artikelnummer sein, ein leeres Feld ein fehlender Wert und `1,50` ein Geldbetrag. Ohne fachlichen Vertrag gehen solche Unterschiede beim Import verloren. @@ -31,8 +38,11 @@ lesson: | ```python run from pathlib import Path pfad = Path("notiz.txt") - pfad.write_text("Größe: 12\n", encoding="utf-8") - print(pfad.read_text(encoding="utf-8").strip()) + pfad.write_text("Größe: 12\n", + encoding="utf-8") + text = pfad.read_text( + encoding="utf-8") + print(text.strip()) ``` Definiere danach ein internes Modell. Eine Artikelnummer bleibt ein String, Mengen werden Integer und Geldbeträge können ganzzahlige Cent oder `Decimal` sein. Mit [`decimal`](https://docs.python.org/3/library/decimal.html) verarbeitest du Dezimalzahlen ohne binäre Rundungsüberraschungen. Erzeuge Decimal aus einem String, nicht aus einem bereits gerundeten Float. @@ -53,11 +63,15 @@ lesson: | ```python run import csv from io import StringIO - text = 'id;titel;menge\n0012;"Bericht; September";3\n' - zeilen = list(csv.DictReader(StringIO(text, newline=""), delimiter=";")) - print(zeilen[0]["id"]) - print(zeilen[0]["titel"]) - print(int(zeilen[0]["menge"])) + text = ('id;titel;menge\n' + '0012;"Bericht; Mai";3\n') + datei = StringIO(text, newline="") + leser = csv.DictReader( + datei, delimiter=";") + zeile = next(leser) + print(zeile["id"]) + print(zeile["titel"]) + print(int(zeile["menge"])) ``` CSV besitzt normalerweise keine verbindlichen Spaltentypen. `DictReader` liefert Strings; auch `"3"` bleibt zunächst Text. Prüfe die erforderlichen Überschriften, melde eine nachvollziehbare Zeilenposition und konvertiere nur Felder, deren Bedeutung feststeht. Ein Sniffer kann bei unbekannten Dateien helfen, aber eine bekannte Exportquelle sollte einen vereinbarten Dialekt haben. @@ -66,13 +80,17 @@ lesson: | import csv from io import StringIO puffer = StringIO(newline="") - writer = csv.DictWriter(puffer, fieldnames=["id", "titel"], delimiter=";") - writer.writeheader() - writer.writerow({"id": "0012", "titel": "Bericht; September"}) + w = csv.DictWriter( + puffer, ["id", "titel"], + delimiter=";") + w.writeheader() + zeile = {"id": "0012", + "titel": "Bericht; Mai"} + w.writerow(zeile) print(puffer.getvalue().strip()) ``` - `DictWriter` übernimmt das Quoting. Die feste Feldreihenfolge macht Exporte stabil. Wenn die Datei in einer Tabellenkalkulation geöffnet wird, können Texte mit bestimmten Anfangszeichen als Formel interpretiert werden. Behandle solche Exporte als eigene Zielumgebung und lege eine geeignete Textstrategie fest; ändere Rohdaten nicht stillschweigend ohne dokumentierten Vertrag. + `DictWriter` übernimmt das Quoting. Die feste Feldreihenfolge macht Exporte stabil. Wenn die Datei in einer Tabellenkalkulation geöffnet wird, können Texte mit bestimmten Anfangszeichen als Formel interpretiert werden. Behandle solche Exporte als eigene Zielumgebung und lege eine geeignete Textstrategie fest; ändere Rohdaten nicht stillschweigend ohne dokumentierten Vertrag. Für Excel-Dateien zeigt der Abschnitt über Formeln weiter unten, wie du Text ausdrücklich als Text speicherst. ## JSON: gültige Syntax ist noch kein Schema @@ -80,11 +98,16 @@ lesson: | ```python run import json - text = '{"id":"0012","menge":3,"aktiv":true,"datum":null}' + text = """{"id": "0012", + "menge": 3, "aktiv": true, + "datum": null}""" daten = json.loads(text) - print(type(daten["menge"]).__name__) - print(daten["aktiv"], daten["datum"]) - print(json.dumps(daten, ensure_ascii=False, sort_keys=True)) + menge = daten["menge"] + print(type(menge).__name__) + print(daten["aktiv"]) + print(daten["datum"]) + print(json.dumps(daten, + sort_keys=True)) ``` Für öffentliche JSON-Schnittstellen solltest du nichtstandardisierte Zahlenwerte wie NaN vermeiden. Python erlaubt beim Schreiben standardmäßig einige solche Werte; `allow_nan=False` lehnt sie ab. Beim Lesen kannst du zusätzliche Regeln setzen. Das ist ein Beispiel dafür, dass das Verhalten einer konkreten Bibliothek breiter sein kann als dein Datenvertrag. @@ -92,9 +115,10 @@ lesson: | ```python run import json try: - json.dumps({"wert": float("nan")}, allow_nan=False) + json.dumps(float("nan"), + allow_nan=False) except ValueError: - print("Kein gültiger endlicher Wert") + print("Kein endlicher Wert") ``` `isinstance(wert, int)` akzeptiert in Python auch `True`, weil bool eine Unterklasse von int ist. Wenn eine Menge eine echte Integerzahl sein muss, ist `type(wert) is int` eine mögliche präzise Prüfung. Trenne ferner ein fehlendes Feld von einem ausdrücklich gesetzten `null`, wenn diese Zustände fachlich verschieden sind. @@ -105,49 +129,137 @@ lesson: | ```python import yaml - konfiguration = yaml.safe_load("name: bericht\nlimit: 20\n") - if not isinstance(konfiguration, dict): - raise ValueError("Konfiguration muss eine Zuordnung sein") + konf = yaml.safe_load( + "name: bericht\nlimit: 20\n") + if not isinstance(konf, dict): + raise ValueError( + "Konfiguration ist keine " + "Zuordnung") ``` YAML-Implementierungen können implizite Typen unterschiedlich auslegen. Quote IDs, Datumsähnliches und absichtlich textuelle Werte, wenn du eine eindeutige Darstellung willst. Für Anwendungskonfiguration mit klarer Tabellenstruktur ist TOML oft übersichtlich. [`tomllib`](https://docs.python.org/3/library/tomllib.html) liest es in der Standardbibliothek. ```python run import tomllib - daten = tomllib.loads('[export]\nformat = "csv"\nlimit = 20\n') + text = ('[export]\n' + 'format = "csv"\n') + daten = tomllib.loads(text) print(daten["export"]["format"]) ``` Unabhängig vom Format brauchst du erlaubte Schlüssel, Wertebereiche und Defaults. Ein Tippfehler wie `limti` sollte nicht unbemerkt einen Standardwert auslösen, wenn dadurch plötzlich alle Datensätze exportiert werden. Gib bei ungültiger Konfiguration einen hilfreichen Fehler aus und starte keine halb konfigurierte Verarbeitung. - ## Excel bewusst als Arbeitsmappe behandeln + ## Excel: Arbeitsmappe, Blätter und Zellen - Eine XLSX-Datei ist eine Arbeitsmappe mit Tabellenblättern, Zellen, Formeln und Formatierung. [`openpyxl`](https://openpyxl.readthedocs.io/en/stable/tutorial.html) kann solche Dateien lokal lesen und schreiben. Die Browserumgebung dieses Kurses enthält openpyxl nicht; die Excel-Aufgabe wird daher lokal durchgeführt. + Eine XLSX-Datei ist ein ZIP-Archiv mit XML-Teilen. Darin liegt eine Arbeitsmappe mit Tabellenblättern, und jede Zelle hat einen Wert, einen Typ und eine Formatierung. [`openpyxl`](https://openpyxl.readthedocs.io/en/stable/tutorial.html) liest und schreibt dieses Format; das alte Binärformat `.xls` kann es nicht. In diesem Kurs läuft openpyxl auch im Browser: Beim ersten `import openpyxl` lädt die Seite das Paket nach. - ```python + ```python run + from openpyxl import Workbook + + buch = Workbook() + blatt = buch.active + blatt.title = "Umsatz" + blatt.append(["Region", "Betrag"]) + blatt.append(["Nord", 120.5]) + blatt.append(["Süd", 80]) + fehler = buch.create_sheet( + "Fehler") + fehler.append(["Zeile", "Grund"]) + buch.save("bericht.xlsx") + print(buch.sheetnames) + print(blatt.dimensions) + ``` + + Eine neue Mappe hat genau ein Blatt namens `Sheet`; `buch.active` liefert es. `append` schreibt eine Zeile unter die letzte belegte. `create_sheet(name, index)` fügt ein Blatt an einer Position ein, ohne Index am Ende. Achtung: `active` ist eine **Position**, kein fest gemerktes Blatt. Fügst du an Position 0 ein, zeigt `active` danach auf das neue Blatt. Greife deshalb beim Lesen und Schreiben über den Namen zu, `buch["Umsatz"]`, statt dich auf das aktive Blatt zu verlassen. + + Beim Lesen öffnet [`load_workbook`](https://openpyxl.readthedocs.io/en/stable/tutorial.html#loading-from-a-file) die Datei. `iter_rows(values_only=True)` liefert Tupel mit Werten statt Zellobjekten. Die Eigenschaft `values` liefert alle Zeilen auf einmal, die erste ist meist die Kopfzeile. + + ```python run + from openpyxl import Workbook from openpyxl import load_workbook - buch = load_workbook("eingang.xlsx", read_only=True, data_only=True) - try: - blatt = buch["Verkäufe"] - for zeile in blatt.iter_rows(min_row=2, values_only=True): - print(zeile) - finally: - buch.close() + + buch = Workbook() + ws = buch.active + ws.title = "Eingang" + ws.append(["menge", "id"]) + ws.append([3, "0012"]) + buch.save("ein.xlsx") + + ws = load_workbook("ein.xlsx")[ + "Eingang"] + kopf, *zeilen = ws.values + pos = {name: i for i, name + in enumerate(kopf)} + for zeile in zeilen: + print(zeile[pos["id"]], + zeile[pos["menge"]]) ``` - `data_only=True` liest bei Formelzellen den zuletzt gespeicherten Ergebniswert, soweit vorhanden. openpyxl berechnet Formeln nicht neu. Eine fehlende oder alte Berechnung in der Datei kann deshalb zu `None` oder veralteten Ergebnissen führen. Verwechsle erfolgreiches Öffnen nicht mit korrekter Neuberechnung. + Die Kopfzeile wird zu einer Zuordnung von Spaltenname auf Position. So überlebt der Import, wenn jemand Spalten umsortiert oder eine Notizspalte einfügt. Prüfe, dass alle Pflichtspalten vorhanden sind, und melde fehlende mit Namen. Zellen haben Typen: `"0012"` bleibt Text, `3` wird eine Zahl. Leere Zellen ergeben `None`, und eine vollständig leere Zeile in der Mitte ist häufig, weil Menschen Tabellen gliedern. + + Für sehr große Mappen gibt es [Read-only- und Write-only-Modi](https://openpyxl.readthedocs.io/en/stable/optimized.html). Eine im Read-only-Modus geöffnete Mappe musst du mit `close()` schließen. Öffne und speichere die Originaldatei eines Kollegen nicht unter demselben Namen: Das Tutorial warnt, dass openpyxl nicht alle Inhalte einer Excel-Datei liest und Formen beim Speichern verloren gehen. `save` überschreibt außerdem ohne Rückfrage. + + ## Excel: Formeln, Formate und gespeicherte Ergebnisse + + Eine Zelle, deren Text mit `=` beginnt, speichert openpyxl als **Formel**. Es rechnet sie nicht aus. Das tut erst die Tabellenkalkulation beim Öffnen, und sie legt das Ergebnis beim Speichern als zwischengespeicherten Wert in der Datei ab. Formeln stehen in der Datei mit englischen Funktionsnamen und Kommas: Microsoft beschreibt in [Range.FormulaLocal](https://learn.microsoft.com/en-us/office/vba/api/excel.range.formulalocal), dass ein deutsches Excel dieselbe gespeicherte Formel `=SUM(A1:A10)` als `=SUMME(A1:A10)` anzeigt. + + ```python run + from openpyxl import Workbook + from openpyxl import load_workbook + from openpyxl.styles import Font + + buch = Workbook() + ws = buch.active + ws.append(["Region", "Betrag"]) + ws.append(["Nord", 120.5]) + ws.append(["Süd", 80]) + ws["A4"] = "Summe" + ws["B4"] = "=SUM(B2:B3)" + ws["A1"].font = Font(bold=True) + for zeile in range(2, 5): + z = ws.cell(zeile, 2) + z.number_format = "#,##0.00" + ws.freeze_panes = "A2" + buch.save("umsatz.xlsx") + + roh = load_workbook("umsatz.xlsx") + print(roh.active["B4"].value) + werte = load_workbook( + "umsatz.xlsx", data_only=True) + print(werte.active["B4"].value) + ``` - Achte bei großen Mappen auf Read-only- beziehungsweise Write-only-Modi. Prüfe Blattnamen und Überschriften, bevor du feste Spaltenpositionen auswertest. Schreibe einen Bericht zunächst in eine neue Ausgabedatei, damit eine fehlerhafte Transformation nicht die einzige Originalmappe überschreibt. Datumswerte, Formatierung und führende Nullen brauchen gezielte Testdaten. + Ohne Option liest `load_workbook` den Formeltext. Mit `data_only=True` liest es den zwischengespeicherten Wert, und den gibt es hier nicht, weil nie eine Tabellenkalkulation die Datei geöffnet hat: Ergebnis `None`. Bei einer Datei, die Excel gespeichert hat, steht dort der Wert vom letzten Speichern. Er kann veraltet sein, wenn danach jemand die Eingaben per Skript geändert hat. Braucht dein Skript eine Summe, rechne sie in Python aus. Eine Formel in der Ausgabedatei ist für die Menschen, die später Zahlen ändern. + + Formatierung ist getrennt vom Wert. `number_format = "#,##0.00"` zeigt `120.5` als `120,50` in einem deutschen Excel, der gespeicherte Wert bleibt die Zahl. Schreibe deshalb Zahlen als `int` oder `float`, nie als vorformatierten Text wie `"120,50 €"`: Den kann niemand mehr summieren. [Schriften, Füllungen und Zahlenformate](https://openpyxl.readthedocs.io/en/stable/styles.html) setzt du pro Zelle. `freeze_panes = "A2"` hält die Kopfzeile beim Scrollen sichtbar. + + Die Formelregel hat eine Kehrseite. Ein Kundenname, der mit `=` beginnt, würde ebenfalls zur Formel. Bei fremden Texten setzt du nach der Zuweisung den Zelltyp ausdrücklich auf Text. [`Cell.data_type`](https://openpyxl.readthedocs.io/en/stable/api/openpyxl.cell.cell.html) ist `"f"` für Formeln, `"s"` für Text und `"n"` für Zahlen. + + ```python run + from openpyxl import Workbook + + ws = Workbook().active + ws["A1"] = "=2+3" + print(ws["A1"].data_type) + ws["A2"] = "=2+3" + ws["A2"].data_type = "s" + print(ws["A2"].data_type) + ``` + + > [!WARNING] Formeln aus fremden Daten + > Text aus einer API oder einem Formular landet sonst als ausführbare Formel in einer Datei, die jemand mit Excel öffnet. Formeln wie `HYPERLINK` oder Verweise auf andere Dateien sind dort mehr als harmlose Zeichen. ## Häufige Fehler und Praxis - Die häufigsten Defekte entstehen durch implizite Annahmen: Alle CSV-Dateien seien kommagetrennt, jede JSON-Zahl sei eine gültige Menge oder jede Excel-Formel sei frisch berechnet. Erzeuge kleine Referenzdateien mit genau diesen Gegenbeispielen und halte erwartete Normalformen daneben fest. + Die häufigsten Defekte entstehen durch implizite Annahmen: Alle CSV-Dateien seien kommagetrennt, jede JSON-Zahl sei eine gültige Menge, jede Excel-Formel sei frisch berechnet oder die Mengenspalte stehe immer an dritter Stelle. Erzeuge kleine Referenzdateien mit genau diesen Gegenbeispielen und halte erwartete Normalformen daneben fest. + + Bei Excel kommen drei Fehler dazu. Erstens wird `buch.active` als „das richtige Blatt“ benutzt, obwohl jemand ein Deckblatt eingefügt hat. Zweitens werden Beträge als formatierter Text geschrieben. Drittens überschreibt ein Skript die einzige Originalmappe, und Diagramme oder Formen sind weg. > [!PRAXIS] - > Gib Fehler als Zeilennummer, Feldname und kurze Ursache zurück. So kann jemand die Eingabedatei reparieren, ohne deinen Code zu lesen. Bei personenbezogenen Daten gehört nicht automatisch die ganze Zeile ins Log. + > Gib Fehler als Zeilennummer, Feldname und kurze Ursache zurück. So kann jemand die Eingabedatei reparieren, ohne deinen Code zu lesen. Bei personenbezogenen Daten gehört nicht automatisch die ganze Zeile ins Log. Ein Excel-Bericht bekommt ein eigenes Blatt „Fehler“ mit genau diesen Angaben. > [!INTERVIEW] - > „Was prüfst du nach json.loads?“ Die Wurzelform, Pflichtfelder, Typen und fachlichen Wertebereiche. Ein syntaktisch gültiges Dokument kann weiterhin vollständig unbrauchbar sein. + > „Was prüfst du nach json.loads?“ Die Wurzelform, Pflichtfelder, Typen und fachlichen Wertebereiche. Ein syntaktisch gültiges Dokument kann weiterhin vollständig unbrauchbar sein. Bei Excel kommt die Frage dazu, ob du Formeltext oder Ergebnis liest und woher das Ergebnis stammt. > [!TIP] > Ein Roundtrip-Test liest einen erzeugten Export wieder ein und vergleicht fachliche Werte. Er ergänzt einen bekannten Referenzfall, ersetzt ihn aber nicht: Schreiber und Leser könnten denselben Fehler teilen. @@ -158,7 +270,9 @@ lesson: | - CSV braucht einen echten Parser und festgelegten Dialekt. - JSON-Syntax ersetzt keine fachliche Validierung. - YAML wird sicher geladen und danach geprüft. - - Excel-Formeln werden von openpyxl nicht neu berechnet. + - Excel-Blätter sprichst du über ihren Namen an, Spalten über die Kopfzeile. + - openpyxl speichert Formeln und rechnet sie nicht; `data_only` liest nur gespeicherte Ergebnisse. + - Zahlen bleiben Zahlen mit Zahlenformat, fremder Text wird ausdrücklich Text. - Originale bleiben erhalten; Exporte und Fehler sind nachvollziehbar. exercises: - id: parser @@ -183,13 +297,13 @@ exercises: type: order title: Vom Dokument zum Bericht prompt: Ordne die drei Prüfgrenzen und den Export. - lang: python + lang: prose lines: |- - # Bytes mit vereinbartem Encoding lesen - # Format mit passendem Parser einlesen - # Schema und fachliche Werte prüfen - # In das interne Datenmodell umwandeln - # Bericht in eine neue Datei schreiben + Bytes mit vereinbartem Encoding lesen + Format mit passendem Parser einlesen + Schema und fachliche Werte prüfen + In das interne Datenmodell umwandeln + Bericht in eine neue Datei schreiben explanation: Jede Grenze hat eigene Fehler. Erst nach erfolgreicher Validierung sollten fachliche Ergebnisse ausgegeben werden. resources: @@ -202,7 +316,10 @@ exercises: code: |- import csv from io import StringIO - zeile = next(csv.reader(StringIO('1;"a;b";3\n'), delimiter=";")) + text = StringIO('1;"a;b";3\n') + leser = csv.reader(text, + delimiter=";") + zeile = next(leser) print(len(zeile)) print(zeile[1]) expected: |- @@ -219,7 +336,9 @@ exercises: lang: python code: |- import json - d = json.loads('{"aktiv":true,"wert":null,"menge":3}') + d = json.loads( + '{"aktiv": true, "wert": null,' + ' "menge": 3}') print(type(d["aktiv"]).__name__) print(d["wert"] is None) print(type(d["menge"]).__name__) @@ -230,6 +349,27 @@ exercises: explanation: JSON-Bools und null haben eigene Python-Entsprechungen. Die Zahl 3 wird hier als Integer eingelesen. resources: - *id001 +- id: blattreihenfolge + type: output + title: Welches Blatt ist aktiv? + prompt: | + Welche Ausgabe erzeugt dieses Programm? Eine neue Mappe beginnt mit einem einzigen Blatt. + lang: python + code: |- + from openpyxl import Workbook + buch = Workbook() + buch.active.title = "Bericht" + buch.create_sheet("Fehler", 0) + buch.create_sheet("Roh") + print(buch.sheetnames) + print(buch.active.title) + expected: |- + ['Fehler', 'Bericht', 'Roh'] + Fehler + explanation: | + `create_sheet("Fehler", 0)` fügt das Blatt vor `Bericht` ein, `create_sheet("Roh")` hängt es ans Ende. `active` merkt sich nur die Position 0, nicht das Blatt `Bericht`. Nach dem Einfügen steht an Position 0 das Blatt `Fehler`. Deshalb greift robuster Code über den Namen zu: `buch["Bericht"]`. + resources: + - *xl - id: csv-summe type: code title: Mengen aus CSV addieren @@ -242,8 +382,13 @@ exercises: solution: |- import csv from io import StringIO + def summe_csv(text): - return sum(int(z["menge"]) for z in csv.DictReader(StringIO(text, newline=""), delimiter=";")) + leser = csv.DictReader( + StringIO(text, newline=""), + delimiter=";") + return sum(int(z["menge"]) + for z in leser) tests: - name: Mehrere code: assert summe_csv("id;menge\na;2\nb;3\n") == 5 @@ -267,8 +412,10 @@ exercises: solution: |- def menge(daten): wert = daten.get("menge") - if type(wert) is not int or wert < 0: - raise ValueError("menge muss eine nichtnegative Integerzahl sein") + if type(wert) is not int: + raise ValueError("Typ") + if wert < 0: + raise ValueError("negativ") return wert tests: - name: Normal @@ -315,9 +462,13 @@ exercises: solution: |- import csv from io import StringIO + def export_csv(zeilen): out = StringIO(newline="") - writer = csv.DictWriter(out, fieldnames=["id", "titel"], delimiter=";", lineterminator="\n") + writer = csv.DictWriter( + out, ["id", "titel"], + delimiter=";", + lineterminator="\n") writer.writeheader() writer.writerows(zeilen) return out.getvalue() @@ -332,6 +483,206 @@ exercises: machen die Ausgabe reproduzierbar. resources: - *id001 +- id: excel-bericht + type: code + title: Ein Excel-Bericht mit zwei Blättern + prompt: | + Schreibe `schreibe_bericht(pfad, posten)`. `posten` ist eine Liste aus Paaren `(name, betrag)`. Speichere unter `pfad` eine Arbeitsmappe mit genau zwei Blättern in dieser Reihenfolge: + + 1. **„Bericht“**: Zeile 1 enthält `Posten` und `Betrag`, beide fett. Ab Zeile 2 folgt ein Posten pro Zeile. Darunter steht `Summe` mit der Formel `=SUM(B2:Bn)`, wobei `n` die letzte Postenzeile ist. Ohne Posten steht dort statt der Formel die Zahl `0`. + 2. **„Prüfung“**: In `A1` steht `Anzahl`, in `B1` die Zahl der Posten. + + Alle Beträge und die Summe bekommen das Zahlenformat `#,##0.00`. Namen sind immer Text, auch wenn sie mit `=` beginnen. + lang: python + starter: |- + from openpyxl import Workbook + from openpyxl.styles import Font + + FORMAT = "#,##0.00" + + def schreibe_bericht(pfad, posten): + buch = Workbook() + ... + buch.save(pfad) + solution: |- + from openpyxl import Workbook + from openpyxl.styles import Font + + FORMAT = "#,##0.00" + + def schreibe_bericht(pfad, posten): + buch = Workbook() + ws = buch.active + ws.title = "Bericht" + ws.append(["Posten", "Betrag"]) + for z in ws[1]: + z.font = Font(bold=True) + n = 2 + for name, betrag in posten: + a = ws.cell(n, 1, name) + a.data_type = "s" + b = ws.cell(n, 2, betrag) + b.number_format = FORMAT + n += 1 + ws.cell(n, 1, "Summe") + summe = f"=SUM(B2:B{n - 1})" + if not posten: + summe = 0 + s = ws.cell(n, 2, summe) + s.number_format = FORMAT + pruef = buch.create_sheet( + "Prüfung") + pruef.append( + ["Anzahl", len(posten)]) + buch.save(pfad) + tests: + - name: Zwei Blätter in fester Reihenfolge + code: |- + from openpyxl import load_workbook + schreibe_bericht("a.xlsx", + [("Miete", 950), ("Strom", 61.5)]) + buch = load_workbook("a.xlsx") + namen = buch.sheetnames + assert namen == ["Bericht", "Prüfung"], ( + f"Blätter: {namen}") + zahl = buch["Prüfung"]["B1"].value + assert zahl == 2, f"Anzahl: {zahl!r}" + - name: Fette Kopfzeile, Beträge als Zahl mit Format + code: |- + from openpyxl import load_workbook + schreibe_bericht("b.xlsx", + [("Miete", 950), ("Strom", 61.5)]) + ws = load_workbook("b.xlsx")["Bericht"] + kopf = [ws["A1"].value, ws["B1"].value] + assert kopf == ["Posten", "Betrag"], kopf + assert ws["A1"].font.b and ws["B1"].font.b, ( + "Die Kopfzeile ist nicht fett.") + assert ws["B3"].value == 61.5, ( + f"B3 ist {ws['B3'].value!r}") + fmt = ws["B3"].number_format + assert fmt == "#,##0.00", f"Format: {fmt}" + - name: Summenzeile mit Formel und Format + code: |- + from openpyxl import load_workbook + schreibe_bericht("c.xlsx", + [("Miete", 950), ("Strom", 61.5)]) + ws = load_workbook("c.xlsx")["Bericht"] + assert ws["A4"].value == "Summe", ( + f"A4 ist {ws['A4'].value!r}") + assert ws["B4"].value == "=SUM(B2:B3)", ( + f"B4 ist {ws['B4'].value!r}") + fmt = ws["B4"].number_format + assert fmt == "#,##0.00", f"Format: {fmt}" + - name: Ein Name mit = bleibt Text + code: |- + from openpyxl import load_workbook + schreibe_bericht("d.xlsx", [("=2+3", 1)]) + ws = load_workbook("d.xlsx")["Bericht"] + assert ws["A2"].value == "=2+3" + assert ws["A2"].data_type == "s", ( + "Der Name wurde als Formel gespeichert.") + - name: Ohne Posten steht 0 in der Summe + code: |- + from openpyxl import load_workbook + schreibe_bericht("e.xlsx", []) + buch = load_workbook("e.xlsx") + ws = buch["Bericht"] + assert ws["A2"].value == "Summe", ( + f"A2 ist {ws['A2'].value!r}") + assert ws["B2"].value == 0, ( + f"B2 ist {ws['B2'].value!r}") + assert buch["Prüfung"]["B1"].value == 0 + hints: + - Setze `ws.title`, bevor du das zweite Blatt mit `create_sheet` anhängst. + - '`ws[1]` liefert die Zellen der ersten Zeile; `Font(bold=True)` macht sie fett.' + - Schreibe Postenzeilen mit `ws.cell(zeile, spalte, wert)` und setze danach `data_type` und `number_format` auf + der zurückgegebenen Zelle. + explanation: | + Werte, Typen und Formatierung sind getrennte Eigenschaften einer Zelle. Der Betrag bleibt eine Zahl, das Zahlenformat bestimmt nur die Anzeige. Die Summenformel rechnet erst Excel aus, deshalb prüfen die Tests den Formeltext und nicht ein Ergebnis. Der Sonderfall ohne Posten verhindert die Formel `=SUM(B2:B1)`: Ein Bereich ist durch zwei Ecken bestimmt, `B2:B1` umfasst also die Kopfzeile statt keiner Zeile. `data_type = "s"` nach der Zuweisung macht aus einem Namen wie `=2+3` wieder Text. + resources: + - *xl + - *xlstyle +- id: excel-nach-kopf + type: code + title: Spalten über die Kopfzeile finden + prompt: | + Schreibe `lies_posten(pfad)`. Lies das Blatt **„Eingang“**; es muss nicht das erste Blatt sein. Zeile 1 ist die Kopfzeile und enthält die Spalten `id` und `menge` in beliebiger Reihenfolge, eventuell neben weiteren Spalten. Gib für jede weitere Zeile ein Dictionary `{"id": …, "menge": …}` mit den Zellwerten zurück. Überspringe Zeilen, in denen alle Zellen leer sind. Fehlt `id` oder `menge` in der Kopfzeile, wirf `ValueError` mit dem Namen der fehlenden Spalte in der Meldung. + lang: python + setup: |- + from openpyxl import Workbook + + def _mappe(pfad, blaetter): + buch = Workbook() + buch.remove(buch.active) + for name, zeilen in blaetter: + ws = buch.create_sheet(name) + for zeile in zeilen: + ws.append(zeile) + buch.save(pfad) + + _mappe("a.xlsx", [("Eingang", [["menge", "notiz", "id"], [3, "Ecke", "0012"], [None, None, None], [5, None, "0099"]])]) + _mappe("b.xlsx", [("Eingang", [["id", "anzahl"], ["1", 2]])]) + _mappe("c.xlsx", [("Deckblatt", [["Bitte nicht ändern"]]), ("Eingang", [["id", "menge"], ["A7", 1]])]) + starter: |- + from openpyxl import load_workbook + + def lies_posten(pfad): + ... + solution: |- + from openpyxl import load_workbook + + def lies_posten(pfad): + buch = load_workbook(pfad) + ws = buch["Eingang"] + kopf, *zeilen = ws.values + for name in ("id", "menge"): + if name not in kopf: + raise ValueError(name) + posten = [] + for zeile in zeilen: + if set(zeile) == {None}: + continue + w = dict(zip(kopf, zeile)) + posten.append({ + "id": w["id"], + "menge": w["menge"], + }) + return posten + tests: + - name: Spalten nach Namen statt Position + code: |- + p = lies_posten("a.xlsx") + assert p[0] == {"id": "0012", + "menge": 3}, p + - name: Leere Zeilen überspringen + code: |- + p = lies_posten("a.xlsx") + assert p == [ + {"id": "0012", "menge": 3}, + {"id": "0099", "menge": 5}, + ], p + - name: Fehlende Spalte wird benannt + code: |- + try: + lies_posten("b.xlsx") + except ValueError as fehler: + assert "menge" in str(fehler), ( + f"Meldung: {fehler}") + else: + assert False, "ValueError erwartet" + - name: Das Blatt Eingang, nicht das erste + code: |- + p = lies_posten("c.xlsx") + assert p == [{"id": "A7", + "menge": 1}], p + hints: + - '`load_workbook(pfad)["Eingang"]` wählt das Blatt über den Namen.' + - '`kopf, *zeilen = ws.values` trennt die Kopfzeile von den Datenzeilen.' + - '`dict(zip(kopf, zeile))` ordnet jedem Spaltennamen den Wert der Zeile zu. Prüfe vorher mit `in`, ob jede Pflichtspalte im Kopf steht.' + explanation: | + Der Name des Blatts und die Namen der Spalten sind der Vertrag, nicht ihre Positionen. Ein eingefügtes Deckblatt oder eine Notizspalte bricht diesen Import deshalb nicht. Die ID `0012` bleibt Text, weil die Zelle Text enthält; der Import wandelt nichts um, was er nicht verstehen muss. Eine fehlende Pflichtspalte ist ein Fehler der Eingabe und wird mit Namen gemeldet, statt später als `IndexError` oder als leere Liste aufzufallen. + resources: + - *xl - id: id-verlust type: bug title: Die führenden Nullen fehlen @@ -339,13 +690,17 @@ exercises: lang: python code: |- def artikel(zeile): - return {"id": int(zeile["id"]), "menge": int(zeile["menge"])} + menge = int(zeile["menge"]) + nummer = int(zeile["id"]) + return {"id": nummer, + "menge": menge} lines: - - 2 + - 3 fix: - - line: 2 + - line: 3 answers: - - 'return {"id": zeile["id"], "menge": int(zeile["menge"])}' + - nummer = zeile["id"] + - nummer = str(zeile["id"]) explanation: Eine Kennung ist kein Rechenwert. Die Konvertierung in int zerstört ihre Darstellung; nur die fachliche Menge wird numerisch konvertiert. resources: @@ -356,8 +711,12 @@ exercises: prompt: Erkläre die Aussagekraft dieses lokalen Excel-Lesevorgangs. lang: python code: |- - buch = load_workbook("bericht.xlsx", data_only=True, read_only=True) - wert = buch["Übersicht"]["B2"].value + buch = load_workbook( + "bericht.xlsx", + data_only=True, + read_only=True) + blatt = buch["Übersicht"] + wert = blatt["B2"].value points: - data_only liest gespeicherte Formelresultate statt Formeltext. - openpyxl berechnet die Formel nicht neu. @@ -366,30 +725,44 @@ exercises: explanation: Das Lesen ist eine Extraktion aus dem gespeicherten Zustand. Ein numerischer Zellwert beweist nicht, dass die zugrunde liegende Formel gerade mit den aktuellen Eingaben berechnet wurde. resources: - - *id001 + - *xl - id: import-bericht type: practice title: Ein nachvollziehbarer Dateiimport - prompt: Baue einen CSV-zu-JSON-Import mit id als String und menge als nichtnegativem int. Sammle Fehler mit Datenzeilennummer; - schreibe bei einem Fehler keinen neuen Ergebnisbericht. + prompt: Entwirf einen CSV-zu-JSON-Import mit id als String und menge als nichtnegativem int. Sammle Fehler mit + Datenzeilennummer; liefere bei einem Fehler keinen Ergebnisbericht. Vergleiche danach mit der Musterlösung. lang: python solution: |- import csv, json from io import StringIO + FEHLER = (ValueError, KeyError, + TypeError) + def importiere(text): daten, fehler = [], [] - for nummer, zeile in enumerate(csv.DictReader(StringIO(text), delimiter=";"), 2): + leser = csv.DictReader( + StringIO(text), + delimiter=";") + for nr, z in enumerate( + leser, start=2): try: - anzahl = int(zeile["menge"]) - if anzahl < 0: - raise ValueError("negative Menge") - daten.append({"id": zeile["id"], "menge": anzahl}) - except (ValueError, KeyError, TypeError): - fehler.append({"zeile": nummer, "feld": "menge"}) + menge = int(z["menge"]) + if menge < 0: + raise ValueError + daten.append( + {"id": z["id"], + "menge": menge}) + except FEHLER: + fehler.append( + {"zeile": nr, + "feld": "menge"}) if fehler: return None, fehler - return json.dumps(daten, ensure_ascii=False, allow_nan=False), [] + text = json.dumps( + daten, ensure_ascii=False, + allow_nan=False) + return text, [] checklist: - Führende Nullen bleiben erhalten. - Quotierte Felder werden korrekt verarbeitet. diff --git a/src/python/packages.ts b/src/python/packages.ts index 44c1024e..d0c04fd4 100644 --- a/src/python/packages.ts +++ b/src/python/packages.ts @@ -15,8 +15,20 @@ export const TEST_WHEELS = [ /** Code that needs the wheels: it imports pytest or Hypothesis, or a test calls the harness helper `run_pytest`. */ export const USES_TESTS = /\b(?:pytest|hypothesis|run_pytest)\b/; +// openpyxl for the Excel exercises of the automation course. Pure Python like its only dependency et_xmlfile, and +// not in Pyodide's lock file either, so it loads by file name too. +export const EXCEL_WHEELS = [ + 'openpyxl-3.1.5-py2.py3-none-any.whl', + 'et_xmlfile-2.0.0-py3-none-any.whl', +]; + +/** Code that imports openpyxl. */ +export const USES_EXCEL = /\bopenpyxl\b/; + /** `name==version` for every wheel (wheel file names are `name-version-…`). */ -export const testPins = () => TEST_WHEELS.map((file) => { +const pins = (wheels: string[]) => wheels.map((file) => { const [name, version] = file.split('-'); return `${name}==${version}`; }); +export const testPins = () => pins(TEST_WHEELS); +export const excelPins = () => pins(EXCEL_WHEELS); diff --git a/src/python/worker.ts b/src/python/worker.ts index 333ea348..0f0dce13 100644 --- a/src/python/worker.ts +++ b/src/python/worker.ts @@ -1,7 +1,7 @@ /// // Runs Python in Pyodide, off the main thread. The main thread terminates this worker on timeout. import harness from './harness.py?raw'; -import { TEST_WHEELS, USES_TESTS } from './packages.ts'; +import { EXCEL_WHEELS, TEST_WHEELS, USES_EXCEL, USES_TESTS } from './packages.ts'; type Pyodide = { FS: { writeFile(path: string, data: string): void }; @@ -43,7 +43,8 @@ self.onmessage = async (event: MessageEvent) if (job.kind === 'boot') { self.postMessage({ id: job.id, result: { ready: true } }); return; } if (job.kind === 'prepare') { // Before the imports: resolving `import pytest` through the lock file would ask for wheels that are not there. - if (USES_TESTS.test(job.code)) await pyodide.loadPackage(TEST_WHEELS.map((file) => `${self.location.origin}/pyodide/${file}`)); + const wheels = [...(USES_TESTS.test(job.code) ? TEST_WHEELS : []), ...(USES_EXCEL.test(job.code) ? EXCEL_WHEELS : [])]; + if (wheels.length) await pyodide.loadPackage(wheels.map((file) => `${self.location.origin}/pyodide/${file}`)); try { await pyodide.loadPackagesFromImports(job.code); } catch { /* unknown imports surface as ImportError in Python */ } if (/zoneinfo|ZoneInfo/.test(job.code)) await pyodide.loadPackage('tzdata'); // Exercises that only call run_pytest import pytest inside the timed run; its first import takes a second or two. diff --git a/tooling/verify-code.ts b/tooling/verify-code.ts index 69234492..edd4e915 100644 --- a/tooling/verify-code.ts +++ b/tooling/verify-code.ts @@ -29,7 +29,7 @@ import { assemble, fixedCode, gapSolution, normalizeOutput } from '../src/engine import { languageOf } from '../src/engine/highlight.ts'; import { compareSql } from '../src/engine/sql.ts'; import type { PgEngine } from '../src/pg/engine.ts'; -import { testPins, USES_TESTS } from '../src/python/packages.ts'; +import { excelPins, testPins, USES_EXCEL, USES_TESTS } from '../src/python/packages.ts'; import { nodeEngine } from './pg-node.ts'; import type { BugExercise, GapExercise, OrderExercise, OutputExercise, SqlExercise } from '../src/content/types.ts'; @@ -60,21 +60,21 @@ function exec(command: string, args: string[], options: { input?: string; cwd?: }); } -// pandas and numpy in the browser's versions, pinned by Pyodide's lock file, plus pytest and Hypothesis in the -// versions of the wheels the browser loads. +// pandas and numpy in the browser's versions, pinned by Pyodide's lock file, plus pytest, Hypothesis and openpyxl in +// the versions of the wheels the browser loads. const DATA_PACKAGES = ['numpy', 'pandas', 'python-dateutil', 'pytz', 'six']; const USES_DATA = /^\s*(?:import|from)\s+(?:pandas|numpy)\b/m; let packagePython: Promise | null = null; function packageEnvironment(): Promise { packagePython ??= (async () => { const lock = JSON.parse(readFileSync(join(ROOT, 'node_modules/pyodide/pyodide-lock.json'), 'utf8')); - const pins = [...DATA_PACKAGES.map((name) => `${name}==${lock.packages[name].version}`), ...testPins()]; + const pins = [...DATA_PACKAGES.map((name) => `${name}==${lock.packages[name].version}`), ...testPins(), ...excelPins()]; const dir = join(ROOT, 'node_modules/.cache/verify-python', createHash('sha256').update(pins.join(' ')).digest('hex').slice(0, 12)); const bin = join(dir, 'bin/python'); if (existsSync(bin)) return bin; for (const args of [['venv', '--quiet', '--python', '3.14', dir], ['pip', 'install', '--quiet', '--python', bin, ...pins]]) { const result = await exec('uv', args, { timeout: 300000 }); - if (result.code !== 0) throw new Error(`uv ${args[0]} fehlgeschlagen (für pandas- und pytest-Übungen wird uv gebraucht):\n${result.stderr.trim()}`); + if (result.code !== 0) throw new Error(`uv ${args[0]} fehlgeschlagen (für pandas-, pytest- und openpyxl-Übungen wird uv gebraucht):\n${result.stderr.trim()}`); } return bin; })(); @@ -83,7 +83,7 @@ function packageEnvironment(): Promise { async function python(job: { code: string; setup?: string; tests?: { code: string }[] } & Record): Promise { const source = [job.setup ?? '', job.code, ...(job.tests ?? []).map((test) => test.code)].join('\n'); - const interpreter = job.kind === 'pytest' || USES_DATA.test(source) || USES_TESTS.test(source) ? await packageEnvironment() : 'python3'; + const interpreter = job.kind === 'pytest' || USES_DATA.test(source) || USES_TESTS.test(source) || USES_EXCEL.test(source) ? await packageEnvironment() : 'python3'; const result = await exec(interpreter, [HARNESS], { input: JSON.stringify(job), timeout: 15000 }); if (result.timedOut) throw new Error('Zeitüberschreitung (15 s)'); if (result.code !== 0) throw new Error(result.stderr.trim().split('\n').slice(-5).join('\n')); diff --git a/tooling/vite-plugins.ts b/tooling/vite-plugins.ts index ea18c99c..b116046b 100644 --- a/tooling/vite-plugins.ts +++ b/tooling/vite-plugins.ts @@ -75,8 +75,8 @@ const PYODIDE_FILES = ['pyodide.mjs', 'pyodide.asm.mjs', 'pyodide.asm.wasm', 'py /** * Self-hosts the Pyodide runtime under /pyodide/ (copied from node_modules into public/, not committed), * plus the extra wheels exercises may import (vendor/pyodide: tzdata, beautifulsoup4, PyYAML, pandas with numpy, - * pytest and Hypothesis). Wheels must match the file names and hashes in node_modules/pyodide/pyodide-lock.json; - * Hypothesis is not in the lock file and loads by file name (src/python/packages.ts). + * pytest, Hypothesis and openpyxl). Wheels must match the file names and hashes in node_modules/pyodide/pyodide-lock.json; + * Hypothesis and openpyxl are not in the lock file and load by file name (src/python/packages.ts). */ export function pyodide(): Plugin { return { diff --git a/vendor/pyodide/et_xmlfile-2.0.0-py3-none-any.whl b/vendor/pyodide/et_xmlfile-2.0.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..52a7eecaf4c9067dd2c0053da9b2a5080a29d696 GIT binary patch literal 18059 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 literal 0 HcmV?d00001 diff --git a/vendor/pyodide/openpyxl-3.1.5-py2.py3-none-any.whl b/vendor/pyodide/openpyxl-3.1.5-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e1dccd4abf26c7d396c9f7bb865e6a6391f8eb89 GIT binary patch literal 250910 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