Skip to content

The tap.python spike (9.0) on a Mac: init/ maps the object, get<attr> needs the dumpout method, a str is a symbol - #43

Merged
tap merged 1 commit into
mainfrom
v2/spike
Oct 9, 2026
Merged

tap merged 1 commit into
mainfrom
v2/spike

Conversation

@tap

@tap tap commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Plan item 9.0 (docs/TAP-PYTHON-PLAN.md): before any of 9.1 onward is built, ask a real Max what tap.python's design rests on. This runs the spike on a Mac (Max 9.1.5, macOS 15.7.9, x86_64) and writes every answer into the plan under 9.0, as verified in Max with the version. Windows is still to run, so 9.0 stays unticked. Nothing is tagged.

What is in it

  • The spike class (source/projects/tap.python_tilde/tap.python_spike.cpp, throwaway). It is a plain SDK class, tap.python (class_new/class_addmethod/class_register, no min, no Python), registered by tap.python~'s own ext_main after min's wrap_as_max_external(), in the same binary (D11). It has:

    • a dumpout stored in the obex, and value outlets that outlets n changes between the box's dynlet_begin and dynlet_end;
    • an instance attribute and instance methods, a class attribute, and a class-level anything;
    • messages that ask Max things: describe, fire, probe, thread, outmsg/outsym, getdump, attrmethod;
    • two helper classes: the same class without its dumpout method, and a recorder that posts each message it receives with its atom types.

    It builds only with -DTAP_PYTHON_SPIKE=ON, which is off by default and affects the external only (the mock-kernel test compiles tap.python_tilde.cpp without it). With the option off, ext_main is MIN_EXTERNAL(python) exactly as before.

  • init/tap.python.txt (max objectfile tap.python tap.python~;). scripts/assemble-package.py now ships init/; I checked that an assembled macOS package carries it.

  • runtime-tests/spike/:

    • a generated patcher per question;
    • run_spike.py, which opens each patcher in a fresh Max through run.py's harness and reads Max's own log. It turns "Restore Windows on Launch" off for the run and restores the preference files after;
    • results/macos.txt, the evidence;
    • a README on running it by hand on Windows.
  • The plan:

    • verdicts under each 9.0 question, plus a "What 9.0 found, on a Mac" section with the evidence;
    • the design sections the answers change: Ports, anything, the guard, reserved names, the str row of the output table, and 9.3–9.5;
    • a revision-record entry. PRODUCTION-PLAN.md notes the Mac run.
  • CLAUDE.md: a pointer to the spike, and one correction (below).

What Max said (Mac)

  1. One binary, two classes. A fresh Max with only [tap.python] loads it through the init/ mapping. With the file moved aside, Max says No such object. Both objects load and work in one patch, whichever is created first. No stub external is needed.
  2. Ports. outlet_insert_after (the last value outlet) puts the new outlet before the dumpout. The dumpout's cord moves with it and every other cord stays. outlet_delete removes exactly the outlets it is given, with their cords.
  3. get<attr> needs the class's dumpout method. The class must register class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0), as Cycling '74's SDK examples do. Without it, get<attr> is dropped silently: no getter call, no output, no error. The plan's Ports did not have this. In passing: object_attr_getdump() wants the get… message name, not the attribute name its header documents.
  4. Dispatch.
    • Instance attributes and methods win over a class-level anything, and an instance method wins over a class method of the same name, bang included.
    • int, float, bang and list never reach anything. So the forwarder also needs class-level methods for those four.
    • object_getmethod() still answers an unknown name with method_false() on such a class, so 1.0.2's guard stands. dumpout joins the reserved names.
  5. Threads. A metro-driven message runs on the main thread with Overdrive off, on the scheduler thread with it on, and on the audio thread with Scheduler in Audio Interrupt on and audio running. systhread_isaudiothread() detects that last case. Both settings were put back.
  6. A str return → symbol <s> (the maintainer left this to the spike).
    • Output as the message it names, six strings are not data. list is dropped. int and float raise "missing arguments" in every receiver. bang becomes a real bang. symbol and "" misbehave too.
    • As symbol <s>, every string arrives as itself, and Max's own text objects output single symbols the same way.
    • Costs: [route C] won't match it (use [sel C]), and [prepend] keeps the word symbol. A list still names a message: ["start"].
  7. Max's documentation system. Max's file database records vignettes and tutorials anywhere under docs/, and an extras/ patcher as the package's. I could not look at the Documentation window or the Extras menu: the Mac's screen was locked for the whole session.

