Teil 6: Konfiguration¶
KI-gestützte Übersetzung - mehr erfahren & Verbesserungen vorschlagen
Dein Plugin hat benutzerdefinierte Funktionen und einen Observer, aber alles ist fest im Code verankert. Benutzer*innen können den Task-Zähler nicht deaktivieren oder den Decorator ändern, ohne den Quellcode zu bearbeiten und neu zu bauen.
In Teil 1 hast du validation {} und co2footprint {} Blöcke in nextflow.config verwendet, um das Verhalten von nf-schema und nf-co2footprint zu steuern.
Diese Konfigurationsblöcke existieren, weil die Plugin-Entwickler*innen diese Funktionalität eingebaut haben.
In diesem Abschnitt machst du dasselbe für dein eigenes Plugin.
Ziele:
- Benutzer*innen können Präfix und Suffix des Greeting-Decorators anpassen
- Benutzer*innen können das Plugin über
nextflow.configaktivieren oder deaktivieren - Einen formalen Konfigurations-Scope registrieren, damit Nextflow den
greeting {}Block erkennt
Was du änderst:
| Datei | Änderung |
|---|---|
GreetingExtension.groovy |
Präfix/Suffix-Konfiguration in init() einlesen |
GreetingFactory.groovy |
Konfigurationswerte einlesen, um die Observer-Erstellung zu steuern |
GreetingConfig.groovy |
Neue Datei: formale @ConfigScope-Klasse |
build.gradle |
Die Konfigurationsklasse als Extension Point registrieren |
nextflow.config |
Einen greeting {} Block zum Testen hinzufügen |
Hier eingestiegen?
Wenn du erst ab diesem Teil mitmachst, kopiere die Lösung aus Teil 5 als Ausgangspunkt:
Offizielle Dokumentation
Umfassende Details zur Konfiguration findest du in der Nextflow-Dokumentation zu Konfigurations-Scopes.
1. Den Decorator konfigurierbar machen¶
Die Funktion decorateGreeting umschließt jede Begrüßung mit *** ... ***.
Benutzer*innen möchten vielleicht andere Markierungen verwenden, aber im Moment ist die einzige Möglichkeit, sie zu ändern, den Quellcode zu bearbeiten und neu zu bauen.
Die Nextflow-Session stellt eine Methode namens session.config.navigate() bereit, die verschachtelte Werte aus nextflow.config liest:
// 'greeting.prefix' aus nextflow.config einlesen, Standardwert ist '***'
final prefix = session.config.navigate('greeting.prefix', '***') as String
Das entspricht einem Konfigurationsblock in der nextflow.config der Benutzer*innen:
1.1. Das Einlesen der Konfiguration hinzufügen (das wird fehlschlagen!)¶
Bearbeite GreetingExtension.groovy, um die Konfiguration in init() einzulesen und in decorateGreeting() zu verwenden:
Versuche zu bauen:
1.2. Den Fehler beobachten¶
Der Build schlägt fehl:
> Task :compileGroovy FAILED
GreetingExtension.groovy: 30: [Static type checking] - The variable [prefix] is undeclared.
@ line 30, column 9.
prefix = session.config.navigate('greeting.prefix', '***') as String
^
GreetingExtension.groovy: 31: [Static type checking] - The variable [suffix] is undeclared.
In Groovy (und Java) musst du eine Variable deklarieren, bevor du sie verwendest.
Der Code versucht, Werte prefix und suffix zuzuweisen, aber die Klasse hat keine Felder mit diesen Namen.
1.3. Durch Deklaration von Instanzvariablen beheben¶
Füge Variablendeklarationen am Anfang der Klasse hinzu, direkt nach der öffnenden geschweifte Klammer:
Diese zwei Zeilen deklarieren Instanzvariablen (auch Felder genannt), die zu jedem GreetingExtension-Objekt gehören.
Das Schlüsselwort private bedeutet, dass nur Code innerhalb dieser Klasse darauf zugreifen kann.
Jede Variable wird mit dem Standardwert '***' initialisiert.
Wenn das Plugin geladen wird, ruft Nextflow die Methode init() auf, die diese Standardwerte mit dem überschreibt, was die Benutzer*innen in nextflow.config gesetzt haben.
Wenn nichts gesetzt wurde, gibt navigate() denselben Standardwert zurück, sodass das Verhalten unverändert bleibt.
Die Methode decorateGreeting() liest diese Felder dann bei jedem Aufruf.
Aus Fehlern lernen
Dieses Muster „erst deklarieren, dann verwenden" ist grundlegend für Java/Groovy, aber ungewohnt, wenn du aus Python oder R kommst, wo Variablen beim ersten Zuweisen entstehen. Diesen Fehler einmal zu erleben hilft dir, ihn in Zukunft schnell zu erkennen und zu beheben.
1.4. Bauen und testen¶
Bauen und installieren:
Aktualisiere nextflow.config, um die Dekoration anzupassen:
Die Pipeline ausführen:
Der Decorator verwendet jetzt das benutzerdefinierte Präfix und Suffix aus der Konfigurationsdatei.
Beachte, dass Nextflow eine Warnung „Unrecognized config option" ausgibt, weil noch nichts greeting als gültigen Scope deklariert hat.
Der Wert wird trotzdem korrekt über navigate() eingelesen, aber Nextflow markiert ihn als unbekannt.
Das behebst du in Abschnitt 3.
2. Den Task-Zähler konfigurierbar machen¶
Die Observer-Factory erstellt Observer derzeit bedingungslos. Benutzer*innen sollten das Plugin über die Konfiguration vollständig deaktivieren können.
Die Factory hat Zugriff auf die Nextflow-Session und ihre Konfiguration und ist daher der richtige Ort, um die Einstellung enabled einzulesen und zu entscheiden, ob Observer erstellt werden sollen.
Die Factory liest jetzt greeting.enabled aus der Konfiguration und gibt eine leere Liste zurück, wenn die Benutzer*innen es auf false gesetzt haben.
Wenn die Liste leer ist, werden keine Observer erstellt und die Lifecycle-Hooks des Plugins werden stillschweigend übersprungen.
2.1. Bauen und testen¶
Das Plugin neu bauen und installieren:
Die Pipeline ausführen, um zu bestätigen, dass alles noch funktioniert:
Das Plugin vollständig deaktivieren
Setze greeting.enabled = false in nextflow.config und führe die Pipeline erneut aus.
Was ändert sich in der Ausgabe?
Lösung
// Konfiguration für Plugin-Entwicklungsübungen
plugins {
id 'nf-schema@2.6.1'
id 'nf-greeting@0.1.0'
}
greeting {
enabled = false
}
Die Meldungen „Pipeline is starting!", „Pipeline complete!" und die Task-Zählung verschwinden alle, weil die Factory eine leere Liste zurückgibt, wenn enabled false ist.
Die Pipeline selbst läuft weiterhin, aber es sind keine Observer aktiv.
Denke daran, enabled wieder auf true zu setzen (oder die Zeile zu entfernen), bevor du weitermachst.
3. Formale Konfiguration mit ConfigScope¶
Deine Plugin-Konfiguration funktioniert, aber Nextflow gibt weiterhin Warnungen „Unrecognized config option" aus.
Das liegt daran, dass session.config.navigate() nur Werte liest; nichts hat Nextflow mitgeteilt, dass greeting ein gültiger Konfigurations-Scope ist.
Eine ConfigScope-Klasse schließt diese Lücke.
Sie deklariert, welche Optionen dein Plugin akzeptiert, ihre Typen und ihre Standardwerte.
Sie ersetzt deine navigate()-Aufrufe nicht. Stattdessen arbeitet sie parallel dazu:
flowchart LR
A["nextflow.config<br/>greeting { ... }"] --> B["ConfigScope<br/><i>declares valid options</i>"]
A --> C["navigate()<br/><i>reads values at runtime</i>"]
B -. "validates &<br/>documents" .-> A
C -- "provides values to" --> D["Plugin code<br/><i>factory, extension</i>"]
style B fill:#e8f5e9,stroke:#4caf50
style C fill:#e3f2fd,stroke:#2196f3
Ohne eine ConfigScope-Klasse funktioniert navigate() zwar noch, aber:
- Nextflow warnt vor unbekannten Optionen (wie du bereits gesehen hast)
- Keine IDE-Autovervollständigung für Benutzer*innen, die
nextflow.configschreiben - Konfigurationsoptionen sind nicht selbstdokumentierend
- Typkonvertierung ist manuell (
as String,as boolean)
Das Registrieren einer formalen Konfigurations-Scope-Klasse behebt die Warnung und löst alle drei Probleme.
Das ist derselbe Mechanismus hinter den validation {} und co2footprint {} Blöcken, die du in Teil 1 verwendet hast.
3.1. Die Konfigurationsklasse erstellen¶
Eine neue Datei erstellen:
Die Konfigurationsklasse mit allen drei Optionen hinzufügen:
- Entspricht dem
greeting { }Block innextflow.config - Erforderliches Interface für Konfigurationsklassen
- Sowohl ein No-Arg- als auch ein Map-Konstruktor werden benötigt, damit Nextflow die Konfiguration instanziieren kann
@ConfigOptionmarkiert ein Feld als Konfigurationsoption;@Descriptiondokumentiert es für Tooling
Wichtige Punkte:
@ScopeName('greeting'): Entspricht demgreeting { }Block in der Konfigurationimplements ConfigScope: Erforderliches Interface für Konfigurationsklassen@ConfigOption: Jedes Feld wird zu einer Konfigurationsoption@Description: Dokumentiert jede Option für Language-Server-Unterstützung (importiert ausnextflow.script.dsl)- Konstruktoren: Sowohl No-Arg- als auch Map-Konstruktor werden benötigt
3.2. Die Konfigurationsklasse registrieren¶
Die Klasse zu erstellen reicht allein nicht aus.
Nextflow muss wissen, dass sie existiert. Daher registrierst du sie in build.gradle zusammen mit den anderen Extension Points.
Beachte den Unterschied zwischen der Registrierung von Factory und Extension Points:
extensionPointsinbuild.gradle: Registrierung zur Kompilierzeit. Teilt dem Nextflow-Plugin-System mit, welche Klassen Extension Points implementieren.create()-Methode der Factory: Registrierung zur Laufzeit. Die Factory erstellt Observer-Instanzen, wenn ein Workflow tatsächlich startet.
3.3. Bauen und testen¶
Das Verhalten der Pipeline ist identisch, aber die Warnung „Unrecognized config option" ist verschwunden.
Was sich geändert hat und was nicht
Deine GreetingFactory und GreetingExtension verwenden weiterhin session.config.navigate(), um Werte zur Laufzeit einzulesen.
Dieser Code hat sich nicht geändert.
Die ConfigScope-Klasse ist eine parallele Deklaration, die Nextflow mitteilt, welche Optionen existieren.
Beide Teile werden benötigt: ConfigScope deklariert, navigate() liest.
Dein Plugin hat jetzt dieselbe Struktur wie die Plugins, die du in Teil 1 verwendet hast.
Wenn nf-schema einen validation {} Block oder nf-co2footprint einen co2footprint {} Block bereitstellt, verwenden sie genau dieses Muster: eine ConfigScope-Klasse mit annotierten Feldern, die als Extension Point registriert ist.
Dein greeting {} Block funktioniert auf dieselbe Weise.
Fazit¶
Du hast gelernt, dass:
session.config.navigate()Konfigurationswerte zur Laufzeit liest@ConfigScope-Klassen deklarieren, welche Konfigurationsoptionen existieren; sie arbeiten parallel zunavigate(), nicht anstelle davon- Konfiguration sowohl auf Observer als auch auf Extension-Funktionen angewendet werden kann
- Instanzvariablen in Groovy/Java vor der Verwendung deklariert werden müssen;
init()befüllt sie aus der Konfiguration, wenn das Plugin geladen wird
| Anwendungsfall | Empfohlener Ansatz |
|---|---|
| Schneller Prototyp oder einfaches Plugin | Nur session.config.navigate() |
| Produktions-Plugin mit vielen Optionen | ConfigScope-Klasse parallel zu den navigate()-Aufrufen hinzufügen |
| Plugin, das du öffentlich teilen möchtest | ConfigScope-Klasse parallel zu den navigate()-Aufrufen hinzufügen |
Wie geht es weiter?¶
Dein Plugin hat jetzt alle Bestandteile eines Produktions-Plugins: benutzerdefinierte Funktionen, Trace-Observer und eine benutzerorientierte Konfiguration. Der letzte Schritt ist die Paketierung für die Verteilung.