mposcli 0.3.0__tar.gz → 0.4.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 (49) hide show
  1. {mposcli-0.3.0 → mposcli-0.4.0}/.pre-commit-config.yaml +1 -1
  2. {mposcli-0.3.0 → mposcli-0.4.0}/.run/Unittests __all__.run.xml +2 -3
  3. mposcli-0.4.0/.venv-app/lib/python3.14/site-packages/cli_base/tests/shell_complete_snapshots/.gitignore +1 -0
  4. {mposcli-0.3.0 → mposcli-0.4.0}/PKG-INFO +45 -7
  5. {mposcli-0.3.0 → mposcli-0.4.0}/README.md +44 -6
  6. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/__init__.py +1 -1
  7. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_app/build.py +1 -0
  8. mposcli-0.4.0/mposcli/cli_app/copy_mpos.py +195 -0
  9. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_app/flash.py +1 -1
  10. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_app/run_deskop.py +3 -2
  11. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_app/update.py +1 -1
  12. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/testing.py +3 -3
  13. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/update_readme_history.py +1 -1
  14. mposcli-0.4.0/mposcli/fs_utils.py +26 -0
  15. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/mpos_utils.py +3 -3
  16. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tests/__init__.py +2 -2
  17. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tests/test_readme.py +2 -2
  18. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/user_input.py +59 -4
  19. {mposcli-0.3.0 → mposcli-0.4.0}/uv.lock +81 -81
  20. mposcli-0.3.0/mposcli/cli_app/copy_mpos.py +0 -92
  21. mposcli-0.3.0/mposcli/fs_utils.py +0 -6
  22. {mposcli-0.3.0 → mposcli-0.4.0}/.editorconfig +0 -0
  23. {mposcli-0.3.0 → mposcli-0.4.0}/.github/workflows/tests.yml +0 -0
  24. {mposcli-0.3.0 → mposcli-0.4.0}/.gitignore +0 -0
  25. {mposcli-0.3.0 → mposcli-0.4.0}/.idea/.gitignore +0 -0
  26. {mposcli-0.3.0 → mposcli-0.4.0}/.pre-commit-hooks.yaml +0 -0
  27. {mposcli-0.3.0 → mposcli-0.4.0}/.run/Template Python tests.run.xml +0 -0
  28. {mposcli-0.3.0 → mposcli-0.4.0}/.run/Template Python.run.xml +0 -0
  29. {mposcli-0.3.0 → mposcli-0.4.0}/.run/cli --help.run.xml +0 -0
  30. {mposcli-0.3.0 → mposcli-0.4.0}/.run/dev-cli --help.run.xml +0 -0
  31. {mposcli-0.3.0 → mposcli-0.4.0}/.run/dev-cli test.run.xml +0 -0
  32. {mposcli-0.3.0 → mposcli-0.4.0}/.venv-app/.gitignore +0 -0
  33. {mposcli-0.3.0 → mposcli-0.4.0}/cli.py +0 -0
  34. {mposcli-0.3.0 → mposcli-0.4.0}/dev-cli.py +0 -0
  35. {mposcli-0.3.0 → mposcli-0.4.0}/dist/.gitignore +0 -0
  36. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/__main__.py +0 -0
  37. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_app/__init__.py +0 -0
  38. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/__init__.py +0 -0
  39. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/__main__.py +0 -0
  40. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/code_style.py +0 -0
  41. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/packaging.py +0 -0
  42. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/cli_dev/shell_completion.py +0 -0
  43. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/constants.py +0 -0
  44. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tests/test_doctests.py +0 -0
  45. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tests/test_project_setup.py +0 -0
  46. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tests/test_readme_history.py +0 -0
  47. {mposcli-0.3.0 → mposcli-0.4.0}/mposcli/tools.py +0 -0
  48. {mposcli-0.3.0 → mposcli-0.4.0}/noxfile.py +0 -0
  49. {mposcli-0.3.0 → mposcli-0.4.0}/pyproject.toml +0 -0
@@ -2,6 +2,6 @@
2
2
  # See https://pre-commit.com for more information
3
3
  repos:
4
4
  - repo: https://github.com/jedie/cli-base-utilities
5
- rev: v0.27.4
5
+ rev: v0.28.0
6
6
  hooks:
7
7
  - id: update-readme-history
@@ -4,10 +4,9 @@
4
4
  <option name="ENV_FILES" value="" />
5
5
  <option name="INTERPRETER_OPTIONS" value="" />
6
6
  <option name="PARENT_ENVS" value="true" />
7
- <option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python3" />
8
- <option name="SDK_NAME" value="uv (mposcli) (2)" />
7
+ <option name="SDK_HOME" value="" />
9
8
  <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
10
- <option name="IS_MODULE_SDK" value="false" />
9
+ <option name="IS_MODULE_SDK" value="true" />
11
10
  <option name="ADD_CONTENT_ROOTS" value="false" />
12
11
  <option name="ADD_SOURCE_ROOTS" value="false" />
13
12
  <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mposcli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: CLI helper for MicroPythonOS: https://github.com/MicroPythonOS/MicroPythonOS
5
5
  Project-URL: Documentation, https://github.com/jedie/mposcli
6
6
  Project-URL: Source, https://github.com/jedie/mposcli
@@ -47,7 +47,7 @@ cd ~/MicroPythonOS
47
47
 
48
48
  [comment]: <> (✂✂✂ auto generated main help start ✂✂✂)
49
49
  ```
50
- usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version}
50
+ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,version}
51
51
 
52
52
 
53
53
 
@@ -63,6 +63,11 @@ usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version
63
63
  │ files to copy/update. But can also be used to copy/update all │
64
64
  │ files. see: https://docs.micropythonos.com/os-development/insta │
65
65
  │ lling-on-esp32/ │
66
+ │ • cp-app Copy/update internal_filesystem/apps to the device via │
67
+ │ "mpremote fs cp". Display a file chooser to select which app to │
68
+ │ copy/update. But can also be used to copy/update all files. │
69
+ │ see: https://docs.micropythonos.com/os-development/installing-o │
70
+ │ n-esp32/ │
66
71
  │ • flash Flash MicroPythonOS to the device. Display a file chooser to │
67
72
  │ select the image to flash. All lvgl_micropython/build/*.bin │
68
73
  │ files will be shown in the file chooser. see: https://docs.micr │
@@ -88,15 +93,17 @@ usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version
88
93
 
89
94
  [comment]: <> (✂✂✂ auto generated build start ✂✂✂)
90
95
  ```
91
- usage: mposcli build [-h] [--target {esp32,esp32s3,unix,macOS}] [-v]
96
+ usage: mposcli build [-h] [{esp32,esp32s3,unix,macOS}] [-v]
92
97
 
93
98
  Build MicroPythonOS by calling: ./scripts/build_mpos.sh <target> see:
94
99
  https://docs.micropythonos.com/os-development/
95
100
 
101
+ ╭─ positional arguments ───────────────────────────────────────────────────╮
102
+ │ [{esp32,esp32s3,unix,macOS}] │
103
+ │ Target platform to build for. (default: unix) │
104
+ ╰──────────────────────────────────────────────────────────────────────────╯
96
105
  ╭─ options ────────────────────────────────────────────────────────────────╮
97
106
  │ -h, --help show this help message and exit │
98
- │ --target {esp32,esp32s3,unix,macOS} │
99
- │ Target platform to build for. (default: unix) │
100
107
  │ -v, --verbosity Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable) │
101
108
  ╰──────────────────────────────────────────────────────────────────────────╯
102
109
  ```
@@ -108,13 +115,15 @@ https://docs.micropythonos.com/os-development/
108
115
 
109
116
  [comment]: <> (✂✂✂ auto generated cp start ✂✂✂)
110
117
  ```
111
- usage: mposcli cp [-h] [--new-file-limit INT] [--reset | --no-reset] [--repl | --no-repl]
112
- [-v]
118
+ usage: mposcli cp [-h] [CP OPTIONS]
113
119
 
114
120
  Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp". Display
115
121
  a file chooser to select which files to copy/update. But can also be used to copy/update
116
122
  all files. see: https://docs.micropythonos.com/os-development/installing-on-esp32/
117
123
 
124
+ ╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
125
+ │ [{None}|PATH] Optional file or directory path. (default: None) │
126
+ ╰────────────────────────────────────────────────────────────────────────────────────────╯
118
127
  ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
119
128
  │ -h, --help show this help message and exit │
120
129
  │ --new-file-limit INT How many of the newest files to show in the file chooser? │
