From 3f1f5233be3568c92ba652d44948452dab24274f Mon Sep 17 00:00:00 2001 From: Timothy Place Date: Wed, 7 Oct 2026 17:37:53 -0500 Subject: [PATCH] =?UTF-8?q?Release=201.0.1:=20the=20Mac=20session=20(8.8)?= =?UTF-8?q?=20=E2=80=94=20a=20reload=20kept=20no=20attributes;=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run in Max on main, the suite found a second bug in 8.2's guard: the attributes the object adds for a class's fields answer their names in Max, so on a reload the guard took them for Max's own and reserved every field — a save left the object with no attributes. answered_by_max() now leaves out the object's own attributes as it did its own messages. The glue test's kernel answers an added attribute's name while a new scenario asks, and that scenario fails without the fix. The worker runtime test's console count was wrong, not the worker: the reload before `hang 1` begins a new load, so the hang is reported late again and, outlasting the ring, drops input too — it now expects each of the four lines. Whole suite: 12 patchers pass. The plan records 8.8's results and the stack measurement (512 KiB by default: about 98 levels of recursion through sorted's key; 16 MiB: CPython's own limit). CHANGELOG: 1.0.1 dated, with this fix. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_019rVV9SkjkWmiBXFMT4whkz --- CHANGELOG.md | 8 +- docs/PRODUCTION-PLAN.md | 16 + runtime-tests/make_patchers.py | 7 +- .../tap.python~.worker.maxtest.maxpat | 979 ++++++++++++------ .../tap.python_tilde/tap.python_tilde.h | 10 +- .../tap.python_tilde_test.cpp | 77 +- 6 files changed, 747 insertions(+), 350 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc15626..2e66f7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ breaking changes to the contract were allowed where they bought correctness (D5 `docs/PRODUCTION-PLAN.md`); each is recorded here. From 1.0.0, a change to the class contract is a major version. -## 1.0.1 — unreleased +## 1.0.1 — 2026-10-07 ### Fixed @@ -17,6 +17,12 @@ a major version. still worked. The mock kernel the unit tests run against answers null, which is why they passed. The guard now recognizes `method_false()`, the unit test's kernel answers as Max does, and the Mac session (8.8) is what would have caught it before the tag. +- **A reload lost every attribute.** With that fixed, the guard still took the attributes the + object had added for the class's fields — which answer their names in Max, as messages do — for + Max's own, so a save left the object with no attributes ("the field … is reserved by the host", + then "doesn't understand …"). It now leaves out the object's own attributes as it did its own + messages. Found in the Mac session; the unit test's kernel now answers an added attribute's name + as Max does, and fails without the fix. ### Changed diff --git a/docs/PRODUCTION-PLAN.md b/docs/PRODUCTION-PLAN.md index aa38ef6..8fe6199 100644 --- a/docs/PRODUCTION-PLAN.md +++ b/docs/PRODUCTION-PLAN.md @@ -751,6 +751,22 @@ down rather than discovered again. *This plan was itself audited before being ad - [ ] **8.8 The Mac session for this phase.** Runtime tests for 8.2 (`faults`) and 8.3 (`worker`); 8.3's stack measurement; the macOS half of 8.7 is not needed (POSIX paths are UTF-8). Then repeat the runbook's step 4 on the release packages, and tag `v0.11.0`. + *Run 2026-10-07 (Intel Mac, Max 9, on main with 1.0.1's fix):* the suite found **a second bug in + 8.2's guard** — `reload` failed: the attributes the object adds for a class's fields answer their + names in Max, so on a reload the guard took them for Max's own and reserved every field ("the + field gain is reserved by the host", then "doesn't understand gain"); `worker`'s + `hang` failed the same way after its reload, silently (an unchanged file announces nothing). + Fixed for 1.0.1 (`answered_by_max()` leaves out `m_python_attributes` as it did + `m_python_messages`), against a glue scenario that fails without it — the test's kernel now + answers an added attribute's name, as Max does, while the scenario asks. `faults` passes (DSP + toggled and a cord connected with the C-argument names in the class). `worker`'s console count + was wrong, not the worker: the reload before `hang 1` begins a new load, so the 500 ms hang is + reported late again, and it outlasts the ring (latency + 0.25 s), so input is dropped too — four + lines, not two; the test now expects each. Whole suite: 12 patchers pass. *Stack measured:* a + fresh `pthread_attr_t` (`std::thread`'s) has 512 KiB here, and recursion through `sorted`'s key + crashes there after about 98 levels — short of `deep_recursion.py`'s 450; at the worker's + 16 MiB it reaches CPython's own recursion limit (about 2,500 levels), as `threading.Thread` + does. *Still by hand:* step 4 on the release packages, and Build Collective for `fileusage`. *The 1.0 gate (revised):* 8.1–8.6 change what the ReadMe promises or what a class can rely on, so they land before 1.0, where D5 still allows them; 8.7 may follow it. *Decided 2026-10-01 (the maintainer):* diff --git a/runtime-tests/make_patchers.py b/runtime-tests/make_patchers.py index 2f01329..22caa98 100644 --- a/runtime-tests/make_patchers.py +++ b/runtime-tests/make_patchers.py @@ -629,6 +629,7 @@ def worker(latency_ms: float | None = None, quiet_windows: int = 0) -> Test: t.patcher.connect(delay_by_latency, 1, py) t.patcher.connect(delay_by_latency, 0, delay, 1) t.count_errors("late.for") + t.count_errors("dropped") t.count_errors("interrupted") watch = t.no_change("delayed-by-latencysamples", late, 0.0) @@ -656,7 +657,11 @@ def worker(latency_ms: float | None = None, quiet_windows: int = 0) -> Test: t.step(t.attribute_equals("direct-reports-no-latency", py, "latencysamples", 0), t.sample_equals("direct-has-no-delay", now, 0.0), t.errors_are("hang-interrupted-once", "== 1", "interrupted"), - t.errors_are("console-only-the-stall-and-the-interruption", "== 2"), wait=1000) + # the reload began a new load, so the hang's lateness is reported again, and at 500 ms it + # outlasts the ring (latency + 0.25 s) and drops input too (found in Max, plan 8.8) + t.errors_are("hang-late-again-after-the-reload", "== 2", "late.for"), + t.errors_are("hang-outlasts-the-ring", "== 1", "dropped"), + t.errors_are("console-only-the-stall-and-the-hang", "== 4"), wait=1000) return t diff --git a/runtime-tests/patchers/tap.python~.worker.maxtest.maxpat b/runtime-tests/patchers/tap.python~.worker.maxtest.maxpat index 9863076..fe691af 100644 --- a/runtime-tests/patchers/tap.python~.worker.maxtest.maxpat +++ b/runtime-tests/patchers/tap.python~.worker.maxtest.maxpat @@ -244,10 +244,10 @@ "patching_rect": [ 1020.0, 140.0, - 142.0, + 114.0, 22.0 ], - "text": "regexp interrupted" + "text": "regexp dropped" } }, { @@ -293,6 +293,67 @@ "box": { "id": "obj-15", "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 1020.0, + 230.0, + 142.0, + 22.0 + ], + "text": "regexp interrupted" + } + }, + { + "box": { + "id": "obj-16", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1020.0, + 260.0, + 40.0, + 22.0 + ], + "text": "t b" + } + }, + { + "box": { + "id": "obj-17", + "maxclass": "newobj", + "numinlets": 3, + "numoutlets": 4, + "outlettype": [ + "", + "", + "", + "" + ], + "patching_rect": [ + 1020.0, + 290.0, + 135.0, + 22.0 + ], + "text": "counter 1 1000000" + } + }, + { + "box": { + "id": "obj-18", + "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, "outlettype": [ @@ -309,7 +370,7 @@ }, { "box": { - "id": "obj-16", + "id": "obj-19", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -327,7 +388,7 @@ }, { "box": { - "id": "obj-17", + "id": "obj-20", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -346,7 +407,7 @@ }, { "box": { - "id": "obj-18", + "id": "obj-21", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -364,7 +425,7 @@ }, { "box": { - "id": "obj-19", + "id": "obj-22", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -382,7 +443,7 @@ }, { "box": { - "id": "obj-20", + "id": "obj-23", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -400,7 +461,7 @@ }, { "box": { - "id": "obj-21", + "id": "obj-24", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -418,7 +479,7 @@ }, { "box": { - "id": "obj-22", + "id": "obj-25", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -436,7 +497,7 @@ }, { "box": { - "id": "obj-23", + "id": "obj-26", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -454,7 +515,7 @@ }, { "box": { - "id": "obj-24", + "id": "obj-27", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -472,7 +533,7 @@ }, { "box": { - "id": "obj-25", + "id": "obj-28", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -491,7 +552,7 @@ }, { "box": { - "id": "obj-26", + "id": "obj-29", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -509,7 +570,7 @@ }, { "box": { - "id": "obj-27", + "id": "obj-30", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -527,7 +588,7 @@ }, { "box": { - "id": "obj-28", + "id": "obj-31", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -545,7 +606,7 @@ }, { "box": { - "id": "obj-29", + "id": "obj-32", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -563,7 +624,7 @@ }, { "box": { - "id": "obj-30", + "id": "obj-33", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -581,7 +642,7 @@ }, { "box": { - "id": "obj-31", + "id": "obj-34", "maxclass": "newobj", "numinlets": 1, "numoutlets": 3, @@ -601,7 +662,7 @@ }, { "box": { - "id": "obj-32", + "id": "obj-35", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -619,7 +680,7 @@ }, { "box": { - "id": "obj-33", + "id": "obj-36", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -637,7 +698,7 @@ }, { "box": { - "id": "obj-34", + "id": "obj-37", "maxclass": "newobj", "numinlets": 1, "numoutlets": 3, @@ -648,7 +709,7 @@ ], "patching_rect": [ 1020.0, - 230.0, + 320.0, 240.0, 22.0 ], @@ -657,7 +718,7 @@ }, { "box": { - "id": "obj-35", + "id": "obj-38", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -666,7 +727,7 @@ ], "patching_rect": [ 1020.0, - 260.0, + 350.0, 170.0, 22.0 ], @@ -675,14 +736,14 @@ }, { "box": { - "id": "obj-36", + "id": "obj-39", "maxclass": "newobj", "numinlets": 1, "numoutlets": 0, "outlettype": [], "patching_rect": [ 1020.0, - 290.0, + 380.0, 128.0, 22.0 ], @@ -691,7 +752,7 @@ }, { "box": { - "id": "obj-37", + "id": "obj-40", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -709,7 +770,7 @@ }, { "box": { - "id": "obj-38", + "id": "obj-41", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -718,7 +779,7 @@ ], "patching_rect": [ 1020.0, - 320.0, + 410.0, 40.0, 22.0 ], @@ -727,7 +788,7 @@ }, { "box": { - "id": "obj-39", + "id": "obj-42", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -736,7 +797,7 @@ ], "patching_rect": [ 1020.0, - 350.0, + 440.0, 44.0, 22.0 ], @@ -745,7 +806,7 @@ }, { "box": { - "id": "obj-40", + "id": "obj-43", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -763,7 +824,7 @@ }, { "box": { - "id": "obj-41", + "id": "obj-44", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -781,7 +842,7 @@ }, { "box": { - "id": "obj-42", + "id": "obj-45", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -799,7 +860,7 @@ }, { "box": { - "id": "obj-43", + "id": "obj-46", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -817,7 +878,7 @@ }, { "box": { - "id": "obj-44", + "id": "obj-47", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -835,7 +896,7 @@ }, { "box": { - "id": "obj-45", + "id": "obj-48", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -853,7 +914,7 @@ }, { "box": { - "id": "obj-46", + "id": "obj-49", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -871,7 +932,7 @@ }, { "box": { - "id": "obj-47", + "id": "obj-50", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -889,7 +950,7 @@ }, { "box": { - "id": "obj-48", + "id": "obj-51", "maxclass": "newobj", "numinlets": 1, "numoutlets": 3, @@ -909,7 +970,7 @@ }, { "box": { - "id": "obj-49", + "id": "obj-52", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -927,7 +988,7 @@ }, { "box": { - "id": "obj-50", + "id": "obj-53", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -945,7 +1006,7 @@ }, { "box": { - "id": "obj-51", + "id": "obj-54", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -963,7 +1024,7 @@ }, { "box": { - "id": "obj-52", + "id": "obj-55", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -981,7 +1042,7 @@ }, { "box": { - "id": "obj-53", + "id": "obj-56", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -999,7 +1060,7 @@ }, { "box": { - "id": "obj-54", + "id": "obj-57", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1008,7 +1069,7 @@ ], "patching_rect": [ 1020.0, - 380.0, + 470.0, 40.0, 22.0 ], @@ -1017,7 +1078,7 @@ }, { "box": { - "id": "obj-55", + "id": "obj-58", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1026,7 +1087,7 @@ ], "patching_rect": [ 1020.0, - 410.0, + 500.0, 44.0, 22.0 ], @@ -1035,7 +1096,7 @@ }, { "box": { - "id": "obj-56", + "id": "obj-59", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -1053,64 +1114,61 @@ }, { "box": { - "id": "obj-57", + "id": "obj-60", "maxclass": "newobj", - "numinlets": 1, + "numinlets": 2, "numoutlets": 1, "outlettype": [ "" ], "patching_rect": [ 1020.0, - 440.0, + 530.0, 40.0, 22.0 ], - "text": "t b" + "text": "i" } }, { "box": { - "id": "obj-58", + "id": "obj-61", "maxclass": "newobj", - "numinlets": 3, - "numoutlets": 4, + "numinlets": 2, + "numoutlets": 1, "outlettype": [ - "", - "", - "", "" ], "patching_rect": [ 1020.0, - 470.0, - 135.0, + 560.0, + 44.0, 22.0 ], - "text": "counter 1 1000000" + "text": "== 2" } }, { "box": { - "id": "obj-59", + "id": "obj-62", "maxclass": "newobj", - "numinlets": 2, + "numinlets": 1, "numoutlets": 1, "outlettype": [ "" ], "patching_rect": [ - 1020.0, - 500.0, - 40.0, + 770.0, + 950.0, + 324.0, 22.0 ], - "text": "i" + "text": "test.assert hang-late-again-after-the-reload" } }, { "box": { - "id": "obj-60", + "id": "obj-63", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1119,50 +1177,52 @@ ], "patching_rect": [ 1020.0, - 530.0, - 44.0, + 590.0, + 40.0, 22.0 ], - "text": "== 2" + "text": "i" } }, { "box": { - "id": "obj-61", + "id": "obj-64", "maxclass": "newobj", - "numinlets": 1, + "numinlets": 2, "numoutlets": 1, "outlettype": [ "" ], "patching_rect": [ - 770.0, - 950.0, - 401.0, + 1020.0, + 620.0, + 44.0, 22.0 ], - "text": "test.assert console-only-the-stall-and-the-interruption" + "text": "== 1" } }, { "box": { - "id": "obj-62", + "id": "obj-65", "maxclass": "newobj", "numinlets": 1, - "numoutlets": 0, - "outlettype": [], + "numoutlets": 1, + "outlettype": [ + "" + ], "patching_rect": [ - 20.0, - 108.0, - 114.0, + 770.0, + 980.0, + 254.0, 22.0 ], - "text": "test.terminate" + "text": "test.assert hang-outlasts-the-ring" } }, { "box": { - "id": "obj-63", + "id": "obj-66", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -1171,56 +1231,165 @@ ], "patching_rect": [ 1020.0, - 560.0, - 72.0, + 650.0, + 40.0, 22.0 ], - "text": "tosymbol" + "text": "t b" } }, { "box": { - "id": "obj-64", + "id": "obj-67", "maxclass": "newobj", - "numinlets": 1, - "numoutlets": 5, + "numinlets": 3, + "numoutlets": 4, "outlettype": [ "", "", "", - "", "" ], "patching_rect": [ 1020.0, - 590.0, - 177.0, + 680.0, + 135.0, 22.0 ], - "text": "regexp \\\" @substitute '" + "text": "counter 1 1000000" } }, { "box": { - "id": "obj-65", + "id": "obj-68", "maxclass": "newobj", - "numinlets": 1, + "numinlets": 2, "numoutlets": 1, "outlettype": [ "" ], "patching_rect": [ 1020.0, - 620.0, - 86.0, + 710.0, + 40.0, 22.0 ], - "text": "fromsymbol" + "text": "i" } }, { "box": { - "id": "obj-66", + "id": "obj-69", + "maxclass": "newobj", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1020.0, + 740.0, + 44.0, + 22.0 + ], + "text": "== 4" + } + }, + { + "box": { + "id": "obj-70", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 770.0, + 1010.0, + 345.0, + 22.0 + ], + "text": "test.assert console-only-the-stall-and-the-hang" + } + }, + { + "box": { + "id": "obj-71", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 0, + "outlettype": [], + "patching_rect": [ + 20.0, + 108.0, + 114.0, + 22.0 + ], + "text": "test.terminate" + } + }, + { + "box": { + "id": "obj-72", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1020.0, + 770.0, + 72.0, + 22.0 + ], + "text": "tosymbol" + } + }, + { + "box": { + "id": "obj-73", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 5, + "outlettype": [ + "", + "", + "", + "", + "" + ], + "patching_rect": [ + 1020.0, + 800.0, + 177.0, + 22.0 + ], + "text": "regexp \\\" @substitute '" + } + }, + { + "box": { + "id": "obj-74", + "maxclass": "newobj", + "numinlets": 1, + "numoutlets": 1, + "outlettype": [ + "" + ], + "patching_rect": [ + 1020.0, + 830.0, + 86.0, + 22.0 + ], + "text": "fromsymbol" + } + }, + { + "box": { + "id": "obj-75", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1230,7 +1399,7 @@ ], "patching_rect": [ 1020.0, - 650.0, + 860.0, 51.0, 22.0 ], @@ -1239,7 +1408,7 @@ }, { "box": { - "id": "obj-67", + "id": "obj-76", "maxclass": "newobj", "numinlets": 3, "numoutlets": 4, @@ -1251,7 +1420,7 @@ ], "patching_rect": [ 1020.0, - 680.0, + 890.0, 135.0, 22.0 ], @@ -1260,7 +1429,7 @@ }, { "box": { - "id": "obj-68", + "id": "obj-77", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1269,7 +1438,7 @@ ], "patching_rect": [ 1020.0, - 710.0, + 920.0, 51.0, 22.0 ], @@ -1278,7 +1447,7 @@ }, { "box": { - "id": "obj-69", + "id": "obj-78", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1287,7 +1456,7 @@ ], "patching_rect": [ 1020.0, - 740.0, + 950.0, 72.0, 22.0 ], @@ -1296,14 +1465,14 @@ }, { "box": { - "id": "obj-70", + "id": "obj-79", "maxclass": "newobj", "numinlets": 1, "numoutlets": 0, "outlettype": [], "patching_rect": [ 1020.0, - 770.0, + 980.0, 170.0, 22.0 ], @@ -1312,7 +1481,7 @@ }, { "box": { - "id": "obj-71", + "id": "obj-80", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -1330,7 +1499,7 @@ }, { "box": { - "id": "obj-72", + "id": "obj-81", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1349,7 +1518,7 @@ }, { "box": { - "id": "obj-73", + "id": "obj-82", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -1367,7 +1536,7 @@ }, { "box": { - "id": "obj-74", + "id": "obj-83", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1385,7 +1554,7 @@ }, { "box": { - "id": "obj-75", + "id": "obj-84", "maxclass": "newobj", "numinlets": 1, "numoutlets": 4, @@ -1406,7 +1575,7 @@ }, { "box": { - "id": "obj-76", + "id": "obj-85", "maxclass": "newobj", "numinlets": 2, "numoutlets": 2, @@ -1425,7 +1594,7 @@ }, { "box": { - "id": "obj-77", + "id": "obj-86", "maxclass": "newobj", "numinlets": 2, "numoutlets": 2, @@ -1444,7 +1613,7 @@ }, { "box": { - "id": "obj-78", + "id": "obj-87", "maxclass": "message", "numinlets": 2, "numoutlets": 1, @@ -1462,7 +1631,7 @@ }, { "box": { - "id": "obj-79", + "id": "obj-88", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, @@ -1471,7 +1640,7 @@ ], "patching_rect": [ 770.0, - 980.0, + 1040.0, 191.0, 22.0 ], @@ -1480,7 +1649,7 @@ }, { "box": { - "id": "obj-80", + "id": "obj-89", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1498,7 +1667,7 @@ }, { "box": { - "id": "obj-81", + "id": "obj-90", "maxclass": "newobj", "numinlets": 1, "numoutlets": 4, @@ -1519,7 +1688,7 @@ }, { "box": { - "id": "obj-82", + "id": "obj-91", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1537,7 +1706,7 @@ }, { "box": { - "id": "obj-83", + "id": "obj-92", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1556,7 +1725,7 @@ }, { "box": { - "id": "obj-84", + "id": "obj-93", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1574,7 +1743,7 @@ }, { "box": { - "id": "obj-85", + "id": "obj-94", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1593,7 +1762,7 @@ }, { "box": { - "id": "obj-86", + "id": "obj-95", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1611,7 +1780,7 @@ }, { "box": { - "id": "obj-87", + "id": "obj-96", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1630,7 +1799,7 @@ }, { "box": { - "id": "obj-88", + "id": "obj-97", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1648,7 +1817,7 @@ }, { "box": { - "id": "obj-89", + "id": "obj-98", "maxclass": "newobj", "numinlets": 1, "numoutlets": 3, @@ -1668,7 +1837,7 @@ }, { "box": { - "id": "obj-90", + "id": "obj-99", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1686,7 +1855,7 @@ }, { "box": { - "id": "obj-91", + "id": "obj-100", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1705,7 +1874,7 @@ }, { "box": { - "id": "obj-92", + "id": "obj-101", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1723,7 +1892,7 @@ }, { "box": { - "id": "obj-93", + "id": "obj-102", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1742,7 +1911,7 @@ }, { "box": { - "id": "obj-94", + "id": "obj-103", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1760,7 +1929,7 @@ }, { "box": { - "id": "obj-95", + "id": "obj-104", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1779,7 +1948,7 @@ }, { "box": { - "id": "obj-96", + "id": "obj-105", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1797,7 +1966,7 @@ }, { "box": { - "id": "obj-97", + "id": "obj-106", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1816,7 +1985,7 @@ }, { "box": { - "id": "obj-98", + "id": "obj-107", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1834,7 +2003,7 @@ }, { "box": { - "id": "obj-99", + "id": "obj-108", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1853,7 +2022,7 @@ }, { "box": { - "id": "obj-100", + "id": "obj-109", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1871,7 +2040,7 @@ }, { "box": { - "id": "obj-101", + "id": "obj-110", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -1890,7 +2059,7 @@ }, { "box": { - "id": "obj-102", + "id": "obj-111", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1908,29 +2077,31 @@ }, { "box": { - "id": "obj-103", + "id": "obj-112", "maxclass": "newobj", "numinlets": 1, - "numoutlets": 5, + "numoutlets": 7, "outlettype": [ "", "", "", "", + "", + "", "" ], "patching_rect": [ 20.0, 1068.0, - 93.0, + 121.0, 22.0 ], - "text": "t b b b b b" + "text": "t b b b b b b b" } }, { "box": { - "id": "obj-104", + "id": "obj-113", "maxclass": "newobj", "numinlets": 2, "numoutlets": 1, @@ -1948,7 +2119,7 @@ }, { "box": { - "id": "obj-105", + "id": "obj-114", "maxclass": "newobj", "numinlets": 1, "numoutlets": 2, @@ -2138,7 +2309,7 @@ { "patchline": { "source": [ - "obj-5", + "obj-8", 0 ], "destination": [ @@ -2151,7 +2322,7 @@ "patchline": { "source": [ "obj-15", - 0 + 2 ], "destination": [ "obj-16", @@ -2162,11 +2333,11 @@ { "patchline": { "source": [ - "obj-17", - 1 + "obj-16", + 0 ], "destination": [ - "obj-16", + "obj-17", 0 ] } @@ -2174,11 +2345,11 @@ { "patchline": { "source": [ - "obj-17", + "obj-5", 0 ], "destination": [ - "obj-20", + "obj-18", 0 ] } @@ -2186,7 +2357,7 @@ { "patchline": { "source": [ - "obj-20", + "obj-18", 0 ], "destination": [ @@ -2198,11 +2369,11 @@ { "patchline": { "source": [ - "obj-18", - 0 + "obj-20", + 1 ], "destination": [ - "obj-16", + "obj-19", 0 ] } @@ -2210,23 +2381,23 @@ { "patchline": { "source": [ - "obj-16", + "obj-20", 0 ], "destination": [ - "obj-19", - 1 + "obj-23", + 0 ] } }, { "patchline": { "source": [ - "obj-19", + "obj-23", 0 ], "destination": [ - "obj-21", + "obj-22", 0 ] } @@ -2238,7 +2409,7 @@ 0 ], "destination": [ - "obj-22", + "obj-19", 0 ] } @@ -2246,19 +2417,19 @@ { "patchline": { "source": [ - "obj-5", + "obj-19", 0 ], "destination": [ - "obj-23", - 0 + "obj-22", + 1 ] } }, { "patchline": { "source": [ - "obj-23", + "obj-22", 0 ], "destination": [ @@ -2270,11 +2441,11 @@ { "patchline": { "source": [ - "obj-25", - 1 + "obj-24", + 0 ], "destination": [ - "obj-24", + "obj-25", 0 ] } @@ -2282,11 +2453,11 @@ { "patchline": { "source": [ - "obj-25", + "obj-5", 0 ], "destination": [ - "obj-28", + "obj-26", 0 ] } @@ -2294,7 +2465,7 @@ { "patchline": { "source": [ - "obj-28", + "obj-26", 0 ], "destination": [ @@ -2306,11 +2477,11 @@ { "patchline": { "source": [ - "obj-26", - 0 + "obj-28", + 1 ], "destination": [ - "obj-24", + "obj-27", 0 ] } @@ -2318,23 +2489,23 @@ { "patchline": { "source": [ - "obj-24", + "obj-28", 0 ], "destination": [ - "obj-27", - 1 + "obj-31", + 0 ] } }, { "patchline": { "source": [ - "obj-27", + "obj-31", 0 ], "destination": [ - "obj-29", + "obj-30", 0 ] } @@ -2346,7 +2517,7 @@ 0 ], "destination": [ - "obj-30", + "obj-27", 0 ] } @@ -2354,19 +2525,19 @@ { "patchline": { "source": [ - "obj-31", - 1 + "obj-27", + 0 ], "destination": [ - "obj-3", - 0 + "obj-30", + 1 ] } }, { "patchline": { "source": [ - "obj-31", + "obj-30", 0 ], "destination": [ @@ -2427,7 +2598,7 @@ "patchline": { "source": [ "obj-37", - 0 + 1 ], "destination": [ "obj-3", @@ -2438,12 +2609,12 @@ { "patchline": { "source": [ - "obj-11", + "obj-37", 0 ], "destination": [ "obj-38", - 1 + 0 ] } }, @@ -2462,11 +2633,11 @@ { "patchline": { "source": [ - "obj-39", + "obj-40", 0 ], "destination": [ - "obj-40", + "obj-3", 0 ] } @@ -2474,19 +2645,19 @@ { "patchline": { "source": [ - "obj-41", + "obj-11", 0 ], "destination": [ - "obj-3", - 0 + "obj-41", + 1 ] } }, { "patchline": { "source": [ - "obj-5", + "obj-41", 0 ], "destination": [ @@ -2510,11 +2681,11 @@ { "patchline": { "source": [ - "obj-43", + "obj-44", 0 ], "destination": [ - "obj-44", + "obj-3", 0 ] } @@ -2522,11 +2693,11 @@ { "patchline": { "source": [ - "obj-45", + "obj-5", 0 ], "destination": [ - "obj-3", + "obj-45", 0 ] } @@ -2534,11 +2705,11 @@ { "patchline": { "source": [ - "obj-46", + "obj-45", 0 ], "destination": [ - "obj-3", + "obj-46", 0 ] } @@ -2546,11 +2717,11 @@ { "patchline": { "source": [ - "obj-47", + "obj-46", 0 ], "destination": [ - "obj-3", + "obj-47", 0 ] } @@ -2559,7 +2730,7 @@ "patchline": { "source": [ "obj-48", - 1 + 0 ], "destination": [ "obj-3", @@ -2570,11 +2741,11 @@ { "patchline": { "source": [ - "obj-48", + "obj-49", 0 ], "destination": [ - "obj-49", + "obj-3", 0 ] } @@ -2582,11 +2753,11 @@ { "patchline": { "source": [ - "obj-49", + "obj-50", 0 ], "destination": [ - "obj-50", + "obj-3", 0 ] } @@ -2594,11 +2765,11 @@ { "patchline": { "source": [ - "obj-6", - 0 + "obj-51", + 1 ], "destination": [ - "obj-51", + "obj-3", 0 ] } @@ -2630,12 +2801,12 @@ { "patchline": { "source": [ - "obj-14", + "obj-6", 0 ], "destination": [ "obj-54", - 1 + 0 ] } }, @@ -2666,12 +2837,12 @@ { "patchline": { "source": [ - "obj-8", + "obj-17", 0 ], "destination": [ "obj-57", - 0 + 1 ] } }, @@ -2695,19 +2866,19 @@ ], "destination": [ "obj-59", - 1 + 0 ] } }, { "patchline": { "source": [ - "obj-59", + "obj-11", 0 ], "destination": [ "obj-60", - 0 + 1 ] } }, @@ -2726,11 +2897,11 @@ { "patchline": { "source": [ - "obj-8", + "obj-61", 0 ], "destination": [ - "obj-63", + "obj-62", 0 ] } @@ -2738,23 +2909,23 @@ { "patchline": { "source": [ - "obj-63", + "obj-14", 0 ], "destination": [ - "obj-64", - 0 + "obj-63", + 1 ] } }, { "patchline": { "source": [ - "obj-64", + "obj-63", 0 ], "destination": [ - "obj-65", + "obj-64", 0 ] } @@ -2763,7 +2934,7 @@ "patchline": { "source": [ "obj-64", - 3 + 0 ], "destination": [ "obj-65", @@ -2774,7 +2945,7 @@ { "patchline": { "source": [ - "obj-65", + "obj-8", 0 ], "destination": [ @@ -2787,7 +2958,7 @@ "patchline": { "source": [ "obj-66", - 1 + 0 ], "destination": [ "obj-67", @@ -2803,7 +2974,7 @@ ], "destination": [ "obj-68", - 0 + 1 ] } }, @@ -2822,23 +2993,23 @@ { "patchline": { "source": [ - "obj-66", + "obj-69", 0 ], "destination": [ - "obj-69", - 1 + "obj-70", + 0 ] } }, { "patchline": { "source": [ - "obj-69", + "obj-8", 0 ], "destination": [ - "obj-70", + "obj-72", 0 ] } @@ -2846,11 +3017,11 @@ { "patchline": { "source": [ - "obj-71", + "obj-72", 0 ], "destination": [ - "obj-72", + "obj-73", 0 ] } @@ -2858,8 +3029,8 @@ { "patchline": { "source": [ - "obj-72", - 1 + "obj-73", + 0 ], "destination": [ "obj-74", @@ -2870,11 +3041,11 @@ { "patchline": { "source": [ - "obj-72", - 0 + "obj-73", + 3 ], "destination": [ - "obj-73", + "obj-74", 0 ] } @@ -2886,7 +3057,7 @@ 0 ], "destination": [ - "obj-62", + "obj-75", 0 ] } @@ -2895,7 +3066,7 @@ "patchline": { "source": [ "obj-75", - 0 + 1 ], "destination": [ "obj-76", @@ -2930,23 +3101,23 @@ { "patchline": { "source": [ - "obj-78", + "obj-75", 0 ], "destination": [ - "obj-79", - 0 + "obj-78", + 1 ] } }, { "patchline": { "source": [ - "obj-77", + "obj-78", 0 ], "destination": [ - "obj-80", + "obj-79", 0 ] } @@ -2967,10 +3138,10 @@ "patchline": { "source": [ "obj-81", - 3 + 1 ], "destination": [ - "obj-31", + "obj-83", 0 ] } @@ -2979,6 +3150,114 @@ "patchline": { "source": [ "obj-81", + 0 + ], + "destination": [ + "obj-82", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-83", + 0 + ], + "destination": [ + "obj-71", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-84", + 0 + ], + "destination": [ + "obj-85", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-85", + 0 + ], + "destination": [ + "obj-86", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 0 + ], + "destination": [ + "obj-87", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-87", + 0 + ], + "destination": [ + "obj-88", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-86", + 0 + ], + "destination": [ + "obj-89", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-89", + 0 + ], + "destination": [ + "obj-90", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-90", + 3 + ], + "destination": [ + "obj-34", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-90", 2 ], "destination": [ @@ -2990,11 +3269,11 @@ { "patchline": { "source": [ - "obj-81", + "obj-90", 1 ], "destination": [ - "obj-34", + "obj-37", 0 ] } @@ -3002,11 +3281,11 @@ { "patchline": { "source": [ - "obj-81", + "obj-90", 0 ], "destination": [ - "obj-82", + "obj-91", 0 ] } @@ -3014,11 +3293,11 @@ { "patchline": { "source": [ - "obj-82", + "obj-91", 0 ], "destination": [ - "obj-83", + "obj-92", 0 ] } @@ -3026,11 +3305,11 @@ { "patchline": { "source": [ - "obj-83", + "obj-92", 1 ], "destination": [ - "obj-17", + "obj-20", 0 ] } @@ -3038,11 +3317,11 @@ { "patchline": { "source": [ - "obj-83", + "obj-92", 0 ], "destination": [ - "obj-84", + "obj-93", 0 ] } @@ -3050,11 +3329,11 @@ { "patchline": { "source": [ - "obj-84", + "obj-93", 0 ], "destination": [ - "obj-85", + "obj-94", 0 ] } @@ -3062,11 +3341,11 @@ { "patchline": { "source": [ - "obj-85", + "obj-94", 1 ], "destination": [ - "obj-18", + "obj-21", 0 ] } @@ -3074,11 +3353,11 @@ { "patchline": { "source": [ - "obj-85", + "obj-94", 0 ], "destination": [ - "obj-86", + "obj-95", 0 ] } @@ -3086,11 +3365,11 @@ { "patchline": { "source": [ - "obj-86", + "obj-95", 0 ], "destination": [ - "obj-87", + "obj-96", 0 ] } @@ -3098,11 +3377,11 @@ { "patchline": { "source": [ - "obj-87", + "obj-96", 1 ], "destination": [ - "obj-37", + "obj-40", 0 ] } @@ -3110,11 +3389,11 @@ { "patchline": { "source": [ - "obj-87", + "obj-96", 0 ], "destination": [ - "obj-88", + "obj-97", 0 ] } @@ -3122,11 +3401,11 @@ { "patchline": { "source": [ - "obj-88", + "obj-97", 0 ], "destination": [ - "obj-89", + "obj-98", 0 ] } @@ -3134,11 +3413,11 @@ { "patchline": { "source": [ - "obj-89", + "obj-98", 2 ], "destination": [ - "obj-38", + "obj-41", 0 ] } @@ -3146,11 +3425,11 @@ { "patchline": { "source": [ - "obj-89", + "obj-98", 1 ], "destination": [ - "obj-25", + "obj-28", 0 ] } @@ -3158,11 +3437,11 @@ { "patchline": { "source": [ - "obj-89", + "obj-98", 0 ], "destination": [ - "obj-90", + "obj-99", 0 ] } @@ -3170,11 +3449,11 @@ { "patchline": { "source": [ - "obj-90", + "obj-99", 0 ], "destination": [ - "obj-91", + "obj-100", 0 ] } @@ -3182,11 +3461,11 @@ { "patchline": { "source": [ - "obj-91", + "obj-100", 1 ], "destination": [ - "obj-26", + "obj-29", 0 ] } @@ -3194,11 +3473,11 @@ { "patchline": { "source": [ - "obj-91", + "obj-100", 0 ], "destination": [ - "obj-92", + "obj-101", 0 ] } @@ -3206,11 +3485,11 @@ { "patchline": { "source": [ - "obj-92", + "obj-101", 0 ], "destination": [ - "obj-93", + "obj-102", 0 ] } @@ -3218,11 +3497,11 @@ { "patchline": { "source": [ - "obj-93", + "obj-102", 1 ], "destination": [ - "obj-41", + "obj-44", 0 ] } @@ -3230,11 +3509,11 @@ { "patchline": { "source": [ - "obj-93", + "obj-102", 0 ], "destination": [ - "obj-94", + "obj-103", 0 ] } @@ -3242,11 +3521,11 @@ { "patchline": { "source": [ - "obj-94", + "obj-103", 0 ], "destination": [ - "obj-95", + "obj-104", 0 ] } @@ -3254,11 +3533,11 @@ { "patchline": { "source": [ - "obj-95", + "obj-104", 1 ], "destination": [ - "obj-42", + "obj-45", 0 ] } @@ -3266,11 +3545,11 @@ { "patchline": { "source": [ - "obj-95", + "obj-104", 0 ], "destination": [ - "obj-96", + "obj-105", 0 ] } @@ -3278,11 +3557,11 @@ { "patchline": { "source": [ - "obj-96", + "obj-105", 0 ], "destination": [ - "obj-97", + "obj-106", 0 ] } @@ -3290,11 +3569,11 @@ { "patchline": { "source": [ - "obj-97", + "obj-106", 1 ], "destination": [ - "obj-45", + "obj-48", 0 ] } @@ -3302,11 +3581,11 @@ { "patchline": { "source": [ - "obj-97", + "obj-106", 0 ], "destination": [ - "obj-98", + "obj-107", 0 ] } @@ -3314,11 +3593,11 @@ { "patchline": { "source": [ - "obj-98", + "obj-107", 0 ], "destination": [ - "obj-99", + "obj-108", 0 ] } @@ -3326,11 +3605,11 @@ { "patchline": { "source": [ - "obj-99", + "obj-108", 1 ], "destination": [ - "obj-46", + "obj-49", 0 ] } @@ -3338,11 +3617,11 @@ { "patchline": { "source": [ - "obj-99", + "obj-108", 0 ], "destination": [ - "obj-100", + "obj-109", 0 ] } @@ -3350,11 +3629,11 @@ { "patchline": { "source": [ - "obj-100", + "obj-109", 0 ], "destination": [ - "obj-101", + "obj-110", 0 ] } @@ -3362,11 +3641,11 @@ { "patchline": { "source": [ - "obj-101", + "obj-110", 1 ], "destination": [ - "obj-47", + "obj-50", 0 ] } @@ -3374,11 +3653,11 @@ { "patchline": { "source": [ - "obj-101", + "obj-110", 0 ], "destination": [ - "obj-102", + "obj-111", 0 ] } @@ -3386,11 +3665,11 @@ { "patchline": { "source": [ - "obj-102", + "obj-111", 0 ], "destination": [ - "obj-103", + "obj-112", 0 ] } @@ -3398,11 +3677,35 @@ { "patchline": { "source": [ - "obj-103", + "obj-112", + 6 + ], + "destination": [ + "obj-51", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-112", + 5 + ], + "destination": [ + "obj-54", + 0 + ] + } + }, + { + "patchline": { + "source": [ + "obj-112", 4 ], "destination": [ - "obj-48", + "obj-57", 0 ] } @@ -3410,11 +3713,11 @@ { "patchline": { "source": [ - "obj-103", + "obj-112", 3 ], "destination": [ - "obj-51", + "obj-60", 0 ] } @@ -3422,11 +3725,11 @@ { "patchline": { "source": [ - "obj-103", + "obj-112", 2 ], "destination": [ - "obj-54", + "obj-63", 0 ] } @@ -3434,11 +3737,11 @@ { "patchline": { "source": [ - "obj-103", + "obj-112", 1 ], "destination": [ - "obj-59", + "obj-68", 0 ] } @@ -3446,11 +3749,11 @@ { "patchline": { "source": [ - "obj-103", + "obj-112", 0 ], "destination": [ - "obj-104", + "obj-113", 0 ] } @@ -3458,11 +3761,11 @@ { "patchline": { "source": [ - "obj-104", + "obj-113", 0 ], "destination": [ - "obj-105", + "obj-114", 0 ] } @@ -3470,11 +3773,11 @@ { "patchline": { "source": [ - "obj-105", + "obj-114", 1 ], "destination": [ - "obj-62", + "obj-71", 0 ] } diff --git a/source/projects/tap.python_tilde/tap.python_tilde.h b/source/projects/tap.python_tilde/tap.python_tilde.h index 85b3cde..39eada2 100644 --- a/source/projects/tap.python_tilde/tap.python_tilde.h +++ b/source/projects/tap.python_tilde/tap.python_tilde.h @@ -478,11 +478,13 @@ class python : public object, public vector_operator<> { /// Whether the Max object already answers `name` itself — a method its class registered (min's /// dsp64, assist, the ones above), which Max would call before anything added for a Python - /// method, or with C arguments. The messages this object added for the previous incarnation's - /// Python methods are its own, not Max's: still registered while load() runs, they must not - /// make the class's methods reserved on a reload. Main thread. + /// method, or with C arguments. The messages and attributes this object added for the previous + /// incarnation's methods and fields are its own, not Max's: still registered while load() runs + /// (an attribute answers its name, as a message does), they must not make the class's names + /// reserved on a reload — 1.0.1 left out the attributes, so a reload lost every field. Main thread. bool answered_by_max(const std::string& name) { - if (m_python_messages.find(name) != m_python_messages.end()) { + if (m_python_messages.find(name) != m_python_messages.end() + || m_python_attributes.find(name) != m_python_attributes.end()) { return false; } return found_method(c74::max::object_getmethod(maxobj(), c74::max::gensym(name.c_str()))); diff --git a/source/projects/tap.python_tilde/tap.python_tilde_test.cpp b/source/projects/tap.python_tilde/tap.python_tilde_test.cpp index d515df3..8c640eb 100644 --- a/source/projects/tap.python_tilde/tap.python_tilde_test.cpp +++ b/source/projects/tap.python_tilde/tap.python_tilde_test.cpp @@ -4,9 +4,11 @@ #include #include +#include #include #include #include +#include #include #include "c74_min_unittest.h" // required unit-test header (defines main via Catch) @@ -17,17 +19,42 @@ // test binary links (the headers declare them with C linkage). With these stubs the Max-side attribute and method // registrations fail harmlessly, so the tests below drive the object's own // attribute and message handlers directly, as Max's dispatch would. +// What Max does with an attribute the object adds, which the stubs below model only while a +// scenario asks (the test process makes many objects in turn, and the others rely on the +// registration failing quietly): the object then answers the attribute's name, so +// object_getmethod() finds it. 1.0.1's guard took the previous load's attributes for Max's own +// and reserved every field on a reload (found in the Mac session, plan 8.8). +namespace attributes { + bool modeled{}; + std::deque made; // attribute_new's, by address (a deque keeps them in place) + std::unordered_set added; // object_addattr's, until object_deleteattr + + void model(const bool on) { + modeled = on; + made.clear(); + added.clear(); + } +} // namespace attributes + namespace c74 { namespace max { extern "C" { - t_object* attribute_new(const char*, t_symbol*, long, method, method) { - return nullptr; + t_object* attribute_new(const char* name, t_symbol*, long, method, method) { + if (!attributes::modeled) { + return nullptr; + } + attributes::made.emplace_back(name); + return reinterpret_cast(&attributes::made.back()); } - t_max_err object_addattr(void*, t_object*) { - return MAX_ERR_GENERIC; + t_max_err object_addattr(void*, t_object* attribute) { + if (!attributes::modeled || attribute == nullptr) { + return MAX_ERR_GENERIC; + } + attributes::added.insert(*reinterpret_cast(attribute)); + return MAX_ERR_NONE; } t_max_err object_attr_addattr_parse(t_object*, const char*, const char*, t_symbol*, long, const char*) { - return MAX_ERR_GENERIC; + return attributes::modeled ? MAX_ERR_NONE : MAX_ERR_GENERIC; } t_max_err object_addmethod(t_object*, method, const char*, ...) { return MAX_ERR_NONE; @@ -74,6 +101,9 @@ namespace c74 { return reinterpret_cast(answered_method); } } + if (attributes::added.count(s->s_name) != 0) { // an attribute the object added answers its name + return reinterpret_cast(answered_method); + } return reinterpret_cast(method_false); } void* qelem_new(void*, method) { @@ -81,7 +111,8 @@ namespace c74 { return &s_qelem; } void qelem_set(void*) {} - t_max_err object_deleteattr(void*, t_symbol*) { + t_max_err object_deleteattr(void*, t_symbol* name) { + attributes::added.erase(name->s_name); return MAX_ERR_NONE; } void qelem_free(void*) {} @@ -555,3 +586,37 @@ SCENARIO("The runtime is support/ in a package that carries every plat } std::filesystem::remove_all(package); } + +SCENARIO("A reload keeps the class's attributes: those the object added are its own, not Max's (1.0.1)") { + ext_main(nullptr); + attributes::model(true); + // a fixture in the package's python folder, where python/maxtest_*.py is ignored by git + const auto file = tap::python::package_root() / "python" / "maxtest_mock_fields.py"; + const auto source = std::string{"class maxtest_mock_fields:\n level: float = 1.0\n\n" + " def process(self, x: float) -> float:\n return x * self.level\n"}; + write_file(file, source); + const auto argument = symbol_atom("maxtest_mock_fields"); + auto* wrapped = c74::min::wrapper_new(c74::min::symbol("dummy"), 1, &argument); + REQUIRE(wrapped); + python& my_object = wrapped->m_min_object; + REQUIRE(attributes::added.count("level") == 1); // the object answers "level" now, as in Max + + const auto half = float_atom(0.5); + my_object.attr_set(c74::min::symbol{"level"}, 1, &half); + const auto before = get(my_object, "level"); + REQUIRE(c74::max::atom_getfloat(&before) == 0.5); + + WHEN("a save changes the file and it reloads") { + write_file(file, source + " # saved again\n"); + my_object.update_source(); + THEN("the field is still an attribute, with its value") { + const auto got = get(my_object, "level"); + CHECK(c74::max::atom_getfloat(&got) == 0.5); + CHECK(attributes::added.count("level") == 1); + } + } + + c74::max::object_free(wrapped); + std::filesystem::remove(file); + attributes::model(false); +}