mpytool 2.3.5__tar.gz → 2.3.6__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 (47) hide show
  1. {mpytool-2.3.5 → mpytool-2.3.6}/PKG-INFO +2 -1
  2. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/_version.py +3 -3
  3. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/mpy_cross.py +23 -2
  4. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/PKG-INFO +2 -1
  5. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/SOURCES.txt +1 -0
  6. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/requires.txt +1 -0
  7. {mpytool-2.3.5 → mpytool-2.3.6}/pyproject.toml +1 -0
  8. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_cli.py +8 -0
  9. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_cmd_cp.py +7 -6
  10. mpytool-2.3.6/tests/test_helpers.py +24 -0
  11. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_integration.py +8 -0
  12. {mpytool-2.3.5 → mpytool-2.3.6}/.github/workflows/integration.yml +0 -0
  13. {mpytool-2.3.5 → mpytool-2.3.6}/.github/workflows/publish.yml +0 -0
  14. {mpytool-2.3.5 → mpytool-2.3.6}/.github/workflows/tests.yml +0 -0
  15. {mpytool-2.3.5 → mpytool-2.3.6}/.gitignore +0 -0
  16. {mpytool-2.3.5 → mpytool-2.3.6}/LICENSE +0 -0
  17. {mpytool-2.3.5 → mpytool-2.3.6}/README.md +0 -0
  18. {mpytool-2.3.5 → mpytool-2.3.6}/README_API.md +0 -0
  19. {mpytool-2.3.5 → mpytool-2.3.6}/README_bench.md +0 -0
  20. {mpytool-2.3.5 → mpytool-2.3.6}/README_mpremote.md +0 -0
  21. {mpytool-2.3.5 → mpytool-2.3.6}/completions/_mpytool +0 -0
  22. {mpytool-2.3.5 → mpytool-2.3.6}/completions/mpytool.bash +0 -0
  23. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/__init__.py +0 -0
  24. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/cmd_cp.py +0 -0
  25. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/conn.py +0 -0
  26. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/conn_serial.py +0 -0
  27. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/conn_socket.py +0 -0
  28. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/logger.py +0 -0
  29. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/mount.py +0 -0
  30. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/mpy.py +0 -0
  31. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/mpy_comm.py +0 -0
  32. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/mpytool.py +0 -0
  33. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/speedtest.py +0 -0
  34. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/terminal.py +0 -0
  35. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/terminal_unix.py +0 -0
  36. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/terminal_win.py +0 -0
  37. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool/utils.py +0 -0
  38. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/dependency_links.txt +0 -0
  39. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/entry_points.txt +0 -0
  40. {mpytool-2.3.5 → mpytool-2.3.6}/mpytool.egg-info/top_level.txt +0 -0
  41. {mpytool-2.3.5 → mpytool-2.3.6}/setup.cfg +0 -0
  42. {mpytool-2.3.5 → mpytool-2.3.6}/tests/__init__.py +0 -0
  43. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_errors.py +0 -0
  44. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_mount.py +0 -0
  45. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_mpy.py +0 -0
  46. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_mpytool.py +0 -0
  47. {mpytool-2.3.5 → mpytool-2.3.6}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpytool
3
- Version: 2.3.5
3
+ Version: 2.3.6
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
@@ -10,6 +10,7 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: pyserial>=3.0
13
+ Requires-Dist: mpy-cross
13
14
  Dynamic: license-file
14
15
 
15
16
  # mpytool
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2.3.5'
32
- __version_tuple__ = version_tuple = (2, 3, 5)
31
+ __version__ = version = '2.3.6'
32
+ __version_tuple__ = version_tuple = (2, 3, 6)
33
33
 
34
- __commit_id__ = commit_id = 'gc5fff94f9'
34
+ __commit_id__ = commit_id = 'g9d48ac194'
@@ -5,6 +5,7 @@ import os as _os
5
5
  import re as _re
6
6
  import shutil as _shutil
7
7
  import subprocess as _subprocess
8
+ import sys as _sys
8
9
 
9
10
  from mpytool.logger import SimpleColorLogger as _SimpleColorLogger
10
11
 