@@ -129,6 +138,29 @@ all files. see: https://docs.micropythonos.com/os-development/installing-on-esp3
129
138
 
130
139
 
131
140
 
141
+
142
+ ## mposcli cp-app
143
+
144
+ [comment]: <> (✂✂✂ auto generated cp-app start ✂✂✂)
145
+ ```
146
+ usage: mposcli cp-app [-h] [--reset | --no-reset] [--repl | --no-repl] [-v]
147
+
148
+ Copy/update internal_filesystem/apps to the device via "mpremote fs cp". Display a file
149
+ chooser to select which app to copy/update. But can also be used to copy/update all files.
150
+ see: https://docs.micropythonos.com/os-development/installing-on-esp32/
151
+
152
+ ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
153
+ │ -h, --help show this help message and exit │
154
+ │ --reset, --no-reset Reset the device after copy/update? (default: True) │
155
+ │ --repl, --no-repl After flashing/verify start REPL with mpremote to see the output │
156
+ │ of the device? (default: True) │
157
+ │ -v, --verbosity Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable) │
158
+ ╰────────────────────────────────────────────────────────────────────────────────────────╯
159
+ ```
160
+ [comment]: <> (✂✂✂ auto generated cp-app end ✂✂✂)
161
+
162
+
163
+
132
164
  ## mposcli flash
133
165
 
134
166
  [comment]: <> (✂✂✂ auto generated flash start ✂✂✂)
@@ -285,6 +317,12 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
285
317
 
286
318
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
287
319
 
