Skip to content

Add Python stub module generation for IDE autocomplete#8

Open
chrishyoroklee wants to merge 4 commits intoBarnard-PL-Labs:mainfrom
chrishyoroklee:main
Open

Add Python stub module generation for IDE autocomplete#8
chrishyoroklee wants to merge 4 commits intoBarnard-PL-Labs:mainfrom
chrishyoroklee:main

Conversation

@chrishyoroklee
Copy link

@chrishyoroklee chrishyoroklee commented Feb 28, 2026

Motivation

MaxPyLang lets users build Max patches in Python, but there's no way for IDE autocomplete or LLM agents
(like Claude, Cursor, Copilot) to know what Max objects exist. Users have to memorize object names and
manually write MaxObject("cycle~") every time. The generated stub modules solve both problems: IDEs
can autocomplete object names, and LLM agents can read the docstrings to understand what each object
does, what arguments it takes, and how to use it.

Example

Before:

  patch.place(MaxObject("cycle~"))  # user must know the exact Max name

After:

  from maxpylang.objects import cycle_tilde, dac_tilde, metro, toggle

  patch = MaxPatch()
  [t] = patch.place(toggle)
  [m] = patch.place(metro)
  [c] = patch.place(cycle_tilde)
  [d] = patch.place(dac_tilde)
  patch.connect(
      (t.outs[0], m.ins[0]),
      (m.outs[0], c.ins[0]),
      (c.outs[0], d.ins[0]),
  )
  patch.save("my_patch.maxpat")

An LLM agent can read the docstring for cycle_tilde and learn it's a sinusoidal oscillator, what
inlets/outlets it has, what attributes are available, and what related objects exist — all without
needing access to Max documentation.

Summary

  • Extracts XML documentation metadata (digest, description, inlets, outlets, methods, seealso) from Max
    reference files and stores it in JSON alongside existing object info
  • Generates Python stub modules (objects/max.py, objects/msp.py, objects/jit.py) containing
    pre-instantiated MaxObject variables with rich docstrings, so users get IDE autocomplete and can
    write patch.place(cycle_tilde) instead of patch.place(MaxObject("cycle~"))
  • Adds sanitize_py_name() to convert Max names to valid Python identifiers (cycle~cycle_tilde,
    jit.gl.renderjit_gl_render, 2d.wave~_2d_wave_tilde, ifif_, dictdict_)

How it works

  1. User runs import_objs("vanilla") (one-time setup, Max must be open)
  2. JSON object info is saved as before
  3. New: generate_stubs() reads the JSON and writes maxpylang/objects/{package}.py with one
    variable per object
  4. User imports stubs: from maxpylang.objects import cycle_tilde, metro, dac_tilde
  5. Each stub is a real MaxObject instance — cycle_tilde.name returns "cycle~"

Generated stub format

  """
  cycle~ - Sinusoidal oscillator

  Args:
    frequency (number, optional)

  Inlets:
    0 (signal/float): Frequency
    ...

  Attributes: frequency, phase, buffer, ...

  See also: saw~, tri~, rect~
  """
  cycle_tilde = MaxObject('cycle~')

Files changed

  • maxpylang/importobjs.py — Added sanitize_py_name(), _build_docstring(), generate_stubs();
    wired into import_objs(); added import keyword, builtins
  • maxpylang/objects/__init__.py — New file. Re-exports from generated sub-modules with try/except
    guards
  • maxpylang/__init__.py — Added optional from . import objects

Edge cases handled

  • ~_tilde, ._, -_
  • Leading digit → prepend _
  • Python keywords/builtins → append _
  • Triple-quote injection in docstrings → escaped
  • stdout suppressed during stub import to silence MaxObject arg validation warnings
  • objects/__init__.py dynamically regenerated based on existing stub files (supports third-party
    packages)

Automated tests passed(43 tests)

- sanitize_py_name: all transform rules (tilde, dot, hyphen, leading digit, keywords, builtins, combined, empty string)            
- _build_docstring: digest-only, full docstring, empty doc, no args, COMMON filtering, triple-quote safety
- generate_stubs: files created, valid Python (ast.parse), __all__/_NAMES consistency, __init__.py regenerated, custom packages
- Backward compatibility: MaxObject("cycle~"), MaxPatch create/place/connect/save, imports with and without stubs
- Stub E2E: import, isinstance check, name preserved, place/connect in patch, string equivalence, no stdout noise
- Examples: hello_world produces valid .maxpat JSON

@chrishyoroklee chrishyoroklee marked this pull request as ready for review February 28, 2026 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant