florisla / stm32loader

Flash firmware to STM32 microcontrollers using Python.
GNU General Public License v3.0
109 stars 53 forks source link

Fix build with python 3.12 #79

Open ghpzin opened 2 months ago

ghpzin commented 2 months ago

Fixes incorrect use of assert with mock object that became error on python 3.12: https://github.com/python/cpython/issues/100690

Error while running tests before this fix: ```text ============================= test session starts ============================== platform linux -- Python 3.12.4, pytest-8.2.2, pluggy-1.5.0 rootdir: /build/stm32loader-0.7.1 configfile: pyproject.toml collected 49 items tests/unit/test_arguments.py ..... [ 10%] tests/unit/test_bootloader.py ..............................FFF......... [ 95%] . [ 97%] tests/unit/test_hexfile.py . [100%] =================================== FAILURES =================================== __________ test_get_uid_for_known_family_reads_at_correct_address[F1] __________ connection = , family = 'F1' @pytest.mark.parametrize( "family", ["F1", "F3", "F7"], ) def test_get_uid_for_known_family_reads_at_correct_address(connection, family): bootloader = Stm32Bootloader(connection, device_family=family) bootloader.read_memory = MagicMock() bootloader.get_uid() uid_address = bootloader.UID_ADDRESS[family] > assert bootloader.read_memory.called_once_with(uid_address) tests/unit/test_bootloader.py:216: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , name = 'called_once_with' def __getattr__(self, name): if name in {'_mock_methods', '_mock_unsafe'}: raise AttributeError(name) elif self._mock_methods is not None: if name not in self._mock_methods or name in _all_magics: raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST: > raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " f"for the mock if {name!r} is meant to be an attribute.") E AttributeError: 'called_once_with' is not a valid assertion. Use a spec for the mock if 'called_once_with' is meant to be an attribute.. Did you mean: 'assert_called_once_with'? /nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4/lib/python3.12/unittest/mock.py:663: AttributeError __________ test_get_uid_for_known_family_reads_at_correct_address[F3] __________ connection = , family = 'F3' @pytest.mark.parametrize( "family", ["F1", "F3", "F7"], ) def test_get_uid_for_known_family_reads_at_correct_address(connection, family): bootloader = Stm32Bootloader(connection, device_family=family) bootloader.read_memory = MagicMock() bootloader.get_uid() uid_address = bootloader.UID_ADDRESS[family] > assert bootloader.read_memory.called_once_with(uid_address) tests/unit/test_bootloader.py:216: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , name = 'called_once_with' def __getattr__(self, name): if name in {'_mock_methods', '_mock_unsafe'}: raise AttributeError(name) elif self._mock_methods is not None: if name not in self._mock_methods or name in _all_magics: raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST: > raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " f"for the mock if {name!r} is meant to be an attribute.") E AttributeError: 'called_once_with' is not a valid assertion. Use a spec for the mock if 'called_once_with' is meant to be an attribute.. Did you mean: 'assert_called_once_with'? /nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4/lib/python3.12/unittest/mock.py:663: AttributeError __________ test_get_uid_for_known_family_reads_at_correct_address[F7] __________ connection = , family = 'F7' @pytest.mark.parametrize( "family", ["F1", "F3", "F7"], ) def test_get_uid_for_known_family_reads_at_correct_address(connection, family): bootloader = Stm32Bootloader(connection, device_family=family) bootloader.read_memory = MagicMock() bootloader.get_uid() uid_address = bootloader.UID_ADDRESS[family] > assert bootloader.read_memory.called_once_with(uid_address) tests/unit/test_bootloader.py:216: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , name = 'called_once_with' def __getattr__(self, name): if name in {'_mock_methods', '_mock_unsafe'}: raise AttributeError(name) elif self._mock_methods is not None: if name not in self._mock_methods or name in _all_magics: raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST: > raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " f"for the mock if {name!r} is meant to be an attribute.") E AttributeError: 'called_once_with' is not a valid assertion. Use a spec for the mock if 'called_once_with' is meant to be an attribute.. Did you mean: 'assert_called_once_with'? /nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4/lib/python3.12/unittest/mock.py:663: AttributeError =========================== short test summary info ============================ FAILED tests/unit/test_bootloader.py::test_get_uid_for_known_family_reads_at_correct_address[F1] - AttributeError: 'called_once_with' is not a valid assertion. Use a spec for... FAILED tests/unit/test_bootloader.py::test_get_uid_for_known_family_reads_at_correct_address[F3] - AttributeError: 'called_once_with' is not a valid assertion. Use a spec for... FAILED tests/unit/test_bootloader.py::test_get_uid_for_known_family_reads_at_correct_address[F7] - AttributeError: 'called_once_with' is not a valid assertion. Use a spec for... ========================= 3 failed, 46 passed in 0.88s ========================= ```

Second arg (12) seems to come from inside get_uid, which I assume is the one that was tested with this mock: https://github.com/florisla/stm32loader/blob/c56da3ddf552df033f5445963b0a0fce0259125f/stm32loader/bootloader.py#L530