mpytool 2.2.3__tar.gz → 2.3.0__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.
Files changed (35) hide show
  1. {mpytool-2.2.3 → mpytool-2.3.0}/PKG-INFO +175 -13
  2. {mpytool-2.2.3 → mpytool-2.3.0}/README.md +174 -12
  3. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/__init__.py +1 -1
  4. mpytool-2.3.0/mpytool/cmd_cp.py +847 -0
  5. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/conn.py +99 -13
  6. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/conn_serial.py +6 -1
  7. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/conn_socket.py +14 -1
  8. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/logger.py +11 -4
  9. mpytool-2.3.0/mpytool/mount.py +1053 -0
  10. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/mpy.py +321 -37
  11. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/mpy_comm.py +111 -56
  12. mpytool-2.3.0/mpytool/mpy_cross.py +173 -0
  13. mpytool-2.3.0/mpytool/mpytool.py +1518 -0
  14. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/terminal.py +6 -6
  15. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/terminal_unix.py +0 -2
  16. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/utils.py +6 -7
  17. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/PKG-INFO +175 -13
  18. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/SOURCES.txt +5 -0
  19. {mpytool-2.2.3 → mpytool-2.3.0}/pyproject.toml +1 -1
  20. mpytool-2.2.3/tests/test_mpytool.py → mpytool-2.3.0/tests/test_cmd_cp.py +373 -498
  21. {mpytool-2.2.3 → mpytool-2.3.0}/tests/test_integration.py +1189 -7
  22. mpytool-2.3.0/tests/test_mount.py +2062 -0
  23. {mpytool-2.2.3 → mpytool-2.3.0}/tests/test_mpy.py +86 -30
  24. mpytool-2.3.0/tests/test_mpytool.py +964 -0
  25. mpytool-2.2.3/mpytool/mpytool.py +0 -1601
  26. {mpytool-2.2.3 → mpytool-2.3.0}/LICENSE +0 -0
  27. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/speedtest.py +0 -0
  28. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool/terminal_win.py +0 -0
  29. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/dependency_links.txt +0 -0
  30. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/entry_points.txt +0 -0
  31. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/requires.txt +0 -0
  32. {mpytool-2.2.3 → mpytool-2.3.0}/mpytool.egg-info/top_level.txt +0 -0
  33. {mpytool-2.2.3 → mpytool-2.3.0}/setup.cfg +0 -0
  34. {mpytool-2.2.3 → mpytool-2.3.0}/tests/test_errors.py +0 -0
  35. {mpytool-2.2.3 → mpytool-2.3.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpytool
3
- Version: 2.2.3
3
+ Version: 2.3.0
4
4
  Summary: MPY tool - manage files on devices running MicroPython
5
5
  Author-email: Pavel Revak <pavel.revak@gmail.com>
6
6
  License-Expression: MIT
@@ -20,17 +20,31 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
20
20
 
21
21
  ## Features
22
22
 
23
- - **Fast file transfers** - optimized chunked transfer with automatic compression
24
- - **Skip unchanged files** - compares size + SHA256 hash, re-upload in <1s
25
- - **Auto-detect serial port** - no need to specify `-p` when only one device connected
26
- - **Robust REPL handling** - works reliably with USB-UART bridges (CP2102, CH340)
27
- - **Multiple reset options** - soft, MCU, hardware (RTS), bootloader entry
28
- - **General-purpose serial terminal** - `repl` and `monitor` work with any serial device
23
+ - **Fast file transfers** - optimized chunked transfer with
24
+ automatic compression
25
+ - **Skip unchanged files** - compares size + SHA256 hash,
26
+ re-upload in <1s
27
+ - **Auto-detect serial port** - no need to specify `-p` when only
28
+ one device connected
29
+ - **Robust REPL handling** - works reliably with USB-UART bridges
30
+ (CP2102, CH340)
31
+ - **Multiple reset options** - soft, MCU, hardware (RTS),
32
+ bootloader entry
33
+ - **General-purpose serial terminal** - `repl` and `monitor` work
34
+ with any serial device
35
+ - **Mount local directory** - VFS mount (read-only or read-write)
36
+ for development without uploading to flash, with transparent
37
+ `.mpy` compilation and auto-remount after soft reset
38
+ - **Virtual submounts** - `ln` links files/directories into
39
+ mounted VFS (PC-side only, no device changes)
29
40
  - **Python API** - suitable for IDE integration and automation
30
- - **Raw-paste mode** - flow-controlled code execution with reduced RAM usage (API)
41
+ - **Raw-paste mode** - flow-controlled code execution with reduced
42
+ RAM usage (API)
31
43
  - **Shell completion** - ZSH and Bash with remote path completion
32
44
  - **Network support** - connect over TCP
33
45
 
46
+ See [README_mpremote.md](README_mpremote.md) for detailed comparison with mpremote.
47
+
34
48
  ## Installation
35
49
 
36
50
  ```
@@ -103,6 +117,7 @@ $ mpytool cp :/main.py ./ # download file to current directory
103
117
  $ mpytool cp :/ ./backup/ # download entire device to backup/
104
118
  $ mpytool cp :/old.py :/new.py # copy file on device
105
119
  $ mpytool cp -f main.py :/ # force upload even if unchanged
120
+ $ mpytool cp -m myapp :/ # compile .py to .mpy via mpy-cross
106
121
  ```
107
122
 
108
123
  Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
@@ -114,11 +129,20 @@ Use `-f` or `--force` to upload all files regardless.
114
129
  $ mpytool cp -z main.py :/ # force compression (auto-detected by default)
115
130
  $ mpytool cp --no-compress data.bin :/ # disable compression
116
131
  $ mpytool -c 8K cp main.py :/ # set chunk size (512, 1K, 2K, 4K, 8K, 16K, 32K)
132
+ $ mpytool cp -m myapp :/ # compile .py to .mpy before upload
133
+ $ mpytool cp -fm myapp :/ # force upload + compile .mpy
117
134
  ```
118
135
 
119
136
  Compression is auto-detected based on device RAM and deflate module availability.
120
137
  Chunk size is auto-detected based on free RAM (larger chunks = faster transfer).
121
138
 
139
+ The `-m`/`--mpy` flag compiles `.py` files to `.mpy` bytecode using `mpy-cross` before upload.
140
+ Compiled files are cached in `__pycache__/` (recompiled only when source changes).
141
+ `boot.py` and `main.py` are always uploaded as `.py` (firmware requirement).
142
+ Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version differs
143
+ from device, automatically uses `-b` flag to target the correct bytecode version.
144
+ If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
145
+
122
146
  ### Move/rename on device
123
147
  ```
124
148
  $ mpytool mv :/old.py :/new.py # rename file
@@ -153,8 +177,24 @@ $ mpytool cd :.. # change to parent directory
153
177
  $ mpytool cd :/lib -- ls # change directory and list files
154
178
  ```
155
179
 
156
- ### Reset and REPL
180
+ ### Module search path (sys.path)
181
+ ```
182
+ $ mpytool path # show current sys.path
183
+ '', '/lib'
184
+ $ mpytool path : :/lib # replace entire sys.path
185
+ $ mpytool path -f :/custom # prepend to sys.path (remove duplicates)
186
+ $ mpytool path -a :/sdcard/lib # append to sys.path (remove duplicates)
187
+ $ mpytool path -d :/custom # delete from sys.path
188
+ $ mpytool path -f : :/lib :/extra # prepend multiple paths
189
+ $ mpytool cd :/app -- path -f : # combine with cd to set working dir
190
+ ```
191
+
192
+ Path semantics: `:` = empty string (CWD) in sys.path, `:/` = root directory. The `-f` and `-a` flags automatically remove duplicates (move existing paths to new position).
193
+
194
+ ### Stop, reset and REPL
157
195
  ```
196
+ $ mpytool stop # stop running program (Ctrl-C)
197
+ $ mpytool stop -- repl # stop and enter REPL
158
198
  $ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
159
199
  $ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
160
200
  $ mpytool reset --machine # MCU reset (machine.reset, auto-reconnect)
@@ -170,11 +210,18 @@ $ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
170
210
  ### Serial terminal and monitor (general purpose)
171
211
  ```
172
212
  $ mpytool repl # auto-detect port, 115200 baud
213
+ REPL (Ctrl+] to exit)
214
+
215
+ $ mpytool -v repl # verbose: shows CWD and sys.path before REPL
216
+ REPL (Ctrl+] to exit) CWD: / PATH: : :.frozen :/lib
217
+
173
218
  $ mpytool -p /dev/ttyUSB0 repl # specify port
174
219
  $ mpytool -b 9600 repl # specify baudrate
175
220
  $ mpytool -p /dev/ttyUSB0 -b 9600 monitor # monitor at 9600 baud
176
221
  ```
177
222
 
223
+ The `repl` command with `-v` flag displays current working directory (CWD) and `sys.path` before entering REPL, making it easy to see the device state after `mount`, `cd`, or `path` commands.
224
+
178
225
  Both `repl` and `monitor` can be used as general-purpose serial tools - not just for MicroPython devices. Use them to interact with any serial device (Arduino, ESP with custom firmware, GPS modules, etc.). When only one serial port is detected, it is used automatically. Default baudrate is 115200.
179
226
 
180
227
  ### Execute Python code on device
@@ -189,6 +236,121 @@ $ mpytool run script.py # run script (fire-and-forget)
189
236
  $ mpytool run script.py -- monitor # run script and capture output
190
237
  ```
191
238
 
239
+ ### Edit file on device
240
+ ```
241
+ $ mpytool edit :boot.py # edit file (uses $VISUAL or $EDITOR)
242
+ $ mpytool edit :/lib/config.py # absolute path
243
+ $ mpytool edit --editor vim :main.py # explicit editor
244
+ $ mpytool edit :newfile.py # create new file if doesn't exist
245
+ ```
246
+
247
+ Downloads file to a temp file, opens in editor, uploads back if changed.
248
+ Editor priority: `--editor` > `$VISUAL` > `$EDITOR` > error.
249
+
250
+ ### Device RTC (real-time clock)
251
+ ```
252
+ $ mpytool rtc # display current RTC
253
+ 2026-02-21 15:30:45
254
+
255
+ $ mpytool rtc --set # set RTC to local PC time
256
+ RTC set to 2026-02-21 15:30:45 (local)
257
+
258
+ $ mpytool rtc --utc # set RTC to UTC time
259
+ RTC set to 2026-02-21 14:30:45 (UTC)
260
+
261
+ $ mpytool rtc "2026-02-21 14:30:00" # set RTC to specific datetime
262
+ RTC set to 2026-02-21 14:30:00
263
+ ```
264
+
265
+ Flags: `-s`/`--set` and `-l`/`--local` both set local time, `-u`/`--utc` sets UTC.
266
+ Manual datetime format: `YYYY-MM-DD HH:MM:SS`.
267
+
268
+ ### Mount local directory on device
269
+ ```
270
+ $ mpytool mount ./src # mount ./src as /remote, auto-start REPL
271
+ Mounted ./src on /remote (readonly)
272
+ Changed CWD to /remote
273
+ REPL (Ctrl+] to exit)
274
+
275
+ $ mpytool -v mount ./src :/app # mount as /app instead of /remote (verbose)
276
+ Mounted ./src on /app (readonly)
277
+ Changed CWD to /app
278
+ REPL (Ctrl+] to exit) CWD: /app PATH: : :.frozen :/lib
279
+
280
+ $ mpytool mount -m ./src :/app # mount with automatic .mpy compilation
281
+ Mounted ./src on /app (readonly, .mpy compilation)
282
+ Changed CWD to /app
283
+ >>> import foo # imports /app/foo.mpy (compiled from ./src/foo.py)
284
+
285
+ $ mpytool mount -w ./workspace :/work # mount as read-write (can create/modify/delete files)
286
+ Mounted ./workspace on /work (read-write)
287
+ Changed CWD to /work
288
+ >>> f = open('/work/test.txt', 'w'); f.write('hello'); f.close() # creates local file
289
+ >>> import os; os.mkdir('/work/newdir') # creates local directory
290
+
291
+ $ mpytool mount ./src -- exec "import main" # mount and run code
292
+ $ mpytool mount ./src -- monitor # mount and monitor output
293
+ $ mpytool mount ./app :/app -- mount ./lib :/lib -- repl # multiple mounts (CWD = /app)
294
+ Mounted ./app on /app (readonly)
295
+ Changed CWD to /app
296
+ Mounted ./lib on /lib (readonly)
297
+ ```
298
+
299
+ Mounts a local directory on the device as a VFS. The device can read,
300
+ import and execute files from the local directory without uploading
301
+ them to flash. Changes to local files are immediately visible on
302
+ the device.
303
+
304
+ **Memory overhead:** The VFS agent uses ~4.2KB of RAM on the device
305
+ (154 lines of MicroPython code injected during mount).
306
+
307
+ **By default, mounts are read-only.** Use `-w` / `--writable` flag
308
+ to enable write support — the device can create, modify and delete
309
+ files in the mounted directory. All changes are written directly to
310
+ the local filesystem.
311
+
312
+ **`-m` / `--mpy` flag:** Enables transparent `.mpy` compilation.
313
+ When the device imports a module, the `.py` file is automatically
314
+ compiled to `.mpy` bytecode on-demand and served from cache
315
+ (`__pycache__/`). Benefits: faster imports, less RAM usage, support
316
+ for `@native`/`@viper` code with `-march`. Boot files (`boot.py`,
317
+ `main.py`) and empty files remain as `.py`. If compilation fails,
318
+ automatically falls back to `.py`. Prebuilt `.mpy` files have
319
+ priority over cache.
320
+
321
+ The first mount automatically changes CWD to the mount point (mpremote
322
+ compatibility). Subsequent mounts do not change CWD. Use `cd` command
323
+ to change working directory manually if needed. Multiple independent
324
+ (non-nested) mounts are supported.
325
+
326
+ After mount, mpytool automatically enters REPL (unless `monitor` or
327
+ `repl` follows). In REPL you can import modules from the mounted
328
+ directory, Ctrl+D triggers soft reset with automatic re-mount and CWD
329
+ restoration to the mount point.
330
+
331
+ **Best for:** Development, testing, prototyping, debugging. For bulk
332
+ file transfers (uploading/downloading many files), use `cp` command
333
+ instead — it's optimized for throughput.
334
+
335
+ **Security:** Paths are validated to prevent traversal outside mount
336
+ root. Symlinks are followed and checked. Write operations require
337
+ explicit `-w` flag.
338
+
339
+ See [Mount VFS Comparison](README_mpremote.md#mount-vfs-comparison) for detailed comparison with mpremote.
340
+
341
+ ### Link files into mounted VFS
342
+ ```
343
+ $ mpytool mount ./app :/ -- ln ./drivers :/lib/ -- repl # mount app, link drivers into /lib/
344
+ $ mpytool mount ./app :/ -- ln ./extra.py :/lib/ -- repl # link single file into /lib/
345
+ $ mpytool mount ./app :/ -- ln ./pkg :/lib/pkg -- repl # link as specific name
346
+ $ mpytool mount ./app :/ -- ln ./pkg/ :/lib/ -- repl # link package contents into /lib/
347
+ $ mpytool mount ./app :/ -- ln ./a.py ./b.py :/lib/ -- repl # link multiple sources
348
+ ```
349
+
350
+ Links local files or directories into an already mounted VFS as virtual submounts. Works entirely on the PC side — no changes needed on the device. Useful for composing a virtual filesystem from multiple local directories without copying.
351
+
352
+ Path semantics follow the same trailing `/` rules as `cp`: `ln dir :/lib/` creates `:/lib/dir/...`, `ln dir/ :/lib/` copies contents into `:/lib/...`. Destination must be an absolute device path with `:` prefix.
353
+
192
354
  ### Serial link speed test
193
355
  ```
