mpytool 2.1.0__tar.gz → 2.2.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.
- {mpytool-2.1.0 → mpytool-2.2.0}/PKG-INFO +41 -30
- {mpytool-2.1.0 → mpytool-2.2.0}/README.md +40 -29
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn_serial.py +16 -6
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpy.py +26 -3
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpytool.py +407 -345
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/utils.py +2 -3
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/PKG-INFO +41 -30
- {mpytool-2.1.0 → mpytool-2.2.0}/pyproject.toml +1 -1
- {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_integration.py +155 -9
- {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_mpy.py +95 -0
- mpytool-2.2.0/tests/test_mpytool.py +1053 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_utils.py +4 -1
- mpytool-2.1.0/tests/test_mpytool.py +0 -363
- {mpytool-2.1.0 → mpytool-2.2.0}/LICENSE +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/__init__.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn_socket.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/logger.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpy_comm.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/terminal.py +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/SOURCES.txt +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/dependency_links.txt +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/entry_points.txt +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/requires.txt +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/top_level.txt +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/setup.cfg +0 -0
- {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_errors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mpytool
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -83,27 +83,29 @@ $ mpytool --help
|
|
|
83
83
|
|
|
84
84
|
list files:
|
|
85
85
|
```
|
|
86
|
-
$ mpytool -p /dev/ttyACM0 ls
|
|
87
|
-
$ mpytool -p /dev/ttyACM0 ls lib
|
|
86
|
+
$ mpytool -p /dev/ttyACM0 ls # list CWD (default)
|
|
87
|
+
$ mpytool -p /dev/ttyACM0 ls :/lib # list /lib
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
tree
|
|
90
|
+
tree:
|
|
91
91
|
```
|
|
92
|
-
$ mpytool -p /dev/ttyACM0 tree
|
|
92
|
+
$ mpytool -p /dev/ttyACM0 tree # tree of CWD (default)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
copy files (: prefix = device path):
|
|
96
96
|
```
|
|
97
97
|
$ mpytool cp main.py :/ # upload file to device root
|
|
98
98
|
$ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
|
|
99
|
-
$ mpytool cp myapp
|
|
100
|
-
$ mpytool cp myapp
|
|
99
|
+
$ mpytool cp myapp :/ # upload directory (creates :/myapp/)
|
|
100
|
+
$ mpytool cp myapp :/lib/ # upload directory into :/lib/ (creates :/lib/myapp/)
|
|
101
|
+
$ mpytool cp myapp/ :/lib/ # upload directory contents into :/lib/
|
|
101
102
|
$ mpytool cp :/main.py ./ # download file to current directory
|
|
102
103
|
$ mpytool cp :/ ./backup/ # download entire device to backup/
|
|
103
104
|
$ mpytool cp :/old.py :/new.py # copy file on device
|
|
104
105
|
$ mpytool cp -f main.py :/ # force upload even if unchanged
|
|
105
106
|
```
|
|
106
107
|
|
|
108
|
+
Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
|
|
107
109
|
Unchanged files are automatically skipped (compares size and SHA256 hash).
|
|
108
110
|
Use `-f` or `--force` to upload all files regardless.
|
|
109
111
|
|
|
@@ -124,29 +126,43 @@ $ mpytool mv :/file.py :/lib/ # move file to directory
|
|
|
124
126
|
$ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
|
|
125
127
|
```
|
|
126
128
|
|
|
127
|
-
|
|
129
|
+
view file contents:
|
|
128
130
|
```
|
|
129
|
-
$ mpytool
|
|
130
|
-
$ mpytool
|
|
131
|
+
$ mpytool cat :boot.py # print file from CWD
|
|
132
|
+
$ mpytool cat :/lib/module.py # print file with absolute path
|
|
131
133
|
```
|
|
132
134
|
|
|
133
|
-
make directory, delete files:
|
|
135
|
+
make directory, delete files (: prefix = device path):
|
|
134
136
|
```
|
|
135
|
-
$ mpytool mkdir
|
|
136
|
-
$ mpytool
|
|
137
|
-
$ mpytool rm
|
|
138
|
-
$ mpytool rm
|
|
137
|
+
$ mpytool mkdir :lib :data # create directories in CWD
|
|
138
|
+
$ mpytool mkdir :/lib/subdir # create with absolute path
|
|
139
|
+
$ mpytool rm :old.py # delete file in CWD
|
|
140
|
+
$ mpytool rm :mydir # delete directory and contents
|
|
141
|
+
$ mpytool rm :mydir/ # delete contents only, keep directory
|
|
142
|
+
$ mpytool rm : # delete everything in CWD
|
|
143
|
+
$ mpytool rm :/ # delete everything on device (root)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
current working directory:
|
|
147
|
+
```
|
|
148
|
+
$ mpytool pwd # print current directory
|
|
149
|
+
/
|
|
150
|
+
$ mpytool cd :/lib # change to /lib
|
|
151
|
+
$ mpytool cd :subdir # change to relative path (from CWD)
|
|
152
|
+
$ mpytool cd :.. # change to parent directory
|
|
153
|
+
$ mpytool cd :/lib -- ls # change directory and list files
|
|
139
154
|
```
|
|
140
155
|
|
|
141
156
|
reset and REPL:
|
|
142
157
|
```
|
|
143
158
|
$ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
|
|
144
|
-
$ mpytool
|
|
145
|
-
$ mpytool
|
|
146
|
-
$ mpytool
|
|
147
|
-
$ mpytool
|
|
148
|
-
$ mpytool
|
|
149
|
-
$ mpytool reset
|
|
159
|
+
$ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
|
|
160
|
+
$ mpytool reset --machine # MCU reset (machine.reset, auto-reconnect)
|
|
161
|
+
$ mpytool reset --machine -t 30 # MCU reset with 30s reconnect timeout
|
|
162
|
+
$ mpytool reset --rts # hardware reset via RTS signal (serial only)
|
|
163
|
+
$ mpytool reset --boot # enter bootloader (machine.bootloader)
|
|
164
|
+
$ mpytool reset --dtr-boot # enter bootloader via DTR/RTS (ESP32 only)
|
|
165
|
+
$ mpytool reset -- monitor # reset and monitor output
|
|
150
166
|
$ mpytool repl # enter REPL mode
|
|
151
167
|
$ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
152
168
|
```
|
|
@@ -214,13 +230,14 @@ $ mpytool flash erase vfs --full # full erase partition
|
|
|
214
230
|
OTA firmware update (ESP32):
|
|
215
231
|
```
|
|
216
232
|
$ mpytool ota firmware.app-bin # flash to next OTA partition
|
|
217
|
-
$ mpytool ota firmware.app-bin --
|
|
233
|
+
$ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
|
|
234
|
+
$ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
|
|
218
235
|
```
|
|
219
236
|
|
|
220
237
|
multiple commands separated by `--`:
|
|
221
238
|
```
|
|
222
|
-
$ mpytool cp main.py boot.py
|
|
223
|
-
$ mpytool
|
|
239
|
+
$ mpytool cp main.py boot.py : -- reset -- monitor
|
|
240
|
+
$ mpytool rm :old.py -- cp new.py : -- reset
|
|
224
241
|
```
|
|
225
242
|
|
|
226
243
|
auto-detect serial port (if only one device is connected):
|
|
@@ -265,12 +282,6 @@ show version:
|
|
|
265
282
|
$ mpytool -V
|
|
266
283
|
```
|
|
267
284
|
|
|
268
|
-
Command aliases:
|
|
269
|
-
- `dir` = `ls`
|
|
270
|
-
- `cat` = `get`
|
|
271
|
-
- `del`, `rm` = `delete`
|
|
272
|
-
- `follow` = `monitor`
|
|
273
|
-
|
|
274
285
|
## Python API
|
|
275
286
|
|
|
276
287
|
```python
|
|
@@ -69,27 +69,29 @@ $ mpytool --help
|
|
|
69
69
|
|
|
70
70
|
list files:
|
|
71
71
|
```
|
|
72
|
-
$ mpytool -p /dev/ttyACM0 ls
|
|
73
|
-
$ mpytool -p /dev/ttyACM0 ls lib
|
|
72
|
+
$ mpytool -p /dev/ttyACM0 ls # list CWD (default)
|
|
73
|
+
$ mpytool -p /dev/ttyACM0 ls :/lib # list /lib
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
tree
|
|
76
|
+
tree:
|
|
77
77
|
```
|
|
78
|
-
$ mpytool -p /dev/ttyACM0 tree
|
|
78
|
+
$ mpytool -p /dev/ttyACM0 tree # tree of CWD (default)
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
copy files (: prefix = device path):
|
|
82
82
|
```
|
|
83
83
|
$ mpytool cp main.py :/ # upload file to device root
|
|
84
84
|
$ mpytool cp main.py lib.py :/lib/ # upload multiple files to directory
|
|
85
|
-
$ mpytool cp myapp
|
|
86
|
-
$ mpytool cp myapp
|
|
85
|
+
$ mpytool cp myapp :/ # upload directory (creates :/myapp/)
|
|
86
|
+
$ mpytool cp myapp :/lib/ # upload directory into :/lib/ (creates :/lib/myapp/)
|
|
87
|
+
$ mpytool cp myapp/ :/lib/ # upload directory contents into :/lib/
|
|
87
88
|
$ mpytool cp :/main.py ./ # download file to current directory
|
|
88
89
|
$ mpytool cp :/ ./backup/ # download entire device to backup/
|
|
89
90
|
$ mpytool cp :/old.py :/new.py # copy file on device
|
|
90
91
|
$ mpytool cp -f main.py :/ # force upload even if unchanged
|
|
91
92
|
```
|
|
92
93
|
|
|
94
|
+
Path semantics: `:` = device CWD, `:/` = device root. Trailing `/` on source = copy contents only.
|
|
93
95
|
Unchanged files are automatically skipped (compares size and SHA256 hash).
|
|
94
96
|
Use `-f` or `--force` to upload all files regardless.
|
|
95
97
|
|
|
@@ -110,29 +112,43 @@ $ mpytool mv :/file.py :/lib/ # move file to directory
|
|
|
110
112
|
$ mpytool mv :/a.py :/b.py :/lib/ # move multiple files to directory
|
|
111
113
|
```
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
view file contents:
|
|
114
116
|
```
|
|
115
|
-
$ mpytool
|
|
116
|
-
$ mpytool
|
|
117
|
+
$ mpytool cat :boot.py # print file from CWD
|
|
118
|
+
$ mpytool cat :/lib/module.py # print file with absolute path
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
make directory, delete files:
|
|
121
|
+
make directory, delete files (: prefix = device path):
|
|
120
122
|
```
|
|
121
|
-
$ mpytool mkdir
|
|
122
|
-
$ mpytool
|
|
123
|
-
$ mpytool rm
|
|
124
|
-
$ mpytool rm
|
|
123
|
+
$ mpytool mkdir :lib :data # create directories in CWD
|
|
124
|
+
$ mpytool mkdir :/lib/subdir # create with absolute path
|
|
125
|
+
$ mpytool rm :old.py # delete file in CWD
|
|
126
|
+
$ mpytool rm :mydir # delete directory and contents
|
|
127
|
+
$ mpytool rm :mydir/ # delete contents only, keep directory
|
|
128
|
+
$ mpytool rm : # delete everything in CWD
|
|
129
|
+
$ mpytool rm :/ # delete everything on device (root)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
current working directory:
|
|
133
|
+
```
|
|
134
|
+
$ mpytool pwd # print current directory
|
|
135
|
+
/
|
|
136
|
+
$ mpytool cd :/lib # change to /lib
|
|
137
|
+
$ mpytool cd :subdir # change to relative path (from CWD)
|
|
138
|
+
$ mpytool cd :.. # change to parent directory
|
|
139
|
+
$ mpytool cd :/lib -- ls # change directory and list files
|
|
125
140
|
```
|
|
126
141
|
|
|
127
142
|
reset and REPL:
|
|
128
143
|
```
|
|
129
144
|
$ mpytool reset # soft reset (Ctrl-D, runs boot.py/main.py)
|
|
130
|
-
$ mpytool
|
|
131
|
-
$ mpytool
|
|
132
|
-
$ mpytool
|
|
133
|
-
$ mpytool
|
|
134
|
-
$ mpytool
|
|
135
|
-
$ mpytool reset
|
|
145
|
+
$ mpytool reset --raw # soft reset in raw REPL (clears RAM only)
|
|
146
|
+
$ mpytool reset --machine # MCU reset (machine.reset, auto-reconnect)
|
|
147
|
+
$ mpytool reset --machine -t 30 # MCU reset with 30s reconnect timeout
|
|
148
|
+
$ mpytool reset --rts # hardware reset via RTS signal (serial only)
|
|
149
|
+
$ mpytool reset --boot # enter bootloader (machine.bootloader)
|
|
150
|
+
$ mpytool reset --dtr-boot # enter bootloader via DTR/RTS (ESP32 only)
|
|
151
|
+
$ mpytool reset -- monitor # reset and monitor output
|
|
136
152
|
$ mpytool repl # enter REPL mode
|
|
137
153
|
$ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
138
154
|
```
|
|
@@ -200,13 +216,14 @@ $ mpytool flash erase vfs --full # full erase partition
|
|
|
200
216
|
OTA firmware update (ESP32):
|
|
201
217
|
```
|
|
202
218
|
$ mpytool ota firmware.app-bin # flash to next OTA partition
|
|
203
|
-
$ mpytool ota firmware.app-bin --
|
|
219
|
+
$ mpytool ota firmware.app-bin -- reset --machine # flash and reboot
|
|
220
|
+
$ mpytool ota firmware.app-bin -- reset --machine -t 30 # flash and reboot with 30s timeout
|
|
204
221
|
```
|
|
205
222
|
|
|
206
223
|
multiple commands separated by `--`:
|
|
207
224
|
```
|
|
208
|
-
$ mpytool cp main.py boot.py
|
|
209
|
-
$ mpytool
|
|
225
|
+
$ mpytool cp main.py boot.py : -- reset -- monitor
|
|
226
|
+
$ mpytool rm :old.py -- cp new.py : -- reset
|
|
210
227
|
```
|
|
211
228
|
|
|
212
229
|
auto-detect serial port (if only one device is connected):
|
|
@@ -251,12 +268,6 @@ show version:
|
|
|
251
268
|
$ mpytool -V
|
|
252
269
|
```
|
|
253
270
|
|
|
254
|
-
Command aliases:
|
|
255
|
-
- `dir` = `ls`
|
|
256
|
-
- `cat` = `get`
|
|
257
|
-
- `del`, `rm` = `delete`
|
|
258
|
-
- `follow` = `monitor`
|
|
259
|
-
|
|
260
271
|
## Python API
|
|
261
272
|
|
|
262
273
|
```python
|
|
@@ -6,7 +6,7 @@ import mpytool.conn as _conn
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class ConnSerial(_conn.Conn):
|
|
9
|
-
RECONNECT_TIMEOUT =
|
|
9
|
+
RECONNECT_TIMEOUT = 10 # seconds
|
|
10
10
|
|
|
11
11
|
def __init__(self, log=None, **serial_config):
|
|
12
12
|
super().__init__(log)
|
|
@@ -28,14 +28,24 @@ class ConnSerial(_conn.Conn):
|
|
|
28
28
|
|
|
29
29
|
def _read_available(self):
|
|
30
30
|
"""Read available data from serial port"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if self._serial is None:
|
|
32
|
+
raise _conn.ConnError("Not connected")
|
|
33
|
+
try:
|
|
34
|
+
in_waiting = self._serial.in_waiting
|
|
35
|
+
if in_waiting > 0:
|
|
36
|
+
return self._serial.read(in_waiting)
|
|
37
|
+
return None
|
|
38
|
+
except OSError as err:
|
|
39
|
+
raise _conn.ConnError(f"Connection lost: {err}") from err
|
|
35
40
|
|
|
36
41
|
def _write_raw(self, data):
|
|
37
42
|
"""Write data to serial port"""
|
|
38
|
-
|
|
43
|
+
if self._serial is None:
|
|
44
|
+
raise _conn.ConnError("Not connected")
|
|
45
|
+
try:
|
|
46
|
+
return self._serial.write(data)
|
|
47
|
+
except OSError as err:
|
|
48
|
+
raise _conn.ConnError(f"Connection lost: {err}") from err
|
|
39
49
|
|
|
40
50
|
def _is_usb_cdc(self):
|
|
41
51
|
"""Check if this is a USB-CDC port (native USB on ESP32-S/C)"""
|
|
@@ -65,7 +65,8 @@ def _mt_tree(p):
|
|
|
65
65
|
def _mt_mkdir(p):
|
|
66
66
|
p=p.rstrip('/');c='';f=1
|
|
67
67
|
for d in p.split('/'):
|
|
68
|
-
c
|
|
68
|
+
if not d:c='/';continue
|
|
69
|
+
c='/'+d if c=='/' else (c+'/'+d if c else d)
|
|
69
70
|
if f:
|
|
70
71
|
try:
|
|
71
72
|
if os.stat(c)[0]=={_ATTR_FILE}:return 1
|
|
@@ -295,6 +296,27 @@ def _mt_pfind(label):
|
|
|
295
296
|
self.import_module('os')
|
|
296
297
|
self._mpy_comm.exec(f"os.rename('{_escape_path(src)}', '{_escape_path(dst)}')")
|
|
297
298
|
|
|
299
|
+
def getcwd(self):
|
|
300
|
+
"""Get current working directory
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
current working directory path
|
|
304
|
+
"""
|
|
305
|
+
self.import_module('os')
|
|
306
|
+
return self._mpy_comm.exec_eval("repr(os.getcwd())")
|
|
307
|
+
|
|
308
|
+
def chdir(self, path):
|
|
309
|
+
"""Change current working directory
|
|
310
|
+
|
|
311
|
+
Arguments:
|
|
312
|
+
path: directory path to change to
|
|
313
|
+
"""
|
|
314
|
+
self.import_module('os')
|
|
315
|
+
try:
|
|
316
|
+
self._mpy_comm.exec(f"os.chdir('{_escape_path(path)}')")
|
|
317
|
+
except _mpy_comm.CmdError as err:
|
|
318
|
+
raise DirNotFound(path) from err
|
|
319
|
+
|
|
298
320
|
def hashfile(self, path):
|
|
299
321
|
"""Compute SHA256 hash of file
|
|
300
322
|
|
|
@@ -1124,11 +1146,12 @@ def _mt_pfind(label):
|
|
|
1124
1146
|
self._mpy_comm.soft_reset_raw()
|
|
1125
1147
|
self.reset_state()
|
|
1126
1148
|
|
|
1127
|
-
def machine_reset(self, reconnect=True):
|
|
1149
|
+
def machine_reset(self, reconnect=True, timeout=None):
|
|
1128
1150
|
"""MCU reset using machine.reset()
|
|
1129
1151
|
|
|
1130
1152
|
Arguments:
|
|
1131
1153
|
reconnect: if True, attempt to reconnect after reset
|
|
1154
|
+
timeout: reconnect timeout in seconds (None = default)
|
|
1132
1155
|
|
|
1133
1156
|
Returns:
|
|
1134
1157
|
True if reconnected successfully, False otherwise
|
|
@@ -1139,7 +1162,7 @@ def _mt_pfind(label):
|
|
|
1139
1162
|
self._conn.write(b"import machine; machine.reset()\x04")
|
|
1140
1163
|
self.reset_state()
|
|
1141
1164
|
if reconnect:
|
|
1142
|
-
self._conn.reconnect()
|
|
1165
|
+
self._conn.reconnect(timeout=timeout)
|
|
1143
1166
|
return True
|
|
1144
1167
|
return False
|
|
1145
1168
|
|