pycodedj 0.6.0__tar.gz → 0.6.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.
- {pycodedj-0.6.0 → pycodedj-0.6.1}/CHANGELOG.md +10 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/PKG-INFO +1 -1
- {pycodedj-0.6.0 → pycodedj-0.6.1}/README.ja.md +1 -1
- pycodedj-0.6.1/docs/favicon.svg +17 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/index.html +11 -1
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/manual.html +13 -3
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/manual.ja.html +12 -2
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/manual.ja.md +2 -2
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/manual.md +3 -3
- {pycodedj-0.6.0 → pycodedj-0.6.1}/pyproject.toml +1 -1
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/__init__.py +1 -1
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/__main__.py +1 -1
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/watcher.py +12 -5
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_watcher.py +36 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/.github/workflows/workflow.yml +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/.gitignore +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/LICENSE +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/README.md +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/docs/CNAME +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/examples/club_set.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/examples/demo.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/examples/hello_sc.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/examples/sound_showcase.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/sc/synths.scd +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/_loop.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/analyzer.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/block_parser.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/engine.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/mapper.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/osc_bridge.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/src/pycodedj/pattern.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/__init__.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_analyzer.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_block_parser.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_cli.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_engine.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_mapper.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_osc_bridge.py +0 -0
- {pycodedj-0.6.0 → pycodedj-0.6.1}/tests/test_pattern.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.1] - 2026-05-09
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
- `pycodedj watch` が保存時に変更されたループだけを再評価するように変更。未変更のループは OSC 再送信されず、パターンの不要な再起動感を抑える
|
|
8
|
+
|
|
9
|
+
### Docs
|
|
10
|
+
|
|
11
|
+
- watch モードの説明を、起動時は全ループ評価・保存時は変更ループのみ再評価する仕様に更新
|
|
12
|
+
|
|
3
13
|
## [0.6.0] - 2026-05-08
|
|
4
14
|
|
|
5
15
|
### Features
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="PyCodeDJ">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="10" y1="8" x2="54" y2="58" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#16161f"/>
|
|
5
|
+
<stop offset="1" stop-color="#08080e"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<linearGradient id="wave" x1="14" y1="32" x2="50" y2="32" gradientUnits="userSpaceOnUse">
|
|
8
|
+
<stop stop-color="#a6e3a1"/>
|
|
9
|
+
<stop offset="0.52" stop-color="#89b4fa"/>
|
|
10
|
+
<stop offset="1" stop-color="#cba6f7"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect width="64" height="64" rx="14" fill="url(#bg)"/>
|
|
14
|
+
<rect x="8" y="8" width="48" height="48" rx="10" fill="none" stroke="#1e1e2e" stroke-width="2"/>
|
|
15
|
+
<path d="M15 32c4-12 8-12 12 0s8 12 12 0 8-12 12 0" fill="none" stroke="url(#wave)" stroke-width="5" stroke-linecap="round"/>
|
|
16
|
+
<circle cx="48" cy="18" r="4" fill="#f38ba8"/>
|
|
17
|
+
</svg>
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>PyCodeDJ — Code is the Instrument</title>
|
|
7
|
+
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
|
8
|
+
<!-- Google tag (gtag.js) -->
|
|
9
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J87606964E"></script>
|
|
10
|
+
<script>
|
|
11
|
+
window.dataLayer = window.dataLayer || [];
|
|
12
|
+
function gtag(){dataLayer.push(arguments);}
|
|
13
|
+
gtag('js', new Date());
|
|
14
|
+
|
|
15
|
+
gtag('config', 'G-J87606964E');
|
|
16
|
+
</script>
|
|
7
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
19
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,600;1,400&family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
|
|
@@ -533,7 +543,7 @@
|
|
|
533
543
|
|
|
534
544
|
<!-- Hero -->
|
|
535
545
|
<section class="hero">
|
|
536
|
-
<div class="hero-badge">v0.
|
|
546
|
+
<div class="hero-badge">v0.6.0 — now on PyPI</div>
|
|
537
547
|
<h1>
|
|
538
548
|
<span class="accent-green">Code</span> is<br>
|
|
539
549
|
the <span class="accent-mauve">instrument</span>
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>PyCodeDJ Manual</title>
|
|
7
|
+
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
|
8
|
+
<!-- Google tag (gtag.js) -->
|
|
9
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J87606964E"></script>
|
|
10
|
+
<script>
|
|
11
|
+
window.dataLayer = window.dataLayer || [];
|
|
12
|
+
function gtag(){dataLayer.push(arguments);}
|
|
13
|
+
gtag('js', new Date());
|
|
14
|
+
|
|
15
|
+
gtag('config', 'G-J87606964E');
|
|
16
|
+
</script>
|
|
7
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
19
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,600;1,400&family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
|
|
@@ -503,12 +513,12 @@ def bass(volume=0.4):
|
|
|
503
513
|
<hr>
|
|
504
514
|
</section>
|
|
505
515
|
<section id="s5"><h2>5. Watch mode — save to reload</h2>
|
|
506
|
-
<p>Running <code>pycodedj eval</code> manually each time gets tedious. The <code>watch</code> command evaluates all loops at startup and then <strong>re-evaluates
|
|
516
|
+
<p>Running <code>pycodedj eval</code> manually each time gets tedious. The <code>watch</code> command evaluates all loops at startup and then <strong>re-evaluates only changed loops on every save</strong>.</p>
|
|
507
517
|
<h3>Start watching</h3>
|
|
508
518
|
<pre data-lang="bash"><code>pycodedj watch examples/demo.py</code></pre>
|
|
509
519
|
<pre data-lang="text"><code>[pycodedj] watching demo.py — save to reload (Ctrl+C to stop)
|
|
510
520
|
[pycodedj] reloaded demo.py (3 loop(s))</code></pre>
|
|
511
|
-
<p>All loops play immediately. From here, just write code and save. Every save shows:</p>
|
|
521
|
+
<p>All loops play immediately. From here, just write code and save. Unchanged loops keep running without being resent. Every save shows:</p>
|
|
512
522
|
<pre data-lang="text"><code>[pycodedj] reloaded demo.py (3 loop(s))</code></pre>
|
|
513
523
|
<p><strong>Write code → Save → Sound changes.</strong> That's the live-coding workflow.</p>
|
|
514
524
|
<h3>Stop watching</h3>
|
|
@@ -1349,7 +1359,7 @@ s.boot;</code></pre>
|
|
|
1349
1359
|
pycodedj eval demo.py::melody --sc-host 192.168.1.10
|
|
1350
1360
|
pycodedj eval demo.py::pad --sc-port 57200</code></pre>
|
|
1351
1361
|
<h3><code>pycodedj watch</code></h3>
|
|
1352
|
-
<p>Watch a file
|
|
1362
|
+
<p>Watch a file, evaluate all loops at startup, and re-evaluate changed loops on every save. Stop with Ctrl+C.</p>
|
|
1353
1363
|
<pre data-lang="text"><code>pycodedj watch FILE [--sc-host HOST] [--sc-port PORT] [--debounce SECS]</code></pre>
|
|
1354
1364
|
<table>
|
|
1355
1365
|
<thead><tr>
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>PyCodeDJ マニュアル</title>
|
|
7
|
+
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
|
8
|
+
<!-- Google tag (gtag.js) -->
|
|
9
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J87606964E"></script>
|
|
10
|
+
<script>
|
|
11
|
+
window.dataLayer = window.dataLayer || [];
|
|
12
|
+
function gtag(){dataLayer.push(arguments);}
|
|
13
|
+
gtag('js', new Date());
|
|
14
|
+
|
|
15
|
+
gtag('config', 'G-J87606964E');
|
|
16
|
+
</script>
|
|
7
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
19
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,600;1,400&family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
|
|
@@ -601,7 +611,7 @@ def bass(volume=0.7): # ← 大きくする
|
|
|
601
611
|
<section id="s5">
|
|
602
612
|
<h2>5. 保存するだけで音が変わる — watch モード</h2>
|
|
603
613
|
|
|
604
|
-
<p>毎回 <code>pycodedj eval</code> を打つのは面倒です。<code>watch</code> コマンドを使うと、起動時に一度すべてのループを評価し、その後は<strong
|
|
614
|
+
<p>毎回 <code>pycodedj eval</code> を打つのは面倒です。<code>watch</code> コマンドを使うと、起動時に一度すべてのループを評価し、その後は<strong>ファイルを保存するだけで変更されたループだけが再評価されます</strong>。</p>
|
|
605
615
|
|
|
606
616
|
<h3>起動方法</h3>
|
|
607
617
|
|
|
@@ -610,7 +620,7 @@ def bass(volume=0.7): # ← 大きくする
|
|
|
610
620
|
<pre data-lang="text"><code>[pycodedj] watching demo.py — save to reload (Ctrl+C to stop)
|
|
611
621
|
[pycodedj] reloaded demo.py (3 loop(s))</code></pre>
|
|
612
622
|
|
|
613
|
-
<p
|
|
623
|
+
<p>起動直後に一度音が鳴ります。あとはエディタでコードを書いて保存するだけです。変更していないループは再送信されず、そのまま鳴り続けます。保存のたびに次のように出力されます。</p>
|
|
614
624
|
|
|
615
625
|
<pre data-lang="text"><code>[pycodedj] reloaded demo.py (3 loop(s))</code></pre>
|
|
616
626
|
|
|
@@ -318,7 +318,7 @@ def bass(volume=0.7): # 大きくする
|
|
|
318
318
|
|
|
319
319
|
## 5. watch モード — 保存するだけで音が変わる
|
|
320
320
|
|
|
321
|
-
毎回 `pycodedj eval` を打つのは手間です。`watch`
|
|
321
|
+
毎回 `pycodedj eval` を打つのは手間です。`watch` コマンドを使うと、起動時に全ループをまとめて評価し、その後は**ファイルを保存するだけで変更されたループだけが再評価**されます。
|
|
322
322
|
|
|
323
323
|
### 起動する
|
|
324
324
|
|
|
@@ -331,7 +331,7 @@ pycodedj watch examples/demo.py
|
|
|
331
331
|
[pycodedj] reloaded demo.py (3 loop(s))
|
|
332
332
|
```
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
起動直後に一度すべてのループが評価されて音が鳴ります。あとはエディタでコードを書いて保存するだけです。変更していないループは再送信されず、そのまま鳴り続けます。
|
|
335
335
|
|
|
336
336
|
```
|
|
337
337
|
[pycodedj] reloaded demo.py (3 loop(s))
|
|
@@ -301,7 +301,7 @@ The filter opened up and the modulation got faster. Deeper nesting = brighter so
|
|
|
301
301
|
|
|
302
302
|
## 5. Watch mode — save to reload
|
|
303
303
|
|
|
304
|
-
Running `pycodedj eval` manually each time gets tedious. The `watch` command evaluates all loops at startup and then **re-evaluates
|
|
304
|
+
Running `pycodedj eval` manually each time gets tedious. The `watch` command evaluates all loops at startup and then **re-evaluates only changed loops on every save**.
|
|
305
305
|
|
|
306
306
|
### Start watching
|
|
307
307
|
|
|
@@ -314,7 +314,7 @@ pycodedj watch examples/demo.py
|
|
|
314
314
|
[pycodedj] reloaded demo.py (3 loop(s))
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
All loops play immediately. From here, just write code and save. Every save shows:
|
|
317
|
+
All loops play immediately. From here, just write code and save. Unchanged loops keep running without being resent. Every save shows:
|
|
318
318
|
|
|
319
319
|
```
|
|
320
320
|
[pycodedj] reloaded demo.py (3 loop(s))
|
|
@@ -1081,7 +1081,7 @@ pycodedj eval demo.py::pad --sc-port 57200
|
|
|
1081
1081
|
|
|
1082
1082
|
### `pycodedj watch`
|
|
1083
1083
|
|
|
1084
|
-
Watch a file
|
|
1084
|
+
Watch a file, evaluate all loops at startup, and re-evaluate changed loops on every save. Stop with Ctrl+C.
|
|
1085
1085
|
|
|
1086
1086
|
```
|
|
1087
1087
|
pycodedj watch FILE [--sc-host HOST] [--sc-port PORT] [--debounce SECS]
|
|
@@ -24,7 +24,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
24
24
|
eval_p.add_argument("--sc-host", default="127.0.0.1", help="SuperCollider host")
|
|
25
25
|
eval_p.add_argument("--sc-port", default=57120, type=int, help="SuperCollider port")
|
|
26
26
|
|
|
27
|
-
watch_p = sub.add_parser("watch", help="Watch a file and re-eval
|
|
27
|
+
watch_p = sub.add_parser("watch", help="Watch a file and re-eval changed loops on save")
|
|
28
28
|
watch_p.add_argument("file", metavar="FILE", help="Source file to watch")
|
|
29
29
|
watch_p.add_argument("--sc-host", default="127.0.0.1", help="SuperCollider host")
|
|
30
30
|
watch_p.add_argument("--sc-port", default=57120, type=int, help="SuperCollider port")
|
|
@@ -6,13 +6,13 @@ import threading
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Callable
|
|
8
8
|
|
|
9
|
-
from .block_parser import parse_blocks
|
|
9
|
+
from .block_parser import LoopBlock, parse_blocks
|
|
10
10
|
from .engine import Engine
|
|
11
11
|
from .osc_bridge import OscError
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class _LoopFileHandler:
|
|
15
|
-
"""
|
|
15
|
+
"""ファイル変更イベントを受け取り、デバウンス後に変更ループを再評価する。"""
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
18
18
|
self,
|
|
@@ -27,6 +27,7 @@ class _LoopFileHandler:
|
|
|
27
27
|
self._debounce = debounce
|
|
28
28
|
self._timer: threading.Timer | None = None
|
|
29
29
|
self._active_names: set[str] = set()
|
|
30
|
+
self._blocks_by_name: dict[str, LoopBlock] = {}
|
|
30
31
|
|
|
31
32
|
def dispatch(self, src_path: str) -> None:
|
|
32
33
|
"""watchdog の on_modified から呼ぶ。対象ファイル以外は無視する。"""
|
|
@@ -50,17 +51,23 @@ class _LoopFileHandler:
|
|
|
50
51
|
if not result.ok:
|
|
51
52
|
sys.stderr.write(f"[pycodedj] syntax error in {self._path}: {result.error}\n")
|
|
52
53
|
return # _active_names を維持してループを継続する
|
|
53
|
-
|
|
54
|
+
current_blocks = {b.name: b for b in result.blocks}
|
|
55
|
+
current_names = set(current_blocks)
|
|
54
56
|
|
|
55
57
|
for name in self._active_names - current_names:
|
|
56
58
|
try:
|
|
57
59
|
self._engine.stop_loop(name)
|
|
58
60
|
except OscError:
|
|
59
61
|
pass
|
|
62
|
+
self._blocks_by_name.pop(name, None)
|
|
60
63
|
self._active_names = current_names
|
|
61
64
|
|
|
62
65
|
for block in result.blocks:
|
|
63
|
-
self.
|
|
66
|
+
if self._blocks_by_name.get(block.name) == block:
|
|
67
|
+
continue
|
|
68
|
+
params = self._engine.eval_block(block)
|
|
69
|
+
if params is not None:
|
|
70
|
+
self._blocks_by_name[block.name] = block
|
|
64
71
|
if self._on_eval is not None:
|
|
65
72
|
self._on_eval(self._path, len(result.blocks))
|
|
66
73
|
|
|
@@ -71,7 +78,7 @@ def watch(
|
|
|
71
78
|
debounce: float = 0.3,
|
|
72
79
|
on_eval: Callable[[str, int], None] | None = None,
|
|
73
80
|
) -> None:
|
|
74
|
-
"""
|
|
81
|
+
"""ファイルを監視し、変更のたびに変更されたループを再評価する。Ctrl+C で停止。
|
|
75
82
|
|
|
76
83
|
Args:
|
|
77
84
|
path: 監視するファイルのパス
|
|
@@ -19,6 +19,10 @@ def _make_handler(
|
|
|
19
19
|
|
|
20
20
|
_BASS_BLOCK = '@loop("bass")\ndef f(): pass\n'
|
|
21
21
|
_BASS_PAD_BLOCK = '@loop("bass")\ndef bass(): pass\n@loop("pad")\ndef pad(): pass\n'
|
|
22
|
+
_BASS_PAD_CHANGED_BLOCK = (
|
|
23
|
+
'@loop("bass")\ndef bass(): pass\n'
|
|
24
|
+
'@loop("pad")\ndef pad(volume=0.5): pass\n'
|
|
25
|
+
)
|
|
22
26
|
_BASS_MELODY_BLOCK = '@loop("bass")\ndef bass(): pass\n@loop("melody")\ndef melody(): pass\n'
|
|
23
27
|
|
|
24
28
|
|
|
@@ -69,6 +73,36 @@ def test_eval_all_loops_in_file(tmp_path: Path) -> None:
|
|
|
69
73
|
assert engine.eval_block.call_count == 2
|
|
70
74
|
|
|
71
75
|
|
|
76
|
+
def test_eval_only_changed_loop_on_reload(tmp_path: Path) -> None:
|
|
77
|
+
target = tmp_path / "demo.py"
|
|
78
|
+
target.write_text(_BASS_PAD_BLOCK)
|
|
79
|
+
handler, engine = _make_handler(path=str(target), debounce=0.0)
|
|
80
|
+
|
|
81
|
+
handler.eval_now()
|
|
82
|
+
engine.eval_block.reset_mock()
|
|
83
|
+
|
|
84
|
+
target.write_text(_BASS_PAD_CHANGED_BLOCK)
|
|
85
|
+
handler.dispatch(str(target))
|
|
86
|
+
time.sleep(0.05)
|
|
87
|
+
|
|
88
|
+
assert engine.eval_block.call_count == 1
|
|
89
|
+
assert engine.eval_block.call_args.args[0].name == "pad"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_eval_skips_unchanged_loops_on_reload(tmp_path: Path) -> None:
|
|
93
|
+
target = tmp_path / "demo.py"
|
|
94
|
+
target.write_text(_BASS_PAD_BLOCK)
|
|
95
|
+
handler, engine = _make_handler(path=str(target), debounce=0.0)
|
|
96
|
+
|
|
97
|
+
handler.eval_now()
|
|
98
|
+
engine.eval_block.reset_mock()
|
|
99
|
+
|
|
100
|
+
handler.dispatch(str(target))
|
|
101
|
+
time.sleep(0.05)
|
|
102
|
+
|
|
103
|
+
engine.eval_block.assert_not_called()
|
|
104
|
+
|
|
105
|
+
|
|
72
106
|
def test_eval_now_evaluates_without_file_event(tmp_path: Path) -> None:
|
|
73
107
|
target = tmp_path / "demo.py"
|
|
74
108
|
target.write_text(_BASS_BLOCK)
|
|
@@ -150,6 +184,7 @@ def test_adapter_on_moved_fires_eval(tmp_path: Path) -> None:
|
|
|
150
184
|
adapter = _make_watchdog_adapter(str(target), engine)
|
|
151
185
|
engine.reset_mock()
|
|
152
186
|
|
|
187
|
+
target.write_text('@loop("bass")\ndef bass(volume=0.5): pass\n')
|
|
153
188
|
event = MagicMock()
|
|
154
189
|
event.dest_path = str(target)
|
|
155
190
|
adapter.on_moved(event)
|
|
@@ -166,6 +201,7 @@ def test_adapter_on_created_fires_eval(tmp_path: Path) -> None:
|
|
|
166
201
|
adapter = _make_watchdog_adapter(str(target), engine)
|
|
167
202
|
engine.reset_mock()
|
|
168
203
|
|
|
204
|
+
target.write_text('@loop("bass")\ndef bass(volume=0.5): pass\n')
|
|
169
205
|
event = MagicMock()
|
|
170
206
|
event.src_path = str(target)
|
|
171
207
|
adapter.on_created(event)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|