194
356
  $ mpytool speedtest
@@ -248,9 +410,9 @@ $ mpytool flash erase vfs --full # full erase partition
248
410
 
249
411
  ### OTA firmware update (ESP32)
250
412
  ```
251
- $ mpytool ota firmware.app-bin # flash to next OTA partition
252
- $ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
253
- $ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
413
+ $ mpytool flash ota firmware.app-bin # flash to next OTA partition
414
+ $ mpytool flash ota firmware.app-bin -- reset --machine # flash and reboot
415
+ $ mpytool flash ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
254
416
  ```
255
417
 
256
418
  ### Multiple commands separated by `--`
@@ -378,7 +540,7 @@ For reporting bugs, please include `-ddd` output in the issue.
378
540
 
379
541
  ## Performance
380
542
 
381
- `mpytool` uses optimized chunked transfer with automatic compression, which allows copying files very quickly. See [README_BENCH.md](README_BENCH.md) for detailed benchmarks.
543
+ `mpytool` uses optimized chunked transfer with automatic compression, which allows copying files very quickly. See [README_bench.md](README_bench.md) for detailed benchmarks.
382
544
 
383
545
  ### Summary
384
546
 
@@ -6,17 +6,31 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
6
6
 
7
7
  ## Features
8
8
 
9
- - **Fast file transfers** - optimized chunked transfer with automatic compression
10
- - **Skip unchanged files** - compares size + SHA256 hash, re-upload in <1s
11
- - **Auto-detect serial port** - no need to specify `-p` when only one device connected
12
- - **Robust REPL handling** - works reliably with USB-UART bridges (CP2102, CH340)
13
- - **Multiple reset options** - soft, MCU, hardware (RTS), bootloader entry
14
- - **General-purpose serial terminal** - `repl` and `monitor` work with any serial device
9
+ - **Fast file transfers** - optimized chunked transfer with
10
+ automatic compression
11
+ - **Skip unchanged files** - compares size + SHA256 hash,
12
+ re-upload in <1s
13
+ - **Auto-detect serial port** - no need to specify `-p` when only
14
+ one device connected
15
+ - **Robust REPL handling** - works reliably with USB-UART bridges
16
+ (CP2102, CH340)
17
+ - **Multiple reset options** - soft, MCU, hardware (RTS),
18
+ bootloader entry
19
+ - **General-purpose serial terminal** - `repl` and `monitor` work
20
+ with any serial device
21
+ - **Mount local directory** - VFS mount (read-only or read-write)
22
+ for development without uploading to flash, with transparent
23
+ `.mpy` compilation and auto-remount after soft reset
24
+ - **Virtual submounts** - `ln` links files/directories into
25
+ mounted VFS (PC-side only, no device changes)
15
26
  - **Python API** - suitable for IDE integration and automation
