Python Integration

PatchLang provides Python bindings via PyO3 and maturin, producing a native Python extension module.

Building

./scripts/build-python.sh

This creates a virtual environment (.venv/) and installs the patchlang_python package in development mode.

Usage

import json
import patchlang_python

with open('my-venue.patch') as f:
    source = f.read()

result = json.loads(patchlang_python.parse(source))

if result['errors']:
    for err in result['errors']:
        print(f"Error: {err['message']}")
else:
    statements = result['program']['statements']
    print(f"Parsed {len(statements)} statements")

# Quick validation
is_valid = patchlang_python.validate(source)

Django Integration

import json
from django.core.exceptions import ValidationError
import patchlang_python

def validate_patch_content(content: str) -> dict:
    """Parse and validate .patch content on save."""
    result = json.loads(patchlang_python.parse(content))

    if result['errors']:
        err = result['errors'][0]
        raise ValidationError(f"Invalid PatchLang: {err['message']}")

    return result['program']

def extract_project_summary(content: str) -> dict:
    """Extract device counts and metadata for search/billing."""
    program = validate_patch_content(content)

    templates = [s for s in program['statements'] if s['type'] == 'Template']
    instances = [s for s in program['statements'] if s['type'] == 'Instance']
    connects  = [s for s in program['statements'] if s['type'] == 'Connect']

    return {
        'template_count': len(templates),
        'instance_count': len(instances),
        'connection_count': len(connects),
        'device_names': [i['name'] for i in instances],
    }

API

Most functions return a JSON string — call json.loads() on the result. Exceptions: validate returns bool; resolve_uses returns a native list[str].


parse(source: str) -> str

Parse PatchLang source. Returns JSON { "program": {...} | null, "errors": [...] }.

result = json.loads(patchlang_python.parse(source))
if not result['errors']:
    print(result['program']['statements'])

validate(source: str) -> bool

Returns True if the source parses with no errors. Does not run DRC.

if not patchlang_python.validate(source):
    raise ValueError("invalid .patch file")

check(source: str) -> str

Parse + Design Rule Check. Returns JSON { "program": {...} | null, "errors": [...], "diagnostics": [...] }. Use this instead of parse when you also want DRC warnings (e.g. unconnected ports).

result = json.loads(patchlang_python.check(source))
for d in result['diagnostics']:
    print(f"[{d['severity']}] {d['message']}")

resolve_uses(source: str) -> list[str]

Quick-parse and return the namespace strings from all use statements. Returns a native Python list[str] — no json.loads() needed.

namespaces = patchlang_python.resolve_uses(source)
# e.g. ["vendor/dante", "vendor/madi"]

compile_project(files: dict[str, str], entry: str) -> str

Multi-file compilation with namespace resolution and merged DRC.

Returns JSON { "program": {...} | null, "errors": [...], "diagnostics": [...], "files": [...], "templateFiles": {...}, "useGraph": {...} }.

files = {
    "main.patch": open("main.patch").read(),
    "vendor/dante.patch": open("vendor/dante.patch").read(),
}
result = json.loads(patchlang_python.compile_project(files, "main.patch"))

generate_port_id(instance_name: str, template_name: str, port_name: str, index: int | None = None) -> str

Generate a deterministic port ID string. Returns a plain str — no json.loads() needed.

port_id = patchlang_python.generate_port_id("Desk", "DiGiCo_SD12", "AES_Out", 3)

generate_route_id(template_name: str, source_port: str, target_port: str) -> str

Generate a deterministic route ID string. Returns a plain str.

route_id = patchlang_python.generate_route_id("DiGiCo_SD12", "AES_Out_3", "MADI_In_1")

generate_slot_id(template_name: str, slot_name: str) -> str

Generate a deterministic slot ID string. Returns a plain str.

slot_id = patchlang_python.generate_slot_id("DiGiCo_SD12", "Option_Slot_A")

parse_manifest(json: str) -> str

Parse a project.json manifest string. Returns JSON { "manifest": {...} | null, "errors": [...] }.

with open("project.json") as f:
    raw = f.read()
result = json.loads(patchlang_python.parse_manifest(raw))

validate_layout(json: str) -> str

Validate a .layout.json string against the layout schema. Returns JSON { "valid": bool, "errors": [...] }.

with open("MTG.layout.json") as f:
    raw = f.read()
result = json.loads(patchlang_python.validate_layout(raw))
if not result['valid']:
    print(result['errors'])

validate_project_consistency(patch: str, layout: str) -> str

Cross-validate instance names between a .patch source string and a .layout.json string. Returns JSON { "valid": bool, "errors": [...], "warnings": [...] }.

patch_src  = open("MTG.patch").read()
layout_src = open("MTG.layout.json").read()
result = json.loads(patchlang_python.validate_project_consistency(patch_src, layout_src))
if result['warnings']:
    for w in result['warnings']:
        print(f"Warning: {w}")

Requirements