Skip to content

loader

loader

Instrument profile loader.

Scans directories for YAML instrument profiles and parses them into validated InstrumentProfile instances. This is the entry point for the instrument plugin system — the conductor calls the loader at startup to discover available instruments.

Loading order matters
  1. Built-in profiles (shipped with Marianne, lowest precedence)
  2. Organization profiles (~/.marianne/instruments/)
  3. Venue profiles (.marianne/instruments/, highest precedence)

Later directories override earlier ones on name collision. This lets venue-specific profiles customize organization-wide defaults, which in turn customize built-in defaults.

Invalid YAML files, validation failures, and other errors are logged and skipped — one broken profile should not prevent other instruments from loading. This is a reliability-first design: degrade gracefully, log clearly, continue operating.

Usage

from marianne.instruments.loader import InstrumentProfileLoader

profiles = InstrumentProfileLoader.load_directories([ Path.home() / ".marianne" / "instruments", Path(".marianne/instruments"), ])

Classes

InstrumentProfileLoader

Loads InstrumentProfile instances from YAML files in directories.

The loader is deliberately simple: scan a directory for YAML files, parse each one, validate via Pydantic, and collect the results. No recursion into subdirectories. No implicit file discovery magic.

Error handling: every failure is logged with the file path and reason. The loader continues past failures — one broken YAML file should not prevent other instruments from loading.

Functions
load_directory staticmethod
load_directory(directory)

Load all instrument profiles from a single directory.

Parameters:

Name Type Description Default
directory str | Path

Path to scan for .yaml and .yml files. If the directory does not exist, returns empty dict.

required

Returns:

Type Description
dict[str, InstrumentProfile]

Dict of profile name → InstrumentProfile. When two files in

dict[str, InstrumentProfile]

the same directory define the same name, the last one

dict[str, InstrumentProfile]

(alphabetically by filename) wins.

Source code in src/marianne/instruments/loader.py
@staticmethod
def load_directory(directory: str | Path) -> dict[str, InstrumentProfile]:
    """Load all instrument profiles from a single directory.

    Args:
        directory: Path to scan for *.yaml and *.yml files.
            If the directory does not exist, returns empty dict.

    Returns:
        Dict of profile name → InstrumentProfile. When two files in
        the same directory define the same name, the last one
        (alphabetically by filename) wins.
    """
    dir_path = Path(directory)
    if not dir_path.is_dir():
        _logger.debug(
            "instruments_dir_not_found",
            directory=str(dir_path),
        )
        return {}

    profiles: dict[str, InstrumentProfile] = {}

    # Sort files alphabetically for deterministic override behavior
    yaml_files = sorted(
        f for f in dir_path.iterdir()
        if f.is_file() and f.suffix in _YAML_EXTENSIONS
    )

    for yaml_file in yaml_files:
        profile = InstrumentProfileLoader._load_file(yaml_file)
        if profile is not None:
            if profile.name in profiles:
                _logger.info(
                    "instrument_name_override",
                    name=profile.name,
                    file=str(yaml_file),
                    previous_file="(same directory)",
                )
            profiles[profile.name] = profile

    if profiles:
        _logger.info(
            "instruments_loaded",
            directory=str(dir_path),
            count=len(profiles),
            names=sorted(profiles.keys()),
        )

    return profiles
load_directories staticmethod
load_directories(directories)

Load profiles from multiple directories with override semantics.

Later directories override earlier ones on name collision. The intended loading order: 1. Built-in profiles (lowest precedence) 2. Organization profiles (~/.marianne/instruments/) 3. Venue profiles (.marianne/instruments/, highest precedence)

Parameters:

Name Type Description Default
directories list[str | Path]

Ordered list of directories to scan. Missing directories are silently skipped.

required

Returns:

Type Description
dict[str, InstrumentProfile]

Merged dict of profile name → InstrumentProfile.

Source code in src/marianne/instruments/loader.py
@staticmethod
def load_directories(
    directories: list[str | Path],
) -> dict[str, InstrumentProfile]:
    """Load profiles from multiple directories with override semantics.

    Later directories override earlier ones on name collision. The
    intended loading order:
        1. Built-in profiles (lowest precedence)
        2. Organization profiles (~/.marianne/instruments/)
        3. Venue profiles (.marianne/instruments/, highest precedence)

    Args:
        directories: Ordered list of directories to scan. Missing
            directories are silently skipped.

    Returns:
        Merged dict of profile name → InstrumentProfile.
    """
    merged: dict[str, InstrumentProfile] = {}

    for directory in directories:
        dir_profiles = InstrumentProfileLoader.load_directory(directory)
        for name, profile in dir_profiles.items():
            if name in merged:
                _logger.info(
                    "instrument_overridden_by_later_dir",
                    name=name,
                    directory=str(directory),
                )
            merged[name] = profile

    _logger.info(
        "instruments_total_loaded",
        count=len(merged),
        names=sorted(merged.keys()),
    )

    return merged

Functions

load_all_profiles

load_all_profiles()

Load all instrument profiles from all standard sources.

Convenience function that encapsulates the standard loading order
  1. Native instruments (4 built-in backends)
  2. Built-in YAML profiles (shipped with Marianne)
  3. Organization profiles (~/.marianne/instruments/)
  4. Venue profiles (.marianne/instruments/)

Later sources override earlier ones on name collision.

Returns:

Type Description
dict[str, InstrumentProfile]

Dict of profile name → InstrumentProfile.

Source code in src/marianne/instruments/loader.py
def load_all_profiles() -> dict[str, InstrumentProfile]:
    """Load all instrument profiles from all standard sources.

    Convenience function that encapsulates the standard loading order:
        1. Native instruments (4 built-in backends)
        2. Built-in YAML profiles (shipped with Marianne)
        3. Organization profiles (~/.marianne/instruments/)
        4. Venue profiles (.marianne/instruments/)

    Later sources override earlier ones on name collision.

    Returns:
        Dict of profile name → InstrumentProfile.
    """
    from marianne.instruments.registry import InstrumentRegistry, register_native_instruments

    registry = InstrumentRegistry()
    register_native_instruments(registry)

    profiles: dict[str, InstrumentProfile] = {
        p.name: p for p in registry.list_all()
    }

    builtins_dir = Path(__file__).resolve().parent / "builtins"
    org_dir = Path.home() / ".marianne" / "instruments"
    venue_dir = Path(".marianne") / "instruments"

    yaml_profiles = InstrumentProfileLoader.load_directories(
        [builtins_dir, org_dir, venue_dir]
    )

    profiles.update(yaml_profiles)
    return profiles