16
- - **Raw-paste mode** - flow-controlled code execution with reduced RAM usage (API)
27
+ - **Raw-paste mode** - flow-controlled code execution with reduced
28
+ RAM usage (API)
17
29
  - **Shell completion** - ZSH and Bash with remote path completion
18
30
  - **Network support** - connect over TCP
19
31
 
32
+ See [README_mpremote.md](README_mpremote.md) for detailed comparison with mpremote.
33
+
20
34
  ## Installation
21
35
 
22
36
  ```
@@ -89,6 +103,7 @@ $ mpytool cp :/main.py ./ # download file to current directory
89
103
  $ mpytool cp :/ ./backup/ # download entire device to backup/
90
104
  $ mpytool cp :/old.py :/new.py # copy file on device
91
105
  $ mpytool cp -f main.py :/ # force upload even if unchanged
106
+ $ mpytool cp -m myapp :/ # compile .py to .mpy via mpy-cross
92
107
  ```
93
108
 
94
109
  Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
@@ -100,11 +115,20 @@ Use `-f` or `--force` to upload all files regardless.
100
115
  $ mpytool cp -z main.py :/ # force compression (auto-detected by default)
101
116
  $ mpytool cp --no-compress data.bin :/ # disable compression
102
117
  $ mpytool -c 8K cp main.py :/ # set chunk size (512, 1K, 2K, 4K, 8K, 16K, 32K)
