py2pd 0.2.0__tar.gz → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py2pd
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Roundtrip parsing and generation of pure-data patches from python
5
5
  Keywords: puredata,pd,audio,dsp,music,synthesis,patching
6
6
  Author: Shakeeb Alireza
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "py2pd"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Roundtrip parsing and generation of pure-data patches from python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "py2pd"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Roundtrip parsing and generation of pure-data patches from python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -89,4 +89,4 @@ __all__ = [
89
89
  "extract_declare_paths",
90
90
  ]
91
91
 
92
- __version__ = "0.2.0"
92
+ __version__ = "0.2.1"
@@ -1,4 +1,5 @@
1
1
  from collections import deque
2
+ from pathlib import Path
2
3
  import re
3
4
  from typing import Any, Callable, Dict, FrozenSet, List, Optional, Sequence, Set, Tuple, Union
4
5
  import warnings
@@ -73,8 +74,10 @@ def _fmt_coord(value: float) -> str:
73
74
  def escape(text: str) -> str:
74
75
  """Escape special characters for PureData format."""
75
76
  save = re.sub(r"\\", r"\\\\", text)
76
- save = re.sub(r";", r" \; ", save)
77
- save = re.sub(r",", r" \, ", save)
77
+ # Absorb whitespace already flanking the separator: PureData puts one space
78
+ # between atoms, and "0, 1 10" would otherwise gain a second one.
79
+ save = re.sub(r"[ \t]*;[ \t]*", r" \; ", save)
80
+ save = re.sub(r"[ \t]*,[ \t]*", r" \, ", save)
78
81
  save = re.sub(r"\$(?=[0-9])", r"\$", save)
79
82
  return save
80
83
 
@@ -310,7 +313,8 @@ class Msg(Node):
310
313
  text : str
311
314
  Message content. Will be escaped for the PureData file format.
312
315
  num_inlets : int, optional
313
- Number of inlets (default: 2 -- hot inlet + cold inlet for setting contents)
316
+ Number of inlets (default: 1). A message box has one inlet; contents
317
+ are replaced by sending ``set ...`` to it, not through a second inlet.
314
318
  num_outlets : int, optional
315
319
  Number of outlets (default: 1)
316
320
  escaped : bool, optional
@@ -323,7 +327,7 @@ class Msg(Node):
323
327
  x_pos: int,
324
328
  y_pos: int,
325
329
  text: str,
326
- num_inlets: Optional[int] = 2,
330
+ num_inlets: Optional[int] = 1,
327
331
  num_outlets: Optional[int] = 1,
328
332
  *,
329
333
  escaped: bool = False,
@@ -368,10 +372,12 @@ class Float(Node):
368
372
  Y position in patch coordinates
369
373
  width : int
370
374
  Display width in characters (default: 5)
371
- upper_limit : float
372
- Maximum value; 0 means no limit (default: 0)
373
375
  lower_limit : float
374
376
  Minimum value; 0 means no limit (default: 0)
377
+ upper_limit : float
378
+ Maximum value; 0 means no limit (default: 0)
379
+ label_pos : int
380
+ Label placement: 0=left, 1=right, 2=top, 3=bottom (default: 0)
375
381
  label : str
376
382
  Label text (default: ``'-'`` for none)
377
383
  receive : str
@@ -385,8 +391,9 @@ class Float(Node):
385
391
  x_pos: int,
386
392
  y_pos: int,
387
393
  width: int = 5,
388
- upper_limit: float = 0,
389
394
  lower_limit: float = 0,
395
+ upper_limit: float = 0,
396
+ label_pos: int = 0,
390
397
  label: str = "-",
391
398
  receive: str = "-",
392
399
  send: str = "-",
@@ -397,8 +404,9 @@ class Float(Node):
397
404
  "x_pos": x_pos,
398
405
  "y_pos": y_pos,
399
406
  "width": width,
400
- "upper_limit": upper_limit,
401
407
  "lower_limit": lower_limit,
408
+ "upper_limit": upper_limit,
409
+ "label_pos": label_pos,
402
410
  "label": label,
