mposcli 0.4.1__tar.gz → 0.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.
Files changed (50) hide show
  1. {mposcli-0.4.1 → mposcli-0.5.0}/PKG-INFO +17 -10
  2. {mposcli-0.4.1 → mposcli-0.5.0}/README.md +16 -9
  3. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/__init__.py +1 -1
  4. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/build.py +1 -1
  5. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/copy_mpos.py +24 -44
  6. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/flash.py +15 -7
  7. mposcli-0.5.0/mposcli/tests/test_mpremote_cp_utils.py +74 -0
  8. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tools.py +3 -0
  9. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/user_input.py +5 -4
  10. mposcli-0.5.0/mposcli/utilities/__init__.py +0 -0
  11. mposcli-0.5.0/mposcli/utilities/mpremote.py +64 -0
  12. {mposcli-0.4.1 → mposcli-0.5.0}/.editorconfig +0 -0
  13. {mposcli-0.4.1 → mposcli-0.5.0}/.github/workflows/tests.yml +0 -0
  14. {mposcli-0.4.1 → mposcli-0.5.0}/.gitignore +0 -0
  15. {mposcli-0.4.1 → mposcli-0.5.0}/.idea/.gitignore +0 -0
  16. {mposcli-0.4.1 → mposcli-0.5.0}/.pre-commit-config.yaml +0 -0
  17. {mposcli-0.4.1 → mposcli-0.5.0}/.pre-commit-hooks.yaml +0 -0
  18. {mposcli-0.4.1 → mposcli-0.5.0}/.run/Template Python tests.run.xml +0 -0
  19. {mposcli-0.4.1 → mposcli-0.5.0}/.run/Template Python.run.xml +0 -0
  20. {mposcli-0.4.1 → mposcli-0.5.0}/.run/Unittests __all__.run.xml +0 -0
  21. {mposcli-0.4.1 → mposcli-0.5.0}/.run/cli --help.run.xml +0 -0
  22. {mposcli-0.4.1 → mposcli-0.5.0}/.run/dev-cli --help.run.xml +0 -0
  23. {mposcli-0.4.1 → mposcli-0.5.0}/.run/dev-cli test.run.xml +0 -0
  24. {mposcli-0.4.1 → mposcli-0.5.0}/.venv-app/.gitignore +0 -0
  25. {mposcli-0.4.1 → mposcli-0.5.0}/.venv-app/lib/python3.14/site-packages/cli_base/tests/shell_complete_snapshots/.gitignore +0 -0
  26. {mposcli-0.4.1 → mposcli-0.5.0}/cli.py +0 -0
  27. {mposcli-0.4.1 → mposcli-0.5.0}/dev-cli.py +0 -0
  28. {mposcli-0.4.1 → mposcli-0.5.0}/dist/.gitignore +0 -0
  29. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/__main__.py +0 -0
  30. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/__init__.py +0 -0
  31. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/run_deskop.py +0 -0
  32. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_app/update.py +0 -0
  33. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/__init__.py +0 -0
  34. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/__main__.py +0 -0
  35. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/code_style.py +0 -0
  36. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/packaging.py +0 -0
  37. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/shell_completion.py +0 -0
  38. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/testing.py +0 -0
  39. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/cli_dev/update_readme_history.py +0 -0
  40. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/constants.py +0 -0
  41. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/fs_utils.py +0 -0
  42. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/mpos_utils.py +0 -0
  43. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tests/__init__.py +0 -0
  44. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tests/test_doctests.py +0 -0
  45. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tests/test_project_setup.py +0 -0
  46. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tests/test_readme.py +0 -0
  47. {mposcli-0.4.1 → mposcli-0.5.0}/mposcli/tests/test_readme_history.py +0 -0
  48. {mposcli-0.4.1 → mposcli-0.5.0}/noxfile.py +0 -0
  49. {mposcli-0.4.1 → mposcli-0.5.0}/pyproject.toml +0 -0
  50. {mposcli-0.4.1 → mposcli-0.5.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mposcli
3
- Version: 0.4.1
3
+ Version: 0.5.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
@@ -61,8 +61,8 @@ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,
61
61
  │ • cp Copy/update internal_filesystem/lib/mpos files to the device │
62
62
  │ via "mpremote fs cp". Display a file chooser to select which │
63
63
  │ files to copy/update. But can also be used to copy/update all │
64
- │ files. see: https://docs.micropythonos.com/os-development/insta
65
- lling-on-esp32/
64
+ │ files. see:
65
+ https://docs.micropythonos.com/architecture/filesystem/
66
66
  │ • cp-app Copy/update internal_filesystem/apps to the device via │
67
67
  │ "mpremote fs cp". Display a file chooser to select which app to │
68
68
  │ copy/update. But can also be used to copy/update all files. │
@@ -93,13 +93,13 @@ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,
93
93
 
94
94
  [comment]: <> (✂✂✂ auto generated build start ✂✂✂)
95
95
  ```
96
- usage: mposcli build [-h] [{esp32,esp32s3,unix,macOS}] [-v]
96
+ usage: mposcli build [-h] [{esp32,esp32s3,unphone,unix,macOS}] [-v]
97
97
 
98
98
  Build MicroPythonOS by calling: ./scripts/build_mpos.sh <target> see:
99
99
  https://docs.micropythonos.com/os-development/
100
100
 
101
101
  ╭─ positional arguments ───────────────────────────────────────────────────╮
102
- │ [{esp32,esp32s3,unix,macOS}]
102
+ │ [{esp32,esp32s3,unphone,unix,macOS}]
103
103
  │ Target platform to build for. (default: unix) │
104
104
  ╰──────────────────────────────────────────────────────────────────────────╯
105
105
  ╭─ options ────────────────────────────────────────────────────────────────╮
@@ -119,7 +119,7 @@ usage: mposcli cp [-h] [CP OPTIONS]
119
119
 
120
120
  Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp". Display
121
121
  a file chooser to select which files to copy/update. But can also be used to copy/update
122
- all files. see: https://docs.micropythonos.com/os-development/installing-on-esp32/
122
+ all files. see: https://docs.micropythonos.com/architecture/filesystem/
123
123
 
124
124
  ╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
125
125
  │ [{None}|PATH] Optional file or directory path. (default: None) │
@@ -173,7 +173,9 @@ https://docs.micropythonos.com/os-development/installing-on-esp32/
173
173
 
174
174
  ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
175
175
  │ -h, --help show this help message and exit │
176
- │ --port STR Port used for esptool and mpremote (default: /dev/ttyUSB0)
176
+ │ --port {None}|STR Port used for esptool and mpremote, e.g.: "/dev/ttyUSB0" or
177
+ │ "/dev/ttyACM0" etc. Leave empty for autodetection (default: │
178
+ │ None) │
177
179
  │ --address STR Address (default: 0x0) │
178
180
  │ --flash-size STR Flash Size (default: detect) │
179
181
  │ --verify, --no-verify Verify after flashing? (default: True) │
@@ -317,6 +319,11 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
317
319
 
318
320
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
319
321
 
322
+ * [v0.5.0](https://github.com/jedie/mposcli/compare/v0.4.1...v0.5.0)
323
+ * 2026-03-08 - update README
324
+ * 2026-03-05 - Enhance "cp" command and auto restart "mpremote repl"
325
+ * 2026-03-03 - Refactor "cp" command
326
+ * 2026-03-03 - flash command: use port auto detection as default
320
327
  * [v0.4.1](https://github.com/jedie/mposcli/compare/v0.4.0...v0.4.1)
321
328
  * 2026-02-27 - Use "--force" for pulling submodules to overwrite local changes
322
329
  * [v0.4.0](https://github.com/jedie/mposcli/compare/v0.3.0...v0.4.0)
@@ -329,13 +336,13 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
329
336
  * 2026-02-18 - Add "update" beside "update-submodules"
330
337
  * 2026-02-17 - Update requirements
331
338
  * 2026-02-16 - update README
339
+
340
+ <details><summary>Expand older history entries ...</summary>
341
+
332
342
  * [v0.2.0](https://github.com/jedie/mposcli/compare/v0.1.0...v0.2.0)
333
343
  * 2026-02-16 - New CLI command: "cp" with convenience features.
334
344
  * 2026-02-16 - New command: "flash" with file selector
335
345
  * 2026-02-16 - Update README.md
336
-
337
- <details><summary>Expand older history entries ...</summary>
338
-
339
346
  * [v0.1.0](https://github.com/jedie/mposcli/compare/1695026...v0.1.0)
340
347
  * 2026-02-16 - Add "update-submodules" command
341
348
  * 2026-02-16 - Add "build" command
@@ -46,8 +46,8 @@ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,
46
46
  │ • cp Copy/update internal_filesystem/lib/mpos files to the device │
47
47
  │ via "mpremote fs cp". Display a file chooser to select which │
48
48
  │ files to copy/update. But can also be used to copy/update all │
49
- │ files. see: https://docs.micropythonos.com/os-development/insta
50
- lling-on-esp32/
49
+ │ files. see:
50
+ https://docs.micropythonos.com/architecture/filesystem/
51
51
  │ • cp-app Copy/update internal_filesystem/apps to the device via │
52
52
  │ "mpremote fs cp". Display a file chooser to select which app to │
53
53
  │ copy/update. But can also be used to copy/update all files. │
@@ -78,13 +78,13 @@ usage: mposcli [-h] {build,cp,cp-app,flash,run-desktop,update,update-submodules,
78
78
 
79
79
  [comment]: <> (✂✂✂ auto generated build start ✂✂✂)
80
80
  ```
81
- usage: mposcli build [-h] [{esp32,esp32s3,unix,macOS}] [-v]
81
+ usage: mposcli build [-h] [{esp32,esp32s3,unphone,unix,macOS}] [-v]
82
82
 
83
83
  Build MicroPythonOS by calling: ./scripts/build_mpos.sh <target> see:
84
84
  https://docs.micropythonos.com/os-development/
85
85
 
86
86
  ╭─ positional arguments ───────────────────────────────────────────────────╮
87
- │ [{esp32,esp32s3,unix,macOS}]
87
+ │ [{esp32,esp32s3,unphone,unix,macOS}]
88
88
  │ Target platform to build for. (default: unix) │
89
89
  ╰──────────────────────────────────────────────────────────────────────────╯
90
90
  ╭─ options ────────────────────────────────────────────────────────────────╮
@@ -104,7 +104,7 @@ usage: mposcli cp [-h] [CP OPTIONS]
104
104
 
105
105
  Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp". Display
106
106
  a file chooser to select which files to copy/update. But can also be used to copy/update
107
- all files. see: https://docs.micropythonos.com/os-development/installing-on-esp32/
107
+ all files. see: https://docs.micropythonos.com/architecture/filesystem/
108
108
 
109
109
  ╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
110
110
  │ [{None}|PATH] Optional file or directory path. (default: None) │
@@ -158,7 +158,9 @@ https://docs.micropythonos.com/os-development/installing-on-esp32/
158
158
 
159
159
  ╭─ options ──────────────────────────────────────────────────────────────────────────────╮
160
160
  │ -h, --help show this help message and exit │
161
- │ --port STR Port used for esptool and mpremote (default: /dev/ttyUSB0)
161
+ │ --port {None}|STR Port used for esptool and mpremote, e.g.: "/dev/ttyUSB0" or
162
+ │ "/dev/ttyACM0" etc. Leave empty for autodetection (default: │
163
+ │ None) │
162
164
  │ --address STR Address (default: 0x0) │
163
165
  │ --flash-size STR Flash Size (default: detect) │
164
166
  │ --verify, --no-verify Verify after flashing? (default: True) │
@@ -302,6 +304,11 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
302
304
 
303
305
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
304
306
 
307
+ * [v0.5.0](https://github.com/jedie/mposcli/compare/v0.4.1...v0.5.0)
308
+ * 2026-03-08 - update README
309
+ * 2026-03-05 - Enhance "cp" command and auto restart "mpremote repl"
310
+ * 2026-03-03 - Refactor "cp" command
311
+ * 2026-03-03 - flash command: use port auto detection as default
305
312
  * [v0.4.1](https://github.com/jedie/mposcli/compare/v0.4.0...v0.4.1)
306
313
  * 2026-02-27 - Use "--force" for pulling submodules to overwrite local changes
307
314
  * [v0.4.0](https://github.com/jedie/mposcli/compare/v0.3.0...v0.4.0)
@@ -314,13 +321,13 @@ completion,test,update,update-readme-history,update-test-snapshot-files,version}
314
321
  * 2026-02-18 - Add "update" beside "update-submodules"
315
322
  * 2026-02-17 - Update requirements
316
323
  * 2026-02-16 - update README
324
+
325
+ <details><summary>Expand older history entries ...</summary>
326
+
317
327
  * [v0.2.0](https://github.com/jedie/mposcli/compare/v0.1.0...v0.2.0)
318
328
  * 2026-02-16 - New CLI command: "cp" with convenience features.
319
329
  * 2026-02-16 - New command: "flash" with file selector
320
330
  * 2026-02-16 - Update README.md
321
-
322
- <details><summary>Expand older history entries ...</summary>
323
-
324
331
  * [v0.1.0](https://github.com/jedie/mposcli/compare/1695026...v0.1.0)
325
332
  * 2026-02-16 - Add "update-submodules" command
326
333
  * 2026-02-16 - Add "build" command
@@ -4,5 +4,5 @@
4
4
  """
5
5
 
6
6
  # See https://packaging.python.org/en/latest/specifications/version-specifiers/
7
- __version__ = '0.4.1'
7
+ __version__ = '0.5.0'
8
8
  __author__ = 'Jens Diemer <cookiecutter_templates@jensdiemer.de>'
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
18
18
  @app.command
19
19
  def build(
20
20
  target: Annotated[
21
- Literal['esp32', 'esp32s3', 'unix', 'macOS'],
21
+ Literal['esp32', 'esp32s3', 'unphone', 'unix', 'macOS'],
22
22
  tyro.conf.arg(
23
23
  help='Target platform to build for.',
24
24
  ),
@@ -14,6 +14,7 @@ from mposcli.cli_app import app
14
14
  from mposcli.mpos_utils import get_mpos_path
15
15
  from mposcli.tools import get_mpremote_bin
16
16
  from mposcli.user_input import choose_newest_modified_directory, get_newest_files
17
+ from mposcli.utilities.mpremote import MpOsPathResolver, start_mpremote_repl
17
18
 
18
19
 
19
20
  logger = logging.getLogger(__name__)
@@ -44,58 +45,45 @@ def cp(
44
45
  Copy/update internal_filesystem/lib/mpos files to the device via "mpremote fs cp".
45
46
  Display a file chooser to select which files to copy/update.
46
47
  But can also be used to copy/update all files.
47
- see: https://docs.micropythonos.com/os-development/installing-on-esp32/
48
+ see: https://docs.micropythonos.com/architecture/filesystem/
48
49
  """
49
50
  setup_logging(verbosity=verbosity)
50
51
 
51
52
  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)
53
+ resolver = MpOsPathResolver(mpos=mpos_path)
58
54
 
59
55
  mpremote_bin = get_mpremote_bin()
60
56
 
61
57
  print('\n')
62
58
 
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
59
+ if not local_path:
60
+ # Let's the user select from the list of the newest modified files in internal_filesystem/lib/mpos:
61
+ local_path = get_newest_files(resolver.lib_mpos, limit=new_file_limit)
62
+ if not local_path:
63
+ print('Copy/update all files in lib/mpos to the device')
64
+ local_path = resolver.lib_mpos
68
65
 
69
- print(f'Copy/update app: "{local_path}" ...')
66
+ print(f'Copy/update app: "{local_path}" ...')
70
67
 
71
- if not local_path.exists():
72
- print(f'[red]Error: The specified source path "{local_path}" does not exist.[/red]')
73
- return
68
+ if not local_path.exists():
69
+ print(f'[red]Error: The specified source path "{local_path}" does not exist.[/red]')
70
+ return
74
71
 
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
72
+ local_path_str, remote_str = resolver.resolve(local_path)
86
73
 
87
- local_rel_path = local_path.relative_to(mpos_path)
88
- remote_path = f':/{local_path.relative_to(lib_mpos.parent)}'
74
+ popenargs = (mpremote_bin, 'fs')
89
75
 
90
76
  if local_path.is_dir():
91
77
  popenargs += ('-r',)
92
- print(f'Copying directory "[bold]{local_rel_path}[/bold]" to device at "[bold]{remote_path}[/bold]" ...')
78
+ print(f'Copying directory "[bold]{local_path_str}[/bold]" to device at "[bold]{remote_str}[/bold]" ...')
93
79
  else:
94
- print(f'Copying file "[bold]{local_rel_path}[/bold]" to device at "[bold]{remote_path}[/bold]" ...')
80
+ print(f'Copying file "[bold]{local_path_str}[/bold]" to device at "[bold]{remote_str}[/bold]" ...')
95
81
 
96
- popenargs += (local_rel_path, remote_path)
97
82
  verbose_check_call(
98
83
  *popenargs,
84
+ 'cp',
85
+ local_path_str,
86
+ remote_str,
99
87
  verbose=True,
100
88
  cwd=mpos_path,
101
89
  text=None,
@@ -112,15 +100,7 @@ def cp(
112
100
  )
113
101
 
114
102
  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
- )
103
+ start_mpremote_repl()
124
104
 
125
105
 
126
106
  @app.command
@@ -158,14 +138,14 @@ def cp_app(
158
138
 
159
139
  if not app:
160
140
  print('Copy/update all apps in "internal_filesystem/apps" to the device')
161
- local_rel_path = 'internal_filesystem/apps'
141
+ local_path = 'internal_filesystem/apps'
162
142
  remote_path = ':/apps'
163
143
  else:
164
144
  print(f'Copy/update {app=} ...')
165
- local_rel_path = f'internal_filesystem/apps/{app.name}'
145
+ local_path = f'internal_filesystem/apps/{app.name}'
166
146
  remote_path = f':/apps/{app.name}'
167
147
 
168
- popenargs += (local_rel_path, remote_path)
148
+ popenargs += (local_path, remote_path)
169
149
  verbose_check_call(
170
150
  *popenargs,
171
151
  verbose=True,
@@ -18,7 +18,15 @@ logger = logging.getLogger(__name__)
18
18
 
19
19
  @app.command
20
20
  def flash(
21
- port: Annotated[str, tyro.conf.arg(help='Port used for esptool and mpremote')] = '/dev/ttyUSB0',
21
+ port: Annotated[
22
+ str | None,
23
+ tyro.conf.arg(
24
+ help=(
25
+ 'Port used for esptool and mpremote, e.g.: "/dev/ttyUSB0" or "/dev/ttyACM0" etc.'
26
+ ' Leave empty for autodetection'
27
+ )
28
+ ),
29
+ ] = None,
22
30
  address: Annotated[str, tyro.conf.arg(help='Address')] = '0x0',
23
31
  flash_size: Annotated[str, tyro.conf.arg(help='Flash Size')] = 'detect',
24
32
  verify: Annotated[bool, tyro.conf.arg(help='Verify after flashing?')] = True,
@@ -46,10 +54,12 @@ def flash(
46
54
  image_files = lvgl_micropython_build_path.glob('*.bin')
47
55
  image_file = file_chooser(image_files)
48
56
 
57
+ popenargs = (esptool_bin,)
58
+ if port:
59
+ popenargs += ('--port', port)
60
+
49
61
  verbose_check_call(
50
- esptool_bin,
51
- '--port',
52
- port,
62
+ *popenargs,
53
63
  'write-flash',
54
64
  '--flash-size',
55
65
  flash_size,
@@ -63,9 +73,7 @@ def flash(
63
73
 
64
74
  if verify:
65
75
  verbose_check_call(
66
- esptool_bin,
67
- '--port',
68
- port,
76
+ *popenargs,
69
77
  'verify-flash',
70
78
  address,
71
79
  image_file,
@@ -0,0 +1,74 @@
1
+ import tempfile
2
+ from pathlib import Path
3
+ from unittest import TestCase
4
+
5
+ from mposcli.utilities.mpremote import MpOsPathResolver
6
+
7
+
8
+ class ProjectSetupTestCase(TestCase):
9
+ def test_mpos_path_resolver(self):
10
+ with tempfile.TemporaryDirectory() as temp_dir:
11
+ mpos_path = Path(temp_dir).resolve()
12
+
13
+ camera_assets = mpos_path / 'internal_filesystem/apps/com.micropythonos.camera/assets/'
14
+ camera_assets.mkdir(parents=True)
15
+ (camera_assets / 'camera_app.py').touch()
16
+
17
+ board_path = mpos_path / 'internal_filesystem/lib/mpos/board/'
18
+ board_path.mkdir(parents=True)
19
+ (board_path / 'unphone.py').touch()
20
+
21
+ (mpos_path / 'internal_filesystem/lib/drivers/display/hx8357d/').mkdir(parents=True)
22
+
23
+ resolver = MpOsPathResolver(mpos=mpos_path)
24
+ self.assertEqual(
25
+ resolver.resolve(mpos_path / 'internal_filesystem/apps/com.micropythonos.camera'),
26
+ (
27
+ 'internal_filesystem/apps/com.micropythonos.camera/',
28
+ ':apps/',
29
+ ),
30
+ )
31
+ self.assertEqual(
32
+ resolver.resolve(mpos_path / 'internal_filesystem/apps/com.micropythonos.camera/assets/'),
33
+ (
34
+ 'internal_filesystem/apps/com.micropythonos.camera/assets/',
35
+ ':apps/com.micropythonos.camera/',
36
+ ),
37
+ )
38
+ self.assertEqual(
39
+ resolver.resolve(mpos_path / 'internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py'),
40
+ (
41
+ 'internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py',
42
+ ':apps/com.micropythonos.camera/assets/camera_app.py',
43
+ ),
44
+ )
45
+
46
+ self.assertEqual(
47
+ resolver.resolve(mpos_path / 'internal_filesystem/lib/mpos/board/'),
48
+ (
49
+ 'internal_filesystem/lib/mpos/board/',
50
+ ':mpos/',
51
+ ),
52
+ )
53
+ self.assertEqual(
54
+ resolver.resolve(mpos_path / 'internal_filesystem/lib/mpos/board/unphone.py'),
55
+ (
56
+ 'internal_filesystem/lib/mpos/board/unphone.py',
57
+ ':mpos/board/unphone.py',
58
+ ),
59
+ )
60
+
61
+ self.assertEqual(
62
+ resolver.resolve(mpos_path / 'internal_filesystem/lib/drivers/display/hx8357d'),
63
+ (
64
+ 'internal_filesystem/lib/drivers/display/hx8357d/',
65
+ ':lib/drivers/display/',
66
+ ),
67
+ )
68
+ self.assertEqual(
69
+ resolver.resolve(mpos_path / 'internal_filesystem/lib/drivers/display'),
70
+ (
71
+ 'internal_filesystem/lib/drivers/display/',
72
+ ':lib/drivers/',
73
+ ),
74
+ )
@@ -1,3 +1,4 @@
1
+ import functools
1
2
  import shutil
2
3
  import sys
3
4
  from pathlib import Path
@@ -15,12 +16,14 @@ def get_bin(name: str) -> Path:
15
16
  return Path(bin_path)
16
17
 
17
18
 
19
+ @functools.cache
18
20
  def get_esptool_bin() -> Path:
19
21
  esptool_bin = get_bin('esptool')
20
22
  verbose_check_call(esptool_bin, 'version')
21
23
  return esptool_bin
22
24
 
23
25
 
26
+ @functools.cache
24
27
  def get_mpremote_bin():
25
28
  mpremote_bin = get_bin('mpremote')
26
29
  verbose_check_call(mpremote_bin, '--version')
@@ -46,7 +46,7 @@ def file_chooser(paths) -> Path | None:
46
46
  return selection
47
47
 
48
48
 
49
- def get_newest_files(directory, limit=10) -> Path | None:
49
+ def get_newest_files(directory: Path, limit=10) -> Path | None:
50
50
  files = []
51
51
 
52
52
  def scan(dir_path):
@@ -60,7 +60,7 @@ def get_newest_files(directory, limit=10) -> Path | None:
60
60
  scan(directory)
61
61
  files.sort(key=lambda x: x[1], reverse=True)
62
62
 
63
- print(f'[bold]Choose a file[/bold] (only from the newest {limit}):\n')
63
+ print(f'[bold]Choose a file[/bold] (only from the newest {limit} from {directory}):\n')
64
64
  for idx, (entry, mtime) in enumerate(files[:limit]):
65
65
  dt = datetime.datetime.fromtimestamp(mtime).astimezone().strftime('%Y-%m-%d %H:%M:%S')
66
66
  rel_path = Path(entry.path).relative_to(directory)
@@ -85,8 +85,9 @@ def get_newest_files(directory, limit=10) -> Path | None:
85
85
  print(f'[red]Invalid selection: {number}[/red]')
86
86
  sys.exit(1)
87
87
 
88
- print(f'Selected file: {selection}')
89
- return Path(selection)
88
+ absolute_path = directory / selection
89
+ print(f'Selected file: {absolute_path}')
90
+ return absolute_path
90
91
 
91
92
 
92
93
  def choose_newest_modified_directory(base_dir: Path) -> Path:
File without changes
@@ -0,0 +1,64 @@
1
+ import shlex
2
+ import subprocess
3
+ import time
4
+ from pathlib import Path
5
+
6
+ from bx_py_utils.path import assert_is_dir
7
+ from rich import print
8
+
9
+ from mposcli.tools import get_mpremote_bin
10
+
11
+
12
+ class MpOsPathResolver:
13
+ def __init__(self, mpos: Path):
14
+ self.mpos = mpos # e.g.: ~/repos/MicroPythonOS
15
+ self.internal_fs = mpos / 'internal_filesystem'
16
+ self.lib_mpos = self.internal_fs / 'lib' / 'mpos'
17
+ assert_is_dir(self.lib_mpos)
18
+ self.apps_path = self.internal_fs / 'apps'
19
+ assert_is_dir(self.apps_path)
20
+
21
+ def resolve(self, source: Path):
22
+ source = source.resolve()
23
+ assert source.exists(), f'Not existing path: {source=}'
24
+ assert source.is_relative_to(self.mpos), f'{source=} is not inside {self.mpos=}'
25
+
26
+ if source.is_relative_to(self.apps_path):
27
+ device_base_path = self.apps_path.parent
28
+ elif source.is_relative_to(self.lib_mpos):
29
+ device_base_path = self.lib_mpos.parent
30
+ elif source.is_relative_to(self.internal_fs):
31
+ device_base_path = self.internal_fs
32
+ else:
33
+ raise ValueError(f'Path {source} is not in a recognized location')
34
+
35
+ remote_path = source.relative_to(device_base_path)
36
+ source_path = source.relative_to(self.mpos)
37
+ if source.is_dir():
38
+ remote_path = remote_path.parent
39
+ local_path_str = f'{source_path}/'
40
+ remote_str = f':{remote_path}/'
41
+ else:
42
+ local_path_str = f'{source_path}'
43
+ remote_str = f':{remote_path}'
44
+
45
+ return local_path_str, remote_str
46
+
47
+
48
+ def start_mpremote_repl(max_try=10, wait_time=1):
49
+ print('Starting mpremote REPL...')
50
+ time.sleep(wait_time)
51
+ mpremote_bin = get_mpremote_bin()
52
+
53
+ popen_args = (mpremote_bin, 'repl')
54
+
55
+ for try_count in range(max_try):
56
+ print(f'\n+ {shlex.join(str(arg) for arg in popen_args)}')
57
+ try:
58
+ return subprocess.check_call(popen_args)
59
+ except subprocess.CalledProcessError as err:
60
+ print(
61
+ f'[yellow]mpremote finished with [red]exit code {err.returncode!r}[/red]'
62
+ f' Retrying in {wait_time} seconds... (try {try_count + 1}/{max_try})'
63
+ )
64
+ time.sleep(wait_time)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes