diff --git a/.gitignore b/.gitignore index 4b84051..62b9af2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc +*.py.bak # Virtualenv venv/ @@ -7,3 +8,7 @@ venv/ build/ dist/ nrfutil.egg-info/ + +# Test artefacts +/nordicsemi/dfu/tests/mypackage.zip +/tests/test.zip diff --git a/README.md b/README.md index dce36b9..296899e 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ You will need to clone the present repository first to run or install nrfutil fr To install nrfutil from source the following prerequisites must be satisfied: -* [Python 2.7 (2.7.10 or newer, not Python 3)](https://www.python.org/downloads/) +* [Python 3.7](https://www.python.org/downloads/) * [pip](https://pip.pypa.io/en/stable/installing.html) * setuptools (upgrade to latest version): `pip install -U setuptools` @@ -81,7 +81,7 @@ pip install -r requirements.txt You can run the program directly without installing it by executing: ``` -python nordicsemi/__main__.py +python ./nordicsemi ``` ### Installing from source @@ -101,8 +101,6 @@ pyinstaller /full/path/to/nrfutil.spec **Note**: Some anti-virus programs will stop PyInstaller from executing correctly when it modifies the executable file. -**Note**: PyInstaller on macOS may have issues with newer versions of Python 2.7.x., e.g. error message `Failed to execute script pyi_rth_pkgres` at runtime. Try rolling back to Python v2.7.10 if you experience such problems. - ## Usage To get info on usage of nrfutil: diff --git a/nordicsemi/__main__.py b/nordicsemi/__main__.py index f826357..43a6e06 100755 --- a/nordicsemi/__main__.py +++ b/nordicsemi/__main__.py @@ -485,7 +485,7 @@ def display(key_file, key, format, out_file): if key == "pk": kstr = signer.get_vk(format, dbg) elif key == "sk": - kstr = "\nWARNING: Security risk! Do not share the private key.\n\n" + kstr = b"\nWARNING: Security risk! Do not share the private key.\n\n" kstr = kstr + signer.get_sk(format, dbg) if not out_file: @@ -806,7 +806,7 @@ def generate(zipfile, try: # This will parse any string starting with 0x as base 16. sd_req_list = sd_req.split(',') - sd_req_list = map(int_as_text_to_int, sd_req_list) + sd_req_list = list(map(int_as_text_to_int, sd_req_list)) except ValueError: raise NordicSemiException("Could not parse value for --sd-req. " "Hex values should be prefixed with 0x.") @@ -816,7 +816,7 @@ def generate(zipfile, try: # This will parse any string starting with 0x as base 16. sd_id_list = sd_id.split(',') - sd_id_list = map(int_as_text_to_int, sd_id_list) + sd_id_list = list(map(int_as_text_to_int, sd_id_list)) # Copy all IDs from sd_id_list to sd_req_list, without duplicates. # This ensures that the softdevice update can be repeated in case @@ -867,11 +867,11 @@ def generate(zipfile, if zigbee: inner_external_app = False - if zigbee_ota_min_hw_version > 0xFFFF: + if zigbee_ota_min_hw_version is not None and zigbee_ota_min_hw_version > 0xFFFF: click.echo('Error: zigbee-ota-min-hw-version exceeds 2-byte long integer.') return - if zigbee_ota_max_hw_version > 0xFFFF: + if zigbee_ota_max_hw_version is not None and zigbee_ota_max_hw_version > 0xFFFF: click.echo('Error: zigbee-ota-max-hw-version exceeds 2-byte long integer.') return @@ -1092,7 +1092,7 @@ def serial(package, port, connect_delay, flow_control, packet_receipt_notificati def enumerate_ports(): - descs = BLEDriver.enum_serial_ports() + descs = list(BLEDriver.enum_serial_ports()) if len(descs) == 0: return None click.echo('Please select connectivity serial port:') @@ -1364,7 +1364,7 @@ def thread(package, port, address, server_port, panid, channel, jlink_snr, flash mcast_dfu = False if address is None: - address = ipaddress.ip_address(u"ff03::1") + address = ipaddress.ip_address("ff03::1") click.echo("Address not specified. Using ff03::1 (all Thread nodes)") else: try: diff --git a/nordicsemi/bluetooth/hci/slip.py b/nordicsemi/bluetooth/hci/slip.py index b95d97b..33a8e08 100644 --- a/nordicsemi/bluetooth/hci/slip.py +++ b/nordicsemi/bluetooth/hci/slip.py @@ -1,5 +1,4 @@ -# -# Copyright (c) 2016 Nordic Semiconductor ASA +# Copyright (c) 2016 - 2019 Nordic Semiconductor ASA # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -33,26 +32,25 @@ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# import logging logger = logging.getLogger(__name__) -class Slip(object): - def __init__(self): - self.SLIP_END = '\xc0' - self.SLIP_ESC = '\xdb' - self.SLIP_ESC_END = '\xdc' - self.SLIP_ESC_ESC = '\xdd' +class Slip: + SLIP_END = 0xc0 + SLIP_ESC = 0xdb + SLIP_ESC_END = 0xdc + SLIP_ESC_ESC = 0xdd + def __init__(self): self.started = False self.escaped = False - self.stream = '' - self.packet = '' + self.stream = bytearray() + self.packet = bytearray() - def append(self, data): + def append(self, data: bytes): """ Append a new :param data: Append a new block of data to do decoding on when calling decode. @@ -66,7 +64,7 @@ def decode(self): Decodes a package according to http://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol :return Slip: A list of decoded slip packets """ - packet_list = list() + packet_list = [] for char in self.stream: if char == self.SLIP_END: @@ -74,51 +72,51 @@ def decode(self): if len(self.packet) > 0: self.started = False packet_list.append(self.packet) - self.packet = '' + self.packet = bytearray() else: self.started = True - self.packet = '' + self.packet = bytearray() elif char == self.SLIP_ESC: self.escaped = True elif char == self.SLIP_ESC_END: if self.escaped: - self.packet += self.SLIP_END + self.packet.append(self.SLIP_END) self.escaped = False else: self.packet += char elif char == self.SLIP_ESC_ESC: if self.escaped: - self.packet += self.SLIP_ESC + self.packet.append(self.SLIP_ESC) self.escaped = False else: - self.packet += char + self.packet.append(char) else: if self.escaped: logging.error("Error in SLIP packet, ignoring error.") - self.packet = '' + self.packet = bytearray() self.escaped = False else: - self.packet += char + self.packet.append(char) - self.stream = '' + self.stream.clear() return packet_list - def encode(self, packet): + def encode(self, packet: bytes): """ Encode a packet according to SLIP. :param packet: A str array that represents the package :return: str array with an encoded SLIP packet """ - encoded = self.SLIP_END + encoded = bytearray([self.SLIP_END]) for char in packet: if char == self.SLIP_END: - encoded += self.SLIP_ESC + self.SLIP_ESC_END + encoded.extend([self.SLIP_ESC, self.SLIP_ESC_END]) elif char == self.SLIP_ESC: - encoded += self.SLIP_ESC + self.SLIP_ESC_ESC + encoded.extend([self.SLIP_ESC, self.SLIP_ESC_ESC]) else: - encoded += char - encoded += self.SLIP_END + encoded.append(char) + encoded.append(self.SLIP_END) return encoded diff --git a/nordicsemi/bluetooth/hci/tests/test_codec.py b/nordicsemi/bluetooth/hci/tests/test_codec.py index b0d2a62..8967d2e 100644 --- a/nordicsemi/bluetooth/hci/tests/test_codec.py +++ b/nordicsemi/bluetooth/hci/tests/test_codec.py @@ -64,7 +64,7 @@ def test_decode_packet(self): for uart_packet in read_packets: hex_string = uart_packet.replace(" ", "") - hex_data = hex_string.decode("hex") + hex_data = bytes.fromhex(hex_string) slip.append(hex_data) packets = slip.decode() diff --git a/nordicsemi/dfu/bl_dfu_sett.py b/nordicsemi/dfu/bl_dfu_sett.py index f0b71cf..32111b7 100755 --- a/nordicsemi/dfu/bl_dfu_sett.py +++ b/nordicsemi/dfu/bl_dfu_sett.py @@ -47,7 +47,6 @@ # 3rd party libraries import intelhex -from intelhex import IntelHexError # Nordic libraries from nordicsemi.dfu.nrfhex import * @@ -112,8 +111,7 @@ class BLDFUSettings(object): bl_sett_52840_addr = 0x000FF000 bl_sett_backup_offset = 0x1000 - - def __init__(self, ): + def __init__(self): """ """ # instantiate a hex object @@ -168,7 +166,7 @@ def _calculate_crc32_from_hex(self, ih_object, start_addr=None, end_addr=None): list = [] if start_addr == None and end_addr == None: hex_dict = ih_object.todict() - for addr, byte in hex_dict.items(): + for addr, byte in list(hex_dict.items()): list.append(byte) else: for addr in range(start_addr, end_addr + 1): @@ -215,21 +213,22 @@ def generate(self, arch, app_file, app_ver, bl_ver, bl_sett_ver, custom_bl_sett_ self.app_boot_validation_bytes = struct.pack('> 8 & 0x00FF) | (crc << 8 & 0xFF00) - crc ^= ord(b) crc ^= (crc & 0x00FF) >> 4 crc ^= (crc << 8) << 4 crc ^= ((crc & 0x00FF) << 4) << 1 diff --git a/nordicsemi/dfu/dfu_transport.py b/nordicsemi/dfu/dfu_transport.py index 939dd88..1910f78 100644 --- a/nordicsemi/dfu/dfu_transport.py +++ b/nordicsemi/dfu/dfu_transport.py @@ -1,5 +1,4 @@ -# -# Copyright (c) 2016 Nordic Semiconductor ASA +# Copyright (c) 2016 - 2019 Nordic Semiconductor ASA # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -33,12 +32,11 @@ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Python specific imports -import abc import logging +from abc import ABC, abstractmethod + # Nordic Semiconductor imports logger = logging.getLogger(__name__) @@ -54,7 +52,7 @@ class DfuEvent: PROGRESS_EVENT = 1 -class DfuTransport(object): +class DfuTransport(ABC): """ This class as an abstract base class inherited from when implementing transports. @@ -62,7 +60,6 @@ class DfuTransport(object): than this class describes. But the intent is that the implementer shall follow the semantic as best as she can. """ - __metaclass__ = abc.ABCMeta OP_CODE = { 'CreateObject' : 0x01, @@ -105,12 +102,12 @@ class DfuTransport(object): "The requested firmware to update was already present on the system.", ] - @abc.abstractmethod + @abstractmethod def __init__(self): self.callbacks = {} - @abc.abstractmethod + @abstractmethod def open(self): """ Open a port if appropriate for the transport. @@ -119,7 +116,7 @@ def open(self): pass - @abc.abstractmethod + @abstractmethod def close(self): """ Close a port if appropriate for the transport. @@ -127,7 +124,7 @@ def close(self): """ pass - @abc.abstractmethod + @abstractmethod def send_init_packet(self, init_packet): """ Send init_packet to device. @@ -140,7 +137,7 @@ def send_init_packet(self, init_packet): pass - @abc.abstractmethod + @abstractmethod def send_firmware(self, firmware): """ Start sending firmware to device. @@ -176,6 +173,6 @@ def _send_event(self, event_type, **kwargs): :param kwargs: Arguments to callback function :return: """ - if event_type in self.callbacks.keys(): + if event_type in list(self.callbacks.keys()): for callback in self.callbacks[event_type]: callback(**kwargs) diff --git a/nordicsemi/dfu/dfu_transport_ant.py b/nordicsemi/dfu/dfu_transport_ant.py index ceead23..03f4655 100755 --- a/nordicsemi/dfu/dfu_transport_ant.py +++ b/nordicsemi/dfu/dfu_transport_ant.py @@ -39,7 +39,7 @@ import binascii from datetime import datetime, timedelta import logging -import Queue +import queue import struct import sys @@ -132,7 +132,7 @@ def __init__(self, ant_dev, timeout, search_timeout, ant_config): self.tx_seq = None self.rx_seq = None self.rx_data = None - self.resp_queue = Queue.Queue() + self.resp_queue = queue.Queue() def open(self): # TODO: use constant from antlib when it exists. @@ -187,7 +187,7 @@ def send_message(self, req): logger.log(TRANSPORT_LOGGING_LEVEL, "ANT: --> {}".format(req)) self.tx_seq = (self.tx_seq + 1) & 0xFF - data = map(ord, struct.pack('= 3: - logger.info("\nBLE: ble_enable with local ATT MTU: {}".format(DFUAdapter.LOCAL_ATT_MTU)) - ble_enable_params.att_mtu = DFUAdapter.LOCAL_ATT_MTU - self.adapter.driver.ble_enable(ble_enable_params) + assert nrf_sd_ble_api_ver in [2, 5] + + if nrf_sd_ble_api_ver == 2: + self.adapter.driver.ble_enable( + BLEEnableParams( + vs_uuid_count = 10, + service_changed = True, + periph_conn_count = 0, + central_conn_count = 1, + central_sec_count = 1, + ) + ) + + if nrf_sd_ble_api_ver == 5: + self.adapter.driver.ble_cfg_set( + BLEConfig.conn_gatt, + BLEConfigConnGatt(att_mtu=DFUAdapter.LOCAL_ATT_MTU), + ) + self.adapter.driver.ble_enable() + self.adapter.driver.ble_vs_uuid_add(DFUAdapter.BASE_UUID) def close(self): @@ -159,7 +169,7 @@ def connect(self, target_device_name, target_device_addr): if nrf_sd_ble_api_ver >= 3: if DFUAdapter.LOCAL_ATT_MTU > ATT_MTU_DEFAULT: logger.info('BLE: Enabling longer ATT MTUs') - self.att_mtu = self.adapter.att_mtu_exchange(self.conn_handle) + self.att_mtu = self.adapter.att_mtu_exchange(self.conn_handle, DFUAdapter.LOCAL_ATT_MTU) else: logger.info('BLE: Using default ATT MTU') @@ -371,7 +381,12 @@ def on_gap_evt_adv_report(self, ble_driver, conn_handle, peer_addr, rssi, adv_ty slave_latency = 0) logger.info('BLE: Found target advertiser, address: 0x{}, name: {}'.format(address_string, dev_name)) logger.info('BLE: Connecting to 0x{}'.format(address_string)) - self.adapter.connect(address = peer_addr, conn_params = self.conn_params) + # Connect must specify tag=1 to enable the settings + # set with BLEConfigConnGatt (that implictly operates + # on connections with tag 1) to allow for larger MTU. + self.adapter.connect(address=peer_addr, + conn_params=self.conn_params, + tag=1) # store the address for subsequent connections self.target_device_addr = address_string self.target_device_addr_type = peer_addr @@ -426,7 +441,7 @@ def __init__(self, att_mtu, target_device_name=None, target_device_addr=None, - baud_rate=115200, + baud_rate=1000000, prn=0): super(DfuTransportBle, self).__init__() DFUAdapter.LOCAL_ATT_MTU = att_mtu @@ -560,7 +575,7 @@ def try_to_recover(): def __set_prn(self): logger.debug("BLE: Set Packet Receipt Notification {}".format(self.prn)) - self.dfu_adapter.write_control_point([DfuTransportBle.OP_CODE['SetPRN']] + map(ord, struct.pack('= settings.bl_sett_addr} + backup_dict = {(k + settings.bl_sett_backup_offset): v for k, v in list(settings.ihex.todict().items()) if k < settings.bl_sett_addr} + settings_dict = {k: v for k, v in list(settings.ihex.todict().items()) if k >= settings.bl_sett_addr} self.assertEqual(backup_dict, settings_dict) def test_generate_with_backup_page_custom_address(self): @@ -206,7 +206,7 @@ def test_generate_with_backup_page_custom_address(self): key_file=None) self.assertEqual(settings.backup_address, 0x0006F000) - self.assertTrue(0x0006F000 in settings.ihex.todict().keys()) + self.assertTrue(0x0006F000 in list(settings.ihex.todict().keys())) def test_generate_with_backup_page_default_address(self): settings = BLDFUSettings() @@ -224,7 +224,7 @@ def test_generate_with_backup_page_default_address(self): key_file=None) self.assertEqual(settings.backup_address, (0x0006F000 - settings.bl_sett_backup_offset)) - self.assertTrue((0x0006F000 - settings.bl_sett_backup_offset) in settings.ihex.todict().keys()) + self.assertTrue((0x0006F000 - settings.bl_sett_backup_offset) in list(settings.ihex.todict().keys())) class TestBLDFUSettingsV2(unittest.TestCase): def setUp(self): @@ -369,7 +369,7 @@ def test_generate_with_backup_page_check_size(self): sd_file=None, key_file=None) - self.assertEqual(len(settings.ihex.todict().keys()), len(settings_raw.ihex.todict().keys()) * 2) + self.assertEqual(len(list(settings.ihex.todict().keys())), len(list(settings_raw.ihex.todict().keys())) * 2) def test_generate_with_backup_page_check_values(self): settings = BLDFUSettings() @@ -386,8 +386,8 @@ def test_generate_with_backup_page_check_values(self): sd_file=None, key_file=None) - backup_dict = {(k + settings.bl_sett_backup_offset): v for k, v in settings.ihex.todict().items() if k < settings.bl_sett_addr} - settings_dict = {k: v for k, v in settings.ihex.todict().items() if k >= settings.bl_sett_addr} + backup_dict = {(k + settings.bl_sett_backup_offset): v for k, v in list(settings.ihex.todict().items()) if k < settings.bl_sett_addr} + settings_dict = {k: v for k, v in list(settings.ihex.todict().items()) if k >= settings.bl_sett_addr} self.assertEqual(backup_dict, settings_dict) def test_generate_with_backup_page_custom_address(self): @@ -406,7 +406,7 @@ def test_generate_with_backup_page_custom_address(self): key_file=None) self.assertEqual(settings.backup_address, 0x0006F000) - self.assertTrue(0x0006F000 in settings.ihex.todict().keys()) + self.assertTrue(0x0006F000 in list(settings.ihex.todict().keys())) def test_generate_with_backup_page_default_address(self): settings = BLDFUSettings() @@ -424,7 +424,7 @@ def test_generate_with_backup_page_default_address(self): key_file=None) self.assertEqual(settings.backup_address, (0x0006F000 - settings.bl_sett_backup_offset)) - self.assertTrue((0x0006F000 - settings.bl_sett_backup_offset) in settings.ihex.todict().keys()) + self.assertTrue((0x0006F000 - settings.bl_sett_backup_offset) in list(settings.ihex.todict().keys())) def test_generate_with_app_boot_validation_crc(self): settings = BLDFUSettings() @@ -464,8 +464,8 @@ def test_generate_with_app_boot_validation_sha256(self): self.assertEqual(0x1316CFD0, settings.crc) self.assertEqual(0xF78E451E, settings.boot_validation_crc) self.assertEqual(0x02, settings.app_boot_validation_type) - self.assertEqual('036F52C9EBB53819D6E2B6FB57803823E864783B04D7331B46C0B5897CA9F1C7', - binascii.hexlify(settings.app_boot_validation_bytes).upper()) + self.assertEqual(bytes.fromhex('036F52C9EBB53819D6E2B6FB57803823E864783B04D7331B46C0B5897CA9F1C7'), + settings.app_boot_validation_bytes) def test_generate_with_app_boot_validation_ecdsa(self): settings = BLDFUSettings() @@ -525,8 +525,8 @@ def test_generate_with_sd_boot_validation_sha256(self): self.assertEqual(0x4637019F, settings.crc) self.assertEqual(0x9C761426, settings.boot_validation_crc) self.assertEqual(0x02, settings.sd_boot_validation_type) - self.assertEqual('036F52C9EBB53819D6E2B6FB57803823E864783B04D7331B46C0B5897CA9F1C7', - binascii.hexlify(settings.sd_boot_validation_bytes).upper()) + self.assertEqual(bytes.fromhex('036F52C9EBB53819D6E2B6FB57803823E864783B04D7331B46C0B5897CA9F1C7'), + settings.sd_boot_validation_bytes) def test_generate_with_sd_boot_validation_ecdsa(self): settings = BLDFUSettings() diff --git a/nordicsemi/dfu/tests/test_dfu_transport_serial.py b/nordicsemi/dfu/tests/test_dfu_transport_serial.py index 33be969..fa049cf 100644 --- a/nordicsemi/dfu/tests/test_dfu_transport_serial.py +++ b/nordicsemi/dfu/tests/test_dfu_transport_serial.py @@ -103,7 +103,7 @@ def error_callback(log_message=""): self.transport.register_events_callback(DfuEvent.PROGRESS_EVENT, progress_callback) self.transport.register_events_callback(DfuEvent.ERROR_EVENT, error_callback()) - firmware = '' + firmware = b'' test_firmware_path = os.path.join("firmwares", "pca10028_nrf51422_xxac_blinky.bin") with open(test_firmware_path, 'rb') as f: diff --git a/nordicsemi/dfu/tests/test_package.py b/nordicsemi/dfu/tests/test_package.py index 6203a9d..18340a8 100644 --- a/nordicsemi/dfu/tests/test_package.py +++ b/nordicsemi/dfu/tests/test_package.py @@ -76,11 +76,11 @@ def test_generate_package_application(self): with open(os.path.join(self.work_directory, 'manifest.json'), 'r') as f: _json = json.load(f) - self.assertEqual(u'bar.bin', _json['manifest']['application']['bin_file']) - self.assertEqual(u'bar.dat', _json['manifest']['application']['dat_file']) - self.assertTrue(u'softdevice' not in _json['manifest']) - self.assertTrue(u'softdevice_bootloader' not in _json['manifest']) - self.assertTrue(u'bootloader' not in _json['manifest']) + self.assertEqual('bar.bin', _json['manifest']['application']['bin_file']) + self.assertEqual('bar.dat', _json['manifest']['application']['dat_file']) + self.assertTrue('softdevice' not in _json['manifest']) + self.assertTrue('softdevice_bootloader' not in _json['manifest']) + self.assertTrue('bootloader' not in _json['manifest']) def test_generate_package_sd_bl(self): self.p = Package(app_version=100, @@ -108,8 +108,8 @@ def test_generate_package_sd_bl(self): with open(os.path.join(self.work_directory, 'manifest.json'), 'r') as f: _json = json.load(f) - self.assertEqual(u'sd_bl.bin', _json['manifest']['softdevice_bootloader']['bin_file']) - self.assertEqual(u'sd_bl.dat', _json['manifest']['softdevice_bootloader']['dat_file']) + self.assertEqual('sd_bl.bin', _json['manifest']['softdevice_bootloader']['bin_file']) + self.assertEqual('sd_bl.dat', _json['manifest']['softdevice_bootloader']['dat_file']) def test_unpack_package_a(self): self.p = Package(app_version=100, @@ -122,7 +122,7 @@ def test_unpack_package_a(self): unpacked_dir = os.path.join(self.work_directory, "unpacked") manifest = self.p.unpack_package(os.path.join(self.work_directory, pkg_name), unpacked_dir) self.assertIsNotNone(manifest) - self.assertEqual(u'bar.bin', manifest.softdevice.bin_file) + self.assertEqual('bar.bin', manifest.softdevice.bin_file) # self.assertEqual(0, manifest.softdevice.init_packet_data.ext_packet_id) # self.assertIsNotNone(manifest.softdevice.init_packet_data.firmware_crc16) @@ -137,7 +137,7 @@ def test_unpack_package_b(self): unpacked_dir = os.path.join(self.work_directory, "unpacked") manifest = self.p.unpack_package(os.path.join(self.work_directory, pkg_name), unpacked_dir) self.assertIsNotNone(manifest) - self.assertEqual(u'bar.bin', manifest.softdevice.bin_file) + self.assertEqual('bar.bin', manifest.softdevice.bin_file) def test_unpack_package_c(self): self.p = Package(app_version=100, @@ -150,7 +150,7 @@ def test_unpack_package_c(self): unpacked_dir = os.path.join(self.work_directory, "unpacked") manifest = self.p.unpack_package(os.path.join(self.work_directory, pkg_name), unpacked_dir) self.assertIsNotNone(manifest) - self.assertEqual(u'bar.bin', manifest.softdevice.bin_file) + self.assertEqual('bar.bin', manifest.softdevice.bin_file) if __name__ == '__main__': diff --git a/nordicsemi/dfu/tests/test_signing.py b/nordicsemi/dfu/tests/test_signing.py index c056513..e74af27 100644 --- a/nordicsemi/dfu/tests/test_signing.py +++ b/nordicsemi/dfu/tests/test_signing.py @@ -44,7 +44,7 @@ from nordicsemi.dfu.signing import Signing -class TestSinging(unittest.TestCase): +class TestSigning(unittest.TestCase): def setUp(self): script_abspath = os.path.abspath(__file__) script_dirname = os.path.dirname(script_abspath) @@ -114,10 +114,10 @@ def test_get_sk_hex(self): def test_get_vk_pem(self): key_file_name = 'key.pem' - expected_vk_pem = "-----BEGIN PUBLIC KEY-----\n" \ - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZY2i7duYH2l9rnIg1oIXq+0/uHAF\n" \ - "7IoFubVru6oX9GCQm67NrXImwgS2ErZi/0/MvRsMkIQQkNg6Wc2tbJgdTA==\n" \ - "-----END PUBLIC KEY-----\n" + expected_vk_pem = b"-----BEGIN PUBLIC KEY-----\n" \ + b"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZY2i7duYH2l9rnIg1oIXq+0/uHAF\n" \ + b"7IoFubVru6oX9GCQm67NrXImwgS2ErZi/0/MvRsMkIQQkNg6Wc2tbJgdTA==\n" \ + b"-----END PUBLIC KEY-----\n" signing = Signing() signing.load_key(key_file_name) diff --git a/nordicsemi/dfu/util.py b/nordicsemi/dfu/util.py index bd9674d..8f85c5b 100644 --- a/nordicsemi/dfu/util.py +++ b/nordicsemi/dfu/util.py @@ -1,4 +1,4 @@ -from __future__ import print_function + # # Copyright (c) 2016 Nordic Semiconductor ASA # All rights reserved. @@ -44,7 +44,7 @@ class NordicEnum(Enum): @classmethod def tostring(cls, val): - for k,v in vars(cls).iteritems(): + for k,v in vars(cls).items(): if v==val: return k @@ -66,7 +66,7 @@ def query_func(question, default=False): while True: print("%s %s" % (question, prompt)) - choice = raw_input().lower() + choice = input().lower() if choice == '': return default elif choice in valid: diff --git a/nordicsemi/lister/lister_backend.py b/nordicsemi/lister/lister_backend.py index 5194419..7d6be80 100644 --- a/nordicsemi/lister/lister_backend.py +++ b/nordicsemi/lister/lister_backend.py @@ -1,4 +1,3 @@ -# # Copyright (c) 2019 Nordic Semiconductor ASA # All rights reserved. # @@ -33,14 +32,12 @@ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import abc +from abc import ABC, abstractmethod -class AbstractLister(object): - __metaclass__ = abc.ABCMeta - @abc.abstractmethod +class AbstractLister(ABC): + @abstractmethod def enumerate(self): """ Enumerate all usb devices diff --git a/nordicsemi/lister/unix/unix_lister.py b/nordicsemi/lister/unix/unix_lister.py index 6716d06..946d1a1 100644 --- a/nordicsemi/lister/unix/unix_lister.py +++ b/nordicsemi/lister/unix/unix_lister.py @@ -69,4 +69,4 @@ def enumerate(self): else: device_identities[id] = EnumeratedDevice(vendor_id, product_id, serial_number, [com_port]) - return [device for device in device_identities.values()] + return [device for device in list(device_identities.values())] diff --git a/nordicsemi/lister/windows/constants.py b/nordicsemi/lister/windows/constants.py index c63cf1b..62cf05d 100644 --- a/nordicsemi/lister/windows/constants.py +++ b/nordicsemi/lister/windows/constants.py @@ -23,7 +23,7 @@ """ import enum -from structures import DevicePropertyKey +from .structures import DevicePropertyKey # noinspection SpellCheckingInspection diff --git a/nordicsemi/lister/windows/lister_win32.py b/nordicsemi/lister/windows/lister_win32.py index bf03da1..e1d9aef 100644 --- a/nordicsemi/lister/windows/lister_win32.py +++ b/nordicsemi/lister/windows/lister_win32.py @@ -40,8 +40,8 @@ from nordicsemi.lister.enumerated_device import EnumeratedDevice if sys.platform == 'win32': - from constants import DIGCF_PRESENT, DEVPKEY, DIGCF_DEVICEINTERFACE - from structures import GUID, DeviceInfoData, ctypesInternalGUID + from .constants import DIGCF_PRESENT, DEVPKEY, DIGCF_DEVICEINTERFACE + from .structures import GUID, DeviceInfoData, ctypesInternalGUID import ctypes from ctypes.wintypes import * diff --git a/nordicsemi/thread/dfu_server.py b/nordicsemi/thread/dfu_server.py index daa0729..215119b 100644 --- a/nordicsemi/thread/dfu_server.py +++ b/nordicsemi/thread/dfu_server.py @@ -122,7 +122,7 @@ def __init__(self): Resource = namedtuple('Resource', ['path', 'data']) class ThreadDfuServer(): - REALM_LOCAL_ADDR = ip_address(u'FF03::1') + REALM_LOCAL_ADDR = ip_address('FF03::1') SPBLK_SIZE = 64 # number of CoAP blocks of BLOCK_SZX size each SPBLK_UPLOAD_RATE = 1 # in blocks / seconds @@ -370,7 +370,7 @@ def receive_request(self, request): ThreadDfuServer.BITMAP_URI : self._handle_bitmap_request, } - for uri, handler in handlers.items(): + for uri, handler in list(handlers.items()): if '/'.join(request.opt.uri_path).startswith(uri): return handler(request) diff --git a/nordicsemi/thread/tncp.py b/nordicsemi/thread/tncp.py index 196da0c..814bf5a 100644 --- a/nordicsemi/thread/tncp.py +++ b/nordicsemi/thread/tncp.py @@ -78,7 +78,7 @@ def __init__(self, port, stream_descriptor, config = None): @staticmethod def _propid_to_str(propid): - for name, value in SPINEL.__dict__.iteritems(): + for name, value in SPINEL.__dict__.items(): if (name.startswith('PROP_') and value == propid): return name @@ -160,7 +160,7 @@ def add_ip_address(self, ipaddr): flags = 0 prefix_len = 64 - prefix = ipaddress.IPv6Interface(unicode(ipaddr)) + prefix = ipaddress.IPv6Interface(str(ipaddr)) arr = prefix.ip.packed arr += self._wpan.encode_fields('CLLC', prefix_len, @@ -174,7 +174,7 @@ def add_ip_address(self, ipaddr): def print_addresses(self): logger.info("NCP Thread IPv6 addresses:") for addr in self._wpan.get_ipaddrs(): - logger.info(unicode(addr)) + logger.info(str(addr)) def send(self, payload, dest): if (dest.addr.is_multicast): diff --git a/nordicsemi/utility/target_registry.py b/nordicsemi/utility/target_registry.py index 6d2ea05..f567546 100644 --- a/nordicsemi/utility/target_registry.py +++ b/nordicsemi/utility/target_registry.py @@ -1,5 +1,4 @@ -# -# Copyright (c) 2016 Nordic Semiconductor ASA +# Copyright (c) 2016 - 2019 Nordic Semiconductor ASA # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -33,17 +32,15 @@ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# import re import os import json -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod -class TargetDatabase(object): - __metaclass__ = ABCMeta +class TargetDatabase(ABC): @abstractmethod def get_targets(self): pass @@ -73,7 +70,7 @@ def get_targets(self): if self.targets is None: self.targets = [] - for key, value in os.environ.iteritems(): + for key, value in os.environ.items(): match = re.match("NORDICSEMI_TARGET_(?P\d+)_(?P[a-zA-Z_]+)", key) if match: diff --git a/nordicsemi/version.py b/nordicsemi/version.py index 08a20e5..6716769 100644 --- a/nordicsemi/version.py +++ b/nordicsemi/version.py @@ -37,4 +37,4 @@ """ Version definition for nrfutil. """ -NRFUTIL_VERSION = "5.1.0" +NRFUTIL_VERSION = "6.0.0a0" diff --git a/nordicsemi/zigbee/prod_config.py b/nordicsemi/zigbee/prod_config.py index 46f3b8d..2374fe3 100644 --- a/nordicsemi/zigbee/prod_config.py +++ b/nordicsemi/zigbee/prod_config.py @@ -63,7 +63,7 @@ def __init__(self, path): # Open the YAML file with open(path, 'r') as f: self._yaml = yaml.load(f) - + except yaml.YAMLError as e: raise ProductionConfigWrongException @@ -85,21 +85,21 @@ def __init__(self, path): self._parsed_values["install_code"] = str(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') self._ic_crc = 0 else: - self._parsed_values["install_code"] = self._yaml["install_code"].decode('hex') + self._parsed_values["install_code"] = bytes.fromhex(self._yaml["install_code"]) self._ic_crc = self._crc16(self._parsed_values["install_code"]) - + # Handle the Transmission Power if "tx_power" not in self._yaml: - self._parsed_values["tx_power"] = str(bytearray(16)) + self._parsed_values["tx_power"] = bytes(16) else: - self._parsed_values["tx_power"] = str(bytearray([self._yaml["tx_power"]] * 16)) + self._parsed_values["tx_power"] = bytes([self._yaml["tx_power"]] * 16) # Handle Application Data (optional) if "app_data" not in self._yaml: - self._parsed_values["app_data"] = '' + self._parsed_values["app_data"] = b'' self._ad_len = 0 else: - self._parsed_values["app_data"] = self._yaml["app_data"].decode('hex') + self._parsed_values["app_data"] = bytes.fromhex(self._yaml["app_data"]) self._ad_len = len(self._parsed_values["app_data"]) except (TypeError, ValueError) as e: @@ -112,7 +112,6 @@ def _custom_crc32(self, data): ZB_CRC32_POLY = 0x04C11DB7 crc = 0 for d in data: - d = ord(d) c = ((( crc ^ d ) & 0xff) << 24); for j in range(8): if c & 0x80000000: diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..533fe0c --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,4 @@ +-r requirements.txt +behave +nose +pyinstaller diff --git a/requirements.txt b/requirements.txt index aac1e77..10d7f02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,14 @@ -six ~= 1.9 -pyserial ~= 3.0 -enum34 ~= 1.0 +antlib == 1.1b0.post0; sys_platform == 'win32' click ~= 7.0 -ecdsa ~= 0.13.0 -behave ~= 1.0 -protobuf ~= 3.6 -pc_ble_driver_py ~= 0.11.4 -tqdm ~= 4.25 -piccata ~= 1.0 -pyspinel == 1.0.0a3 -intelhex ~= 2.2 -pyyaml >= 4.2b1 crcmod ~= 1.7 +ecdsa ~= 0.13.2 +intelhex ~= 2.2 libusb1 ~= 1.7 +pc_ble_driver_py == 0.12.0 +piccata # FIXME: Specify version constraint when piccata with py3 support becomes available. +protobuf ~= 3.8 +pyserial ~= 3.0 +pyspinel == 1.0.0a3 +pywin32 == 225; sys_platform == 'win32' +pyyaml ~= 5.1 +tqdm ~= 4.36 diff --git a/setup.py b/setup.py index d14f653..7fd0688 100644 --- a/setup.py +++ b/setup.py @@ -167,7 +167,7 @@ def run_tests(self): 'License :: Other/Proprietary License', - 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.7', ], keywords = 'nordic nrf51 nrf52 ble bluetooth dfu ota softdevice serialization nrfutil pc-nrfutil', cmdclass={ diff --git a/tests/bdd/steps/common_steps.py b/tests/bdd/steps/common_steps.py index 0aa8d7f..116c64b 100644 --- a/tests/bdd/steps/common_steps.py +++ b/tests/bdd/steps/common_steps.py @@ -35,7 +35,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -from Queue import Queue +from queue import Queue import logging import os import subprocess diff --git a/tests/bdd/steps/dfu_steps.py b/tests/bdd/steps/dfu_steps.py index b90ceb0..c5ec833 100644 --- a/tests/bdd/steps/dfu_steps.py +++ b/tests/bdd/steps/dfu_steps.py @@ -35,7 +35,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -from Queue import Empty +from queue import Empty import logging import os import time @@ -188,7 +188,7 @@ def program_image_ble(nrfjprog, full_image_path, snr): STDOUT_TEXT_WAIT_TIME = 50 # Number of seconds to wait for expected output from stdout -@given(u'the user wants to perform dfu {dfu_type}') +@given('the user wants to perform dfu {dfu_type}') def step_impl(context, dfu_type): runner = CliRunner() context.runner = runner @@ -196,17 +196,17 @@ def step_impl(context, dfu_type): context.args = args -@given(u'using package {package}') +@given('using package {package}') def step_impl(context, package): full_package_path = resolve_hex_path(package) context.args.extend(['-pkg', full_package_path]) context.pkg = full_package_path -@given(u'option {args}') +@given('option {args}') def step_impl(context, args): context.args.extend(args.split(" ")) -@given(u'-snr {device}') +@given('-snr {device}') def step_impl(context, device): assert device in os.environ, \ "Environment variable '{}' must be exported with device serial number".format(device) @@ -214,7 +214,7 @@ def step_impl(context, device): snr = str(os.environ[device]) context.args.extend(["-snr", snr]) -@given(u'nrfjprog {image} for {image_type} {board}') +@given('nrfjprog {image} for {image_type} {board}') def step_impl(context, image, image_type, board): full_image_path = resolve_hex_path(image) @@ -246,7 +246,7 @@ def step_impl(context, image, image_type, board): -@then(u'perform dfu') +@then('perform dfu') def step_impl(context): result = context.runner.invoke(cli, context.args) logger.debug("exit_code: %s, output: \'%s\'", result.exit_code, result.output) diff --git a/tests/bdd/steps/genpkg_generate_dfu_package_steps.py b/tests/bdd/steps/genpkg_generate_dfu_package_steps.py index b777dc9..bcc1c3a 100644 --- a/tests/bdd/steps/genpkg_generate_dfu_package_steps.py +++ b/tests/bdd/steps/genpkg_generate_dfu_package_steps.py @@ -48,25 +48,25 @@ logger = logging.getLogger(__file__) -@given(u'the user wants to generate a DFU package with application {application}, bootloader {bootloader} and SoftDevice {softdevice} with name {package}') +@given('the user wants to generate a DFU package with application {application}, bootloader {bootloader} and SoftDevice {softdevice} with name {package}') def step_impl(context, application, bootloader, softdevice, package): runner = CliRunner() context.runner = runner args = ['dfu', 'genpkg'] - if application != u'not_set': + if application != 'not_set': args.extend(['--application', os.path.join(get_resources_path(), application)]) context.application = application else: context.application = None - if bootloader != u'not_set': + if bootloader != 'not_set': args.extend(['--bootloader', os.path.join(get_resources_path(), bootloader)]) context.bootloader = bootloader else: context.bootloader = None - if softdevice != u'not_set': + if softdevice != 'not_set': args.extend(['--softdevice', os.path.join(get_resources_path(), softdevice)]) context.softdevice = softdevice else: @@ -77,52 +77,52 @@ def step_impl(context, application, bootloader, softdevice, package): context.args = args -@given(u'with option --application-version {app_ver}') +@given('with option --application-version {app_ver}') def step_impl(context, app_ver): context.application_version = None - if app_ver == u'not_set': + if app_ver == 'not_set': context.application_version = 0xFFFFFFFF - elif app_ver == u'none': + elif app_ver == 'none': context.args.extend(['--application-version', 'None']) else: context.args.extend(['--application-version', app_ver]) context.application_version = int_as_text_to_int(app_ver) -@given(u'with option --dev-revision {dev_rev}') +@given('with option --dev-revision {dev_rev}') def step_impl(context, dev_rev): context.dev_revision = None - if dev_rev == u'not_set': + if dev_rev == 'not_set': context.dev_revision = 0xFFFF - elif dev_rev == u'none': + elif dev_rev == 'none': context.args.extend(['--dev-revision', 'None']) else: context.args.extend(['--dev-revision', dev_rev]) context.dev_revision = int_as_text_to_int(dev_rev) -@given(u'with option --dev-type {dev_type}') +@given('with option --dev-type {dev_type}') def step_impl(context, dev_type): context.dev_type = None - if dev_type == u'not_set': + if dev_type == 'not_set': context.dev_type = 0xFFFF - elif dev_type == u'none': + elif dev_type == 'none': context.args.extend(['--dev-type', 'None']) else: context.args.extend(['--dev-type', dev_type]) context.dev_type = int_as_text_to_int(dev_type) -@given(u'with option --dfu-ver {dfu_ver}') +@given('with option --dfu-ver {dfu_ver}') def step_impl(context, dfu_ver): context.firmware_hash = None context.ext_packet_id = None context.init_packet_ecds = None - if dfu_ver == u'not_set': + if dfu_ver == 'not_set': context.dfu_ver = 0.5 context.ext_packet_id = 0 else: @@ -142,13 +142,13 @@ def step_impl(context, dfu_ver): context.dfu_ver = float(dfu_ver) -@given(u'with option --sd-req {sd_reqs}') +@given('with option --sd-req {sd_reqs}') def step_impl(context, sd_reqs): context.sd_req = None - if sd_reqs == u'not_set': + if sd_reqs == 'not_set': context.sd_req = [0xFFFE] - elif sd_reqs == u'none': + elif sd_reqs == 'none': context.args.extend(['--sd-req', 'None']) else: context.args.extend(['--sd-req', sd_reqs]) @@ -162,19 +162,19 @@ def step_impl(context, sd_reqs): context.sd_req = sd_reqs_value -@given(u'with option --key-file {pem_file}') +@given('with option --key-file {pem_file}') def step_impl(context, pem_file): - if pem_file != u'not_set': + if pem_file != 'not_set': context.args.extend(['--key-file', os.path.join(get_resources_path(), pem_file)]) context.dfu_ver = 0.8 -@when(u'user press enter') +@when('user press enter') def step_impl(context): pass -@then(u'the generated DFU package {package} contains correct data') +@then('the generated DFU package {package} contains correct data') def step_impl(context, package): with context.runner.isolated_filesystem(): pkg_full_name = os.path.join(os.getcwd(), package) diff --git a/tests/bdd/steps/help_information_steps.py b/tests/bdd/steps/help_information_steps.py index a34da48..6cedc8a 100644 --- a/tests/bdd/steps/help_information_steps.py +++ b/tests/bdd/steps/help_information_steps.py @@ -35,7 +35,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -from Queue import Empty +from queue import Empty import logging import os import time @@ -52,7 +52,7 @@ STDOUT_TEXT_WAIT_TIME = 50 # Number of seconds to wait for expected output from stdout -@given(u'user types \'{command}\'') +@given('user types \'{command}\'') def step_impl(context, command): args = command.split(' ') assert args[0] == 'nrfutil' @@ -64,7 +64,7 @@ def step_impl(context, command): context.args = exec_args -@then(u'output contains \'{stdout_text}\' and exit code is {exit_code}') +@then('output contains \'{stdout_text}\' and exit code is {exit_code}') def step_impl(context, stdout_text, exit_code): result = context.runner.invoke(cli, context.args) logger.debug("exit_code: %s, output: \'%s\'", result.exit_code, result.output) @@ -72,7 +72,7 @@ def step_impl(context, stdout_text, exit_code): assert result.output != None assert result.output.find(stdout_text) >= 0 -@then(u'output version is correct') +@then('output version is correct') def step_impl(context): assert "nrfutil_version" in os.environ, \ "Environment variable 'nrfutil_version' must be exported" diff --git a/tests/bdd/steps/util.py b/tests/bdd/steps/util.py index 3a67e9e..eb5eff4 100644 --- a/tests/bdd/steps/util.py +++ b/tests/bdd/steps/util.py @@ -79,7 +79,7 @@ def generate_options_table_for_cucumber(): number_of_optional_option_permutations *= int(math.pow(3, number_of_3_option_options)) number_of_optional_option_permutations *= int(math.pow(4, number_of_4_option_options)) - for x in xrange(0, number_of_optional_option_permutations): + for x in range(0, number_of_optional_option_permutations): retval += "{0:<8}".format(" ") retval += "| {0:<12}| {1:<29}| {2:<29}|".format("blinky.bin", "not_set", "not_set") @@ -144,7 +144,7 @@ def generate_options_table_for_cucumber(): if option == 2: sd_reqs = [] - for i in xrange(0, randint(1, 4)): + for i in range(0, randint(1, 4)): sd_reqs.append("0x{0:04x}".format(randint(0, 65535))) retval += " {0:<28}|".format(",".join(sd_reqs)) diff --git a/tests/protobuf/__main__.py b/tests/protobuf/__main__.py index 98932b4..74b03bb 100644 --- a/tests/protobuf/__main__.py +++ b/tests/protobuf/__main__.py @@ -20,7 +20,7 @@ def test_construct_from_empty_string(self): empty buffer """ - self.assertRaisesRegexp( + self.assertRaisesRegex( RuntimeError, "app_size is not set. It must be set when type is APPLICATION", InitPacketPB, diff --git a/tests/test_cli.py b/tests/test_cli.py index bef6134..bee4220 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -41,7 +41,6 @@ def test_dfu_ble_address(self): address = 'AABBCC11223' result = self.runner.invoke(self.cli, argumentList + [address]) self.assertTrue('Invalid address' in result.output) - print(result.exception) self.assertIsNone(result.exception) address = 'AABBCC1122334'