# How to Generate a Python API from a YAML Device Configuration in Nallely

> Generate a Python API from a YAML device configuration using Nallely's CLI tool. Automate MIDI control code and save development time.

- Repository: [dr-schlange/nallely-midi](https://github.com/dr-schlange/nallely-midi)
- Tags: how-to-guide
- Published: 2026-02-28

---

**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`](https://github.com/dr-schlange/nallely-midi/blob/main/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`.

```bash
nallely generate -i mydevice.yaml -o mydevice.py

```

### YAML Parsing and Validation

The `generate_api` function in [`nallely/codegen/midi_module_generator.py`](https://github.com/dr-schlange/nallely-midi/blob/main/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 containing `ModuleParameter` attributes (or specialized variants) for every Continuous Controller (CC), program change, or NRPN defined in the YAML.
- **Device class** derived from `nallely.MidiDevice` that 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.

```yaml
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`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/codegen/__init__.py) and invoked by the CLI parser in [`nallely/cli.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/cli.py).

```bash
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`.

```python
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`](https://github.com/dr-schlange/nallely-midi/blob/main/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 `ModuleParameter` instance (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 generate`** CLI command (implemented in [`nallely/cli.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/cli.py)) orchestrates the workflow.
- **`generate_api`** uses **ruamel.yaml** to safely parse configurations into Python dictionaries ([`nallely/codegen/midi_module_generator.py`](https://github.com/dr-schlange/nallely-midi/blob/main/nallely/codegen/midi_module_generator.py), lines 32‑40).
- **`generate_code`** emits Python classes derived from `nallely.Module` and `nallely.MidiDevice`, complete with `ModuleParameter` attributes 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`](https://github.com/dr-schlange/nallely-midi/blob/main/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.