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.
Files changed (27) hide show
  1. {mpytool-2.1.0 → mpytool-2.2.0}/PKG-INFO +41 -30
  2. {mpytool-2.1.0 → mpytool-2.2.0}/README.md +40 -29
  3. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn_serial.py +16 -6
  4. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpy.py +26 -3
  5. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpytool.py +407 -345
  6. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/utils.py +2 -3
  7. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/PKG-INFO +41 -30
  8. {mpytool-2.1.0 → mpytool-2.2.0}/pyproject.toml +1 -1
  9. {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_integration.py +155 -9
  10. {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_mpy.py +95 -0
  11. mpytool-2.2.0/tests/test_mpytool.py +1053 -0
  12. {mpytool-2.1.0 → mpytool-2.2.0}/tests/test_utils.py +4 -1
  13. mpytool-2.1.0/tests/test_mpytool.py +0 -363
  14. {mpytool-2.1.0 → mpytool-2.2.0}/LICENSE +0 -0
  15. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/__init__.py +0 -0
  16. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn.py +0 -0
  17. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/conn_socket.py +0 -0
  18. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/logger.py +0 -0
  19. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/mpy_comm.py +0 -0
  20. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool/terminal.py +0 -0
  21. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/SOURCES.txt +0 -0
  22. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/dependency_links.txt +0 -0
  23. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/entry_points.txt +0 -0
  24. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/requires.txt +0 -0
  25. {mpytool-2.1.0 → mpytool-2.2.0}/mpytool.egg-info/top_level.txt +0 -0
  26. {mpytool-2.1.0 → mpytool-2.2.0}/setup.cfg +0 -0
  27. {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.1.0
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 files:
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/ :/ # upload directory (creates /myapp/)
100
- $ mpytool cp myapp/ :/lib/ # upload directory into /lib/
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
- legacy upload/download (still available):
129
+ view file contents:
128
130
  ```
129
- $ mpytool put boot.py /
130
- $ mpytool get boot.py >> boot.py
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 a/b/c/d xyz/abc # create directories
136
- $ mpytool rm mydir # delete directory and contents
137
- $ mpytool rm mydir/ # delete contents only, keep directory
138
- $ mpytool rm / # delete everything on device
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 sreset # soft reset in raw REPL (clears RAM only)
145
- $ mpytool mreset # MCU reset (machine.reset, auto-reconnect)
146
- $ mpytool rtsreset # hardware reset via RTS signal (serial only)
147
- $ mpytool bootloader # enter bootloader (machine.bootloader)
148
- $ mpytool dtrboot # enter bootloader via DTR/RTS (ESP32 only)
149
- $ mpytool reset monitor # reset and monitor output
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 -- mreset # flash and reboot
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 :/ -- reset -- monitor
223
- $ mpytool delete old.py -- cp new.py :/ -- reset
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 files:
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/ :/ # upload directory (creates /myapp/)
86
- $ mpytool cp myapp/ :/lib/ # upload directory into /lib/
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
- legacy upload/download (still available):
115
+ view file contents:
114
116
  ```
115
- $ mpytool put boot.py /
116
- $ mpytool get boot.py >> boot.py
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 a/b/c/d xyz/abc # create directories
122
- $ mpytool rm mydir # delete directory and contents
123
- $ mpytool rm mydir/ # delete contents only, keep directory
124
- $ mpytool rm / # delete everything on device
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 sreset # soft reset in raw REPL (clears RAM only)
131
- $ mpytool mreset # MCU reset (machine.reset, auto-reconnect)
132
- $ mpytool rtsreset # hardware reset via RTS signal (serial only)
133
- $ mpytool bootloader # enter bootloader (machine.bootloader)
134
- $ mpytool dtrboot # enter bootloader via DTR/RTS (ESP32 only)
135
- $ mpytool reset monitor # reset and monitor output
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 -- mreset # flash and reboot
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 :/ -- reset -- monitor
209
- $ mpytool delete old.py -- cp new.py :/ -- reset
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 = 5 # seconds
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
- in_waiting = self._serial.in_waiting
32
- if in_waiting > 0:
33
- return self._serial.read(in_waiting)
34
- return None
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
- return self._serial.write(data)
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+='/'+d if c else d
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