py2pd 0.1.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.
- py2pd-0.1.1/PKG-INFO +238 -0
- py2pd-0.1.1/README.md +209 -0
- py2pd-0.1.1/pyproject.toml +62 -0
- py2pd-0.1.1/src/py2pd/__init__.py +86 -0
- py2pd-0.1.1/src/py2pd/api.py +2732 -0
- py2pd-0.1.1/src/py2pd/ast.py +1055 -0
py2pd-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py2pd
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Roundtrip parsing and generation of pure-data patches from python
|
|
5
|
+
Keywords: puredata,pd,audio,dsp,music,synthesis,patching
|
|
6
|
+
Author: Shakeeb Alireza
|
|
7
|
+
Author-email: Shakeeb Alireza <shakfu@users.noreply.github.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Sound Synthesis
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.13
|
|
24
|
+
Project-URL: Homepage, https://github.com/shakfu/py2pd
|
|
25
|
+
Project-URL: Repository, https://github.com/shakfu/py2pd
|
|
26
|
+
Project-URL: Issues, https://github.com/shakfu/py2pd/issues
|
|
27
|
+
Project-URL: Changelog, https://github.com/shakfu/py2pd/blob/main/CHANGELOG.md
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# py2pd - Python <-> PureData
|
|
31
|
+
|
|
32
|
+
Roundtrip parsing and generation of [pure-data](https://puredata.info) patches from python.
|
|
33
|
+
|
|
34
|
+
py2pd is a fork and extensive rewrite of Dylan Burati's [puredata-compiler](https://github.com/dylanburati/puredata-compiler) using some of the ideas from [py2max](https://github.com/shakfu/py2max).
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install py2pd
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from py2pd import Patcher
|
|
46
|
+
|
|
47
|
+
# Create a simple synthesizer patch
|
|
48
|
+
p = Patcher('synth.pd')
|
|
49
|
+
|
|
50
|
+
osc = p.add('osc~ 440')
|
|
51
|
+
gain = p.add('*~ 0.3')
|
|
52
|
+
dac = p.add('dac~')
|
|
53
|
+
|
|
54
|
+
p.link(osc, gain)
|
|
55
|
+
p.link(gain, dac)
|
|
56
|
+
p.link(gain, dac, inlet=1) # stereo
|
|
57
|
+
|
|
58
|
+
p.save()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Builder API
|
|
62
|
+
|
|
63
|
+
The `Patcher` class provides methods to add nodes and connect them.
|
|
64
|
+
|
|
65
|
+
### Adding Nodes
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from py2pd import Patcher
|
|
69
|
+
|
|
70
|
+
p = Patcher('example.pd')
|
|
71
|
+
|
|
72
|
+
# Objects
|
|
73
|
+
osc = p.add('osc~ 440')
|
|
74
|
+
filter_obj = p.add('lop~ 1000')
|
|
75
|
+
|
|
76
|
+
# Messages
|
|
77
|
+
bang = p.add_msg('bang')
|
|
78
|
+
freq_msg = p.add_msg('440')
|
|
79
|
+
|
|
80
|
+
# GUI Elements
|
|
81
|
+
slider = p.add_hslider(min_val=20, max_val=20000, width=150)
|
|
82
|
+
toggle = p.add_toggle(default_value=1, send='onoff')
|
|
83
|
+
numbox = p.add_numberbox(min_val=0, max_val=127)
|
|
84
|
+
bang_btn = p.add_bang(send='trigger', label='Click')
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Connecting Nodes
|
|
88
|
+
|
|
89
|
+
Use `link()` to connect nodes. By default, outlet 0 connects to inlet 0:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
p.link(osc, gain) # outlet 0 -> inlet 0
|
|
93
|
+
p.link(gain, dac) # left channel
|
|
94
|
+
p.link(gain, dac, inlet=1) # right channel (stereo)
|
|
95
|
+
p.link(trigger, pack, outlet=1, inlet=2) # specific ports
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Subpatches
|
|
99
|
+
|
|
100
|
+
Create reusable subpatches:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
def make_envelope() -> Patcher:
|
|
104
|
+
p = Patcher()
|
|
105
|
+
inlet = p.add('inlet')
|
|
106
|
+
vline = p.add('vline~')
|
|
107
|
+
outlet = p.add('outlet~')
|
|
108
|
+
p.link(inlet, vline)
|
|
109
|
+
p.link(vline, outlet)
|
|
110
|
+
return p
|
|
111
|
+
|
|
112
|
+
main = Patcher('main.pd')
|
|
113
|
+
osc = main.add('osc~ 440')
|
|
114
|
+
env = main.add_subpatch('envelope', make_envelope())
|
|
115
|
+
vca = main.add('*~')
|
|
116
|
+
|
|
117
|
+
main.link(osc, vca)
|
|
118
|
+
main.link(env, vca, inlet=1)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Layout Options
|
|
122
|
+
|
|
123
|
+
**Default layout** - nodes flow top-to-bottom:
|
|
124
|
+
```python
|
|
125
|
+
p = Patcher('patch.pd')
|
|
126
|
+
p.add('osc~ 440') # Row 1
|
|
127
|
+
p.add('*~ 0.5') # Row 2
|
|
128
|
+
p.add('dac~') # Row 3
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Grid layout** - organized columns:
|
|
132
|
+
```python
|
|
133
|
+
from py2pd import Patcher, GridLayoutManager
|
|
134
|
+
|
|
135
|
+
grid = GridLayoutManager(columns=4, cell_width=80, cell_height=35)
|
|
136
|
+
p = Patcher('grid.pd', layout=grid)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Auto layout** - arrange by signal flow:
|
|
140
|
+
```python
|
|
141
|
+
p = Patcher('patch.pd')
|
|
142
|
+
# Add nodes in any order...
|
|
143
|
+
p.auto_layout(margin=50, row_spacing=50, col_spacing=100)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Saving and Export
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
p.save() # Save to filename from constructor
|
|
150
|
+
p.save('other.pd') # Save to specific file
|
|
151
|
+
p.save_svg('patch.svg') # Export visualization as SVG
|
|
152
|
+
svg_str = p.to_svg() # Get SVG as string
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Validation
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
p.validate_connections(check_cycles=True) # Raises on invalid connections
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## AST API (Round-trip Parsing)
|
|
162
|
+
|
|
163
|
+
For modifying existing patches with immutable AST nodes:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from py2pd import parse_file, serialize
|
|
167
|
+
|
|
168
|
+
# Parse existing patch
|
|
169
|
+
ast = parse_file('input.pd')
|
|
170
|
+
|
|
171
|
+
# Modify the AST...
|
|
172
|
+
|
|
173
|
+
# Write back
|
|
174
|
+
with open('output.pd', 'w') as f:
|
|
175
|
+
f.write(serialize(ast))
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Converting Between APIs
|
|
179
|
+
|
|
180
|
+
You can convert between AST and Builder representations:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from py2pd import parse_file, to_builder, from_builder
|
|
184
|
+
|
|
185
|
+
# AST -> Builder: parse then edit with the more convenient API
|
|
186
|
+
ast = parse_file('input.pd')
|
|
187
|
+
patch = to_builder(ast)
|
|
188
|
+
patch.add('osc~ 880')
|
|
189
|
+
patch.save('output.pd')
|
|
190
|
+
|
|
191
|
+
# Builder -> AST: for analysis or transformation
|
|
192
|
+
ast = from_builder(patch)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### When to Use Each API
|
|
196
|
+
|
|
197
|
+
| Use Case | Recommended API |
|
|
198
|
+
|----------|-----------------|
|
|
199
|
+
| Creating patches from scratch | Builder |
|
|
200
|
+
| Modifying existing patches | Builder (via `to_builder()`) |
|
|
201
|
+
| Lossless round-trip of complex patches | AST |
|
|
202
|
+
| Building analysis/refactoring tools | AST |
|
|
203
|
+
| Batch search/replace across .pd files | AST |
|
|
204
|
+
|
|
205
|
+
For most workflows, parse to AST then convert to Builder for editing. Use the AST API directly when you need to preserve elements the Builder doesn't model (e.g., `coords`, comments) or need immutable transformations.
|
|
206
|
+
|
|
207
|
+
AST node types are available from the `py2pd.ast` module:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from py2pd.ast import PdPatch, PdObj, PdMsg, Position, transform, find_objects
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## GUI Elements
|
|
214
|
+
|
|
215
|
+
| Method | Description |
|
|
216
|
+
|--------|-------------|
|
|
217
|
+
| `add_bang()` | Bang button |
|
|
218
|
+
| `add_toggle()` | On/off toggle |
|
|
219
|
+
| `add_numberbox()` | Editable number |
|
|
220
|
+
| `add_float()` | Float atom |
|
|
221
|
+
| `add_symbol()` | Symbol/text input |
|
|
222
|
+
| `add_hslider()` | Horizontal slider |
|
|
223
|
+
| `add_vslider()` | Vertical slider |
|
|
224
|
+
| `add_hradio()` | Horizontal radio buttons |
|
|
225
|
+
| `add_vradio()` | Vertical radio buttons |
|
|
226
|
+
| `add_canvas()` | Background/label area |
|
|
227
|
+
| `add_vu()` | VU meter |
|
|
228
|
+
|
|
229
|
+
## Error Handling
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from py2pd import (
|
|
233
|
+
ConnectionError, # Invalid connection arguments
|
|
234
|
+
NodeNotFoundError, # Node not in patch
|
|
235
|
+
InvalidConnectionError, # Bad inlet/outlet index
|
|
236
|
+
CycleWarning, # Feedback loop detected
|
|
237
|
+
)
|
|
238
|
+
```
|
py2pd-0.1.1/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# py2pd - Python <-> PureData
|
|
2
|
+
|
|
3
|
+
Roundtrip parsing and generation of [pure-data](https://puredata.info) patches from python.
|
|
4
|
+
|
|
5
|
+
py2pd is a fork and extensive rewrite of Dylan Burati's [puredata-compiler](https://github.com/dylanburati/puredata-compiler) using some of the ideas from [py2max](https://github.com/shakfu/py2max).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install py2pd
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from py2pd import Patcher
|
|
17
|
+
|
|
18
|
+
# Create a simple synthesizer patch
|
|
19
|
+
p = Patcher('synth.pd')
|
|
20
|
+
|
|
21
|
+
osc = p.add('osc~ 440')
|
|
22
|
+
gain = p.add('*~ 0.3')
|
|
23
|
+
dac = p.add('dac~')
|
|
24
|
+
|
|
25
|
+
p.link(osc, gain)
|
|
26
|
+
p.link(gain, dac)
|
|
27
|
+
p.link(gain, dac, inlet=1) # stereo
|
|
28
|
+
|
|
29
|
+
p.save()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Builder API
|
|
33
|
+
|
|
34
|
+
The `Patcher` class provides methods to add nodes and connect them.
|
|
35
|
+
|
|
36
|
+
### Adding Nodes
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from py2pd import Patcher
|
|
40
|
+
|
|
41
|
+
p = Patcher('example.pd')
|
|
42
|
+
|
|
43
|
+
# Objects
|
|
44
|
+
osc = p.add('osc~ 440')
|
|
45
|
+
filter_obj = p.add('lop~ 1000')
|
|
46
|
+
|
|
47
|
+
# Messages
|
|
48
|
+
bang = p.add_msg('bang')
|
|
49
|
+
freq_msg = p.add_msg('440')
|
|
50
|
+
|
|
51
|
+
# GUI Elements
|
|
52
|
+
slider = p.add_hslider(min_val=20, max_val=20000, width=150)
|
|
53
|
+
toggle = p.add_toggle(default_value=1, send='onoff')
|
|
54
|
+
numbox = p.add_numberbox(min_val=0, max_val=127)
|
|
55
|
+
bang_btn = p.add_bang(send='trigger', label='Click')
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Connecting Nodes
|
|
59
|
+
|
|
60
|
+
Use `link()` to connect nodes. By default, outlet 0 connects to inlet 0:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
p.link(osc, gain) # outlet 0 -> inlet 0
|
|
64
|
+
p.link(gain, dac) # left channel
|
|
65
|
+
p.link(gain, dac, inlet=1) # right channel (stereo)
|
|
66
|
+
p.link(trigger, pack, outlet=1, inlet=2) # specific ports
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Subpatches
|
|
70
|
+
|
|
71
|
+
Create reusable subpatches:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
def make_envelope() -> Patcher:
|
|
75
|
+
p = Patcher()
|
|
76
|
+
inlet = p.add('inlet')
|
|
77
|
+
vline = p.add('vline~')
|
|
78
|
+
outlet = p.add('outlet~')
|
|
79
|
+
p.link(inlet, vline)
|
|
80
|
+
p.link(vline, outlet)
|
|
81
|
+
return p
|
|
82
|
+
|
|
83
|
+
main = Patcher('main.pd')
|
|
84
|
+
osc = main.add('osc~ 440')
|
|
85
|
+
env = main.add_subpatch('envelope', make_envelope())
|
|
86
|
+
vca = main.add('*~')
|
|
87
|
+
|
|
88
|
+
main.link(osc, vca)
|
|
89
|
+
main.link(env, vca, inlet=1)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Layout Options
|
|
93
|
+
|
|
94
|
+
**Default layout** - nodes flow top-to-bottom:
|
|
95
|
+
```python
|
|
96
|
+
p = Patcher('patch.pd')
|
|
97
|
+
p.add('osc~ 440') # Row 1
|
|
98
|
+
p.add('*~ 0.5') # Row 2
|
|
99
|
+
p.add('dac~') # Row 3
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Grid layout** - organized columns:
|
|
103
|
+
```python
|
|
104
|
+
from py2pd import Patcher, GridLayoutManager
|
|
105
|
+
|
|
106
|
+
grid = GridLayoutManager(columns=4, cell_width=80, cell_height=35)
|
|
107
|
+
p = Patcher('grid.pd', layout=grid)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Auto layout** - arrange by signal flow:
|
|
111
|
+
```python
|
|
112
|
+
p = Patcher('patch.pd')
|
|
113
|
+
# Add nodes in any order...
|
|
114
|
+
p.auto_layout(margin=50, row_spacing=50, col_spacing=100)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Saving and Export
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
p.save() # Save to filename from constructor
|
|
121
|
+
p.save('other.pd') # Save to specific file
|
|
122
|
+
p.save_svg('patch.svg') # Export visualization as SVG
|
|
123
|
+
svg_str = p.to_svg() # Get SVG as string
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Validation
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
p.validate_connections(check_cycles=True) # Raises on invalid connections
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## AST API (Round-trip Parsing)
|
|
133
|
+
|
|
134
|
+
For modifying existing patches with immutable AST nodes:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from py2pd import parse_file, serialize
|
|
138
|
+
|
|
139
|
+
# Parse existing patch
|
|
140
|
+
ast = parse_file('input.pd')
|
|
141
|
+
|
|
142
|
+
# Modify the AST...
|
|
143
|
+
|
|
144
|
+
# Write back
|
|
145
|
+
with open('output.pd', 'w') as f:
|
|
146
|
+
f.write(serialize(ast))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Converting Between APIs
|
|
150
|
+
|
|
151
|
+
You can convert between AST and Builder representations:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from py2pd import parse_file, to_builder, from_builder
|
|
155
|
+
|
|
156
|
+
# AST -> Builder: parse then edit with the more convenient API
|
|
157
|
+
ast = parse_file('input.pd')
|
|
158
|
+
patch = to_builder(ast)
|
|
159
|
+
patch.add('osc~ 880')
|
|
160
|
+
patch.save('output.pd')
|
|
161
|
+
|
|
162
|
+
# Builder -> AST: for analysis or transformation
|
|
163
|
+
ast = from_builder(patch)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### When to Use Each API
|
|
167
|
+
|
|
168
|
+
| Use Case | Recommended API |
|
|
169
|
+
|----------|-----------------|
|
|
170
|
+
| Creating patches from scratch | Builder |
|
|
171
|
+
| Modifying existing patches | Builder (via `to_builder()`) |
|
|
172
|
+
| Lossless round-trip of complex patches | AST |
|
|
173
|
+
| Building analysis/refactoring tools | AST |
|
|
174
|
+
| Batch search/replace across .pd files | AST |
|
|
175
|
+
|
|
176
|
+
For most workflows, parse to AST then convert to Builder for editing. Use the AST API directly when you need to preserve elements the Builder doesn't model (e.g., `coords`, comments) or need immutable transformations.
|
|
177
|
+
|
|
178
|
+
AST node types are available from the `py2pd.ast` module:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from py2pd.ast import PdPatch, PdObj, PdMsg, Position, transform, find_objects
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## GUI Elements
|
|
185
|
+
|
|
186
|
+
| Method | Description |
|
|
187
|
+
|--------|-------------|
|
|
188
|
+
| `add_bang()` | Bang button |
|
|
189
|
+
| `add_toggle()` | On/off toggle |
|
|
190
|
+
| `add_numberbox()` | Editable number |
|
|
191
|
+
| `add_float()` | Float atom |
|
|
192
|
+
| `add_symbol()` | Symbol/text input |
|
|
193
|
+
| `add_hslider()` | Horizontal slider |
|
|
194
|
+
| `add_vslider()` | Vertical slider |
|
|
195
|
+
| `add_hradio()` | Horizontal radio buttons |
|
|
196
|
+
| `add_vradio()` | Vertical radio buttons |
|
|
197
|
+
| `add_canvas()` | Background/label area |
|
|
198
|
+
| `add_vu()` | VU meter |
|
|
199
|
+
|
|
200
|
+
## Error Handling
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from py2pd import (
|
|
204
|
+
ConnectionError, # Invalid connection arguments
|
|
205
|
+
NodeNotFoundError, # Node not in patch
|
|
206
|
+
InvalidConnectionError, # Bad inlet/outlet index
|
|
207
|
+
CycleWarning, # Feedback loop detected
|
|
208
|
+
)
|
|
209
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "py2pd"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Roundtrip parsing and generation of pure-data patches from python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Shakeeb Alireza", email = "shakfu@users.noreply.github.com" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["puredata", "pd", "audio", "dsp", "music", "synthesis", "patching"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Intended Audience :: End Users/Desktop",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
23
|
+
"Topic :: Multimedia :: Sound/Audio :: Sound Synthesis",
|
|
24
|
+
"Topic :: Software Development :: Code Generators",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = []
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/shakfu/py2pd"
|
|
33
|
+
Repository = "https://github.com/shakfu/py2pd"
|
|
34
|
+
Issues = "https://github.com/shakfu/py2pd/issues"
|
|
35
|
+
Changelog = "https://github.com/shakfu/py2pd/blob/main/CHANGELOG.md"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"mypy>=1.19.1",
|
|
40
|
+
"pytest>=9.0.2",
|
|
41
|
+
"ruff>=0.14.13",
|
|
42
|
+
"twine>=6.2.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["uv_build>=0.9.26,<0.10.0"]
|
|
47
|
+
build-backend = "uv_build"
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.13"
|
|
54
|
+
warn_return_any = true
|
|
55
|
+
warn_unused_configs = true
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
target-version = "py313"
|
|
59
|
+
line-length = 100
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = ["E", "F", "I", "W"]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
py2pd - Python to PureData
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
Write PureData patches as Python programs.
|
|
6
|
+
|
|
7
|
+
Builder API example:
|
|
8
|
+
>>> from py2pd import Patcher
|
|
9
|
+
>>> p = Patcher('patch.pd')
|
|
10
|
+
>>> osc = p.add('osc~ 440')
|
|
11
|
+
>>> dac = p.add('dac~')
|
|
12
|
+
>>> p.link(osc, dac)
|
|
13
|
+
>>> p.link(osc, dac, inlet=1) # stereo
|
|
14
|
+
>>> p.save()
|
|
15
|
+
|
|
16
|
+
AST API example (round-trip):
|
|
17
|
+
>>> from py2pd import parse_file, serialize
|
|
18
|
+
>>> ast = parse_file('input.pd')
|
|
19
|
+
>>> # Modify the AST...
|
|
20
|
+
>>> with open('output.pd', 'w') as f:
|
|
21
|
+
... f.write(serialize(ast))
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Builder API
|
|
25
|
+
from .api import (
|
|
26
|
+
COLUMN_WIDTH as COLUMN_WIDTH,
|
|
27
|
+
)
|
|
28
|
+
from .api import (
|
|
29
|
+
DEFAULT_MARGIN as DEFAULT_MARGIN,
|
|
30
|
+
)
|
|
31
|
+
from .api import (
|
|
32
|
+
# Layout constants
|
|
33
|
+
ROW_HEIGHT as ROW_HEIGHT,
|
|
34
|
+
)
|
|
35
|
+
from .api import (
|
|
36
|
+
SUBPATCH_CANVAS_HEIGHT as SUBPATCH_CANVAS_HEIGHT,
|
|
37
|
+
)
|
|
38
|
+
from .api import (
|
|
39
|
+
SUBPATCH_CANVAS_WIDTH as SUBPATCH_CANVAS_WIDTH,
|
|
40
|
+
)
|
|
41
|
+
from .api import (
|
|
42
|
+
ConnectionError as ConnectionError,
|
|
43
|
+
)
|
|
44
|
+
from .api import (
|
|
45
|
+
CycleWarning as CycleWarning,
|
|
46
|
+
)
|
|
47
|
+
from .api import (
|
|
48
|
+
GridLayoutManager as GridLayoutManager,
|
|
49
|
+
)
|
|
50
|
+
from .api import (
|
|
51
|
+
InvalidConnectionError as InvalidConnectionError,
|
|
52
|
+
)
|
|
53
|
+
from .api import (
|
|
54
|
+
LayoutManager as LayoutManager,
|
|
55
|
+
)
|
|
56
|
+
from .api import (
|
|
57
|
+
NodeNotFoundError as NodeNotFoundError,
|
|
58
|
+
)
|
|
59
|
+
from .api import (
|
|
60
|
+
Patcher as Patcher,
|
|
61
|
+
)
|
|
62
|
+
from .ast import (
|
|
63
|
+
ParseError as ParseError,
|
|
64
|
+
)
|
|
65
|
+
from .ast import (
|
|
66
|
+
from_builder as from_builder,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# AST API (node types available via: from py2pd.ast import PdPatch, PdObj, ...)
|
|
70
|
+
from .ast import (
|
|
71
|
+
parse as parse,
|
|
72
|
+
)
|
|
73
|
+
from .ast import (
|
|
74
|
+
parse_file as parse_file,
|
|
75
|
+
)
|
|
76
|
+
from .ast import (
|
|
77
|
+
serialize as serialize,
|
|
78
|
+
)
|
|
79
|
+
from .ast import (
|
|
80
|
+
serialize_to_file as serialize_to_file,
|
|
81
|
+
)
|
|
82
|
+
from .ast import (
|
|
83
|
+
to_builder as to_builder,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
__version__ = "0.1.1"
|