320
+ * [v0.4.0](https://github.com/jedie/mposcli/compare/v0.3.0...v0.4.0)
321
+ * 2026-02-24 - NEW command: "cp-app" to install/update internal_filesystem/apps
322
+ * 2026-02-24 - Log skipped files
323
+ * 2026-02-23 - Update requirements and fix code style
324
+ * 2026-02-23 - "build" command: target as positional argument
325
+ * 2026-02-23 - Expand "cp" command and allow optional filesystem path
288
326
  * [v0.3.0](https://github.com/jedie/mposcli/compare/v0.2.0...v0.3.0)
289
327
  * 2026-02-18 - Add "update" beside "update-submodules"
290
328
  * 2026-02-17 - Update requirements
@@ -32,7 +32,7 @@ cd ~/MicroPythonOS
32
32
 
33
33
  [comment]: <> (✂✂✂ auto generated main help start ✂✂✂)
34
34
  ```
35
- usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version}
35
+ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,version}
36
36
 
37
37
 
38
38
 
@@ -48,6 +48,11 @@ usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version
48
48
  │ files to copy/update. But can also be used to copy/update all │
49
49
  │ files. see: https://docs.micropythonos.com/os-development/insta │
50
50
  │ lling-on-esp32/ │
51
+ │ • cp-app Copy/update internal_filesystem/apps to the device via │
52
+ │ "mpremote fs cp". Display a file chooser to select which app to │
53
+ │ copy/update. But can also be used to copy/update all files. │
54
+ │ see: https://docs.micropythonos.com/os-development/installing-o │
55
+ │ n-esp32/ │
51
56
  │ • flash Flash MicroPythonOS to the device. Display a file chooser to │
52
57
  │ select the image to flash. All lvgl_micropython/build/*.bin │
53
58
  │ files will be shown in the file chooser. see: https://docs.micr │
@@ -73,15 +78,17 @@ usage: mposcli [-h] {build,cp,flash,run-desktop,update,update-submodules,version
73
78
 
74
79
  [comment]: <> (✂✂✂ auto generated build start ✂✂✂)
75
80
  ```
76
- usage: mposcli build [-h] [--target {esp32,esp32s3,unix,macOS}] [-v]
81
+ usage: mposcli build [-h] [{esp32,esp32s3,unix,macOS}] [-v]
77
82
 
78
83
  Build MicroPythonOS by calling: ./scripts/build_mpos.sh <target> see:
79
84
  https://docs.micropythonos.com/os-development/
80
85
 
86
+ ╭─ positional arguments ───────────────────────────────────────────────────╮
87
+ │ [{esp32,esp32s3,unix,macOS}] │
88
+ │ Target platform to build for. (default: unix) │
89
+ ╰──────────────────────────────────────────────────────────────────────────╯
81
90
  ╭─ options ────────────────────────────────────────────────────────────────╮
82
91
  │ -h, --help show this help message and exit │
83
- │ --target {esp32,esp32s3,unix,macOS} │
84
- │ Target platform to build for. (default: unix) │
85
92
  │ -v, --verbosity Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable) │
86
93
  ╰──────────────────────────────────────────────────────────────────────────╯
87
94
  ```
@@ -93,13 +100,15 @@ https://docs.micropythonos.com/os-development/
93
100
 
94
101
  [comment]: <> (✂✂✂ auto generated cp start ✂✂✂)
95
102
  ```
96
- usage: mposcli cp [-h] [--new-file-limit INT] [--reset | --no-reset] [--repl | --no-repl]
97
- [-v]
103
+ usage: mposcli cp [-h] [CP OPTIONS]
98
104
 
99
105
  Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp". Display
100
106
  a file chooser to select which files to copy/update. But can also be used to copy/update
101
107
  all files. see: https://docs.micropythonos.com/os-development/installing-on-esp32/
102
108
 
109
+ ╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
110
+ │ [{None}|PATH] Optional file or directory path. (default: None) │
111
+ ╰────────────────────────────────────────────────────────────────────────────────────────╯
103
112
  ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
104
113
  │ -h, --help show this help message and exit │
105
114
  │ --new-file-limit INT How many of the newest files to show in the file chooser? │
@@ -114,6 +123,29 @@ all files. see: https://docs.micropythonos.com/os-development/installing-on-esp3
114
123
 
115
124
 
116
125
 
126
+
127
+ ## mposcli cp-app
128
+
129
+ [comment]: <> (✂✂✂ auto generated cp-app start ✂✂✂)
130
+ ```
131
+ usage: mposcli cp-app [-h] [--reset | --no-reset] [--repl | --no-repl] [-v]
132
+
133
+ Copy/update internal_filesystem/apps to the device via "mpremote fs cp". Display a file
134
+ chooser to select which app to copy/update. But can also be used to copy/update all files.
135
+ see: https://docs.micropythonos.com/os-development/installing-on-esp32/
136
+
137
+ ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
138
+ │ -h, --help show this help message and exit │
139
+ │ --reset, --no-reset Reset the device after copy/update? (default: True) │
140
+ │ --repl, --no-repl After flashing/verify start REPL with mpremote to see the output │
141
+ │ of the device? (default: True) │
142
+ │ -v, --verbosity Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable) │
143
+ ╰────────────────────────────────────────────────────────────────────────────────────────╯
144
+ ```
145
+ [comment]: <> (✂✂✂ auto generated cp-app end ✂✂✂)
146
+
147
+
148
+
117
149
  ## mposcli flash
118
150
 
119
151
  [comment]: <> (✂✂✂ auto generated flash start ✂✂✂)
@@ -270,6 +302,12 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
270
302
 
271
303
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
272
304
 
305
+ * [v0.4.0](https://github.com/jedie/mposcli/compare/v0.3.0...v0.4.0)
306
+ * 2026-02-24 - NEW command: "cp-app" to install/update internal_filesystem/apps
307
+ * 2026-02-24 - Log skipped files
308
+ * 2026-02-23 - Update requirements and fix code style
309
+ * 2026-02-23 - "build" command: target as positional argument
310
+ * 2026-02-23 - Expand "cp" command and allow optional filesystem path
273
311
  * [v0.3.0](https://github.com/jedie/mposcli/compare/v0.2.0...v0.3.0)
274
312
  * 2026-02-18 - Add "update" beside "update-submodules"
275
313
  * 2026-02-17 - Update requirements
@@ -4,5 +4,5 @@
4
4
  """
5
5
 
6
6
  # See https://packaging.python.org/en/latest/specifications/version-specifiers/
7
- __version__ = '0.3.0'
7
+ __version__ = '0.4.0'
8
8
  __author__ = 'Jens Diemer <cookiecutter_templates@jensdiemer.de>'
@@ -23,6 +23,7 @@ def build(
23
23
  help='Target platform to build for.',
24
24
  ),
25
25
  ] = 'unix',
26
+ /,
26
27
  verbosity: TyroVerbosityArgType = 1,
27
28
  ):
28
29
  """
@@ -0,0 +1,195 @@
1
+ import logging
2
+ import time
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ import tyro
7
+ from bx_py_utils.path import assert_is_dir
8
+ from cli_base.cli_tools.subprocess_utils import verbose_check_call
9
+ from cli_base.cli_tools.verbosity import setup_logging
10
+ from cli_base.tyro_commands import TyroVerbosityArgType
11
+ from rich import print
12
+
13
+ from mposcli.cli_app import app
14
+ from mposcli.mpos_utils import get_mpos_path
15
+ from mposcli.tools import get_mpremote_bin
16
+ from mposcli.user_input import choose_newest_modified_directory, get_newest_files
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ @app.command
23
+ def cp(
24
+ local_path: Annotated[
25
+ Path | None,
26
+ tyro.conf.arg(help='Optional file or directory path.'),
27
+ ] = None,
28
+ /,
29
+ new_file_limit: Annotated[
30
+ int,
31
+ tyro.conf.arg(help='How many of the newest files to show in the file chooser?'),
32
+ ] = 10,
33
+ reset: Annotated[
34
+ bool,
35
+ tyro.conf.arg(help='Reset the device after copy/update?'),
36
+ ] = True,
37
+ repl: Annotated[
38
+ bool,
39
+ tyro.conf.arg(help='After flashing/verify start REPL with mpremote to see the output of the device?'),
40
+ ] = True,
41
+ verbosity: TyroVerbosityArgType = 1,
42
+ ):
43
+ """
44
+ Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp".
45
+ Display a file chooser to select which files to copy/update.
46
+ But can also be used to copy/update all files.
47
+ see: https://docs.micropythonos.com/os-development/installing-on-esp32/
48
+ """
49
+ setup_logging(verbosity=verbosity)
50
+
51
+ mpos_path = get_mpos_path()
52
+
53
+ internal_fs = mpos_path / 'internal_filesystem'
54
+ lib_mpos = internal_fs / 'lib' / 'mpos'
55
+ assert_is_dir(lib_mpos)
56
+ apps_path = internal_fs / 'apps'
57
+ assert_is_dir(apps_path)
58
+
59
+ mpremote_bin = get_mpremote_bin()
60
+
61
+ print('\n')
62
+
63
+ popenargs = (mpremote_bin, 'fs', 'cp')
64
+
65
+ if local_path:
66
+ if not local_path.is_absolute():
67
+ local_path = mpos_path / local_path
68
+
69
+ print(f'Copy/update app: "{local_path}" ...')
70
+
71
+ if not local_path.exists():
72
+ print(f'[red]Error: The specified source path "{local_path}" does not exist.[/red]')
73
+ return
74
+
75
+ # Is source a sub path of "apps_path" ?
76
+ if local_path.is_relative_to(apps_path):
77
+ local_rel_path = local_path.relative_to(mpos_path)
78
+ remote_path = f':/{local_path.relative_to(internal_fs)}'
79
+ else:
80
+ raise NotImplementedError
81
+ else:
82
+ local_path = get_newest_files(lib_mpos, limit=new_file_limit)
83
+ if not local_path:
84
+ print('Copy/update all files in lib/mpos to the device')
85
+ local_path = lib_mpos
86
+
87
+ local_rel_path = local_path.relative_to(mpos_path)
88
+ remote_path = f':/{local_path.relative_to(lib_mpos.parent)}'
89
+
90
+ if local_path.is_dir():
91
+ popenargs += ('-r',)
92
+ print(f'Copying directory "[bold]{local_rel_path}[/bold]" to device at "[bold]{remote_path}[/bold]" ...')
93
+ else:
94
+ print(f'Copying file "[bold]{local_rel_path}[/bold]" to device at "[bold]{remote_path}[/bold]" ...')
95
+
96
+ popenargs += (local_rel_path, remote_path)
97
+ verbose_check_call(
98
+ *popenargs,
99
+ verbose=True,
100
+ cwd=mpos_path,
101
+ text=None,
102
+ )
103
+
104
+ if reset:
105
+ time.sleep(1)
106
+ verbose_check_call(
107
+ mpremote_bin,
108
+ 'reset',
109
+ verbose=True,
110
+ cwd=mpos_path,
111
+ text=None,
112
+ )
113
+
114
+ if repl:
115
+ time.sleep(1)
116
+ verbose_check_call(
117
+ mpremote_bin,
118
+ 'repl',
119
+ verbose=True,
120
+ cwd=mpos_path,
121
+ timeout=None,
122
+ text=None,
123
+ )
124
+
125
+
126
+ @app.command
127
+ def cp_app(
128
+ reset: Annotated[
129
+ bool,
130
+ tyro.conf.arg(help='Reset the device after copy/update?'),
131
+ ] = True,
132
+ repl: Annotated[
133
+ bool,
134
+ tyro.conf.arg(help='After flashing/verify start REPL with mpremote to see the output of the device?'),
135
+ ] = True,
136
+ verbosity: TyroVerbosityArgType = 1,
137
+ ):
138
+ """
139
+ Copy/update internal_filesystem/apps to the device via "mpremote fs cp".
140
+ Display a file chooser to select which app to copy/update.
141
+ But can also be used to copy/update all files.
142
+ see: https://docs.micropythonos.com/os-development/installing-on-esp32/
143
+ """
144
+ setup_logging(verbosity=verbosity)
145
+
146
+ mpos_path = get_mpos_path()
147
+
148
+ internal_fs = mpos_path / 'internal_filesystem'
149
+ apps_path = internal_fs / 'apps'
150
+ assert_is_dir(apps_path)
151
+
152
+ mpremote_bin = get_mpremote_bin()
153
+ popenargs = (mpremote_bin, 'fs', 'cp', '-r')
154
+
155
+ app = choose_newest_modified_directory(apps_path)
156
+
157
+ print('\n')
158
+
159
+ if not app:
160
+ print('Copy/update all apps in "internal_filesystem/apps" to the device')
161
+ local_rel_path = 'internal_filesystem/apps'
162
+ remote_path = ':/apps'
163
+ else:
164
+ print(f'Copy/update {app=} ...')
165
+ local_rel_path = f'internal_filesystem/apps/{app.name}'
166
+ remote_path = f':/apps/{app.name}'
167
+
168
+ popenargs += (local_rel_path, remote_path)
169
+ verbose_check_call(
170
+ *popenargs,
171
+ verbose=True,
172
+ cwd=mpos_path,
173
+ text=None,
174
+ )
175
+
176
+ if reset:
177
+ time.sleep(1)
178
+ verbose_check_call(
179
+ mpremote_bin,
180
+ 'reset',
181
+ verbose=True,
182
+ cwd=mpos_path,
183
+ text=None,
184
+ )
185
+
186
+ if repl:
187
+ time.sleep(1)
188
+ verbose_check_call(
189
+ mpremote_bin,
190
+ 'repl',
191
+ verbose=True,
192
+ cwd=mpos_path,
193
+ timeout=None,
194
+ text=None,
195
+ )
@@ -5,7 +5,7 @@ import tyro
5
5
  from cli_base.cli_tools.subprocess_utils import verbose_check_call
6
6
  from cli_base.cli_tools.verbosity import setup_logging
7
7
  from cli_base.tyro_commands import TyroVerbosityArgType
8
- from rich import print # noqa
8
+ from rich import print
9
9
 
10
10
  from mposcli.cli_app import app
11
11
  from mposcli.mpos_utils import get_mpos_path
@@ -10,7 +10,7 @@ from bx_py_utils.path import assert_is_file
10
10
  from cli_base.cli_tools.subprocess_utils import verbose_check_call
11
11
  from cli_base.cli_tools.verbosity import setup_logging
12
12
  from cli_base.tyro_commands import TyroVerbosityArgType
13
- from rich import print # noqa
13
+ from rich import print
14
14
 
15
15
  from mposcli.cli_app import app
16
16
  from mposcli.fs_utils import list_executables
@@ -86,7 +86,8 @@ def run_desktop(
86
86
  with config_file.open('r', encoding='utf-8') as f:
87
87
  try:
88
88
  config = json.load(f)
89
- except Exception:
89
+ except json.JSONDecodeError as err:
90
+ logger.error('Error parsing config file: %s: %s', config_file, err)
90
91
  config = {}
91
92
  else:
92
93
  config = {}
@@ -3,7 +3,7 @@ import logging
3
3
  from cli_base.cli_tools.git import Git
4
4
  from cli_base.cli_tools.verbosity import setup_logging
5
5
  from cli_base.tyro_commands import TyroVerbosityArgType
6
- from rich import print # noqa
6
+ from rich import print
7
7
 
8
8
  from mposcli.cli_app import app
9
9
  from mposcli.mpos_utils import get_mpos_path
@@ -20,9 +20,9 @@ def update_test_snapshot_files(verbosity: TyroVerbosityArgType):
20
20
  with UpdateTestSnapshotFiles(root_path=PACKAGE_ROOT, verbose=verbosity > 0):
21
21
  # Just recreate them by running tests:
22
22
  run_unittest_cli(
23
- extra_env=dict(
24
- RAISE_SNAPSHOT_ERRORS='0', # Recreate snapshot files without error
25
- ),
23
+ extra_env={
24
+ 'RAISE_SNAPSHOT_ERRORS': '0', # Recreate snapshot files without error
25
+ },
26
26
  verbose=verbosity > 1,
27
27
  exit_after_run=False,
28
28
  )
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from cli_base.cli_tools import git_history
6
6
  from cli_base.cli_tools.verbosity import setup_logging
7
7
  from cli_base.tyro_commands import TyroVerbosityArgType
8
- from rich import print # noqa
8
+ from rich import print
9
9
 
10
10
  from mposcli.cli_dev import app
11
11
 
@@ -0,0 +1,26 @@
1
+ import logging
2
+ import os
3
+ from pathlib import Path
4
+
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def iter_files(directory):
10
+ with os.scandir(directory) as it:
11
+ for entry in it:
12
+ if entry.is_file(follow_symlinks=False):
13
+ yield entry
14
+ elif entry.is_dir(follow_symlinks=False):
15
+ yield from iter_files(entry.path)
16
+
17
+
18
+ def list_executables(directory: Path) -> list[Path]:
19
+ executables = []
20
+ for entry in directory.iterdir():
21
+ if entry.is_file():
22
+ if os.access(entry, os.X_OK):
23
+ executables.append(entry)
24
+ else:
25
+ logger.info('Skipping non-executable file: %s', entry)
26
+ return executables
@@ -2,7 +2,7 @@ import logging
2
2
  import sys
3
3
  from pathlib import Path
4
4
 
5
- from rich import print # noqa
5
+ from rich import print
6
6
 
7
7
 
8
8
  logger = logging.getLogger(__name__)
@@ -24,5 +24,5 @@ def get_mpos_path() -> Path:
24
24
  print('Hint: Call "mposcli" only in the root directory of a MicroPythonOS project!')
25
25
  sys.exit(1)
26
26
 
27
- mpos_path = current_path
28
- return mpos_path
27
+ return current_path
28
+
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
 
5
5
  from bx_py_utils.test_utils.deny_requests import deny_any_real_request
6
6
  from cli_base.cli_tools.verbosity import MAX_LOG_LEVEL, setup_logging
7
- from rich import print # noqa
7
+ from rich import print
8
8
  from typeguard import install_import_hook
9
9
 
10
10
 
@@ -17,7 +17,7 @@ def pre_configure_tests() -> None:
17
17
 
18
18
  # Hacky way to display more "assert"-Context in failing tests:
19
19
  _MIN_MAX_DIFF = unittest.util._MAX_LENGTH - unittest.util._MIN_DIFF_LEN
20
- unittest.util._MAX_LENGTH = int(os.environ.get('UNITTEST_MAX_LENGTH', 2000))
20
+ unittest.util._MAX_LENGTH = int(os.environ.get('UNITTEST_MAX_LENGTH') or 2000)
21
21
  unittest.util._MIN_DIFF_LEN = unittest.util._MAX_LENGTH - _MIN_MAX_DIFF
22
22
 
23
23
  # Deny any request via docket/urllib3 because tests they should mock all requests:
@@ -79,11 +79,11 @@ class ReadmeTestCase(BaseTestCase):
79
79
 
80
80
  # Dynamically build command list from app object
81
81
  # tyro SubcommandApp stores subcommands in _subcommands dict
82
- commands = set(command.replace('_', '-') for command in app._subcommands.keys())
82
+ commands = {command.replace('_', '-') for command in app._subcommands}
83
83
 
84
84
  commands.discard('version') # version is pseudo command, because the version always printed on every CLI call
85
85
  commands = sorted(commands)
86
- self.assertEqual(commands, ['build', 'cp', 'flash', 'run-desktop', 'update', 'update-submodules'])
86
+ self.assertEqual(commands, ['build', 'cp', 'cp-app', 'flash', 'run-desktop', 'update', 'update-submodules'])
87
87
 
88
88
  for command in commands:
89
89
  with self.subTest(command):