mpytool 2.3.6__tar.gz → 2.5.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.3.6 → mpytool-2.5.0}/PKG-INFO +78 -4
- {mpytool-2.3.6 → mpytool-2.5.0}/README.md +77 -3
- {mpytool-2.3.6 → mpytool-2.5.0}/README_API.md +53 -5
- {mpytool-2.3.6 → mpytool-2.5.0}/completions/_mpytool +11 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/completions/mpytool.bash +11 -0
- mpytool-2.5.0/mpytool/_version.py +24 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/conn.py +7 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/conn_serial.py +13 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/conn_socket.py +29 -3
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/mpy.py +23 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/mpytool.py +185 -8
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/terminal.py +15 -1
- mpytool-2.5.0/mpytool/terminal_unix.py +65 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/terminal_win.py +3 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/PKG-INFO +78 -4
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/SOURCES.txt +3 -0
- mpytool-2.5.0/mpytool.egg-info/scm_file_list.json +43 -0
- mpytool-2.5.0/mpytool.egg-info/scm_version.json +8 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_cli.py +27 -3
- mpytool-2.5.0/tests/test_conn_socket.py +307 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_integration.py +4 -2
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_mpy.py +55 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_mpytool.py +132 -0
- mpytool-2.3.6/mpytool/_version.py +0 -34
- mpytool-2.3.6/mpytool/terminal_unix.py +0 -43
- {mpytool-2.3.6 → mpytool-2.5.0}/.github/workflows/integration.yml +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/.github/workflows/publish.yml +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/.github/workflows/tests.yml +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/.gitignore +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/LICENSE +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/README_bench.md +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/README_mpremote.md +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/__init__.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/cmd_cp.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/logger.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/mount.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/mpy_comm.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/mpy_cross.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/speedtest.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool/utils.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/dependency_links.txt +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/entry_points.txt +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/requires.txt +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/mpytool.egg-info/top_level.txt +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/pyproject.toml +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/setup.cfg +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/__init__.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_cmd_cp.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_errors.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_helpers.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_mount.py +0 -0
- {mpytool-2.3.6 → mpytool-2.5.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mpytool
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: MPY tool - manage files on devices running MicroPython
|
|
5
5
|
Author-email: Pavel Revak <pavelrevak@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -31,6 +31,11 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
|
|
|
31
31
|
(CP2102, CH340)
|
|
32
32
|
- **Multiple reset options** - soft, MCU, hardware (RTS),
|
|
33
33
|
bootloader entry
|
|
34
|
+
- **Wipe device** - erase the whole filesystem and machine-reset
|
|
35
|
+
for a clean slate (handy at the start of automated tests)
|
|
36
|
+
- **Deploy projects** - `sync` uploads a whole project to the device
|
|
37
|
+
using a `.mpyproject` config file (deploy map, excludes, `.mpy`
|
|
38
|
+
compilation)
|
|
34
39
|
- **General-purpose serial terminal** - `repl` and `monitor` work
|
|
35
40
|
with any serial device
|
|
36
41
|
- **Mount local directory** - VFS mount (read-only or read-write)
|
|
@@ -42,7 +47,7 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
|
|
|
42
47
|
- **Raw-paste mode** - flow-controlled code execution with reduced
|
|
43
48
|
RAM usage (API)
|
|
44
49
|
- **Shell completion** - ZSH and Bash with remote path completion
|
|
45
|
-
- **Network support** - connect over TCP
|
|
50
|
+
- **Network support** - connect over TCP with optional SSL/TLS
|
|
46
51
|
- **Full Unicode support** - handles Unicode in file names and content
|
|
47
52
|
|
|
48
53
|
See [README_mpremote.md](README_mpremote.md) for detailed comparison with mpremote.
|
|
@@ -166,6 +171,47 @@ Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version dif
|
|
|
166
171
|
from device, automatically uses `-b` flag to target the correct bytecode version.
|
|
167
172
|
If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
|
|
168
173
|
|
|
174
|
+
### Deploy project (sync)
|
|
175
|
+
|
|
176
|
+
`sync` uploads a whole project to the device based on a `.mpyproject`
|
|
177
|
+
configuration file. It is the command-line counterpart of the deploy feature in
|
|
178
|
+
the [Sublime Text plugin](https://github.com/cortexm/mpytool-sublime) and shares
|
|
179
|
+
the same `.mpyproject` format. Unlike the plugin it is non-interactive: when more
|
|
180
|
+
than one device is connected, pass `-p` to pick the port.
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
$ mpytool sync # deploy project in CWD (.mpyproject searched upward)
|
|
184
|
+
$ mpytool sync ~/projects/blink # deploy a project in another directory
|
|
185
|
+
$ mpytool -p /dev/ttyACM0 sync # pick port explicitly (multiple devices)
|
|
186
|
+
$ mpytool sync -- reset -- monitor # deploy, then reset and watch output
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The `.mpyproject` file (JSON, trailing commas tolerated) describes what to upload:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"name": "blink",
|
|
194
|
+
"compile": true,
|
|
195
|
+
"deploy": {
|
|
196
|
+
"": ["./"],
|
|
197
|
+
"/lib/": ["../shared/utils.py"],
|
|
198
|
+
"/main.py": "src/main_dev.py"
|
|
199
|
+
},
|
|
200
|
+
"exclude": ["*.bak", "tests"]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
- `deploy` maps a device destination to its source(s), relative to the project
|
|
205
|
+
root. A key of `""` or one ending in `/` is a **directory** (value is a list of
|
|
206
|
+
sources); any other key is a **rename** (value is a single source file).
|
|
207
|
+
Defaults to `{"": ["./"]}` (whole project to root) when omitted.
|
|
208
|
+
- `compile: true` compiles `.py` to `.mpy` (same as `cp -m`).
|
|
209
|
+
- `exclude` adds ignore patterns on top of the defaults (`__pycache__`, `*.pyc`,
|
|
210
|
+
dotfiles). Combined with global `-e` patterns.
|
|
211
|
+
|
|
212
|
+
The connection fields (`port`, `address`) used by the Sublime plugin are ignored
|
|
213
|
+
by the CLI — use the global `-p` / `-a` options instead.
|
|
214
|
+
|
|
169
215
|
### Move/rename on device
|
|
170
216
|
```
|
|
171
217
|
$ mpytool mv :/old.py :/new.py # rename file
|
|
@@ -270,6 +316,30 @@ $ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
|
270
316
|
SLEEP 2.0s
|
|
271
317
|
```
|
|
272
318
|
|
|
319
|
+
### Wipe device (erase all files + machine reset)
|
|
320
|
+
```
|
|
321
|
+
$ mpytool wipe # delete everything from / then machine.reset()
|
|
322
|
+
WIPE
|
|
323
|
+
rm /boot.py
|
|
324
|
+
rm /main.py
|
|
325
|
+
rm /lib
|
|
326
|
+
reset
|
|
327
|
+
|
|
328
|
+
$ mpytool wipe -- ls # wipe, reconnect, then list (now empty)
|
|
329
|
+
WIPE
|
|
330
|
+
rm /main.py
|
|
331
|
+
reset, reconnected
|
|
332
|
+
LS:
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
`wipe` erases the whole filesystem (recursive delete of `/`) and then does a
|
|
336
|
+
full `machine.reset()` — a soft reset would leave open sockets, WiFi and
|
|
337
|
+
mounted VFS alive, so machine reset is used for a true clean slate. It **never
|
|
338
|
+
prompts** (mpytool has no interactive mode); `-y`/`--yes` is accepted as a
|
|
339
|
+
no-op. When `wipe` is the last command it just resets and exits; when more
|
|
340
|
+
commands follow it reconnects first (use `-t SECONDS` for the reconnect
|
|
341
|
+
timeout). See [README_API.md](README_API.md) for `Mpy.wipe()`.
|
|
342
|
+
|
|
273
343
|
### Serial terminal and monitor (general purpose)
|
|
274
344
|
```
|
|
275
345
|
$ mpytool repl # auto-detect port, 115200 baud
|
|
@@ -537,8 +607,12 @@ TREE: /
|
|
|
537
607
|
|
|
538
608
|
### Connect over network (TCP, default port 23)
|
|
539
609
|
```
|
|
540
|
-
$ mpytool -a 192.168.1.100 ls
|
|
541
|
-
$ mpytool -a 192.168.1.100:8266 tree
|
|
610
|
+
$ mpytool -a 192.168.1.100 ls # plain TCP
|
|
611
|
+
$ mpytool -a 192.168.1.100:8266 tree # custom port
|
|
612
|
+
$ mpytool -a 192.168.1.100:8023 --ssl ls # SSL/TLS connection
|
|
613
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-ca ca.pem ls # SSL with custom CA
|
|
614
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-no-hostname ls # SSL, skip hostname check
|
|
615
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-no-verify ls # SSL, skip all verification
|
|
542
616
|
```
|
|
543
617
|
|
|
544
618
|
### Set baudrate (default 115200)
|
|
@@ -16,6 +16,11 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
|
|
|
16
16
|
(CP2102, CH340)
|
|
17
17
|
- **Multiple reset options** - soft, MCU, hardware (RTS),
|
|
18
18
|
bootloader entry
|
|
19
|
+
- **Wipe device** - erase the whole filesystem and machine-reset
|
|
20
|
+
for a clean slate (handy at the start of automated tests)
|
|
21
|
+
- **Deploy projects** - `sync` uploads a whole project to the device
|
|
22
|
+
using a `.mpyproject` config file (deploy map, excludes, `.mpy`
|
|
23
|
+
compilation)
|
|
19
24
|
- **General-purpose serial terminal** - `repl` and `monitor` work
|
|
20
25
|
with any serial device
|
|
21
26
|
- **Mount local directory** - VFS mount (read-only or read-write)
|
|
@@ -27,7 +32,7 @@ It is an alternative to the official [mpremote](https://docs.micropython.org/en/
|
|
|
27
32
|
- **Raw-paste mode** - flow-controlled code execution with reduced
|
|
28
33
|
RAM usage (API)
|
|
29
34
|
- **Shell completion** - ZSH and Bash with remote path completion
|
|
30
|
-
- **Network support** - connect over TCP
|
|
35
|
+
- **Network support** - connect over TCP with optional SSL/TLS
|
|
31
36
|
- **Full Unicode support** - handles Unicode in file names and content
|
|
32
37
|
|
|
33
38
|
See [README_mpremote.md](README_mpremote.md) for detailed comparison with mpremote.
|
|
@@ -151,6 +156,47 @@ Requires `mpy-cross` in PATH (`pip install mpy-cross`). If mpy-cross version dif
|
|
|
151
156
|
from device, automatically uses `-b` flag to target the correct bytecode version.
|
|
152
157
|
If `mpy-cross` is not found, falls back to uploading `.py` with a warning.
|
|
153
158
|
|
|
159
|
+
### Deploy project (sync)
|
|
160
|
+
|
|
161
|
+
`sync` uploads a whole project to the device based on a `.mpyproject`
|
|
162
|
+
configuration file. It is the command-line counterpart of the deploy feature in
|
|
163
|
+
the [Sublime Text plugin](https://github.com/cortexm/mpytool-sublime) and shares
|
|
164
|
+
the same `.mpyproject` format. Unlike the plugin it is non-interactive: when more
|
|
165
|
+
than one device is connected, pass `-p` to pick the port.
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
$ mpytool sync # deploy project in CWD (.mpyproject searched upward)
|
|
169
|
+
$ mpytool sync ~/projects/blink # deploy a project in another directory
|
|
170
|
+
$ mpytool -p /dev/ttyACM0 sync # pick port explicitly (multiple devices)
|
|
171
|
+
$ mpytool sync -- reset -- monitor # deploy, then reset and watch output
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The `.mpyproject` file (JSON, trailing commas tolerated) describes what to upload:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"name": "blink",
|
|
179
|
+
"compile": true,
|
|
180
|
+
"deploy": {
|
|
181
|
+
"": ["./"],
|
|
182
|
+
"/lib/": ["../shared/utils.py"],
|
|
183
|
+
"/main.py": "src/main_dev.py"
|
|
184
|
+
},
|
|
185
|
+
"exclude": ["*.bak", "tests"]
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- `deploy` maps a device destination to its source(s), relative to the project
|
|
190
|
+
root. A key of `""` or one ending in `/` is a **directory** (value is a list of
|
|
191
|
+
sources); any other key is a **rename** (value is a single source file).
|
|
192
|
+
Defaults to `{"": ["./"]}` (whole project to root) when omitted.
|
|
193
|
+
- `compile: true` compiles `.py` to `.mpy` (same as `cp -m`).
|
|
194
|
+
- `exclude` adds ignore patterns on top of the defaults (`__pycache__`, `*.pyc`,
|
|
195
|
+
dotfiles). Combined with global `-e` patterns.
|
|
196
|
+
|
|
197
|
+
The connection fields (`port`, `address`) used by the Sublime plugin are ignored
|
|
198
|
+
by the CLI — use the global `-p` / `-a` options instead.
|
|
199
|
+
|
|
154
200
|
### Move/rename on device
|
|
155
201
|
```
|
|
156
202
|
$ mpytool mv :/old.py :/new.py # rename file
|
|
@@ -255,6 +301,30 @@ $ mpytool sleep 2 # sleep for 2 seconds (useful between commands)
|
|
|
255
301
|
SLEEP 2.0s
|
|
256
302
|
```
|
|
257
303
|
|
|
304
|
+
### Wipe device (erase all files + machine reset)
|
|
305
|
+
```
|
|
306
|
+
$ mpytool wipe # delete everything from / then machine.reset()
|
|
307
|
+
WIPE
|
|
308
|
+
rm /boot.py
|
|
309
|
+
rm /main.py
|
|
310
|
+
rm /lib
|
|
311
|
+
reset
|
|
312
|
+
|
|
313
|
+
$ mpytool wipe -- ls # wipe, reconnect, then list (now empty)
|
|
314
|
+
WIPE
|
|
315
|
+
rm /main.py
|
|
316
|
+
reset, reconnected
|
|
317
|
+
LS:
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`wipe` erases the whole filesystem (recursive delete of `/`) and then does a
|
|
321
|
+
full `machine.reset()` — a soft reset would leave open sockets, WiFi and
|
|
322
|
+
mounted VFS alive, so machine reset is used for a true clean slate. It **never
|
|
323
|
+
prompts** (mpytool has no interactive mode); `-y`/`--yes` is accepted as a
|
|
324
|
+
no-op. When `wipe` is the last command it just resets and exits; when more
|
|
325
|
+
commands follow it reconnects first (use `-t SECONDS` for the reconnect
|
|
326
|
+
timeout). See [README_API.md](README_API.md) for `Mpy.wipe()`.
|
|
327
|
+
|
|
258
328
|
### Serial terminal and monitor (general purpose)
|
|
259
329
|
```
|
|
260
330
|
$ mpytool repl # auto-detect port, 115200 baud
|
|
@@ -522,8 +592,12 @@ TREE: /
|
|
|
522
592
|
|
|
523
593
|
### Connect over network (TCP, default port 23)
|
|
524
594
|
```
|
|
525
|
-
$ mpytool -a 192.168.1.100 ls
|
|
526
|
-
$ mpytool -a 192.168.1.100:8266 tree
|
|
595
|
+
$ mpytool -a 192.168.1.100 ls # plain TCP
|
|
596
|
+
$ mpytool -a 192.168.1.100:8266 tree # custom port
|
|
597
|
+
$ mpytool -a 192.168.1.100:8023 --ssl ls # SSL/TLS connection
|
|
598
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-ca ca.pem ls # SSL with custom CA
|
|
599
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-no-hostname ls # SSL, skip hostname check
|
|
600
|
+
$ mpytool -a 192.168.1.100:8023 --ssl-no-verify ls # SSL, skip all verification
|
|
527
601
|
```
|
|
528
602
|
|
|
529
603
|
### Set baudrate (default 115200)
|
|
@@ -107,25 +107,44 @@ conn.reconnect(timeout=5) # Reconnect after device reset (USB-CDC)
|
|
|
107
107
|
|
|
108
108
|
### ConnSocket
|
|
109
109
|
|
|
110
|
-
TCP socket connection for network-connected devices.
|
|
110
|
+
TCP socket connection for network-connected devices with optional SSL/TLS.
|
|
111
111
|
|
|
112
112
|
```python
|
|
113
|
-
mpytool.ConnSocket(address, log=None
|
|
113
|
+
mpytool.ConnSocket(address, log=None, ssl=False, ssl_verify=True,
|
|
114
|
+
ssl_ca=None, ssl_check_hostname=True)
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
**Parameters:**
|
|
117
118
|
- `address` (str): Host address with optional port (e.g., `192.168.1.100` or `192.168.1.100:8266`)
|
|
118
119
|
- `log`: Optional logger instance
|
|
120
|
+
- `ssl` (bool): Enable SSL/TLS encryption (default: `False`)
|
|
121
|
+
- `ssl_verify` (bool): Verify server certificate (default: `True`). Set to `False` for self-signed certs.
|
|
122
|
+
- `ssl_ca` (str): Path to CA certificate file for custom certificate authorities
|
|
123
|
+
- `ssl_check_hostname` (bool): Verify hostname matches certificate (default: `True`). Set to `False` when one cert is shared across multiple hosts.
|
|
119
124
|
|
|
120
125
|
Default port is 23 if not specified.
|
|
121
126
|
|
|
122
|
-
**
|
|
127
|
+
**Examples:**
|
|
123
128
|
```python
|
|
129
|
+
# Plain TCP
|
|
124
130
|
conn = mpytool.ConnSocket(address='192.168.1.100:8266')
|
|
131
|
+
|
|
132
|
+
# SSL with system CA
|
|
133
|
+
conn = mpytool.ConnSocket(address='server:8023', ssl=True)
|
|
134
|
+
|
|
135
|
+
# SSL with custom CA
|
|
136
|
+
conn = mpytool.ConnSocket(address='server:8023', ssl=True, ssl_ca='ca.pem')
|
|
137
|
+
|
|
138
|
+
# SSL, verify cert but skip hostname check
|
|
139
|
+
conn = mpytool.ConnSocket(address='192.168.1.100:8023',
|
|
140
|
+
ssl=True, ssl_ca='ca.pem', ssl_check_hostname=False)
|
|
141
|
+
|
|
142
|
+
# SSL without any verification (insecure)
|
|
143
|
+
conn = mpytool.ConnSocket(address='server:8023', ssl=True, ssl_verify=False)
|
|
125
144
|
```
|
|
126
145
|
|
|
127
146
|
**Raises:**
|
|
128
|
-
- `ConnError`: If unable to connect
|
|
147
|
+
- `ConnError`: If unable to connect or SSL handshake fails
|
|
129
148
|
|
|
130
149
|
### Common Connection Methods
|
|
131
150
|
|
|
@@ -873,7 +892,7 @@ Soft reset in raw REPL mode (clears RAM only, doesn't run boot.py/main.py).
|
|
|
873
892
|
mpy.soft_reset_raw()
|
|
874
893
|
```
|
|
875
894
|
|
|
876
|
-
#### machine_reset(reconnect=True)
|
|
895
|
+
#### machine_reset(reconnect=True, timeout=None)
|
|
877
896
|
|
|
878
897
|
MCU reset using `machine.reset()`.
|
|
879
898
|
|
|
@@ -883,6 +902,7 @@ mpy.machine_reset(reconnect=True)
|
|
|
883
902
|
|
|
884
903
|
**Parameters:**
|
|
885
904
|
- `reconnect` (bool): If True, attempt to reconnect after reset (for USB-CDC ports)
|
|
905
|
+
- `timeout` (int): Reconnect timeout in seconds (None = default)
|
|
886
906
|
|
|
887
907
|
#### hard_reset()
|
|
888
908
|
|
|
@@ -895,6 +915,34 @@ mpy.hard_reset()
|
|
|
895
915
|
**Raises:**
|
|
896
916
|
- `NotImplementedError`: If connection doesn't support hardware reset
|
|
897
917
|
|
|
918
|
+
#### wipe(reconnect=True, timeout=None, on_delete=None)
|
|
919
|
+
|
|
920
|
+
Erase all files from the filesystem root, then `machine.reset()` the device.
|
|
921
|
+
|
|
922
|
+
Recursively deletes every entry in `/`, then performs a full machine reset so
|
|
923
|
+
runtime state (open sockets, WiFi, mounted VFS) is dropped too — a soft reset
|
|
924
|
+
would leave those alive. Intended for a clean slate, e.g. at the start of
|
|
925
|
+
integration tests.
|
|
926
|
+
|
|
927
|
+
```python
|
|
928
|
+
# Clean slate at the start of a test
|
|
929
|
+
mpy.wipe()
|
|
930
|
+
|
|
931
|
+
# Wipe without waiting to reconnect
|
|
932
|
+
mpy.wipe(reconnect=False)
|
|
933
|
+
|
|
934
|
+
# Show what gets removed
|
|
935
|
+
mpy.wipe(on_delete=lambda path: print(f"removing {path}"))
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
**Parameters:**
|
|
939
|
+
- `reconnect` (bool): If True, wait for the device to come back after reset
|
|
940
|
+
- `timeout` (int): Reconnect timeout in seconds (None = default)
|
|
941
|
+
- `on_delete` (callable): Optional `callback(path)` invoked before each delete
|
|
942
|
+
|
|
943
|
+
**Returns:**
|
|
944
|
+
- `bool`: True if reconnected after reset, False otherwise
|
|
945
|
+
|
|
898
946
|
### Flash/Partition Methods
|
|
899
947
|
|
|
900
948
|
#### partitions()
|
|
@@ -133,6 +133,10 @@ _mpytool() {
|
|
|
133
133
|
'--port[serial port]:port:_mpytool_ports' \
|
|
134
134
|
'-a[network address]:address:' \
|
|
135
135
|
'--address[network address]:address:' \
|
|
136
|
+
'--ssl[use SSL/TLS]' \
|
|
137
|
+
'--ssl-no-verify[disable SSL certificate verification]' \
|
|
138
|
+
'--ssl-ca[CA certificate file]:file:_files' \
|
|
139
|
+
'--ssl-no-hostname[disable SSL hostname verification]' \
|
|
136
140
|
'-b[baud rate]:baud:(9600 19200 38400 57600 115200 230400 460800 921600)' \
|
|
137
141
|
'--baud[baud rate]:baud:(9600 19200 38400 57600 115200 230400 460800 921600)' \
|
|
138
142
|
'-d[debug level]' \
|
|
@@ -265,6 +269,13 @@ _mpytool() {
|
|
|
265
269
|
fi
|
|
266
270
|
fi
|
|
267
271
|
;;
|
|
272
|
+
sync)
|
|
273
|
+
# Optional local project directory, -- anytime
|
|
274
|
+
if [[ $pos -eq 2 ]]; then
|
|
275
|
+
_files -/
|
|
276
|
+
fi
|
|
277
|
+
compadd -- '--'
|
|
278
|
+
;;
|
|
268
279
|
exec)
|
|
269
280
|
# 1 code string, -- after it
|
|
270
281
|
[[ $nargs -ge 1 ]] && compadd -- '--'
|
|
@@ -170,6 +170,10 @@ _mpytool() {
|
|
|
170
170
|
-a|--address)
|
|
171
171
|
return
|
|
172
172
|
;;
|
|
173
|
+
--ssl-ca)
|
|
174
|
+
COMPREPLY=($(compgen -f -- "$cur"))
|
|
175
|
+
return
|
|
176
|
+
;;
|
|
173
177
|
esac
|
|
174
178
|
|
|
175
179
|
# At command position
|
|
@@ -268,6 +272,13 @@ _mpytool() {
|
|
|
268
272
|
fi
|
|
269
273
|
fi
|
|
270
274
|
;;
|
|
275
|
+
sync)
|
|
276
|
+
# Optional local project directory, -- for chaining
|
|
277
|
+
if [[ $pos -eq 2 ]]; then
|
|
278
|
+
COMPREPLY=($(compgen -d -- "$cur"))
|
|
279
|
+
fi
|
|
280
|
+
[[ -z "$cur" || "--" == "$cur"* ]] && COMPREPLY+=("--")
|
|
281
|
+
;;
|
|
271
282
|
exec)
|
|
272
283
|
# 1 code string, -- after it
|
|
273
284
|
[[ $nargs -ge 1 && ( -z "$cur" || "--" == "$cur"* ) ]] && COMPREPLY+=("--")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '2.5.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 5, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'ga70fd9bfe'
|
|
@@ -208,6 +208,13 @@ class Conn():
|
|
|
208
208
|
def close(self):
|
|
209
209
|
"""Close connection"""
|
|
210
210
|
|
|
211
|
+
def discard_output(self):
|
|
212
|
+
"""Discard buffered output not yet transmitted (no-op by default).
|
|
213
|
+
|
|
214
|
+
Serial connections override this; needed before closing a port with
|
|
215
|
+
undeliverable queued data, otherwise close() blocks waiting to drain.
|
|
216
|
+
"""
|
|
217
|
+
|
|
211
218
|
def hard_reset(self):
|
|
212
219
|
"""Hardware reset (only available on serial connections)"""
|
|
213
220
|
raise NotImplementedError("Hardware reset not available on this connection")
|
|
@@ -46,6 +46,19 @@ class ConnSerial(_conn.Conn):
|
|
|
46
46
|
def __del__(self):
|
|
47
47
|
self.close()
|
|
48
48
|
|
|
49
|
+
def discard_output(self):
|
|
50
|
+
"""Discard kernel TX buffer (data written but not yet transmitted).
|
|
51
|
+
|
|
52
|
+
Without this, closing the port blocks while the kernel waits to drain
|
|
53
|
+
output the device never consumed (e.g. keystrokes sent to a device not
|
|
54
|
+
reading its USB-CDC, after leaving the REPL).
|
|
55
|
+
"""
|
|
56
|
+
if self._serial:
|
|
57
|
+
try:
|
|
58
|
+
self._serial.reset_output_buffer()
|
|
59
|
+
except OSError:
|
|
60
|
+
pass
|
|
61
|
+
|
|
49
62
|
@property
|
|
50
63
|
def fd(self):
|
|
51
64
|
return self._serial.fd if self._serial else None
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""MicroPython tool: socket connector"""
|
|
2
2
|
|
|
3
3
|
import socket as _socket
|
|
4
|
+
import ssl as _ssl
|
|
4
5
|
import mpytool.conn as _conn
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class ConnSocket(_conn.Conn):
|
|
8
|
-
def __init__(
|
|
9
|
+
def __init__(
|
|
10
|
+
self, address, log=None, ssl=False, ssl_verify=True,
|
|
11
|
+
ssl_ca=None, ssl_check_hostname=True):
|
|
9
12
|
super().__init__(log)
|
|
10
13
|
self._socket = None
|
|
11
14
|
sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
@@ -16,14 +19,31 @@ class ConnSocket(_conn.Conn):
|
|
|
16
19
|
else:
|
|
17
20
|
host = address
|
|
18
21
|
port = 23
|
|
22
|
+
proto = "ssl" if ssl else "tcp"
|
|
19
23
|
if log:
|
|
20
|
-
log.info(f"Connecting to: {host}:{port}")
|
|
24
|
+
log.info(f"Connecting to: {host}:{port} [{proto}]")
|
|
21
25
|
try:
|
|
22
26
|
sock.connect((host, port))
|
|
23
27
|
except _socket.timeout as err:
|
|
28
|
+
sock.close()
|
|
24
29
|
raise _conn.ConnError(f"Timeout connecting to {host}:{port}") from err
|
|
25
30
|
except _socket.error as err:
|
|
31
|
+
sock.close()
|
|
26
32
|
raise _conn.ConnError(f"Cannot connect to {host}:{port}: {err}") from err
|
|
33
|
+
if ssl:
|
|
34
|
+
try:
|
|
35
|
+
context = _ssl.create_default_context()
|
|
36
|
+
if not ssl_verify:
|
|
37
|
+
context.check_hostname = False
|
|
38
|
+
context.verify_mode = _ssl.CERT_NONE
|
|
39
|
+
elif not ssl_check_hostname:
|
|
40
|
+
context.check_hostname = False
|
|
41
|
+
if ssl_ca:
|
|
42
|
+
context.load_verify_locations(ssl_ca)
|
|
43
|
+
sock = context.wrap_socket(sock, server_hostname=host)
|
|
44
|
+
except (_ssl.SSLError, OSError) as err:
|
|
45
|
+
sock.close()
|
|
46
|
+
raise _conn.ConnError(f"SSL error: {err}") from err
|
|
27
47
|
sock.settimeout(None)
|
|
28
48
|
sock.setblocking(False)
|
|
29
49
|
self._socket = sock
|
|
@@ -51,8 +71,12 @@ class ConnSocket(_conn.Conn):
|
|
|
51
71
|
if data:
|
|
52
72
|
self._log.debug("RX: %r", data)
|
|
53
73
|
return data
|
|
54
|
-
|
|
74
|
+
# recv() returned b'' = peer closed connection
|
|
75
|
+
raise _conn.ConnError("Connection closed")
|
|
76
|
+
except (BlockingIOError, _ssl.SSLWantReadError):
|
|
55
77
|
pass
|
|
78
|
+
except _conn.ConnError:
|
|
79
|
+
raise
|
|
56
80
|
except OSError as err:
|
|
57
81
|
raise _conn.ConnError(f"Connection lost: {err}") from err
|
|
58
82
|
return None
|
|
@@ -64,5 +88,7 @@ class ConnSocket(_conn.Conn):
|
|
|
64
88
|
self._log.debug("TX: %r", data)
|
|
65
89
|
try:
|
|
66
90
|
return self._socket.send(data)
|
|
91
|
+
except _ssl.SSLWantWriteError:
|
|
92
|
+
return 0
|
|
67
93
|
except OSError as err:
|
|
68
94
|
raise _conn.ConnError(f"Connection lost: {err}") from err
|
|
@@ -1329,6 +1329,29 @@ def _mt_pfind(label):
|
|
|
1329
1329
|
return True
|
|
1330
1330
|
return False
|
|
1331
1331
|
|
|
1332
|
+
def wipe(self, reconnect=True, timeout=None, on_delete=None):
|
|
1333
|
+
"""Erase all files from filesystem root, then machine-reset device
|
|
1334
|
+
|
|
1335
|
+
Deletes every entry in '/' recursively, then performs a full
|
|
1336
|
+
machine.reset() so runtime state (open sockets, WiFi, mounted VFS,
|
|
1337
|
+
etc.) is dropped too - a soft reset would leave those alive. Intended
|
|
1338
|
+
for a clean slate, e.g. at the start of integration tests.
|
|
1339
|
+
|
|
1340
|
+
Arguments:
|
|
1341
|
+
reconnect: if True, wait for the device to come back after reset
|
|
1342
|
+
timeout: reconnect timeout in seconds (None = default)
|
|
1343
|
+
on_delete: optional callback(path) invoked before each delete
|
|
1344
|
+
|
|
1345
|
+
Returns:
|
|
1346
|
+
True if reconnected after reset, False otherwise
|
|
1347
|
+
"""
|
|
1348
|
+
for name, _ in self.ls('/'):
|
|
1349
|
+
path = '/' + name
|
|
1350
|
+
if on_delete is not None:
|
|
1351
|
+
on_delete(path)
|
|
1352
|
+
self.delete(path)
|
|
1353
|
+
return self.machine_reset(reconnect=reconnect, timeout=timeout)
|
|
1354
|
+
|
|
1332
1355
|
def machine_bootloader(self):
|
|
1333
1356
|
"""Enter bootloader using machine.bootloader()
|
|
1334
1357
|
|