def test_find_microbit_unknown_os(): """ Raises a NotImplementedError if the host OS is not supported. """ with mock.patch('os.name', 'foo'): with pytest.raises(NotImplementedError) as ex: uflash.find_microbit() assert ex.value.args[0] == 'OS "foo" not supported.'
def flash(pythonfile): drive = uflash.find_microbit() tryn = 0 while drive == "": if tryn == 1: print("Please plug in a microbit") tryn = tryn + 1 input() drive = uflash.find_microbit() pyfilenoext = pythonfile[:-3] os.system("cd " + os.getcwd()) os.system('py2hex "' + pythonfile + '"') print("Moving to microbit.") shutil.move(pyfilenoext + ".hex", drive) print("Done!")
def flashF(folder): print("MicroBit is at: " + microfs.find_microbit()[0] + "\nMicroBit directory is at: " + uflash.find_microbit()) try: mfiles = microfs.ls() except OSError as e: print( str(e) + "\nMicrobit is probably calibrating, calibrate and then try again\nIf it still does not work try to " "replug your microbit or close other programs that is accessing your microbit" ) return "Could not write" print("Removing old stuff: " + str(mfiles)) for file in mfiles: microfs.rm(file) files = os.listdir(folder) print("Flashing new stuff: " + str(files)) for file in files: microfs.put(folder + "\\" + file) print("Flashed new stuff: " + str(microfs.ls()) + "\n") time.sleep(0.1) print("Done!" + "\n" + "Don't forget to name your main file \"main.py\"" + "\n" + InternalTools.bcolors.BOLD + "Reset your MicroBit to apply changes!")
def test_find_microbit_nt_removable_only(): """ We should only be considering removable drives as candidates for micro:bit devices. (Especially so as to avoid interrogating disconnected network drives). Have every drive claim to be a micro:bit, but only drive B: claim to be removable """ def _drive_type(letter): if letter == "B:\\": return 2 # removable else: return 4 # network mock_windll = mock.MagicMock() mock_windll.kernel32 = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW.return_value = None mock_windll.kernel32.GetDriveTypeW = mock.MagicMock() mock_windll.kernel32.GetDriveTypeW.side_effect = _drive_type with mock.patch('os.name', 'nt'): with mock.patch('os.path.exists', return_value=True): return_value = ctypes.create_unicode_buffer('MICROBIT') with mock.patch('ctypes.create_unicode_buffer', return_value=return_value): ctypes.windll = mock_windll assert uflash.find_microbit() == 'B:\\'
def test_find_microbit_posix_missing(): """ Simulate being on os.name == 'posix' and a call to "mount" returns a no records associated with a micro:bit device. """ with open('tests/mount_missing.txt', 'rb') as fixture_file: fixture = fixture_file.read() with mock.patch('os.name', 'posix'): with mock.patch('uflash.check_output', return_value=fixture): assert uflash.find_microbit() is None
def test_find_microbit_posix_exists(): """ Simulate being on os.name == 'posix' and a call to "mount" returns a record indicating a connected micro:bit device. """ with open('tests/mount_exists.txt', 'rb') as fixture_file: fixture = fixture_file.read() with mock.patch('os.name', 'posix'): with mock.patch('uflash.check_output', return_value=fixture): assert uflash.find_microbit() == '/media/ntoll/MICROBIT'
def flash_io_runtime(): """Flashes MicroPython with the default IO .hex""" try: microbit_path = uflash.find_microbit() if microbit_path: _BITIO_RUNTIME = os.path.join(os.path.dirname(__file__), "res", "bitio.hex") uflash.flash(path_to_runtime=_BITIO_RUNTIME) showinfo("Flashing micro:bit", "Flashing the I/O .hex to micro:bit") except Exception: error_msg = traceback.format_exc(0) + '\n' showerror("Error", error_msg)
def test_find_microbit_nt_missing(): """ Simulate being on os.name == 'nt' and a disk with a volume name 'MICROBIT' does not exist for a micro:bit device. """ mock_windll = mock.MagicMock() mock_windll.kernel32 = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW.return_value = None with mock.patch('os.name', 'nt'): with mock.patch('os.path.exists', return_value=True): return_value = ctypes.create_unicode_buffer(1024) with mock.patch('ctypes.create_unicode_buffer', return_value=return_value): ctypes.windll = mock_windll assert uflash.find_microbit() is None
def test_find_microbit_nt_exists(): """ Simulate being on os.name == 'nt' and a disk with a volume name 'MICROBIT' exists indicating a connected micro:bit device. """ mock_windll = mock.MagicMock() mock_windll.kernel32 = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW = mock.MagicMock() mock_windll.kernel32.GetVolumeInformationW.return_value = None # # Have every drive claim to be removable # mock_windll.kernel32.GetDriveTypeW = mock.MagicMock() mock_windll.kernel32.GetDriveTypeW.return_value = 2 with mock.patch('os.name', 'nt'): with mock.patch('os.path.exists', return_value=True): return_value = ctypes.create_unicode_buffer('MICROBIT') with mock.patch('ctypes.create_unicode_buffer', return_value=return_value): ctypes.windll = mock_windll assert uflash.find_microbit() == 'A:\\'
def readyMicroBit(self, printb=False): # shutil.copyfile(os.getcwd() + "\\src\\" + "microbitserialsystem.hex", self.microbitpath+"SerialSystem.hex") # shutil.copy if printb: print("Downloading HEX") url = readymicrobithexurl r = requests.get(url) if printb: print("Downloaded HEX") print("Fixing HEX") content = "" # contentb = r.content # contentb = str(contentb) # contentb = contentb[:-1] # contentb = contentb[2:] # contentsplit = contentb.split("\\n") # for i in contentsplit: # content = content + i + "\n" content = r.content.decode("UTF-8") if printb: print("Fixed HEX\n" + content) print("Moving HEX to microbit") try: file = open("SerialSystem.hex", "w") file.write(content) file.close() shutil.move("SerialSystem.hex", uflash.find_microbit() + "SerialSystem.hex") except shutil.Error as e: print(e) print("SerialSystem hex already installed") os.remove(os.getcwd() + "\\SerialSystem.hex") if printb: print("Moved HEX to microbit")
class SerialSystem: ser = microfs.get_serial() ser.baudrate = 115200 ser.close() microbitpath = uflash.find_microbit() before = { 'Buttons': { 'A': False, 'B': False }, 'Temp': 20, 'CompassHeading': 369, 'Accelerometer': { 'Y': 0, 'X': 0, 'Z': 0 }, 'Brightness': 0 } def close(self): self.ser.close() def open(self): try: self.ser.open() except serial.serialutil.SerialException as e: print(str(e)) # FIXA def readyMicroBit(self, printb=False): # shutil.copyfile(os.getcwd() + "\\src\\" + "microbitserialsystem.hex", self.microbitpath+"SerialSystem.hex") # shutil.copy if printb: print("Downloading HEX") url = readymicrobithexurl r = requests.get(url) if printb: print("Downloaded HEX") print("Fixing HEX") content = "" # contentb = r.content # contentb = str(contentb) # contentb = contentb[:-1] # contentb = contentb[2:] # contentsplit = contentb.split("\\n") # for i in contentsplit: # content = content + i + "\n" content = r.content.decode("UTF-8") if printb: print("Fixed HEX\n" + content) print("Moving HEX to microbit") try: file = open("SerialSystem.hex", "w") file.write(content) file.close() shutil.move("SerialSystem.hex", uflash.find_microbit() + "SerialSystem.hex") except shutil.Error as e: print(e) print("SerialSystem hex already installed") os.remove(os.getcwd() + "\\SerialSystem.hex") if printb: print("Moved HEX to microbit") def display(self, screen=[[5, 6, 7, 6, 5], [6, 7, 8, 7, 6], [7, 8, 9, 8, 7], [6, 7, 8, 7, 6], [5, 6, 7, 6, 5]]): fixedscreen = [0, 0, 0, 0, 0] yn = 0 for y in screen: tempstring = "" xn = 0 for x in y: tempstring = tempstring + str(screen[yn][xn]) xn = xn + 1 if y != 4: tempstring = tempstring + ":" fixedscreen[yn] = tempstring yn = yn + 1 try: self.ser.open() except serial.serialutil.SerialException as e: pass # print(str(e)) self.ser.write(bytes("?" + str(fixedscreen).replace(" ", ""), 'utf-8')) def readRaw(self): try: self.ser.open() except serial.serialutil.SerialException as e: pass # print(str(e)) data_raw = self.ser.readline() try: if data_raw != b'': return (data_raw.decode("utf-8")) except UnicodeEncodeError as e: print("Skipping because of error: " + str(e)) def read(self): try: self.ser.open() except serial.serialutil.SerialException as e: pass # print(str(e)) try: mbd = self.ser.readline().decode("UTF-8") mbd = mbd.replace("'", '"') except serial.serialutil.SerialException: return {"Error": "Can not find MicroBit"} except UnicodeDecodeError as e: mbd = "" time.sleep(0.5) print(e) except IndexError: return {"Error": "Unknown error"} if mbd.startswith("?"): mbds = mbd.split("?") try: result = json.loads(mbds[1]) if result["Buttons"]["A"] == "true": result["Buttons"]["A"] = True elif result["Buttons"]["A"] == "false": result["Buttons"]["A"] = False if result["Buttons"]["B"] == "true": result["Buttons"]["B"] = True elif result["Buttons"]["B"] == "false": result["Buttons"]["B"] = False except json.decoder.JSONDecodeError as e: print(mbds[1]) print(e) result = { "Error": "Cant convert Json (MicroBit probably is using wrong firmware)" } else: result = self.before self.before = result return result
import sys import uflash if len(sys.argv) > 1: filename = sys.argv[1] else: print "Missing filename on command line." sys.exit(1) print "File: " + filename print "Looking for micro:bit..." microbitPath = uflash.find_microbit() if microbitPath == None: print "Cannot find micro:bit. Check that it is plugged in via USB." sys.exit(1) print "micro:bit found @ " + microbitPath print "uflash version " + uflash.get_version() uflash.flash(filename, microbitPath)
def flash(self): """ Takes the currently active tab, compiles the Python script therein into a hex file and flashes it all onto the connected device. WARNING: This method is getting more complex due to several edge cases. Ergo, it's a target for refactoring. """ user_defined_microbit_path = None self.python_script = '' logger.info('Preparing to flash script.') # The first thing to do is check the script is valid and of the # expected length. # Grab the Python script. tab = self.view.current_tab if tab is None: # There is no active text editor. Exit. return # Check the script's contents. python_script = tab.text().encode('utf-8') logger.debug('Python script:') logger.debug(python_script) # Check minification status. minify = False if uflash.get_minifier(): minify = self.editor.minify # Attempt and handle minification. if len(python_script) >= uflash._MAX_SIZE: message = _('Unable to flash "{}"').format(tab.label) if minify and can_minify: orginal = len(python_script) script = python_script.decode('utf-8') try: mangled = nudatus.mangle(script).encode('utf-8') except TokenError as e: msg, (line, col) = e.args logger.debug('Minify failed') logger.exception(e) message = _("Problem with script") information = _("{} [{}:{}]").format(msg, line, col) self.view.show_message(message, information, 'Warning') return saved = orginal - len(mangled) percent = saved / orginal * 100 logger.debug('Script minified, {} bytes ({:.2f}%) saved:' .format(saved, percent)) logger.debug(mangled) python_script = mangled if len(python_script) >= 8192: information = _("Our minifier tried but your " "script is too long!") self.view.show_message(message, information, 'Warning') return elif minify and not can_minify: information = _("Your script is too long and the minifier" " isn't available") self.view.show_message(message, information, 'Warning') return else: information = _("Your script is too long!") self.view.show_message(message, information, 'Warning') return # By this point, there's a valid Python script in "python_script". # Assign this to an attribute for later processing in a different # method. self.python_script = python_script # Next step: find the microbit port and serial number. path_to_microbit = uflash.find_microbit() logger.info('Path to micro:bit: {}'.format(path_to_microbit)) port = None serial_number = None try: port, serial_number = self.find_device() logger.info('Serial port: {}'.format(port)) logger.info('Device serial number: {}'.format(serial_number)) except Exception as ex: logger.warning('Unable to make serial connection to micro:bit.') logger.warning(ex) # Determine the location of the BBC micro:bit. If it can't be found # fall back to asking the user to locate it. if path_to_microbit is None: # Ask the user to locate the device. path_to_microbit = self.view.get_microbit_path(HOME_DIRECTORY) user_defined_microbit_path = path_to_microbit logger.debug('User defined path to micro:bit: {}'.format( user_defined_microbit_path)) # Check the path and that it exists simply because the path maybe based # on stale data. if path_to_microbit and os.path.exists(path_to_microbit): force_flash = False # If set to true, fully flash the device. # If there's no port but there's a path_to_microbit, then we're # probably running on Windows with an old device, so force flash. if not port: force_flash = True if not self.python_script.strip(): # If the script is empty, this is a signal to simply force a # flash. logger.info("Python script empty. Forcing flash.") force_flash = True logger.info("Checking target device.") # Get the version of MicroPython on the device. try: version_info = microfs.version() logger.info(version_info) board_info = version_info['version'].split() if (board_info[0] == 'micro:bit' and board_info[1].startswith('v')): # New style versions, so the correct information will be # in the "release" field. try: # Check the release is a correct semantic version. semver.parse(version_info['release']) board_version = version_info['release'] except ValueError: # If it's an invalid semver, set to unknown version to # force flash. board_version = '0.0.1' else: # 0.0.1 indicates an old unknown version. This is just a # valid arbitrary flag for semver comparison a couple of # lines below. board_version = '0.0.1' logger.info('Board MicroPython: {}'.format(board_version)) logger.info( 'Mu MicroPython: {}'.format(uflash.MICROPYTHON_VERSION)) # If there's an older version of MicroPython on the device, # update it with the one packaged with Mu. if semver.compare(board_version, uflash.MICROPYTHON_VERSION) < 0: force_flash = True except Exception: # Could not get version of MicroPython. This means either the # device has a really old version of MicroPython or is running # something else. In any case, flash MicroPython onto the # device. logger.warning('Could not detect version of MicroPython.') force_flash = True # Check use of custom runtime. rt_hex_path = self.editor.microbit_runtime.strip() message = _('Flashing "{}" onto the micro:bit.').format(tab.label) if (rt_hex_path and os.path.exists(rt_hex_path)): message = message + _(" Runtime: {}").format(rt_hex_path) force_flash = True # Using a custom runtime, so flash it. else: rt_hex_path = None self.editor.microbit_runtime = '' # Check for use of user defined path (to save hex onto local # file system. if user_defined_microbit_path: force_flash = True # If we need to flash the device with a clean hex, do so now. if force_flash: logger.info('Flashing new MicroPython runtime onto device') self.editor.show_status_message(message, 10) self.set_buttons(flash=False) if user_defined_microbit_path or not port: # The user has provided a path to a location on the # filesystem. In this case save the combined hex/script # in the specified path_to_microbit. # Or... Mu has a path to a micro:bit but can't establish # a serial connection, so use the combined hex/script # to flash the device. self.flash_thread = DeviceFlasher([path_to_microbit], self.python_script, rt_hex_path) # Reset python_script so Mu doesn't try to copy it as the # main.py file. self.python_script = '' else: # We appear to need to flash a connected micro:bit device, # so just flash the Python hex with no embedded Python # script, since this will be copied over when the # flashing operation has finished. model_serial_number = int(serial_number[:4]) if rt_hex_path: # If the user has specified a bespoke runtime hex file # assume they know what they're doing and hope for the # best. self.flash_thread = DeviceFlasher([path_to_microbit], b'', rt_hex_path) elif model_serial_number in self.valid_serial_numbers: # The connected board has a serial number that # indicates the MicroPython hex bundled with Mu # supports it. In which case, flash it. self.flash_thread = DeviceFlasher([path_to_microbit], b'', None) else: message = _('Unsupported BBC micro:bit.') information = _("Your device is newer than this " "version of Mu. Please update Mu " "to the latest version to support " "this device.\n\n" "https://codewith.mu/") self.view.show_message(message, information) return if sys.platform == 'win32': # Windows blocks on write. self.flash_thread.finished.connect(self.flash_finished) else: if user_defined_microbit_path: # Call the flash_finished immediately the thread # finishes if Mu is writing the hex file to a user # defined location on the local filesystem. self.flash_thread.finished.connect(self.flash_finished) else: # Other platforms don't block, so schedule the finish # call for 10 seconds (approximately how long flashing # the connected device takes). self.flash_timer = QTimer() self.flash_timer.timeout.connect(self.flash_finished) self.flash_timer.setSingleShot(True) self.flash_timer.start(10000) self.flash_thread.on_flash_fail.connect(self.flash_failed) self.flash_thread.start() else: try: self.copy_main() except IOError as ioex: # There was a problem with the serial communication with # the device, so revert to forced flash... "old style". # THIS IS A HACK! :-( logger.warning('Could not copy file to device.') logger.error(ioex) logger.info('Falling back to old-style flashing.') self.flash_thread = DeviceFlasher([path_to_microbit], self.python_script, rt_hex_path) self.python_script = '' if sys.platform == 'win32': # Windows blocks on write. self.flash_thread.finished.connect(self.flash_finished) else: self.flash_timer = QTimer() self.flash_timer.timeout.connect(self.flash_finished) self.flash_timer.setSingleShot(True) self.flash_timer.start(10000) self.flash_thread.on_flash_fail.connect(self.flash_failed) self.flash_thread.start() except Exception as ex: self.flash_failed(ex) else: # Try to be helpful... essentially there is nothing Mu can do but # prompt for patience while the device is mounted and/or do the # classic "have you tried switching it off and on again?" trick. # This one's for James at the Raspberry Pi Foundation. ;-) message = _('Could not find an attached BBC micro:bit.') information = _("Please ensure you leave enough time for the BBC" " micro:bit to be attached and configured" " correctly by your computer. This may take" " several seconds." " Alternatively, try removing and re-attaching the" " device or saving your work and restarting Mu if" " the device remains unfound.") self.view.show_message(message, information)