403
411
  "receive": receive,
404
412
  "send": send,
@@ -410,8 +418,8 @@ class Float(Node):
410
418
  p = self.parameters
411
419
  return (
412
420
  f"#X floatatom {p['x_pos']} {p['y_pos']} {p['width']} "
413
- f"{p['upper_limit']} {p['lower_limit']} {p['label']} "
414
- f"{p['receive']} {p['send']};\n"
421
+ f"{p['lower_limit']} {p['upper_limit']} {p['label_pos']} "
422
+ f"{p['label']} {p['receive']} {p['send']};\n"
415
423
  )
416
424
 
417
425
  @property
@@ -2143,7 +2151,7 @@ class Patcher:
2143
2151
 
2144
2152
  Attributes
2145
2153
  ----------
2146
- filename : str or None
2154
+ filename : Path or None
2147
2155
  Default filename for save()
2148
2156
  nodes : list of Node
2149
2157
  All nodes in the patch
@@ -2153,7 +2161,7 @@ class Patcher:
2153
2161
  Manages automatic element positioning
2154
2162
  """
2155
2163
 
2156
- filename: Optional[str]
2164
+ filename: Optional[Path]
2157
2165
  nodes: List[Node]
2158
2166
  connections: List[Connection]
2159
2167
  layout: LayoutManager
@@ -2161,7 +2169,7 @@ class Patcher:
2161
2169
 
2162
2170
  def __init__(
2163
2171
  self,
2164
- filename: Optional[str] = None,
2172
+ filename: Optional[Union[str, Path]] = None,
2165
2173
  layout: Optional[LayoutManager] = None,
2166
2174
  *,
2167
2175
  validate_links: bool = True,
@@ -2175,8 +2183,9 @@ class Patcher:
2175
2183
 
2176
2184
  Parameters
2177
2185
  ----------
2178
- filename : str, optional
2179
- Default filename for save(). Can be overridden in save().
2186
+ filename : str or Path, optional
2187
+ Default filename for save(), stored as a ``Path``. Can be
2188
+ overridden in save().
2180
2189
  layout : LayoutManager, optional
2181
2190
  Custom layout manager. If None, creates a default LayoutManager.
2182
2191
  validate_links : bool, optional
@@ -2193,7 +2202,7 @@ class Patcher:
2193
2202
  font_size : int, optional
2194
2203
  Patch font size written on the ``#N canvas`` line (default: 10).
2195
2204
  """
2196
- self.filename = filename
2205
+ self.filename = Path(filename) if filename is not None else None
2197
2206
  self.nodes = []
2198
2207
  self.connections = []
2199
2208
  self.layout = layout if layout is not None else LayoutManager()
@@ -2412,8 +2421,9 @@ class Patcher:
2412
2421
  self,
2413
2422
  *,
2414
2423
  width: int = 5,
2415
- upper_limit: float = 0,
2416
2424
  lower_limit: float = 0,
2425
+ upper_limit: float = 0,
2426
+ label_pos: int = 0,
2417
2427
  label: str = "-",
2418
2428
  receive: str = "-",
2419
2429
  send: str = "-",
@@ -2428,10 +2438,12 @@ class Patcher:
2428
2438
  ----------
2429
2439
  width : int
2430
2440
  Display width in characters (default: 5)
2431
- upper_limit : int
2432
- Maximum value (0 = no limit)
2433
2441
  lower_limit : int
2434
2442
  Minimum value (0 = no limit)
2443
+ upper_limit : int
2444
+ Maximum value (0 = no limit)
2445
+ label_pos : int
2446
+ Label placement: 0=left, 1=right, 2=top, 3=bottom
2435
2447
  label : str
2436
2448
  Label text (default: '-' for none)
2437
2449
  receive : str
@@ -2445,7 +2457,17 @@ class Patcher:
2445
2457
  The created number box