118
+ $ mpytool cp -m myapp :/ # compile .py to .mpy before upload
119
+ $ mpytool cp -fm myapp :/ # force upload + compile .mpy
103
120
  ```
104
121
 
105
122
  Compression is auto-detected based on device RAM and deflate module availability.
106
123
  Chunk size is auto-detected based on free RAM (larger chunks = faster transfer).
107
124
 
125
+ The `-m`/`--mpy` flag compiles `.py` files to `.mpy` bytecode using `mpy-cross` before upload.
126
+ Compiled files are cached in `__pycache__/` (recompiled only when source changes).
127
+ `boot.py` and `main.py` are always uploaded as `.py` (firmware requirement).
128
+ Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version differs
129
+ from device, automatically uses `-b` flag to target the correct bytecode version.
130
+ If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
131
+
108
132
  ### Move/rename on device
109
133
  ```
110
134
  $ mpytool mv :/old.py :/new.py # rename file
@@ -139,8 +163,24 @@ $ mpytool cd :.. # change to parent directory
139
163
  $ mpytool cd :/lib -- ls # change directory and list files
140
164
  ```
141
165
 
142
- ### Reset and REPL
166
+ ### Module search path (sys.path)
167
+ ```
168
+ $ mpytool path # show current sys.path
169
+ '', '/lib'
170
+ $ mpytool path : :/lib # replace entire sys.path
171
+ $ mpytool path -f :/custom # prepend to sys.path (remove duplicates)
172
+ $ mpytool path -a :/sdcard/lib # append to sys.path (remove duplicates)
173
+ $ mpytool path -d :/custom # delete from sys.path
174
+ $ mpytool path -f : :/lib :/extra # prepend multiple paths
175
+ $ mpytool cd :/app -- path -f : # combine with cd to set working dir
176
+ ```
177
+
178
+ Path semantics: `:` = empty string (CWD) in sys.path, `:/` = root directory. The `-f` and `-a` flags automatically remove duplicates (move existing paths to new position).
179
+
180
+ ### Stop, reset and REPL
143
181
  ```
182
+ $ mpytool stop # stop running program (Ctrl-C)
183
+ $ mpytool stop -- repl # stop and enter REPL
144
184
  $ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
145
185
  $ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
146
186
  $ mpytool reset --machine # MCU reset (machine.reset, auto-reconnect)
@@ -156,11 +196,18 @@ $ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
156
196
  ### Serial terminal and monitor (general purpose)
157
197
  ```
158
198
  $ mpytool repl # auto-detect port, 115200 baud
199
+ REPL (Ctrl+] to exit)
200
+
201
+ $ mpytool -v repl # verbose: shows CWD and sys.path before REPL
202
+ REPL (Ctrl+] to exit) CWD: / PATH: : :.frozen :/lib
203
+
159
204
  $ mpytool -p /dev/ttyUSB0 repl # specify port
160
205
  $ mpytool -b 9600 repl # specify baudrate
161
206
  $ mpytool -p /dev/ttyUSB0 -b 9600 monitor # monitor at 9600 baud
162
207
  ```
163
208
 
209
+ The `repl` command with `-v` flag displays current working directory (CWD) and `sys.path` before entering REPL, making it easy to see the device state after `mount`, `cd`, or `path` commands.
210
+
164
211
  Both `repl` and `monitor` can be used as general-purpose serial tools - not just for MicroPython devices. Use them to interact with any serial device (Arduino, ESP with custom firmware, GPS modules, etc.). When only one serial port is detected, it is used automatically. Default baudrate is 115200.
