py2max 0.2.1__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py2max-0.2.1 → py2max-0.3.1}/CHANGELOG.md +101 -0
- {py2max-0.2.1 → py2max-0.3.1}/PKG-INFO +91 -22
- {py2max-0.2.1 → py2max-0.3.1}/README.md +85 -14
- {py2max-0.2.1 → py2max-0.3.1}/py2max/__init__.py +5 -4
- {py2max-0.2.1 → py2max-0.3.1}/py2max/cli.py +38 -158
- {py2max-0.2.1 → py2max-0.3.1}/py2max/core/abstract.py +33 -5
- {py2max-0.2.1 → py2max-0.3.1}/py2max/core/box.py +80 -29
- py2max-0.3.1/py2max/core/colors.py +83 -0
- py2max-0.2.1/py2max/core/patcher.py → py2max-0.3.1/py2max/core/factory.py +529 -695
- py2max-0.3.1/py2max/core/patcher.py +596 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/core/patchline.py +13 -10
- py2max-0.3.1/py2max/core/serialization.py +122 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/export/converters.py +11 -8
- {py2max-0.2.1 → py2max-0.3.1}/py2max/export/svg.py +86 -23
- {py2max-0.2.1 → py2max-0.3.1}/py2max/layout/base.py +5 -5
- {py2max-0.2.1 → py2max-0.3.1}/py2max/layout/flow.py +25 -14
- {py2max-0.2.1 → py2max-0.3.1}/py2max/layout/grid.py +26 -18
- {py2max-0.2.1 → py2max-0.3.1}/py2max/layout/matrix.py +22 -17
- {py2max-0.2.1 → py2max-0.3.1}/py2max/log.py +4 -2
- py2max-0.3.1/py2max/m4l.py +626 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/maxref/__init__.py +15 -3
- py2max-0.3.1/py2max/maxref/data/bundle.json.gz +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/maxref/db.py +42 -24
- {py2max-0.2.1 → py2max-0.3.1}/py2max/maxref/legacy.py +20 -5
- {py2max-0.2.1 → py2max-0.3.1}/py2max/maxref/parser.py +95 -26
- {py2max-0.2.1 → py2max-0.3.1}/py2max/utils.py +22 -1
- {py2max-0.2.1 → py2max-0.3.1}/pyproject.toml +13 -15
- py2max-0.3.1/tests/conftest.py +21 -0
- py2max-0.3.1/tests/data/mydevice.amxd +0 -0
- py2max-0.3.1/tests/data/mydevice2.amxd +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/data_containers.py +5 -5
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/api/box_api_examples.py +7 -7
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/api/patcher_api_examples.py +12 -12
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/auto_layout_demo.py +14 -14
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/layout/columnar_layout_examples.py +5 -5
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/layout/grid_layout_examples.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/layout/matrix_layout_examples.py +2 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/svg_preview_demo.py +7 -7
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/quickstart/layout_examples.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/tutorial/generative_music.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_abstract_coverage.py +1 -1
- py2max-0.3.1/tests/test_amxd.py +336 -0
- py2max-0.3.1/tests/test_basic.py +22 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_bpatcher.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_cli.py +0 -1
- py2max-0.3.1/tests/test_colors_theme.py +94 -0
- py2max-0.3.1/tests/test_comment.py +8 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_connection_validation.py +2 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_converters.py +1 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_core_coverage.py +25 -11
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_db.py +0 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_dict.py +1 -1
- py2max-0.3.1/tests/test_encapsulate.py +106 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_error_handling.py +14 -7
- py2max-0.3.1/tests/test_gen.py +96 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout.py +0 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_builtins.py +6 -6
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_coverage.py +26 -28
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_flow.py +4 -4
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_hola1.py +5 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_hola_graph.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_matrix.py +0 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_networkx2.py +3 -3
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_nx_graphviz.py +3 -3
- py2max-0.3.1/tests/test_m4l.py +140 -0
- py2max-0.3.1/tests/test_maxref_bundle.py +91 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_mc_cycle.py +1 -1
- py2max-0.3.1/tests/test_mc_poly.py +40 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_nested.py +6 -2
- py2max-0.3.1/tests/test_nested_patchers.py +50 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_number_tilde.py +5 -5
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_param.py +2 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_patcher.py +9 -5
- py2max-0.3.1/tests/test_presets.py +64 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_pydantic.py +6 -8
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_rnbo.py +1 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_search.py +12 -12
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_svg.py +48 -10
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_table.py +6 -2
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_tree_builder.py +3 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_tutorial_simple_synthesis.py +1 -1
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_two_sines.py +10 -0
- py2max-0.3.1/tests/test_validate_attrs.py +66 -0
- py2max-0.2.1/py2max/server/__init__.py +0 -54
- py2max-0.2.1/py2max/server/client.py +0 -295
- py2max-0.2.1/py2max/server/inline.py +0 -312
- py2max-0.2.1/py2max/server/repl.py +0 -561
- py2max-0.2.1/py2max/server/rpc.py +0 -240
- py2max-0.2.1/py2max/server/websocket.py +0 -997
- py2max-0.2.1/py2max/static/cola.min.js +0 -4
- py2max-0.2.1/py2max/static/d3.v7.min.js +0 -2
- py2max-0.2.1/py2max/static/dagre-bundle.js +0 -328
- py2max-0.2.1/py2max/static/elk.bundled.js +0 -6663
- py2max-0.2.1/py2max/static/index.html +0 -168
- py2max-0.2.1/py2max/static/interactive.html +0 -589
- py2max-0.2.1/py2max/static/interactive.js +0 -2111
- py2max-0.2.1/py2max/static/live-preview.js +0 -324
- py2max-0.2.1/py2max/static/svg.min.js +0 -13
- py2max-0.2.1/py2max/static/svg.min.js.map +0 -1
- py2max-0.2.1/tests/examples/info_command_demo.py +0 -106
- py2max-0.2.1/tests/examples/inline_repl_verification.py +0 -64
- py2max-0.2.1/tests/examples/interactive_demo.py +0 -228
- py2max-0.2.1/tests/examples/interactive_save_demo.py +0 -164
- py2max-0.2.1/tests/examples/live_preview_demo.py +0 -225
- py2max-0.2.1/tests/examples/refresh_function_verification.py +0 -54
- py2max-0.2.1/tests/examples/repl_client_server_demo.py +0 -171
- py2max-0.2.1/tests/examples/repl_quickstart.py +0 -140
- py2max-0.2.1/tests/test_basic.py +0 -11
- py2max-0.2.1/tests/test_comment.py +0 -8
- py2max-0.2.1/tests/test_gen.py +0 -27
- py2max-0.2.1/tests/test_nested_patchers.py +0 -279
- py2max-0.2.1/tests/test_repl.py +0 -309
- py2max-0.2.1/tests/test_repl_client.py +0 -348
- py2max-0.2.1/tests/test_repl_inline.py +0 -283
- py2max-0.2.1/tests/test_repl_server.py +0 -316
- py2max-0.2.1/tests/test_websocket.py +0 -232
- {py2max-0.2.1 → py2max-0.3.1}/LICENSE +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/__main__.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/core/__init__.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/core/common.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/exceptions.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/export/__init__.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/layout/__init__.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/maxref/category.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/py.typed +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/py2max/transformers.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/__init__.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/complex.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/desc.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/empty.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/nested.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/simple.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/tabular.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/data/umenu.maxref.xml +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/README.md +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/connection_patterns.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/custom_extensions.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/error_handling.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/performance_optimization.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/advanced/subpatchers.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/db/category_db_demo.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/db/maxref_db_demo.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/layout/flow_layout_examples.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/basic_synth.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/basic_synth.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/complex_synth.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/complex_synth.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/flow_layout.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/flow_layout.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/grid_layout.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/grid_layout.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/horizontal_layout.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/horizontal_layout.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/styled_no_ports.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/styled_no_title.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/styled_patch.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/styled_with_ports.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/vertical_layout.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/vertical_layout.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/workflow_demo.maxpat +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/preview/workflow_demo.svg +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/quickstart/basic_patch.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/tutorial/interactive_controller.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/tutorial/signal_processing_chain.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/examples/tutorial/simple_synthesis.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/graphs/random/v30e33.tglf +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/registry.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/scratch.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_abstraction.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_add.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_attrui.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_beap.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_coll.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_colors.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_defaults.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_examples.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_ezdac.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_group.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_itable.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_js.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_kwds_filter.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_graph_layout.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_hola2.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_hola3.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_networkx1.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_nx_orthogonal.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_nx_tsmpy.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_layout_vertical.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_linking.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_maxref.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_message.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_mypatch.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_numbers.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_pitched_osc.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_rnbo_subpatcher.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_scripting_name.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_semantic_ids.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_subpatch.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_transformers.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_tree.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_umenu.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_utils.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_varname.py +0 -0
- {py2max-0.2.1 → py2max-0.3.1}/tests/test_zl_group.py +0 -0
|
@@ -2,6 +2,107 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.3.1]
|
|
6
|
+
|
|
7
|
+
### New: Standalone `gen.codebox~` Support
|
|
8
|
+
|
|
9
|
+
- `Patcher.add_gen_codebox(code)` adds a self-contained `gen.codebox~` object -- a complete gen patch in a single box that lives directly in a regular Max patcher, distinct from the inner `codebox~` (emitted by `add_codebox`) that belongs inside a `gen~`/`rnbo~` subpatcher. This is the form emitted by gen transpilers. Code newlines are normalized to CRLF as Max expects, and `fontname`/`fontsize` default to the monospaced gen style.
|
|
10
|
+
- Inlet/outlet counts are derived automatically from the code (the highest `inN` / `outN` references, floor of 1), matching gen's dynamic-I/O semantics. Explicit `numinlets` / `numoutlets` still override.
|
|
11
|
+
- Available via the `add()` string shortcut too: `p.add("gen.codebox~ out1 = in1 * 0.5;")`. The shortcut suits single-line / `;`-terminated code; pass multi-line source to `add_gen_codebox()` directly.
|
|
12
|
+
- Connection validation for `gen.codebox~` (and `codebox` / `codebox~`) now bound-checks against the box's own declared inlet/outlet counts rather than the static `.maxref.xml` entry, since codebox I/O is code-dependent. This both allows valid connections to/from wider codeboxes (e.g. from a second outlet) and rejects genuinely out-of-range ones.
|
|
13
|
+
|
|
14
|
+
## [0.3.0]
|
|
15
|
+
|
|
16
|
+
### Removed: Interactive Server Split Into `py2max-server` (breaking)
|
|
17
|
+
|
|
18
|
+
The browser-based live editor and remote REPL have moved to a separate companion package, [`py2max-server`](https://github.com/shakfu/py2max-server), so the core library stays small, offline, and dependency-free.
|
|
19
|
+
|
|
20
|
+
- Removed `Patcher.serve()` and the `py2max serve` / `py2max repl` CLI commands; those CLI subcommands now print a pointer to `py2max-server`.
|
|
21
|
+
- Removed the `[server]` optional-dependency extra (`websockets`, `ptpython`) and the bundled browser assets (`py2max/static/`).
|
|
22
|
+
- Install the server features with `pip install py2max-server` and use `py2max-server serve <patch>` / `py2max-server repl …`. The remote REPL now requires token authentication (passed via `--token` or `PY2MAX_REPL_TOKEN`).
|
|
23
|
+
|
|
24
|
+
### New: `Patcher.encapsulate()`
|
|
25
|
+
|
|
26
|
+
- `Patcher.encapsulate(boxes, text="p sub")` wraps a selection of boxes into a subpatcher, auto-generating `inlet`/`outlet` objects for any connections that cross the selection boundary and rewiring the parent through the new subpatcher box. Connections wholly inside the selection move into the subpatcher; connections wholly outside it are untouched. Ports are de-duplicated by source, matching how patches are built by hand. Returns the new subpatcher `Box`.
|
|
27
|
+
|
|
28
|
+
### New: Preset / `pattrstorage` Scaffolding
|
|
29
|
+
|
|
30
|
+
- `Patcher.add_pattrstorage(name)`, `Patcher.add_autopattr()`, and `Patcher.add_preset_system(name)` (which adds both and wires `autopattr` -> `pattrstorage`) scaffold a Max preset system. Any object with a scripting name (`varname`) or `parameter_enable=1` participates.
|
|
31
|
+
- `Patcher.enable_parameter(box, longname, shortname="", ptype=0, initial=None)` turns an existing UI box into a Max parameter (sets `parameter_enable` and the `saved_attribute_attributes`), so it participates in presets and, in a Max for Live device, appears as an automatable parameter.
|
|
32
|
+
|
|
33
|
+
### New: Keyword-Attribute Validation (`validate_attrs`)
|
|
34
|
+
|
|
35
|
+
- `Patcher(validate_attrs=True)` warns (`UserWarning`) when an object is given a keyword that is not a known attribute for its Max class -- catching typos like `inital=` for `initial=`. The known set is the object's maxref attributes plus a universal box-attribute whitelist; objects with no maxref entry are skipped. Off by default and warn-only, so it never changes generated output.
|
|
36
|
+
|
|
37
|
+
### New: Multichannel (`mc.`) / Polyphony Helpers
|
|
38
|
+
|
|
39
|
+
- `Patcher.add_mc(text, chans=None)` adds a multichannel object, prefixing `mc.` and appending `@chans` (e.g. `add_mc("cycle~ 440", chans=4)` -> `mc.cycle~ 440 @chans 4`).
|
|
40
|
+
- `Patcher.add_poly(target, voices=1)` adds a `poly~` object hosting N voices of a target patch.
|
|
41
|
+
|
|
42
|
+
### Improved: SVG Export (Max-faithful preview)
|
|
43
|
+
|
|
44
|
+
- The `preview` / `to_svg` output now approximates Max's look: a light patcher background, signal vs message/control **ports colored distinctly** (signal green, control dark), signal **cables drawn thicker and in a distinct color**, and subpatcher boxes tinted so they stand out. Object text is intentionally not truncated, matching Max (objects size to their text).
|
|
45
|
+
|
|
46
|
+
### Changed: Documentation moved to MkDocs
|
|
47
|
+
|
|
48
|
+
- Documentation migrated from Sphinx/reStructuredText to **MkDocs + Material + mkdocstrings** (all Markdown, matching the rest of the repo). The API reference is generated from the (now fully typed) docstrings, including `Patcher`'s mixin-provided methods. The changelog and contributing pages are single-source includes of `CHANGELOG.md` / `CONTRIBUTING.md`. Build with `make docs`, preview with `make docs-serve`, publish with `make docs-deploy`. `docs/notes/` is retained as a historical journal but excluded from the published site.
|
|
49
|
+
|
|
50
|
+
### New: Color / Theme Helpers
|
|
51
|
+
|
|
52
|
+
- `Box.set_color(bg=..., text=..., border=...)` sets a box's `bgcolor`/`textcolor`/`bordercolor`; each accepts a named color (e.g. `"red"`), a hex string (`"#ff8800"`), or an `[r, g, b(, a)]` float sequence. Returns the box for chaining.
|
|
53
|
+
- `Patcher.apply_theme(theme)` applies a color theme to every box (recursing into subpatchers). Built-in themes: `"light"`, `"dark"`, `"blue"`, `"high-contrast"`; or pass a dict of `bg`/`text`/`border` colors.
|
|
54
|
+
- `py2max.core.colors` exposes the `MAX_COLORS` named palette and `resolve_color()`.
|
|
55
|
+
|
|
56
|
+
### Security
|
|
57
|
+
|
|
58
|
+
- **Removed a misleading path-traversal check** in `Patcher.save_as()`. The previous `..`/`/etc` allowlist was trivially bypassable and gave a false sense of safety; for an offline file generator it provided no real protection. Genuinely unresolvable paths still raise `PatcherIOError`.
|
|
59
|
+
|
|
60
|
+
### Typed: Full `mypy --strict`
|
|
61
|
+
|
|
62
|
+
- The entire package is now annotated and passes `mypy --strict`, backing the shipped `py.typed` marker. `[tool.mypy]` enforces `strict = true`. Core has **no runtime dependencies**.
|
|
63
|
+
|
|
64
|
+
### Changed: Lighter Core Imports
|
|
65
|
+
|
|
66
|
+
- `import py2max` no longer eagerly imports `sqlite3`, the maxref database layer, or `py2max.m4l`. `MaxRefDB` is now available lazily via `from py2max.maxref import MaxRefDB` (removed from the top-level `py2max` namespace).
|
|
67
|
+
|
|
68
|
+
### Internal: `Patcher` Decomposition
|
|
69
|
+
|
|
70
|
+
- Split the ~1660-line `Patcher` class into focused mixins composed via inheritance: object creation (`BoxFactoryMixin` in `core/factory.py`) and serialization (`SerializationMixin` in `core/serialization.py`). The public API is unchanged; adding a new object type now means editing `core/factory.py` rather than the core class.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- Object-name resolution (used by connection validation and object classification) now reads the box `text` property, so it resolves correctly for boxes loaded from a file. Previously it inspected only programmatic kwargs and returned `newobj` for loaded boxes.
|
|
75
|
+
- `Box.oid` now returns the trailing numeric part of any id (e.g. `cycle_1` -> 1) instead of raising `ValueError` under `semantic_ids=True`.
|
|
76
|
+
- The `py2max` CLI now reports all `Py2MaxError`s (not just `InvalidConnectionError`) as a clean error message instead of leaking a traceback.
|
|
77
|
+
- Fixed an `inital` -> `initial` keyword typo in the simple-synthesis tutorial.
|
|
78
|
+
|
|
79
|
+
### Testing & Tooling
|
|
80
|
+
|
|
81
|
+
- The test suite is now hermetic: a `conftest.py` autouse fixture isolates each test in a temporary working directory, so relative `outputs/` writes no longer accumulate in the repo. Fixture reads are anchored at the test file.
|
|
82
|
+
- Promoted the `.amxd` byte-for-byte fixtures from the gitignored `outputs/` into tracked `tests/data/`, so that verification runs in CI and on fresh checkouts instead of only on the author's machine.
|
|
83
|
+
- Repo-wide `ruff` lint and format cleanup.
|
|
84
|
+
|
|
85
|
+
### New: Max for Live Support (`py2max.m4l`)
|
|
86
|
+
|
|
87
|
+
Implements [issue #9](https://github.com/shakfu/py2max/issues/9). See [`docs/notes/amxd.md`](https://github.com/shakfu/py2max/blob/main/docs/notes/amxd.md) for the on-disk format, embedded-project block, and verification details.
|
|
88
|
+
|
|
89
|
+
- **`.amxd` read/write**: byte-for-byte compatible with Max-exported devices; verified against real fixtures and end-to-end in Live 12.
|
|
90
|
+
- **Device-type discrimination**: Audio Effect / Instrument / MIDI Effect via `Patcher(device_type=...)` or the `pack_amxd` / `write_amxd` `device_type` argument.
|
|
91
|
+
- **Presentation-mode helpers**: `Patcher.enable_presentation(devicewidth=...)`, `Patcher.enforce_integer_coords()`, `Box.add_to_presentation([x, y, w, h])` (rejects M4L infrastructure objects, rounds fractional coords with a warning).
|
|
92
|
+
- `Patcher.save()` / `Patcher.from_file()` auto-detect the `.amxd` extension; `.maxpat` path is unchanged.
|
|
93
|
+
|
|
94
|
+
### Changed: M4L Module Layout & Imports
|
|
95
|
+
|
|
96
|
+
- All M4L code (binary format + presentation helpers) lives in a single module `py2max/m4l.py`. Previously briefly split as `py2max/amxd.py`.
|
|
97
|
+
- M4L symbols are reachable only via `from py2max.m4l import …`; nothing is re-exported from the top-level `py2max` namespace.
|
|
98
|
+
|
|
99
|
+
### New: Prebuilt MaxRef Bundle (Linux Support)
|
|
100
|
+
|
|
101
|
+
- Ship `py2max/maxref/data/bundle.json.gz` in the wheel (1175 objects, ~1 MiB compressed, ~7 MiB raw).
|
|
102
|
+
- `MaxRefCache._get_refdict()` falls back to the bundle when no local Max installation is found, pre-seeding the parser cache so `Box.help()`, `get_inlet_count`, `get_outlet_count`, and connection validation work identically on Linux.
|
|
103
|
+
- Regenerate with `uv run python scripts/build_maxref_bundle.py` on a machine with Max installed; commit the result.
|
|
104
|
+
- Bundle stores full parsed data (methods, attributes, inlets/outlets, digests, descriptions) — not a trimmed subset — so introspection parity with macOS/Windows is preserved.
|
|
105
|
+
|
|
5
106
|
## [0.2.1] - 2026-01-11
|
|
6
107
|
|
|
7
108
|
### New: Dagre Layout Algorithm
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py2max
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A library for offline generation of Max/MSP patcher (.maxpat) files.
|
|
5
5
|
Keywords: Max,maxpat,offline
|
|
6
6
|
Author: Shakeeb Alireza
|
|
@@ -12,7 +12,6 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: Intended Audience :: End Users/Desktop
|
|
13
13
|
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
14
14
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
15
|
Classifier: Programming Language :: Python :: 3
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -22,15 +21,14 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.14
|
|
23
22
|
Classifier: Operating System :: OS Independent
|
|
24
23
|
Classifier: Typing :: Typed
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
Maintainer: Shakeeb Alireza
|
|
25
|
+
Maintainer-email: Shakeeb Alireza <shakfu@users.noreply.github.com>
|
|
27
26
|
Requires-Python: >=3.9
|
|
28
|
-
Project-URL: Changelog, https://github.com/shakfu/py2max/blob/main/CHANGELOG.md
|
|
29
|
-
Project-URL: Documentation, https://github.com/shakfu/py2max#readme
|
|
30
27
|
Project-URL: Homepage, https://github.com/shakfu/py2max
|
|
31
|
-
Project-URL:
|
|
28
|
+
Project-URL: Documentation, https://github.com/shakfu/py2max#readme
|
|
32
29
|
Project-URL: Repository, https://github.com/shakfu/py2max.git
|
|
33
|
-
|
|
30
|
+
Project-URL: Issues, https://github.com/shakfu/py2max/issues
|
|
31
|
+
Project-URL: Changelog, https://github.com/shakfu/py2max/blob/main/CHANGELOG.md
|
|
34
32
|
Description-Content-Type: text/markdown
|
|
35
33
|
|
|
36
34
|
# py2max
|
|
@@ -46,9 +44,13 @@ If you are looking for Python 3 externals for Max/MSP, check out the [py-js](htt
|
|
|
46
44
|
|
|
47
45
|
```bash
|
|
48
46
|
pip install py2max
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For the browser-based live editor and remote REPL, install the companion
|
|
50
|
+
[`py2max-server`](https://github.com/shakfu/py2max-server) package:
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
pip install py2max
|
|
52
|
+
```bash
|
|
53
|
+
pip install py2max-server
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
For development:
|
|
@@ -82,15 +84,48 @@ That's it! Open `my-synth.maxpat` in Max to see your patch.
|
|
|
82
84
|
|
|
83
85
|
- **Offline Patch Generation** - Create Max patches programmatically without Max running
|
|
84
86
|
- **Round-trip Conversion** - Load, modify, and save existing `.maxpat` files
|
|
87
|
+
- **Max for Live (.amxd)** - Read/write binary `.amxd` device files with presentation-mode helpers
|
|
85
88
|
- **Universal Object Support** - Works with any Max/MSP/Jitter object
|
|
86
|
-
- **
|
|
89
|
+
- **Fully typed** - Passes `mypy --strict`; no runtime dependencies
|
|
90
|
+
- **High Test Coverage** - 420+ tests ensure reliability
|
|
91
|
+
|
|
92
|
+
### Max for Live (.amxd)
|
|
93
|
+
|
|
94
|
+
Generate Max for Live devices directly. `Patcher.save()` / `Patcher.from_file()`
|
|
95
|
+
auto-detect the `.amxd` extension and read/write the binary device format,
|
|
96
|
+
byte-for-byte compatible with Max-exported devices.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from py2max import Patcher
|
|
100
|
+
|
|
101
|
+
# device_type: "audio_effect" (default), "instrument", or "midi_effect"
|
|
102
|
+
p = Patcher('gain.amxd', device_type='audio_effect')
|
|
103
|
+
p.enable_presentation(devicewidth=120) # render Ableton's device strip
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
plugin = p.add_textbox('plugin~') # audio in from Live
|
|
106
|
+
gain = p.add('live.gain~', maxclass='live.gain~')
|
|
107
|
+
plugout = p.add_textbox('plugout~') # audio back to Live
|
|
108
|
+
gain.add_to_presentation([20, 20, 60, 136]) # show the fader in the device
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
p.add_line(plugin, gain, outlet=0, inlet=0)
|
|
111
|
+
p.add_line(gain, plugout, outlet=0, inlet=0)
|
|
112
|
+
p.save() # writes a binary .amxd
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Helpers: `Patcher.enable_presentation(devicewidth=...)`,
|
|
116
|
+
`Box.add_to_presentation([x, y, w, h])` (rejects M4L infrastructure objects and
|
|
117
|
+
rounds fractional coordinates), and `Patcher.enforce_integer_coords()`. M4L
|
|
118
|
+
binary helpers live in `py2max.m4l`.
|
|
119
|
+
|
|
120
|
+
### Interactive Server (separate package)
|
|
121
|
+
|
|
122
|
+
Real-time browser-based patch editing with bidirectional sync lives in the
|
|
123
|
+
companion [`py2max-server`](https://github.com/shakfu/py2max-server) package, so
|
|
124
|
+
the core library stays small and offline:
|
|
91
125
|
|
|
92
126
|
```bash
|
|
93
|
-
|
|
127
|
+
pip install py2max-server
|
|
128
|
+
py2max-server serve my-patch.maxpat
|
|
94
129
|
# Opens browser at http://localhost:8000
|
|
95
130
|
```
|
|
96
131
|
|
|
@@ -247,6 +282,34 @@ p.link(sbox, dac)
|
|
|
247
282
|
p.save()
|
|
248
283
|
```
|
|
249
284
|
|
|
285
|
+
### Gen Codebox
|
|
286
|
+
|
|
287
|
+
`add_gen_codebox()` adds a standalone `gen.codebox~` object -- a complete gen
|
|
288
|
+
patch in a single box that sits directly in a regular Max patcher (unlike the
|
|
289
|
+
inner `codebox~` from `add_codebox()`, which belongs inside a `gen~`/`rnbo~`
|
|
290
|
+
subpatcher). Inlet/outlet counts are derived automatically from the highest
|
|
291
|
+
`inN`/`outN` references in the code:
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
p = Patcher('fbdelay.maxpat')
|
|
295
|
+
|
|
296
|
+
# 1 inlet (in1), 1 outlet (out1)
|
|
297
|
+
osc = p.add('cycle~ 440')
|
|
298
|
+
cb = p.add_gen_codebox('''
|
|
299
|
+
Param feedback(0.5, min=0.0, max=0.95);
|
|
300
|
+
History fb(0.0);
|
|
301
|
+
out1 = in1 + fb * feedback;
|
|
302
|
+
fb = out1;
|
|
303
|
+
''')
|
|
304
|
+
dac = p.add('ezdac~')
|
|
305
|
+
p.link(osc, cb)
|
|
306
|
+
p.link(cb, dac)
|
|
307
|
+
p.save()
|
|
308
|
+
|
|
309
|
+
# Or via the add() string shortcut (single-line / `;`-terminated code)
|
|
310
|
+
cb = p.add('gen.codebox~ out1 = in1 * 0.5;')
|
|
311
|
+
```
|
|
312
|
+
|
|
250
313
|
### Object Search
|
|
251
314
|
|
|
252
315
|
```python
|
|
@@ -285,12 +348,15 @@ py2max validate demo.maxpat
|
|
|
285
348
|
|
|
286
349
|
### Interactive Server
|
|
287
350
|
|
|
351
|
+
Provided by the separate [`py2max-server`](https://github.com/shakfu/py2max-server)
|
|
352
|
+
package (`pip install py2max-server`):
|
|
353
|
+
|
|
288
354
|
```bash
|
|
289
355
|
# Start server with browser editing
|
|
290
|
-
py2max serve my-patch.maxpat
|
|
356
|
+
py2max-server serve my-patch.maxpat
|
|
291
357
|
|
|
292
358
|
# With REPL in same terminal
|
|
293
|
-
py2max serve my-patch.maxpat --repl
|
|
359
|
+
py2max-server serve my-patch.maxpat --repl
|
|
294
360
|
```
|
|
295
361
|
|
|
296
362
|
### MaxRef Database
|
|
@@ -350,18 +416,21 @@ All classes are extendable via `**kwargs`, allowing any Max object configuration
|
|
|
350
416
|
|
|
351
417
|
## Caveats
|
|
352
418
|
|
|
353
|
-
- Max doesn't refresh from file when open - close and reopen to see changes, or use `py2max serve` for live editing
|
|
419
|
+
- Max doesn't refresh from file when open - close and reopen to see changes, or use `py2max-server serve` (from the separate `py2max-server` package) for live editing
|
|
354
420
|
- For tilde variants, use the `_tilde` suffix: `p.add_gen()` vs `p.add_gen_tilde()`
|
|
355
421
|
- API docs in progress - see `CLAUDE.md` for comprehensive usage
|
|
356
422
|
|
|
357
423
|
## Examples
|
|
358
424
|
|
|
359
|
-
|
|
425
|
+
The [`tests/examples/`](tests/examples/) directory contains working, tested
|
|
426
|
+
examples organized by topic (see its [README](tests/examples/README.md)):
|
|
360
427
|
|
|
361
|
-
- `
|
|
362
|
-
- `
|
|
363
|
-
- `
|
|
364
|
-
- `
|
|
428
|
+
- `quickstart/basic_patch.py` - Simple oscillator patch
|
|
429
|
+
- `tutorial/signal_processing_chain.py` - Complex audio processing chain
|
|
430
|
+
- `tutorial/generative_music.py` - Generative music system with patterns
|
|
431
|
+
- `layout/grid_layout_examples.py` - Grid layout with clustering
|
|
432
|
+
- `advanced/data_containers.py` - Tables, collections, and dictionaries
|
|
433
|
+
- `api/patcher_api_examples.py` - Patcher API reference examples
|
|
365
434
|
|
|
366
435
|
External usage:
|
|
367
436
|
|
|
@@ -11,9 +11,13 @@ If you are looking for Python 3 externals for Max/MSP, check out the [py-js](htt
|
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
pip install py2max
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For the browser-based live editor and remote REPL, install the companion
|
|
17
|
+
[`py2max-server`](https://github.com/shakfu/py2max-server) package:
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
pip install py2max
|
|
19
|
+
```bash
|
|
20
|
+
pip install py2max-server
|
|
17
21
|
```
|
|
18
22
|
|
|
19
23
|
For development:
|
|
@@ -47,15 +51,48 @@ That's it! Open `my-synth.maxpat` in Max to see your patch.
|
|
|
47
51
|
|
|
48
52
|
- **Offline Patch Generation** - Create Max patches programmatically without Max running
|
|
49
53
|
- **Round-trip Conversion** - Load, modify, and save existing `.maxpat` files
|
|
54
|
+
- **Max for Live (.amxd)** - Read/write binary `.amxd` device files with presentation-mode helpers
|
|
50
55
|
- **Universal Object Support** - Works with any Max/MSP/Jitter object
|
|
51
|
-
- **
|
|
56
|
+
- **Fully typed** - Passes `mypy --strict`; no runtime dependencies
|
|
57
|
+
- **High Test Coverage** - 420+ tests ensure reliability
|
|
58
|
+
|
|
59
|
+
### Max for Live (.amxd)
|
|
60
|
+
|
|
61
|
+
Generate Max for Live devices directly. `Patcher.save()` / `Patcher.from_file()`
|
|
62
|
+
auto-detect the `.amxd` extension and read/write the binary device format,
|
|
63
|
+
byte-for-byte compatible with Max-exported devices.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from py2max import Patcher
|
|
67
|
+
|
|
68
|
+
# device_type: "audio_effect" (default), "instrument", or "midi_effect"
|
|
69
|
+
p = Patcher('gain.amxd', device_type='audio_effect')
|
|
70
|
+
p.enable_presentation(devicewidth=120) # render Ableton's device strip
|
|
71
|
+
|
|
72
|
+
plugin = p.add_textbox('plugin~') # audio in from Live
|
|
73
|
+
gain = p.add('live.gain~', maxclass='live.gain~')
|
|
74
|
+
plugout = p.add_textbox('plugout~') # audio back to Live
|
|
75
|
+
gain.add_to_presentation([20, 20, 60, 136]) # show the fader in the device
|
|
76
|
+
|
|
77
|
+
p.add_line(plugin, gain, outlet=0, inlet=0)
|
|
78
|
+
p.add_line(gain, plugout, outlet=0, inlet=0)
|
|
79
|
+
p.save() # writes a binary .amxd
|
|
80
|
+
```
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
Helpers: `Patcher.enable_presentation(devicewidth=...)`,
|
|
83
|
+
`Box.add_to_presentation([x, y, w, h])` (rejects M4L infrastructure objects and
|
|
84
|
+
rounds fractional coordinates), and `Patcher.enforce_integer_coords()`. M4L
|
|
85
|
+
binary helpers live in `py2max.m4l`.
|
|
54
86
|
|
|
55
|
-
|
|
87
|
+
### Interactive Server (separate package)
|
|
88
|
+
|
|
89
|
+
Real-time browser-based patch editing with bidirectional sync lives in the
|
|
90
|
+
companion [`py2max-server`](https://github.com/shakfu/py2max-server) package, so
|
|
91
|
+
the core library stays small and offline:
|
|
56
92
|
|
|
57
93
|
```bash
|
|
58
|
-
|
|
94
|
+
pip install py2max-server
|
|
95
|
+
py2max-server serve my-patch.maxpat
|
|
59
96
|
# Opens browser at http://localhost:8000
|
|
60
97
|
```
|
|
61
98
|
|
|
@@ -212,6 +249,34 @@ p.link(sbox, dac)
|
|
|
212
249
|
p.save()
|
|
213
250
|
```
|
|
214
251
|
|
|
252
|
+
### Gen Codebox
|
|
253
|
+
|
|
254
|
+
`add_gen_codebox()` adds a standalone `gen.codebox~` object -- a complete gen
|
|
255
|
+
patch in a single box that sits directly in a regular Max patcher (unlike the
|
|
256
|
+
inner `codebox~` from `add_codebox()`, which belongs inside a `gen~`/`rnbo~`
|
|
257
|
+
subpatcher). Inlet/outlet counts are derived automatically from the highest
|
|
258
|
+
`inN`/`outN` references in the code:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
p = Patcher('fbdelay.maxpat')
|
|
262
|
+
|
|
263
|
+
# 1 inlet (in1), 1 outlet (out1)
|
|
264
|
+
osc = p.add('cycle~ 440')
|
|
265
|
+
cb = p.add_gen_codebox('''
|
|
266
|
+
Param feedback(0.5, min=0.0, max=0.95);
|
|
267
|
+
History fb(0.0);
|
|
268
|
+
out1 = in1 + fb * feedback;
|
|
269
|
+
fb = out1;
|
|
270
|
+
''')
|
|
271
|
+
dac = p.add('ezdac~')
|
|
272
|
+
p.link(osc, cb)
|
|
273
|
+
p.link(cb, dac)
|
|
274
|
+
p.save()
|
|
275
|
+
|
|
276
|
+
# Or via the add() string shortcut (single-line / `;`-terminated code)
|
|
277
|
+
cb = p.add('gen.codebox~ out1 = in1 * 0.5;')
|
|
278
|
+
```
|
|
279
|
+
|
|
215
280
|
### Object Search
|
|
216
281
|
|
|
217
282
|
```python
|
|
@@ -250,12 +315,15 @@ py2max validate demo.maxpat
|
|
|
250
315
|
|
|
251
316
|
### Interactive Server
|
|
252
317
|
|
|
318
|
+
Provided by the separate [`py2max-server`](https://github.com/shakfu/py2max-server)
|
|
319
|
+
package (`pip install py2max-server`):
|
|
320
|
+
|
|
253
321
|
```bash
|
|
254
322
|
# Start server with browser editing
|
|
255
|
-
py2max serve my-patch.maxpat
|
|
323
|
+
py2max-server serve my-patch.maxpat
|
|
256
324
|
|
|
257
325
|
# With REPL in same terminal
|
|
258
|
-
py2max serve my-patch.maxpat --repl
|
|
326
|
+
py2max-server serve my-patch.maxpat --repl
|
|
259
327
|
```
|
|
260
328
|
|
|
261
329
|
### MaxRef Database
|
|
@@ -315,18 +383,21 @@ All classes are extendable via `**kwargs`, allowing any Max object configuration
|
|
|
315
383
|
|
|
316
384
|
## Caveats
|
|
317
385
|
|
|
318
|
-
- Max doesn't refresh from file when open - close and reopen to see changes, or use `py2max serve` for live editing
|
|
386
|
+
- Max doesn't refresh from file when open - close and reopen to see changes, or use `py2max-server serve` (from the separate `py2max-server` package) for live editing
|
|
319
387
|
- For tilde variants, use the `_tilde` suffix: `p.add_gen()` vs `p.add_gen_tilde()`
|
|
320
388
|
- API docs in progress - see `CLAUDE.md` for comprehensive usage
|
|
321
389
|
|
|
322
390
|
## Examples
|
|
323
391
|
|
|
324
|
-
|
|
392
|
+
The [`tests/examples/`](tests/examples/) directory contains working, tested
|
|
393
|
+
examples organized by topic (see its [README](tests/examples/README.md)):
|
|
325
394
|
|
|
326
|
-
- `
|
|
327
|
-
- `
|
|
328
|
-
- `
|
|
329
|
-
- `
|
|
395
|
+
- `quickstart/basic_patch.py` - Simple oscillator patch
|
|
396
|
+
- `tutorial/signal_processing_chain.py` - Complex audio processing chain
|
|
397
|
+
- `tutorial/generative_music.py` - Generative music system with patterns
|
|
398
|
+
- `layout/grid_layout_examples.py` - Grid layout with clustering
|
|
399
|
+
- `advanced/data_containers.py` - Tables, collections, and dictionaries
|
|
400
|
+
- `api/patcher_api_examples.py` - Patcher API reference examples
|
|
330
401
|
|
|
331
402
|
External usage:
|
|
332
403
|
|
|
@@ -8,7 +8,10 @@ Main Classes:
|
|
|
8
8
|
Patcher: Core class for creating and managing Max patches
|
|
9
9
|
Box: Represents individual Max objects (oscillators, effects, etc.)
|
|
10
10
|
Patchline: Represents connections between objects
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
The SQLite Max-reference database is available as ``from py2max.maxref import
|
|
13
|
+
MaxRefDB`` -- kept out of the top-level import so ``import py2max`` does not pull
|
|
14
|
+
in ``sqlite3`` and the database layer.
|
|
12
15
|
|
|
13
16
|
Exceptions:
|
|
14
17
|
Py2MaxError: Base exception for all py2max errors
|
|
@@ -32,10 +35,9 @@ Example:
|
|
|
32
35
|
>>> p.save()
|
|
33
36
|
"""
|
|
34
37
|
|
|
35
|
-
__version__ = "0.
|
|
38
|
+
__version__ = "0.3.1"
|
|
36
39
|
|
|
37
40
|
from .core import Box, Patcher, Patchline
|
|
38
|
-
from .maxref import MaxRefDB
|
|
39
41
|
from .exceptions import (
|
|
40
42
|
DatabaseError,
|
|
41
43
|
InvalidConnectionError,
|
|
@@ -54,7 +56,6 @@ __all__ = [
|
|
|
54
56
|
"Patcher",
|
|
55
57
|
"Box",
|
|
56
58
|
"Patchline",
|
|
57
|
-
"MaxRefDB",
|
|
58
59
|
# Exceptions
|
|
59
60
|
"Py2MaxError",
|
|
60
61
|
"InvalidConnectionError",
|