2446
2458
  """
2447
2459
  x_pos, y_pos, pos_update = self._resolve_position(x_pos, y_pos, new_row, new_col)
2448
- node = Float(x_pos, y_pos, width, upper_limit, lower_limit, label, receive, send)
2460
+ node = Float(
2461
+ x_pos,
2462
+ y_pos,
2463
+ width=width,
2464
+ lower_limit=lower_limit,
2465
+ upper_limit=upper_limit,
2466
+ label_pos=label_pos,
2467
+ label=label,
2468
+ receive=receive,
2469
+ send=send,
2470
+ )
2449
2471
  self._register(node, pos_update)
2450
2472
  return node
2451
2473
 
@@ -3408,23 +3430,36 @@ class Patcher:
3408
3430
  connections_str = "".join(str(c) for c in self.connections)
3409
3431
  return f"{nodes_str}{connections_str}"
3410
3432
 
3411
- def save(self, filename: Optional[str] = None) -> None:
3433
+ def save(
3434
+ self,
3435
+ filename: Optional[Union[str, Path]] = None,
3436
+ *,
3437
+ to_dir: Optional[Union[str, Path]] = None,
3438
+ ) -> None:
3412
3439
  """Save the patch to a file.
3413
3440
 
3414
3441
  Parameters
3415
3442
  ----------
3416
- filename : str, optional
3443
+ filename : str or Path, optional
3417
3444
  Path to the output .pd file. If not provided, uses the filename
3418
3445
  from the constructor.
3446
+ to_dir : str or Path, optional
3447
+ Directory to write into, created if missing. Only the base name of
3448
+ *filename* is used, so one patch can be written to several
3449
+ directories.
3419
3450
 
3420
3451
  Raises
3421
3452
  ------
3422
3453
  ValueError
3423
3454
  If no filename is provided and none was set in constructor.
3424
3455
  """
3425
- fn = filename or self.filename
3456
+ fn = Path(filename) if filename is not None else self.filename
3426
3457
  if fn is None:
3427
3458
  raise ValueError("No filename specified. Provide filename or set in constructor.")
3459
+ if to_dir is not None:
3460
+ d = Path(to_dir)
3461
+ d.mkdir(parents=True, exist_ok=True)
3462
+ fn = d / fn.name
3428
3463
  with open(fn, "w", encoding="utf-8", newline="\n") as f:
3429
3464
  f.write(str(self) + "\n")
3430
3465
 
@@ -1874,12 +1874,13 @@ def to_builder(ast: PdPatch) -> "api.Patcher":
1874
1874
  node = api.Float(
1875
1875
  elem.position.x,
1876
1876
  elem.position.y,
1877
- elem.width,
1878
- elem.upper_limit,
1879
- elem.lower_limit,
1880
- elem.label,
1881
- elem.receive,
1882
- elem.send,
1877
+ width=elem.width,
1878
+ lower_limit=elem.lower_limit,
1879
+ upper_limit=elem.upper_limit,
1880
+ label_pos=elem.label_pos,
1881
+ label=elem.label,
1882
+ receive=elem.receive,
1883
+ send=elem.send,
1883
1884
  )
1884
1885
  patch.nodes.append(node)
1885
1886
  node_map.append(node)
@@ -28,6 +28,7 @@ Usage::
28
28
  from dataclasses import dataclass, field
29
29
  from enum import Enum
30
30
  import os
31
+ from pathlib import Path
31
32
  import re
32
33
  import shutil
33
34
  import subprocess
@@ -425,7 +426,7 @@ class HeavyPatcher(Patcher):
425
426
 
426
427
  Parameters
427
428
  ----------
428
- filename : str, optional
429
+ filename : str or Path, optional
429
430
  Default filename for save().
430
431
  layout : LayoutManager, optional
431
432
  Custom layout manager.
@@ -435,7 +436,7 @@ class HeavyPatcher(Patcher):
435
436
 
436
437
  def __init__(
437
438
  self,
438
- filename: Optional[str] = None,
439
+ filename: Optional[Union[str, Path]] = None,
439
440
  layout: Optional[LayoutManager] = None,
440
441
  generators: Sequence[HvccGenerator] | None = None,
441
442
  ) -> None:
File without changes
File without changes
File without changes
File without changes