165
212
 
166
213
  ### Execute Python code on device
@@ -175,6 +222,121 @@ $ mpytool run script.py # run script (fire-and-forget)
175
222
  $ mpytool run script.py -- monitor # run script and capture output
176
223
  ```
177
224
 
225
+ ### Edit file on device
226
+ ```
227
+ $ mpytool edit :boot.py # edit file (uses $VISUAL or $EDITOR)
228
+ $ mpytool edit :/lib/config.py # absolute path
229
+ $ mpytool edit --editor vim :main.py # explicit editor
230
+ $ mpytool edit :newfile.py # create new file if doesn't exist
231
+ ```
232
+
233
+ Downloads file to a temp file, opens in editor, uploads back if changed.
234
+ Editor priority: `--editor` > `$VISUAL` > `$EDITOR` > error.
235
+
236
+ ### Device RTC (real-time clock)
237
+ ```
238
+ $ mpytool rtc # display current RTC
239
+ 2026-02-21 15:30:45
240
+
241
+ $ mpytool rtc --set # set RTC to local PC time
242
+ RTC set to 2026-02-21 15:30:45 (local)
243
+
244
+ $ mpytool rtc --utc # set RTC to UTC time
245
+ RTC set to 2026-02-21 14:30:45 (UTC)
246
+
247
+ $ mpytool rtc "2026-02-21 14:30:00" # set RTC to specific datetime
248
+ RTC set to 2026-02-21 14:30:00
249
+ ```
250
+
251
+ Flags: `-s`/`--set` and `-l`/`--local` both set local time, `-u`/`--utc` sets UTC.
252
+ Manual datetime format: `YYYY-MM-DD HH:MM:SS`.
253
+
254
+ ### Mount local directory on device
255
+ ```
256
+ $ mpytool mount ./src # mount ./src as /remote, auto-start REPL
257
+ Mounted ./src on /remote (readonly)
258
+ Changed CWD to /remote
259
+ REPL (Ctrl+] to exit)
260
+
261
+ $ mpytool -v mount ./src :/app # mount as /app instead of /remote (verbose)
262
+ Mounted ./src on /app (readonly)
263
+ Changed CWD to /app
264
+ REPL (Ctrl+] to exit) CWD: /app PATH: : :.frozen :/lib
265
+
266
+ $ mpytool mount -m ./src :/app # mount with automatic .mpy compilation
267
+ Mounted ./src on /app (readonly, .mpy compilation)
268
+ Changed CWD to /app
269
+ >>> import foo # imports /app/foo.mpy (compiled from ./src/foo.py)
270
+
271
+ $ mpytool mount -w ./workspace :/work # mount as read-write (can create/modify/delete files)
272
+ Mounted ./workspace on /work (read-write)
273
+ Changed CWD to /work
274
+ >>> f = open('/work/test.txt', 'w'); f.write('hello'); f.close() # creates local file
275
+ >>> import os; os.mkdir('/work/newdir') # creates local directory
276
+
277
+ $ mpytool mount ./src -- exec "import main" # mount and run code
278
+ $ mpytool mount ./src -- monitor # mount and monitor output
279
+ $ mpytool mount ./app :/app -- mount ./lib :/lib -- repl # multiple mounts (CWD = /app)
280
+ Mounted ./app on /app (readonly)
281
+ Changed CWD to /app
282
+ Mounted ./lib on /lib (readonly)
283
+ ```
284
+
285
+ Mounts a local directory on the device as a VFS. The device can read,
286
+ import and execute files from the local directory without uploading
287
+ them to flash. Changes to local files are immediately visible on
288
+ the device.
289
+
290
+ **Memory overhead:** The VFS agent uses ~4.2KB of RAM on the device
291
+ (154 lines of MicroPython code injected during mount).
292
+
293
+ **By default, mounts are read-only.** Use `-w` / `--writable` flag
294
+ to enable write support — the device can create, modify and delete
295
+ files in the mounted directory. All changes are written directly to
296
+ the local filesystem.
297
+
298
+ **`-m` / `--mpy` flag:** Enables transparent `.mpy` compilation.
299
+ When the device imports a module, the `.py` file is automatically
300
+ compiled to `.mpy` bytecode on-demand and served from cache
301
+ (`__pycache__/`). Benefits: faster imports, less RAM usage, support
302
+ for `@native`/`@viper` code with `-march`. Boot files (`boot.py`,
303
+ `main.py`) and empty files remain as `.py`. If compilation fails,
304
+ automatically falls back to `.py`. Prebuilt `.mpy` files have
305
+ priority over cache.
306
+
307
+ The first mount automatically changes CWD to the mount point (mpremote
308
+ compatibility). Subsequent mounts do not change CWD. Use `cd` command
309
+ to change working directory manually if needed. Multiple independent
310
+ (non-nested) mounts are supported.
311
+
312
+ After mount, mpytool automatically enters REPL (unless `monitor` or
313
+ `repl` follows). In REPL you can import modules from the mounted
314
+ directory, Ctrl+D triggers soft reset with automatic re-mount and CWD
315
+ restoration to the mount point.
316
+
317
+ **Best for:** Development, testing, prototyping, debugging. For bulk
318
+ file transfers (uploading/downloading many files), use `cp` command
319
+ instead — it's optimized for throughput.
320
+
321
+ **Security:** Paths are validated to prevent traversal outside mount
322
+ root. Symlinks are followed and checked. Write operations require
323
+ explicit `-w` flag.
324
+
325
+ See [Mount VFS Comparison](README_mpremote.md#mount-vfs-comparison) for detailed comparison with mpremote.
326
+
327
+ ### Link files into mounted VFS
328
+ ```
329
+ $ mpytool mount ./app :/ -- ln ./drivers :/lib/ -- repl # mount app, link drivers into /lib/
330
+ $ mpytool mount ./app :/ -- ln ./extra.py :/lib/ -- repl # link single file into /lib/
331
+ $ mpytool mount ./app :/ -- ln ./pkg :/lib/pkg -- repl # link as specific name
332
+ $ mpytool mount ./app :/ -- ln ./pkg/ :/lib/ -- repl # link package contents into /lib/
333
+ $ mpytool mount ./app :/ -- ln ./a.py ./b.py :/lib/ -- repl # link multiple sources
334
+ ```
335
+
336
+ Links local files or directories into an already mounted VFS as virtual submounts. Works entirely on the PC side — no changes needed on the device. Useful for composing a virtual filesystem from multiple local directories without copying.
337
+
338
+ Path semantics follow the same trailing `/` rules as `cp`: `ln dir :/lib/` creates `:/lib/dir/...`, `ln dir/ :/lib/` copies contents into `:/lib/...`. Destination must be an absolute device path with `:` prefix.
339
+
178
340
  ### Serial link speed test
179
341
  ```
180
342
  $ mpytool speedtest
@@ -234,9 +396,9 @@ $ mpytool flash erase vfs --full # full erase partition
234
396
 
235
397
  ### OTA firmware update (ESP32)
236
398
  ```
237
- $ mpytool ota firmware.app-bin # flash to next OTA partition
238
- $ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
239
- $ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
399
+ $ mpytool flash ota firmware.app-bin # flash to next OTA partition
400
+ $ mpytool flash ota firmware.app-bin -- reset --machine # flash and reboot
401
+ $ mpytool flash ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
240
402
  ```
241
403
 
242
404
  ### Multiple commands separated by `--`
@@ -364,7 +526,7 @@ For reporting bugs, please include `-ddd` output in the issue.
364
526
 
365
527
  ## Performance
366
528
 
367
- `mpytool` uses optimized chunked transfer with automatic compression, which allows copying files very quickly. See [README_BENCH.md](README_BENCH.md) for detailed benchmarks.
529
+ `mpytool` uses optimized chunked transfer with automatic compression, which allows copying files very quickly. See [README_bench.md](README_bench.md) for detailed benchmarks.
368
530
 
369
531
  ### Summary
370
532
 
@@ -4,5 +4,5 @@ from mpytool.conn import ConnError, Timeout
4
4
  from mpytool.conn_serial import ConnSerial
5
5
  from mpytool.conn_socket import ConnSocket
6
6
  from mpytool.mpy_comm import MpyError, CmdError
7
- from mpytool.mpy import Mpy
7
+ from mpytool.mpy import Mpy, PathNotFound, FileNotFound, DirNotFound
8
8
  from mpytool.logger import SimpleColorLogger