CLAUDE.md correction: it said an object's own messages answer their names through object_getmethod(), as its attributes do. The probes show only attributes do. Messages added with object_addmethod() answer as unknown, on tap.python~ too. The guard leaves both out, so nothing changes in behaviour. The matching comment on answered_by_max() is left for 9.2's move, since this PR doesn't touch shipped source.

Still to do, by hand

  • Windows (runtime-tests/spike/README.md):
    • the init/ mapping, and the first Windows (DLL) build of the second class, in either order (Q1);
    • the dumpout method registered through an imported function's address, which is an import thunk on Windows (Q3);
    • the guard's sentinel on a class with anything (Q4);
    • threads with Windows' audio drivers (Q5).
  • Either platform: whether the Documentation window lists the q7 vignettes and tutorials, and whether the Extras menu shows the patcher.

Checked

  • cmake --build and ctest pass with the option off (build) and on (build-spike), on this Mac.
  • clang-format (pre-commit, the pinned version) passes.
  • clang-tidy-18, run as style.yml does, is clean on tap.python_tilde.cpp with and without -DTAP_PYTHON_SPIKE, and on the spike TU.
  • The Linux legs weren't run here. Shipped code is unchanged with the option off; CI will run them.

🤖 Generated with Claude Code

https://claude.ai/code/session_019rVV9SkjkWmiBXFMT4whkz

… needs the dumpout method, a str is a symbol

Plan 9.0 asks Max what the design of tap.python rests on before any of 9.1
onward is built. This is the spike, run on a Mac (Max 9.1.5), with the answers
written into docs/TAP-PYTHON-PLAN.md under 9.0; Windows is still to run.

- tap.python_spike.cpp (throwaway, -DTAP_PYTHON_SPIKE=ON, off by default, the
  external only): a plain SDK class, tap.python, registered by tap.python~'s
  own ext_main after min's — a dumpout in the obex, value outlets changed
  between dynlet_begin and dynlet_end, an instance attribute and methods, a
  class attribute, a class-level anything, and messages that ask Max things —
  plus the same class without its dumpout method, and a recorder that posts
  every message with its atom types. With the option off, tap.python_tilde.cpp
  is MIN_EXTERNAL(python) as before.
- init/tap.python.txt (max objectfile tap.python tap.python~;), shipped by
  scripts/assemble-package.py.
- runtime-tests/spike/: a patcher per question, generated; run_spike.py, which
  opens each in a fresh Max through run.py's harness and collects Max's log;
  the Mac's results; and how to run it by hand on Windows.

What Max said: the init/ mapping finds [tap.python] in a fresh Max (and
without it, No such object), both objects load in either order;
outlet_insert_after keeps the dumpout last and every cord that stays;
get<attr> is dropped silently unless the class registers object_obex_dumpout
as its dumpout method, as the SDK's examples do; instance attributes and
methods win over a class-level anything, but int, float, bang and list never
reach it; object_getmethod() still answers unknown names with method_false(),
so 1.0.2's guard stands; a metro message runs on the main, scheduler or audio
thread as the plan said, and systhread_isaudiothread() tells; and a str return
is output as symbol <s> — as the message it names, list, int, float, symbol,
bang and "" are not data. The plan's Ports, anything, guard, reserved names,
output table and 9.3-9.5 follow, and CLAUDE.md's guard note is corrected: an
object's own messages do not answer object_getmethod(), only its attributes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rVV9SkjkWmiBXFMT4whkz
@tap
tap merged commit 50a2c75 into main Oct 9, 2026
18 checks passed
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