@@ -19,6 +20,21 @@ _MPY_ARCH_NAMES = (
19
20
  BOOT_FILES = frozenset(('boot.py', 'main.py'))
20
21
 
21
22
 
23
+ def _find_mpy_cross():
24
+ """Find mpy-cross binary: first in same dir as Python, then in PATH"""
25
+ # Look next to current Python interpreter (same venv)
26
+ bin_dir = _os.path.dirname(_sys.executable)
27
+ local = _os.path.join(bin_dir, 'mpy-cross')
28
+ if _os.path.isfile(local) and _os.access(local, _os.X_OK):
29
+ return local
30
+ # Windows: mpy-cross.exe
31
+ local_exe = local + '.exe'
32
+ if _os.path.isfile(local_exe) and _os.access(local_exe, _os.X_OK):
33
+ return local_exe
34
+ # Fallback to PATH
35
+ return _shutil.which('mpy-cross')
36
+
37
+
22
38
  class MpyCross:
23
39
  """Mpy-cross compiler wrapper with caching
24
40
 
@@ -29,6 +45,7 @@ class MpyCross:
29
45
  def __init__(self, log=None):
30
46
  self._log = log if log is not None else _SimpleColorLogger()
31
47
  self.active = False
48
+ self._bin = None # path to mpy-cross binary
32
49
  self._ver = None # (major, sub) device mpy version
33
50
  self._arch = None # architecture name for cache key
34
51
  self._args = [] # extra mpy-cross args (-b, -march)
@@ -53,7 +70,7 @@ class MpyCross:
53
70
  Sets self.active = True on success, False on failure.
54
71
  """
55
72
  self.active = False
56
- mpy_cross_bin = _shutil.which('mpy-cross')
73
+ mpy_cross_bin = _find_mpy_cross()
57
74
  if not mpy_cross_bin:
58
75
  self._log.warning(
59
76
  'mpy-cross not found in PATH (pip install mpy-cross),'
@@ -99,6 +116,7 @@ class MpyCross:
99
116
  self._log.debug(
100
117
  'device v%s mpy v%d.%d%s, mpy-cross %s',
101
118
  dev_version, dev_ver, dev_sub, arch_str, cross_str)
119
+ self._bin = mpy_cross_bin
102
120
  self.active = True
103
121
 
104
122
  def compile(self, src_path):
@@ -106,7 +124,10 @@ class MpyCross:
106
124
 
107
125
  Skips boot.py, main.py, and non-.py files.
108
126
  Uses cache if fresh (mtime check).
127
+ Returns None if not active (init() not called or failed).
109
128
  """
129
+ if not self.active:
130
+ return None
110
131
  basename = _os.path.basename(src_path)
111
132
  if basename in BOOT_FILES:
112
133
  self._log.debug('mpy: skip %s (boot file)', basename)
@@ -126,7 +147,7 @@ class MpyCross:
126
147
  return cache_path
127
148
  # Compile
128
149
  _os.makedirs(cache_dir, exist_ok=True)
129
- cmd = ['mpy-cross'] + self._args + ['-o', cache_path, src_path]
150
+ cmd = [self._bin] + self._args + ['-o', cache_path, src_path]
130
151
  self._log.info('$ %s', ' '.join(cmd))
131
152
  result = _subprocess.run(
132
153
  cmd, capture_output=True, text=True, timeout=30)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpytool
3
- Version: 2.3.5
3
+ Version: 2.3.6
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
@@ -10,6 +10,7 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: pyserial>=3.0
13
+ Requires-Dist: mpy-cross
13
14
  Dynamic: license-file
14
15
 
15
16
  # mpytool
@@ -37,6 +37,7 @@ tests/__init__.py
37
37
  tests/test_cli.py
38
38
  tests/test_cmd_cp.py
39
39
  tests/test_errors.py
40
+ tests/test_helpers.py
40
41
  tests/test_integration.py
41
42
  tests/test_mount.py
42
43
  tests/test_mpy.py
@@ -1 +1,2 @@
1
1
  pyserial>=3.0
2
+ mpy-cross
@@ -15,6 +15,7 @@ authors = [
15
15
  keywords = ["MPY", "micropython"]
16
16
  dependencies = [
17
17
  "pyserial>=3.0",
18
+ "mpy-cross",
18
19
  ]
19
20
 
20
21
  [project.urls]
@@ -51,6 +51,14 @@ def requires_device_rw(cls):
51
51
  return cls
52
52
 
53
53
 
54
+ def setUpModule():
55
+ """Reset device before running CLI tests"""
56
+ if not PORT_RO:
57
+ return # No device, tests will be skipped
58
+ from tests.test_helpers import reset_device
59
+ reset_device(PORT_RO)
60
+
61
+
54
62
  # =============================================================================
55
63
  # Tests without device (always run)
56
64
  # =============================================================================
@@ -738,6 +738,7 @@ class TestMpyCompilation(unittest.TestCase):
738
738
  def _setup_compiler(self, arch=None):
739
739
  """Setup compiler with active state and fake version"""
740
740
  self.compiler.active = True
741
+ self.compiler._bin = 'mpy-cross'
741
742
  self.compiler._ver = (6, 3)
742
743
  self.compiler._arch = arch
743
744
  self.compiler._args = []
@@ -915,9 +916,9 @@ class TestMpyCompilation(unittest.TestCase):
915
916
  self.assertEqual(call_args[0][0], compiled_data)
916
917
  self.assertEqual(call_args[0][1], '/script.mpy')
917
918
 
918
- @patch('mpytool.mpy_cross._shutil.which', return_value='/usr/bin/mpy-cross')
919
+ @patch('mpytool.mpy_cross._find_mpy_cross', return_value='/usr/bin/mpy-cross')
919
920
  @patch('mpytool.mpy_cross._subprocess.run')
920
- def test_init_version_match(self, mock_run, mock_which):
921
+ def test_init_version_match(self, mock_run, mock_find):
921
922
  """init should succeed when versions match"""
922
923
  mock_run.return_value = Mock(
923
924
  returncode=0,
@@ -930,9 +931,9 @@ class TestMpyCompilation(unittest.TestCase):
930
931
  self.assertEqual(self.compiler._ver, (6, 3))
931
932
  self.assertIn('-march=armv6m', self.compiler._args)
932
933
 
933
- @patch('mpytool.mpy_cross._shutil.which', return_value='/usr/bin/mpy-cross')
934
+ @patch('mpytool.mpy_cross._find_mpy_cross', return_value='/usr/bin/mpy-cross')
934
935
  @patch('mpytool.mpy_cross._subprocess.run')
935
- def test_init_version_mismatch_uses_b_flag(self, mock_run, mock_which):
936
+ def test_init_version_mismatch_uses_b_flag(self, mock_run, mock_find):
936
937
  """init should use -b flag on version mismatch"""
937
938
  mock_run.return_value = Mock(
938
939
  returncode=0,
@@ -947,8 +948,8 @@ class TestMpyCompilation(unittest.TestCase):
947
948
  self.assertIn('6.1', self.compiler._args)
948
949
  self.assertIn('-march=xtensawin', self.compiler._args)
949
950
 
950
- @patch('mpytool.mpy_cross._shutil.which', return_value=None)
951
- def test_init_not_found(self, mock_which):
951
+ @patch('mpytool.mpy_cross._find_mpy_cross', return_value=None)
952
+ def test_init_not_found(self, mock_find):
952
953
  """init should deactivate when mpy-cross not in PATH"""
953
954
  self.compiler._log = Mock()
954
955
  self.compiler.init({'mpy_ver': 6, 'mpy_sub': 3, 'version': '1.24.0'})
@@ -0,0 +1,24 @@
1
+ """Shared test helpers for integration and CLI tests"""
2
+
3
+
4
+ def reset_device(port):
5
+ """Reset device before tests and wait for it to be ready
6
+
7
+ Tries hardware reset first (USB-UART), falls back to soft reset (CDC).
8
+ Actively waits for REPL to be ready (no fixed sleep).
9
+ """
10
+ from mpytool import ConnSerial, Mpy
11
+ from mpytool.conn import ConnError
12
+
13
+ conn = ConnSerial(port=port, baudrate=115200)
14
+ mpy = Mpy(conn)
15
+ try:
16
+ # Try hardware reset first (USB-UART only)
17
+ conn.hard_reset()
18
+ except ConnError:
19
+ # CDC devices don't support hardware reset, use soft reset
20
+ mpy.soft_reset()
21
+ # Wait for device to boot and REPL to be ready
22
+ mpy.comm._repl_mode = None # Force re-detection
23
+ mpy.comm.stop_current_operation()
24
+ conn.close()
@@ -53,6 +53,14 @@ def requires_device_rw(cls):
53
53
  requires_device_wipe = requires_device_rw
54
54
 
55
55
 
56
+ def setUpModule():
57
+ """Reset device before running integration tests"""
58
+ if not PORT_RO:
59
+ return # No device, tests will be skipped
60
+ from tests.test_helpers import reset_device
61
+ reset_device(PORT_RO)
62
+
63
+
56
64
  @requires_device
57
65
  class TestDeviceConnection(unittest.TestCase):
58
66
  """Test basic device connection"""
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
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