How to Generate a Python API from a YAML Device Configuration in Nallely
Nallely provides a built‑in CLI tool that transforms a MIDI device description written in YAML into a fully functional Python module, eliminating the need to write boilerplate MIDI control code manually.
Nallely is an open‑source Python framework for controlling MIDI devices through an intuitive, object‑oriented API. According to the dr-schlange/nallely-midi source code, the library includes a dedicated code generation pipeline that parses YAML configurations and emits Python classes derived from nallely.Module and nallely.MidiDevice.
The Code Generation Pipeline
The generation workflow consists of three distinct phases orchestrated by the nallely generate sub‑command. Each phase is implemented in specific modules within the codebase.
CLI Entry Point
The command‑line interface parses arguments in nallely/cli.py (lines 13‑18) and delegates to the code generator. The CLI accepts an input YAML file via -i and an output path via -o.
nallely generate -i mydevice.yaml -o mydevice.py
YAML Parsing and Validation
The generate_api function in nallely/codegen/midi_module_generator.py (lines 32‑40) handles the configuration loading. It uses ruamel.yaml—a safe YAML parser—to load the device description into a nested Python dictionary representing the hierarchical structure of sections and parameters.
Python Code Emission
The generate_code function in the same file (lines 67‑30) traverses the dictionary and writes a Python file containing:
- Section classes derived from
nallely.Module, each containingModuleParameterattributes (or specialized variants) for every Continuous Controller (CC), program change, or NRPN defined in the YAML. - Device class derived from
nallely.MidiDevicethat aggregates the sections as properties, providing a unified interface for device control.
Step‑by‑Step Implementation
Follow this workflow to convert a YAML device specification into a working Python API.
1. Define the YAML Configuration
Create a YAML file that describes your MIDI device’s structure. Organize parameters into logical sections such as oscillators, filters, or envelopes. Each parameter requires a CC number, min/max values, and a description.
Korg:
Minilogue:
oscillators:
wave: { cc: 20, min: 0, max: 7, description: "Waveform selector" }
pitch: { cc: 21, min: 0, max: 127, description: "Coarse pitch" }
filter:
cutoff: { cc: 30, min: 0, max: 127, description: "Filter cutoff" }
resonance: { cc: 31, min: 0, max: 127, description: "Filter resonance" }
The nested structure (Manufacturer → Device → Section → Parameter) maps directly to the generated class hierarchy.
2. Execute the Generator Command
Run the CLI tool to produce the Python module. The generator is exposed through nallely/codegen/__init__.py and invoked by the CLI parser in nallely/cli.py.
nallely generate -i mydevice.yaml -o mydevice.py
This command triggers the full pipeline: YAML parsing, dictionary conversion, and Python file writing via generate_api and generate_code.
3. Import and Control the Device
Import the generated module to access type‑safe device controls. The generated classes inherit from runtime components defined in nallely/modules/*.py, including Module, ModuleParameter, and MidiDevice.
from mydevice import Minilogue
# Instantiate connects to the first detected Minilogue automatically
synth = Minilogue()
# Set the waveform (CC 20) to "Saw" (value 3)
synth.oscillators.wave = 3
# Sweep the filter cutoff (CC 30)
synth.filter.cutoff = 100
The generated mydevice.py contains OscillatorsSection and FilterSection classes (derived from nallely.Module) and the top‑level Minilogue class (derived from nallely.MidiDevice).
Understanding the Generated Architecture
The emitted Python code follows a strict architectural pattern enforced by the Nallely framework.
Section Classes
Each top‑level key under the device name in your YAML (e.g., oscillators, filter) becomes a class inheriting from nallely.Module. These classes encapsulate related MIDI parameters as typed attributes.
- ModuleParameter attributes: Each CC definition becomes a
ModuleParameterinstance (or a specialized subclass) with validated min/max ranges. - Automatic binding: The generator wires these parameters to the correct CC numbers specified in the YAML.
Device Class
The root device class (e.g., Minilogue) inherits from nallely.MidiDevice and exposes each section as a property:
- Property aggregation: Sections are instantiated as properties of the device class, allowing dot‑notation access like
synth.oscillators.wave. - MIDI lifecycle management: The base class handles connection establishment, port enumeration, and message transmission.
Summary
- Nallely provides an end‑to‑end code generation pipeline that converts YAML device configurations into executable Python APIs.
- The
nallely generateCLI command (implemented innallely/cli.py) orchestrates the workflow. generate_apiuses ruamel.yaml to safely parse configurations into Python dictionaries (nallely/codegen/midi_module_generator.py, lines 32‑40).generate_codeemits Python classes derived fromnallely.Moduleandnallely.MidiDevice, complete withModuleParameterattributes for each MIDI CC (lines 67‑30).- The resulting module supports immediate device control through high‑level attribute assignments rather than raw MIDI byte manipulation.
Frequently Asked Questions
What YAML format does Nallely expect for code generation?
Nallely expects a nested YAML structure with the pattern: Manufacturer → Device Name → Section → Parameter. Each parameter must include a cc (Continuous Controller number), min value, max value, and description string. This structure is parsed by generate_api in nallely/codegen/midi_module_generator.py to build the internal representation used for code emission.
Can I generate a Python API from CSV instead of YAML?
Yes. While the examples above focus on YAML, the nallely generate command also accepts CSV input files. The generator normalizes both formats into the same internal dictionary structure before calling generate_code, allowing you to define device configurations in whichever format suits your workflow.
What dependencies are required for code generation?
The code generation feature requires ruamel.yaml for parsing YAML configurations safely. This dependency is typically installed alongside Nallely. The runtime components (nallely.Module, nallely.MidiDevice, ModuleParameter) used by the generated code are part of the core nallely package defined in nallely/modules/*.py.
How do I customize the parameter ranges in the generated API?
Define the min and max keys explicitly in your YAML configuration for each parameter. The generate_code function reads these bounds and injects them into the ModuleParameter constructor calls within the generated Python file. When you assign values to the generated attributes at runtime, Nallely enforces these constraints automatically, preventing out‑of‑range MIDI values from being transmitted to the hardware.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →