python-uflash-1.2.4+dfsg.orig/0000755000175000017500000000000013406517065014636 5ustar nicknickpython-uflash-1.2.4+dfsg.orig/README.rst0000644000175000017500000001172013404702220016311 0ustar nicknickuFlash ====== **THIS MODULE ONLY WORKS WITH PYTHON 2.7 or 3.3+.** A utility for flashing the BBC micro:bit with Python scripts and the MicroPython runtime. You pronounce the name of this utility "micro-flash". ;-) It provides two services: 1. A library of functions to programatically create a hex file and flash it onto a BBC micro:bit. 2. A command line utility called `uflash` that will flash Python scripts onto a BBC micro:bit. Several essential operations are implemented: * Encode Python into the hex format. * Embed the resulting hexified Python into the MicroPython runtime hex. * Extract an encoded Python script from a MicroPython hex file. * Discover the connected micro:bit. * Copy the resulting hex onto the micro:bit, thus flashing the device. * Specify the MicroPython runtime hex in which to embed your Python code. Installation ------------ To install simply type:: $ pip install uflash ...and the package will download from PyPI. If you wish to upgrade to the latest version, use the following command:: $ pip install --no-cache --upgrade uflash **NB:** You must use a USB *data* cable to connect the micro:bit to your computer (some cables are power only). You're in good shape if, when plugged in, the micro:bit appears as a USB storage device on your file system. Linux users: For uflash to work you must ensure the micro:bit is mounted as a USB storage device. Usually this is done automatically. If not you've probably configured automounting to be off. If that's the case, we assume you have the technical knowledge to mount the device yourself or to install the required kernel modules if they're missing. Default installs of popular Linux distros "should just work" (tm) out of the box given a default install. Command Usage ------------- To read help simply type:: $ uflash --help or:: $ uflash -h To discover the version information type:: $ uflash --version If you type the command on its own then uflash will attempt to find a connected BBC micro:bit and flash an unmodified default version of the MicroPython runtime onto it:: $ uflash Flashing Python to: /media/ntoll/MICROBIT/micropython.hex To flash a version of the MicroPython runtime with a specified script embedded within it (so that script is run when the BBC micro:bit boots up) then pass the path to the Python script in as the first argument to the command:: $ uflash my_script.py Flashing Python to: /media/ntoll/MICROBIT/micropython.hex You can let uflash watch for changes of your script. It will be flashed automatically every time you save it:: $ uflash -w my_script.py or:: $ uflash --watch my_script.py At this point uflash will try to automatically detect the path to the device. However, if you have several devices plugged in and/or know what the path on the filesystem to the BBC micro:bit already is, you can specify this as a second argument to the command:: $ uflash myscript.py /media/ntoll/MICROBIT Flashing Python to: /media/ntoll/MICROBIT/micropython.hex You can even flash multiple devices at once:: $ uflash myscript.py /media/ntoll/MICROBIT /media/ntoll/MICROBIT1 Flashing Python to: /media/ntoll/MICROBIT/micropython.hex Flashing Python to: /media/ntoll/MICROBIT1/micropython.hex To extract a Python script from a hex file use the "-e" flag like this:: $ uflash -e something.hex myscript.py This will save the Python script recovered from "something.hex" into the file "myscript.py". If you don't supply a target the recovered script will emit to stdout. If you're developing MicroPython and have a custom runtime hex file you can specify that uflash use it instead of the built-in version of MicroPython in the following way:: $ uflash -r firmware.hex or:: $ uflash --runtime=firmware.hex Development ----------- The source code is hosted in GitHub. Please feel free to fork the repository. Assuming you have Git installed you can download the code from the canonical repository with the following command:: $ git clone https://github.com/ntoll/uflash.git Ensure you have the correct dependencies for development installed by creating a virtualenv and running:: $ pip install -r requirements.txt To locally install your development version of the module into a virtualenv, run the following command:: $ python setup.py develop There is a Makefile that helps with most of the common workflows associated with development. Typing ``make`` on its own will list the options thus:: $ make There is no default Makefile target right now. Try: make clean - reset the project and remove auto-generated assets. make pyflakes - run the PyFlakes code checker. make pep8 - run the PEP8 style checker. make test - run the test suite. make coverage - view a report on test coverage. make check - run all the checkers and tests. make package - create a deployable package for the project. make publish - publish the project to PyPI. make docs - run sphinx to create project documentation. python-uflash-1.2.4+dfsg.orig/CHANGES.rst0000644000175000017500000000644713404702220016436 0ustar nicknickRelease History =============== 1.2.4 ----- * Updated to the latest version of MicroPython for micro:bit (1.0.1) * This is the version of uflash to be used in Mu 1.0.2. 1.2.3 ----- * Update to the latest version of MicroPython for micro:bit (1.0.0). * This is the version of uflash to be used in Mu 1.0.1. 1.2.2 ----- * Update to latest version of MicroPython for micro:bit (1.0.0-rc.3). 1.2.1 ----- * Update to latest version of MicroPython. Thanks to Damien George and Carlos Pereira Atencio for their hard work. * This is the version of uflash to be used in Mu 1.0.0 (final). 1.2.0 ----- * Update to latest version of MicroPython. Thanks to Damien George. * Add attribute called MICROPYTHON_VERSION to report the version of MicroPython bundled with uflash. 1.1.1 ----- * Update to the latest version of MicroPython for the BBC micro:bit -- fixes a bug relating to flooding and the radio module. As always, many thanks to Damien George for his work on MicroPython. 1.1.0 ----- * Update to latest version of MicroPython for the BBC micro:bit (many thanks to Damien George for his amazing efforts!). * Add a --version flag to uflash that causes it to print the current version number (many thanks to Lenz Grimmer for this work). * Allow uflash to accept the content of a script as well as the path to a script (many thanks to Zander Brown for this work). * Ensure uflash works nicely / better with external tools (many thanks to Lex Robinson for this work). * Added copyright and license information to the start of the script. 1.0.8 ----- * Refactor hex extraction to not depend on extended address record before script (thanks Carlos). * Refactor tox tests to fix Windows related Gremlin (thanks again, Carlos). 1.0.7 ----- * Watch for changes in a script. Automatically flash on save. 1.0.5 ----- * Update runtime to include latest bug fixes and inclusion of input() builtin. * Detecting drives on Windows 10 no longer causes pop-ups in certain situations. * Documentation updates. 1.0.4 ----- * Add support for flash multiple microbits. 1.0.3 ----- * Update runtime to include audio and speech modules. 1.0.2 ----- * Update runtime to include the new radio module. 1.0.1 ----- * Update runtime to include file system related changes. 1.0.0.final.0 ------------- * Runtime updated to version 1.0 of MicroPython for the BBC micro:bit. 1.0.0.beta.7 ------------ * Runtime update to fix display related bug. 1.0.0.beta.6 ------------ * Runtime update to latest version of the DAL (swaps pins 4 and 5). 1.0.0.beta.5 ------------ * Runtime update to fix error reporting bug. 1.0.0.beta.4 ------------ * Documentation update. * Help text update. 1.0.0.beta.3 ------------ * Add ability to specify a MicroPython runtime to use. * Test fixes. 1.0.0.beta.2 ------------ * Updated to latest version of MicroPython runtime. 1.0.0.beta.1 ------------ * Works with Python 2.7 (thanks to @Funkyhat). * Updated to the latest build of MicroPython for the BBC micro:bit. * Minor refactoring and updates to the test suite due to MicroPython updates. 0.9.17 ------ * Minor code refactor. * Documentation update. 0.9.14 ------ * Feature complete. * Comprehensive test suite - 100% coverage. * Tested on Linux and Windows. * Documentation. * Access via the "uflash" command. 0.0.1 ----- * Initial release. Basic functionality. python-uflash-1.2.4+dfsg.orig/tests/0000755000175000017500000000000013404702220015763 5ustar nicknickpython-uflash-1.2.4+dfsg.orig/tests/example.py0000644000175000017500000000007613404702220017773 0ustar nicknickfrom microbit import display display.scroll("Hello, World!") python-uflash-1.2.4+dfsg.orig/tests/__init__.py0000644000175000017500000000000013404702220020062 0ustar nicknickpython-uflash-1.2.4+dfsg.orig/tests/mount_missing.txt0000644000175000017500000000067313404702220021425 0ustar nicknicksysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) udev on /dev type devtmpfs (rw,relatime,size=10240k,nr_inodes=489849,mode=755) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) tmpfs on /run type tmpfs (rw,nosuid,relatime,size=787732k,mode=755) /dev/mapper/heraclitus--vg-root on / type ext4 (rw,relatime,errors=remount-ro,data=ordered) python-uflash-1.2.4+dfsg.orig/tests/test_uflash.py0000644000175000017500000006537413404702220020675 0ustar nicknick# -*- coding: utf-8 -*- """ Tests for the uflash module. """ import ctypes import os import os.path import sys import tempfile import time import threading import pytest import uflash try: from unittest import mock except ImportError: import mock else: # mock_open can't read binary data in < 3.4.3 # https://bugs.python.org/issue23004 if (3, 4) <= sys.version_info < (3, 4, 3): import mock if sys.version_info.major == 2: import __builtin__ as builtins else: import builtins TEST_SCRIPT = b"""from microbit import * display.scroll('Hello, World!') """ def test_get_version(): """ Ensure a call to get_version returns the expected string. """ result = uflash.get_version() assert result == '.'.join([str(i) for i in uflash._VERSION]) def test_get_minifier(): """ When a minifier was loaded a string identifing it should be returned, otherwise None """ with mock.patch('uflash.can_minify', False): assert uflash.get_minifier() is None with mock.patch('uflash.can_minify', True): assert len(uflash.get_minifier()) > 0 def test_hexlify(): """ Ensure we get the expected .hex encoded result from a "good" call to the function. """ result = uflash.hexlify(TEST_SCRIPT) lines = result.split() # The first line should be the extended linear address, ox0003 assert lines[0] == ':020000040003F7' # There should be the expected number of lines. assert len(lines) == 5 def test_unhexlify(): """ Ensure that we can get the script back out using unhexlify and that the result is a properly decoded string. """ hexlified = uflash.hexlify(TEST_SCRIPT) unhexlified = uflash.unhexlify(hexlified) assert unhexlified == TEST_SCRIPT.decode('utf-8') def test_unhexlify_not_python(): """ Test that the MicroPython script start format is present. """ assert '' == uflash.unhexlify( ':020000040003F7\n:10E000000000000000000000000000000000000010') def test_unhexlify_bad_unicode(): """ Test that invalid Unicode is dealt gracefully returning an empty string. """ assert '' == uflash.unhexlify( ':020000040003F7\n:10E000004D50FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') def test_hexlify_empty_script(): """ The function returns an empty string if the script is empty. """ assert uflash.hexlify('') == '' def test_embed_hex(): """ Ensure the good case works as expected. """ python = uflash.hexlify(TEST_SCRIPT) result = uflash.embed_hex(uflash._RUNTIME, python) # The resulting hex should be of the expected length. assert len(result) == len(python) + len(uflash._RUNTIME) + 1 # +1 for \n # The hex should end with a newline '\n' assert result[-1:] == '\n' # The Python hex should be in the correct location. py_list = python.split() result_list = result.split() start_of_python_from_end = len(py_list) + 5 start_of_python = len(result_list) - start_of_python_from_end assert result_list[start_of_python:-5] == py_list # The firmware should enclose the Python correctly. firmware_list = uflash._RUNTIME.split() assert firmware_list[:-5] == result_list[:-start_of_python_from_end] assert firmware_list[-5:] == result_list[-5:] def test_embed_no_python(): """ The function returns the firmware hex value if there is no Python hex. """ assert uflash.embed_hex('foo') == 'foo' def test_embed_no_runtime(): """ The function raises a ValueError if there is no runtime hex. """ with pytest.raises(ValueError) as ex: uflash.embed_hex(None) assert ex.value.args[0] == 'MicroPython runtime hex required.' def test_extract(): """ The script should be returned as a string (if there is one). """ python = uflash.hexlify(TEST_SCRIPT) result = uflash.embed_hex(uflash._RUNTIME, python) extracted = uflash.extract_script(result) assert extracted == TEST_SCRIPT.decode('utf-8') def test_extract_sandwiched(): """ The script hex is packed with additional data above and bellow and should still be returned as a the original string only. """ python = uflash.hexlify(TEST_SCRIPT) python_hex_lines = python.split('\n') python_sandwiched = [python_hex_lines[0]] + \ [':10DFE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41'] + \ python_hex_lines[1:] + [':10E50000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B'] result = uflash.embed_hex(uflash._RUNTIME, '\n'.join(python_sandwiched)) extracted = uflash.extract_script(result) assert extracted == TEST_SCRIPT.decode('utf-8') def test_extract_not_valid_hex(): """ Return a sensible message if the hex file isn't valid """ assert uflash.extract_script('invalid input') == '' def test_extract_no_python(): """ Ensure that if there's no Python in the input hex then just return an empty (False) string. """ assert uflash.extract_script(uflash._RUNTIME) == '' 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 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_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 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_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_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 test_save_hex(): """ Ensure the good case works. """ # Ensure we have a temporary file to write to that doesn't already exist. path_to_hex = os.path.join(tempfile.gettempdir(), 'microbit.hex') if os.path.exists(path_to_hex): os.remove(path_to_hex) assert not os.path.exists(path_to_hex) # Create the hex file we want to "flash" python = uflash.hexlify(TEST_SCRIPT) hex_file = uflash.embed_hex(uflash._RUNTIME, python) # Save the hex. uflash.save_hex(hex_file, path_to_hex) # Ensure the hex has been written as expected. assert os.path.exists(path_to_hex) with open(path_to_hex) as written_file: assert written_file.read() == hex_file def test_save_hex_no_hex(): """ The function raises a ValueError if no hex content is provided. """ with pytest.raises(ValueError) as ex: uflash.save_hex('', 'foo') assert ex.value.args[0] == 'Cannot flash an empty .hex file.' def test_save_hex_path_not_to_hex_file(): """ The function raises a ValueError if the path is NOT to a .hex file. """ with pytest.raises(ValueError) as ex: uflash.save_hex('foo', '') assert ex.value.args[0] == 'The path to flash must be for a .hex file.' def test_flash_no_args(): """ The good case with no arguments to the flash() function. When it's possible to find a path to the micro:bit. If no path to a Python script is supplied then just flash the unmodified MicroPython firmware onto the device. """ with mock.patch('uflash.find_microbit', return_value='foo'): with mock.patch('uflash.save_hex') as mock_save: uflash.flash() assert mock_save.call_count == 1 assert mock_save.call_args[0][0] == uflash._RUNTIME expected_path = os.path.join('foo', 'micropython.hex') assert mock_save.call_args[0][1] == expected_path def test_flash_has_python_no_path_to_microbit(): """ The good case with a path to a Python file. When it's possible to find a path to the micro:bit. The resulting payload should be a correctly created micropython.hex file. """ with mock.patch('uflash.find_microbit', return_value='foo'): with mock.patch('uflash.save_hex') as mock_save: uflash.flash('tests/example.py') assert mock_save.call_count == 1 # Create the hex we're expecting to flash onto the device. with open('tests/example.py', 'rb') as py_file: python = uflash.hexlify(py_file.read()) assert python expected_hex = uflash.embed_hex(uflash._RUNTIME, python) assert mock_save.call_args[0][0] == expected_hex expected_path = os.path.join('foo', 'micropython.hex') assert mock_save.call_args[0][1] == expected_path def test_flash_with_path_to_multiple_microbits(): """ Flash the referenced paths to the micro:bit with a hex file generated from the MicroPython firmware and the referenced Python script. """ with mock.patch('uflash.save_hex') as mock_save: uflash.flash('tests/example.py', ['test_path1', 'test_path2']) assert mock_save.call_count == 2 # Create the hex we're expecting to flash onto the device. with open('tests/example.py', 'rb') as py_file: python = uflash.hexlify(py_file.read()) assert python expected_hex = uflash.embed_hex(uflash._RUNTIME, python) assert mock_save.call_args_list[0][0][0] == expected_hex expected_path = os.path.join('test_path1', 'micropython.hex') assert mock_save.call_args_list[0][0][1] == expected_path assert mock_save.call_args_list[1][0][0] == expected_hex expected_path = os.path.join('test_path2', 'micropython.hex') assert mock_save.call_args_list[1][0][1] == expected_path def test_flash_with_path_to_microbit(): """ Flash the referenced path to the micro:bit with a hex file generated from the MicroPython firmware and the referenced Python script. """ with mock.patch('uflash.save_hex') as mock_save: uflash.flash('tests/example.py', ['test_path']) assert mock_save.call_count == 1 # Create the hex we're expecting to flash onto the device. with open('tests/example.py', 'rb') as py_file: python = uflash.hexlify(py_file.read()) assert python expected_hex = uflash.embed_hex(uflash._RUNTIME, python) assert mock_save.call_args[0][0] == expected_hex expected_path = os.path.join('test_path', 'micropython.hex') assert mock_save.call_args[0][1] == expected_path def test_flash_with_path_to_runtime(): """ Use the referenced runtime hex file when building the hex file to be flashed onto the device. """ mock_o = mock.mock_open(read_data=b'script') with mock.patch.object(builtins, 'open', mock_o) as mock_open: with mock.patch('uflash.embed_hex', return_value='foo') as em_h: with mock.patch('uflash.find_microbit', return_value='bar'): with mock.patch('uflash.save_hex') as mock_save: uflash.flash('tests/example.py', path_to_runtime='tests/fake.hex') assert mock_open.call_args[0][0] == 'tests/fake.hex' assert em_h.call_args[0][0] == b'script' expected_hex_path = os.path.join('bar', 'micropython.hex') mock_save.assert_called_once_with('foo', expected_hex_path) def test_flash_with_python_script(): """ If a byte representation of a Python script is passed into the function it should hexlify that. """ python_script = b'import this' with mock.patch('uflash.save_hex'): with mock.patch('uflash.find_microbit', return_value='bar'): with mock.patch('uflash.hexlify') as mock_hexlify: uflash.flash(python_script=python_script) mock_hexlify.assert_called_once_with(python_script, False) def test_flash_cannot_find_microbit(): """ Ensure an IOError is raised if it is not possible to find the micro:bit. """ with mock.patch('uflash.find_microbit', return_value=None): with pytest.raises(IOError) as ex: uflash.flash() expected = 'Unable to find micro:bit. Is it plugged in?' assert ex.value.args[0] == expected def test_flash_wrong_python(): """ Ensures a call to flash will fail if it's not reported that we're using Python 3. """ for version in [(2, 6, 3), (3, 2, 0)]: with pytest.raises(RuntimeError) as ex: with mock.patch('sys.version_info', version): uflash.flash() assert 'Will only run on Python ' in ex.value.args[0] def test_hexlify_minify_without_minifier(): """ When minification but no minifier is available a ValueError should be raised """ with pytest.raises(ValueError): with mock.patch('uflash.can_minify', False): uflash.hexlify(TEST_SCRIPT, minify=True) def test_hexlify_minify(): """ Check mangle is called as expected """ with mock.patch('nudatus.mangle') as mangle: uflash.hexlify(TEST_SCRIPT, minify=True) mangle.assert_called_once_with(TEST_SCRIPT.decode('utf-8')) def test_main_no_args(): """ If there are no args into the main function, it simply calls flash with no arguments. """ with mock.patch('sys.argv', ['uflash', ]): with mock.patch('uflash.flash') as mock_flash: uflash.main() mock_flash.assert_called_once_with(path_to_python=None, paths_to_microbits=[], path_to_runtime=None, minify=False) def test_main_first_arg_python(): """ If there is a single argument that ends with ".py", it calls flash with it as the path to the source Python file. """ with mock.patch('uflash.flash') as mock_flash: uflash.main(argv=['foo.py']) mock_flash.assert_called_once_with(path_to_python='foo.py', paths_to_microbits=[], path_to_runtime=None, minify=False) def test_main_first_arg_help(capsys): """ If there is a single argument of "--help", it prints some help and exits. """ with pytest.raises(SystemExit): uflash.main(argv=['--help']) stdout, _ = capsys.readouterr() # argparse manipulates the help text (e.g. changes line wrap) # so it isn't trivial to compare the output to uflash._HELP_TEXT. expected = 'Flash Python onto the BBC micro:bit' assert expected in stdout def test_main_first_arg_version(capsys): """ If there is a single argument of "--version", it prints the version and exits. """ with pytest.raises(SystemExit): uflash.main(argv=['--version']) stdout, stderr = capsys.readouterr() expected = uflash.get_version() # On python 2 --version prints to stderr. On python 3 to stdout. # https://bugs.python.org/issue18920 assert (expected in stdout) or (expected in stderr) def test_main_first_arg_not_python(capsys): """ If the first argument does not end in ".py" then it should display a useful error message. """ with pytest.raises(SystemExit): uflash.main(argv=['foo.bar']) _, stderr = capsys.readouterr() expected = 'Python files must end in ".py".' assert expected in stderr def test_flash_raises(capsys): """ If the flash system goes wrong, it should say that's what happened """ with mock.patch('uflash.flash', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['test.py']) _, stderr = capsys.readouterr() expected = 'Error flashing test.py' assert expected in stderr def test_flash_raises_with_info(capsys): """ When flash goes wrong it should mention everything you tell it """ with mock.patch('uflash.flash', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['test.py']) _, stderr = capsys.readouterr() expected = 'Error flashing test.py to microbit: boom\n' assert stderr == expected with mock.patch('uflash.flash', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['test.py', 'D:\\']) _, stderr = capsys.readouterr() expected = 'Error flashing test.py to ' + repr(['D:\\']) + ': boom\n' assert stderr == expected with mock.patch('uflash.flash', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['-r', 'foo.hex', 'test.py', 'D:\\']) _, stderr = capsys.readouterr() expected = 'Error flashing test.py to ' + repr(['D:\\']) + \ 'with runtime foo.hex: boom\n' assert stderr == expected def test_watch_raises(capsys): """ If the watch system goes wrong, it should say that's what happened """ with mock.patch('uflash.watch_file', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['--watch', 'test.py']) _, stderr = capsys.readouterr() expected = 'Error watching test.py' assert expected in stderr def test_extract_raises(capsys): """ If the extract system goes wrong, it should say that's what happened """ with mock.patch('uflash.extract', side_effect=RuntimeError("boom")): with pytest.raises(SystemExit): uflash.main(argv=['--extract', 'test.py']) _, stderr = capsys.readouterr() expected = 'Error extracting test.py' assert expected in stderr def test_main_two_args(): """ If there are two arguments passed into main, then it should pass them onto the flash() function. """ with mock.patch('uflash.flash', return_value=None) as mock_flash: uflash.main(argv=['foo.py', '/media/foo/bar']) mock_flash.assert_called_once_with( path_to_python='foo.py', paths_to_microbits=['/media/foo/bar'], path_to_runtime=None, minify=False) def test_main_multiple_microbits(): """ If there are more than two arguments passed into main, then it should pass them onto the flash() function. """ with mock.patch('uflash.flash', return_value=None) as mock_flash: uflash.main(argv=[ 'foo.py', '/media/foo/bar', '/media/foo/baz', '/media/foo/bob']) mock_flash.assert_called_once_with( path_to_python='foo.py', paths_to_microbits=[ '/media/foo/bar', '/media/foo/baz', '/media/foo/bob'], path_to_runtime=None, minify=False) def test_main_runtime(): """ If there are three arguments passed into main, then it should pass them onto the flash() function. """ with mock.patch('uflash.flash') as mock_flash: uflash.main(argv=['-r' 'baz.hex', 'foo.py', '/media/foo/bar']) mock_flash.assert_called_once_with( path_to_python='foo.py', paths_to_microbits=['/media/foo/bar'], path_to_runtime='baz.hex', minify=False) def test_main_named_args(): """ Ensure that named arguments are passed on properly to the flash() function. """ with mock.patch('uflash.flash') as mock_flash: uflash.main(argv=['-r', 'baz.hex']) mock_flash.assert_called_once_with(path_to_python=None, paths_to_microbits=[], path_to_runtime='baz.hex', minify=False) def test_main_watch_flag(): """ The watch flag cause a call the correct function. """ with mock.patch('uflash.watch_file') as mock_watch_file: uflash.main(argv=['-w']) mock_watch_file.assert_called_once_with(None, uflash.flash, path_to_python=None, paths_to_microbits=[], path_to_runtime=None) def test_extract_command(): """ Test the command-line script extract feature """ with mock.patch('uflash.extract') as mock_extract: uflash.main(argv=['-e', 'hex.hex', 'foo.py']) mock_extract.assert_called_once_with('hex.hex', ['foo.py']) def test_extract_paths(): """ Test the different paths of the extract() function. It should open and extract the contents of the file (input arg) When called with only an input it should print the output of extract_script When called with two arguments it should write the output to the output arg """ mock_e = mock.MagicMock(return_value=b'print("hello, world!")') mock_o = mock.mock_open(read_data='script') with mock.patch('uflash.extract_script', mock_e) as mock_extract_script, \ mock.patch.object(builtins, 'print') as mock_print, \ mock.patch.object(builtins, 'open', mock_o) as mock_open: uflash.extract('foo.hex') mock_open.assert_called_once_with('foo.hex', 'r') mock_extract_script.assert_called_once_with('script') mock_print.assert_called_once_with(b'print("hello, world!")') uflash.extract('foo.hex', 'out.py') assert mock_open.call_count == 3 mock_open.assert_called_with('out.py', 'w') assert mock_open.return_value.write.call_count == 1 def test_extract_command_source_only(): """ If there is no target file the extract command should write to stdout. """ mock_e = mock.MagicMock(return_value=b'print("hello, world!")') mock_o = mock.mock_open(read_data='script') with mock.patch('uflash.extract_script', mock_e) as mock_extract_script, \ mock.patch.object(builtins, 'open', mock_o) as mock_open, \ mock.patch.object(builtins, 'print') as mock_print: uflash.main(argv=['-e', 'hex.hex']) mock_open.assert_any_call('hex.hex', 'r') mock_extract_script.assert_called_once_with('script') mock_print.assert_any_call(b'print("hello, world!")') def test_extract_command_no_source(): """ If there is no source file the extract command should complain """ with pytest.raises(TypeError): uflash.extract(None, None) def test_watch_no_source(): """ If there is no source file the watch command should complain. """ with pytest.raises(ValueError): uflash.watch_file(None, lambda: "should never be called!") @mock.patch('uflash.time') @mock.patch('uflash.os') def test_watch_file(mock_os, mock_time): """ Make sure that the callback is called each time the file changes. """ # Our function will throw KeyboardInterrupt when called for the 2nd time, # ending the watching gracefully.This will help in testing the # watch_file function. call_count = [0] def func(): call_count[0] = call_count[0] + 1 if call_count[0] == 2: raise KeyboardInterrupt() # Instead of modifying any file, let's change the return value of # os.path.getmtime. Start with initial value of 0. mock_os.path.getmtime.return_value = 0 t = threading.Thread(target=uflash.watch_file, args=('path/to/file', func)) t.start() time.sleep(0.01) mock_os.path.getmtime.return_value = 1 # Simulate file change time.sleep(0.01) assert t.is_alive() assert call_count[0] == 1 mock_os.path.getmtime.return_value = 2 # Simulate file change t.join() assert call_count[0] == 2 def test_hexlify_validates_script_length(): input = b"A" * 8193 with pytest.raises(ValueError) as excinfo: uflash.hexlify(input) assert str(excinfo.value) == "Python script must be less than 8188 bytes." python-uflash-1.2.4+dfsg.orig/tests/mount_exists.txt0000644000175000017500000000121413404702220021263 0ustar nicknicksysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) udev on /dev type devtmpfs (rw,relatime,size=10240k,nr_inodes=489849,mode=755) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) tmpfs on /run type tmpfs (rw,nosuid,relatime,size=787732k,mode=755) /dev/mapper/heraclitus--vg-root on / type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/sdb on /media/ntoll/MICROBIT type vfat (rw,nosuid,nodev,relatime,uid=1000,gid=1000,fmask=0022,dmask=0077,codepage=437,iocharset=utf8,shortname=mixed,showexec,utf8,flush,errors=remount-ro,uhelper=udisks2) python-uflash-1.2.4+dfsg.orig/setup.py0000644000175000017500000000231213404702220016331 0ustar nicknick#!/usr/bin/env python3 from setuptools import setup from uflash import get_version with open('README.rst') as f: readme = f.read() with open('CHANGES.rst') as f: changes = f.read() setup( name='uflash', version=get_version(), description='A module and utility to flash Python onto the BBC micro:bit.', long_description=readme + '\n\n' + changes, author='Nicholas H.Tollervey', author_email='ntoll@ntoll.org', url='https://github.com/ntoll/uflash', py_modules=['uflash', ], license='MIT', classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: Education', 'License :: OSI Approved :: MIT License', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Education', 'Topic :: Software Development :: Embedded Systems', ], entry_points={ 'console_scripts': ['uflash=uflash:main'], } ) python-uflash-1.2.4+dfsg.orig/tox.ini0000644000175000017500000000066513404702220016143 0ustar nicknick[tox] envlist = py27, py35, lint [testenv] commands = py.test --cov-report term-missing --cov=uflash {posargs:tests/} deps = pytest pytest-cov coveralls nudatus # Mock is bundled as part of unittest since Python 3.3 # mock_open can't read binary data in < 3.4.3 py{27,34}: mock [testenv:lint] commands = pyflakes setup.py uflash.py tests/ pep8 setup.py uflash.py tests/ deps = pyflakes pep8 python-uflash-1.2.4+dfsg.orig/AUTHORS0000644000175000017500000000020313404702220015664 0ustar nicknickNicholas H.Tollervey (ntoll@ntoll.org) Matt Wheeler (m@funkyhat.org) Tom Viner (uflash@viner.tv) Tom Gurion (nagasaki45@gmail.com) python-uflash-1.2.4+dfsg.orig/uflash.py0000644000175000017500000004035013406517065016474 0ustar nicknick# -*- coding: utf-8 -*- """ This module contains functions for turning a Python script into a .hex file and flashing it onto a BBC micro:bit. Copyright (c) 2015-2018 Nicholas H.Tollervey and others. See the LICENSE file for more information, or visit: https://opensource.org/licenses/MIT """ from __future__ import print_function import argparse import binascii import ctypes import os import struct import sys from subprocess import check_output import time # nudatus is an optional dependancy can_minify = True try: import nudatus except ImportError: # pragma: no cover can_minify = False #: The magic start address in flash memory for a Python script. _SCRIPT_ADDR = 0x3e000 #: The help text to be shown when requested. _HELP_TEXT = """ Flash Python onto the BBC micro:bit or extract Python from a .hex file. If no path to the micro:bit is provided uflash will attempt to autodetect the correct path to the device. If no path to the Python script is provided uflash will flash the unmodified MicroPython firmware onto the device. Use the -e flag to recover a Python script from a hex file. Use the -r flag to specify a custom version of the MicroPython runtime. Documentation is here: https://uflash.readthedocs.io/en/latest/ """ #: MAJOR, MINOR, RELEASE, STATUS [alpha, beta, final], VERSION of uflash _VERSION = (1, 2, 4, ) _MAX_SIZE = 8188 #: The version number reported by the bundled MicroPython in os.uname(). MICROPYTHON_VERSION = '1.0.1' def get_version(): """ Returns a string representation of the version information of this project. """ return '.'.join([str(i) for i in _VERSION]) def get_minifier(): """ Report the minifier will be used when minify=True """ if can_minify: return 'nudatus' return None def strfunc(raw): """ Compatibility for 2 & 3 str() """ return str(raw) if sys.version_info[0] == 2 else str(raw, 'utf-8') def hexlify(script, minify=False): """ Takes the byte content of a Python script and returns a hex encoded version of it. Based on the hexlify script in the microbit-micropython repository. """ if not script: return '' # Convert line endings in case the file was created on Windows. script = script.replace(b'\r\n', b'\n') script = script.replace(b'\r', b'\n') if minify: if not can_minify: raise ValueError("No minifier is available") script = nudatus.mangle(script.decode('utf-8')).encode('utf-8') # Add header, pad to multiple of 16 bytes. data = b'MP' + struct.pack(' _MAX_SIZE: # 'MP' = 2 bytes, script length is another 2 bytes. raise ValueError("Python script must be less than 8188 bytes.") # Convert to .hex format. output = [':020000040003F7'] # extended linear address, 0x0003. addr = _SCRIPT_ADDR for i in range(0, len(data), 16): chunk = data[i:min(i + 16, len(data))] chunk = struct.pack('>BHB', len(chunk), addr & 0xffff, 0) + chunk checksum = (-(sum(bytearray(chunk)))) & 0xff hexline = ':%s%02X' % (strfunc(binascii.hexlify(chunk)).upper(), checksum) output.append(hexline) addr += 16 return '\n'.join(output) def unhexlify(blob): """ Takes a hexlified script and turns it back into a string of Python code. """ lines = blob.split('\n')[1:] output = [] for line in lines: # Discard the address, length etc. and reverse the hexlification output.append(binascii.unhexlify(line[9:-2])) # Check the header is correct ("MP") if (output[0][0:2].decode('utf-8') != u'MP'): return '' # Strip off header output[0] = output[0][4:] # and strip any null bytes from the end output[-1] = output[-1].strip(b'\x00') script = b''.join(output) try: result = script.decode('utf-8') return result except UnicodeDecodeError: # Return an empty string because in certain rare circumstances (where # the source hex doesn't include any embedded Python code) this # function may be passed in "raw" bytes from MicroPython. return '' def embed_hex(runtime_hex, python_hex=None): """ Given a string representing the MicroPython runtime hex, will embed a string representing a hex encoded Python script into it. Returns a string representation of the resulting combination. Will raise a ValueError if the runtime_hex is missing. If the python_hex is missing, it will return the unmodified runtime_hex. """ if not runtime_hex: raise ValueError('MicroPython runtime hex required.') if not python_hex: return runtime_hex py_list = python_hex.split() runtime_list = runtime_hex.split() embedded_list = [] # The embedded list should be the original runtime with the Python based # hex embedded two lines from the end. embedded_list.extend(runtime_list[:-5]) embedded_list.extend(py_list) embedded_list.extend(runtime_list[-5:]) return '\n'.join(embedded_list) + '\n' def extract_script(embedded_hex): """ Given a hex file containing the MicroPython runtime and an embedded Python script, will extract the original Python script. Returns a string containing the original embedded script. """ hex_lines = embedded_hex.split('\n') script_addr_high = hex((_SCRIPT_ADDR >> 16) & 0xffff)[2:].upper().zfill(4) script_addr_low = hex(_SCRIPT_ADDR & 0xffff)[2:].upper().zfill(4) start_script = None within_range = False # Look for the script start address for loc, val in enumerate(hex_lines): if val[0:9] == ':02000004': # Reached an extended address record, check if within script range within_range = val[9:13].upper() == script_addr_high elif within_range and val[0:3] == ':10' and \ val[3:7].upper() == script_addr_low: start_script = loc break if start_script: # Find the end of the script end_script = None for loc, val in enumerate(hex_lines[start_script:]): if val[9:41] == 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF': end_script = loc + start_script break # Pass the extracted hex through unhexlify return unhexlify('\n'.join( hex_lines[start_script - 1:end_script if end_script else -6])) return '' def find_microbit(): """ Returns a path on the filesystem that represents the plugged in BBC micro:bit that is to be flashed. If no micro:bit is found, it returns None. Works on Linux, OSX and Windows. Will raise a NotImplementedError exception if run on any other operating system. """ # Check what sort of operating system we're on. if os.name == 'posix': # 'posix' means we're on Linux or OSX (Mac). # Call the unix "mount" command to list the mounted volumes. mount_output = check_output('mount').splitlines() mounted_volumes = [x.split()[2] for x in mount_output] for volume in mounted_volumes: if volume.endswith(b'MICROBIT'): return volume.decode('utf-8') # Return a string not bytes. elif os.name == 'nt': # 'nt' means we're on Windows. def get_volume_name(disk_name): """ Each disk or external device connected to windows has an attribute called "volume name". This function returns the volume name for the given disk/device. Code from http://stackoverflow.com/a/12056414 """ vol_name_buf = ctypes.create_unicode_buffer(1024) ctypes.windll.kernel32.GetVolumeInformationW( ctypes.c_wchar_p(disk_name), vol_name_buf, ctypes.sizeof(vol_name_buf), None, None, None, None, 0) return vol_name_buf.value # # In certain circumstances, volumes are allocated to USB # storage devices which cause a Windows popup to raise if their # volume contains no media. Wrapping the check in SetErrorMode # with SEM_FAILCRITICALERRORS (1) prevents this popup. # old_mode = ctypes.windll.kernel32.SetErrorMode(1) try: for disk in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': path = '{}:\\'.format(disk) # # Don't bother looking if the drive isn't removable # if ctypes.windll.kernel32.GetDriveTypeW(path) != 2: continue if os.path.exists(path) and \ get_volume_name(path) == 'MICROBIT': return path finally: ctypes.windll.kernel32.SetErrorMode(old_mode) else: # No support for unknown operating systems. raise NotImplementedError('OS "{}" not supported.'.format(os.name)) def save_hex(hex_file, path): """ Given a string representation of a hex file, this function copies it to the specified path thus causing the device mounted at that point to be flashed. If the hex_file is empty it will raise a ValueError. If the filename at the end of the path does not end in '.hex' it will raise a ValueError. """ if not hex_file: raise ValueError('Cannot flash an empty .hex file.') if not path.endswith('.hex'): raise ValueError('The path to flash must be for a .hex file.') with open(path, 'wb') as output: output.write(hex_file.encode('ascii')) def flash(path_to_python=None, paths_to_microbits=None, path_to_runtime=None, python_script=None, minify=False): """ Given a path to or source of a Python file will attempt to create a hex file and then flash it onto the referenced BBC micro:bit. If the path_to_python & python_script are unspecified it will simply flash the unmodified MicroPython runtime onto the device. If used, the python_script argument should be a bytes object representing a UTF-8 encoded string. For example:: script = "from microbit import *\\ndisplay.scroll('Hello, World!')" uflash.flash(python_script=script.encode('utf-8')) If paths_to_microbits is unspecified it will attempt to find the device's path on the filesystem automatically. If the path_to_runtime is unspecified it will use the built in version of the MicroPython runtime. This feature is useful if a custom build of MicroPython is available. If the automatic discovery fails, then it will raise an IOError. """ # Check for the correct version of Python. if not ((sys.version_info[0] == 3 and sys.version_info[1] >= 3) or (sys.version_info[0] == 2 and sys.version_info[1] >= 7)): raise RuntimeError('Will only run on Python 2.7, or 3.3 and later.') # Grab the Python script (if needed). python_hex = '' if path_to_python: if not path_to_python.endswith('.py'): raise ValueError('Python files must end in ".py".') with open(path_to_python, 'rb') as python_script: python_hex = hexlify(python_script.read(), minify) elif python_script: python_hex = hexlify(python_script, minify) runtime = _RUNTIME # Load the hex for the runtime. if path_to_runtime: with open(path_to_runtime) as runtime_file: runtime = runtime_file.read() # Generate the resulting hex file. micropython_hex = embed_hex(runtime, python_hex) # Find the micro:bit. if not paths_to_microbits: found_microbit = find_microbit() if found_microbit: paths_to_microbits = [found_microbit] # Attempt to write the hex file to the micro:bit. if paths_to_microbits: for path in paths_to_microbits: hex_path = os.path.join(path, 'micropython.hex') print('Flashing Python to: {}'.format(hex_path)) save_hex(micropython_hex, hex_path) else: raise IOError('Unable to find micro:bit. Is it plugged in?') def extract(path_to_hex, output_path=None): """ Given a path_to_hex file this function will attempt to extract the embedded script from it and save it either to output_path or stdout """ with open(path_to_hex, 'r') as hex_file: python_script = extract_script(hex_file.read()) if output_path: with open(output_path, 'w') as output_file: output_file.write(python_script) else: print(python_script) def watch_file(path, func, *args, **kwargs): """ Watch a file for changes by polling its last modification time. Call the provided function with *args and **kwargs upon modification. """ if not path: raise ValueError('Please specify a file to watch') print('Watching "{}" for changes'.format(path)) last_modification_time = os.path.getmtime(path) try: while True: time.sleep(1) new_modification_time = os.path.getmtime(path) if new_modification_time == last_modification_time: continue func(*args, **kwargs) last_modification_time = new_modification_time except KeyboardInterrupt: pass def main(argv=None): """ Entry point for the command line tool 'uflash'. Will print help text if the optional first argument is "help". Otherwise it will ensure the optional first argument ends in ".py" (the source Python script). An optional second argument is used to reference the path to the micro:bit device. Any more arguments are ignored. Exceptions are caught and printed for the user. """ if not argv: argv = sys.argv[1:] parser = argparse.ArgumentParser(description=_HELP_TEXT) parser.add_argument('source', nargs='?', default=None) parser.add_argument('target', nargs='*', default=None) parser.add_argument('-r', '--runtime', default=None, help="Use the referenced MicroPython runtime.") parser.add_argument('-e', '--extract', action='store_true', help=("Extract python source from a hex file" " instead of creating the hex file."), ) parser.add_argument('-w', '--watch', action='store_true', help='Watch the source file for changes.') parser.add_argument('-m', '--minify', action='store_true', help='Minify the source') parser.add_argument('--version', action='version', version='%(prog)s ' + get_version()) args = parser.parse_args(argv) if args.extract: try: extract(args.source, args.target) except Exception as ex: error_message = "Error extracting {source}: {error!s}" print(error_message.format(source=args.source, error=ex), file=sys.stderr) sys.exit(1) elif args.watch: try: watch_file(args.source, flash, path_to_python=args.source, paths_to_microbits=args.target, path_to_runtime=args.runtime) except Exception as ex: error_message = "Error watching {source}: {error!s}" print(error_message.format(source=args.source, error=ex), file=sys.stderr) sys.exit(1) else: try: flash(path_to_python=args.source, paths_to_microbits=args.target, path_to_runtime=args.runtime, minify=args.minify) except Exception as ex: error_message = ( "Error flashing {source} to {target}{runtime}: {error!s}" ) source = args.source target = args.target if args.target else "microbit" if args.runtime: runtime = "with runtime {runtime}".format(runtime=args.runtime) else: runtime = "" print(error_message.format(source=source, target=target, runtime=runtime, error=ex), file=sys.stderr) sys.exit(1) #: A string representation of the MicroPython runtime hex. if __name__ == '__main__': # pragma: no cover main(sys.argv[1:]) python-uflash-1.2.4+dfsg.orig/docs/0000755000175000017500000000000013406517065015566 5ustar nicknickpython-uflash-1.2.4+dfsg.orig/docs/index.rst0000644000175000017500000000061413404702220017413 0ustar nicknick.. uFlash documentation master file, created by sphinx-quickstart on Thu Dec 17 19:01:47 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. include:: ../README.rst .. include:: ../CONTRIBUTING.rst API === .. automodule:: uflash :members: .. include:: ../CHANGES.rst License ======= .. include:: ../LICENSE python-uflash-1.2.4+dfsg.orig/docs/conf.py0000644000175000017500000002605413404702220017057 0ustar nicknick#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # uFlash documentation build configuration file, created by # sphinx-quickstart on Thu Dec 17 19:01:47 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) import uflash # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'uFlash' copyright = '2015, Nicholas H.Tollervey' author = 'Nicholas H.Tollervey' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = uflash.get_version() # The full version, including alpha/beta/rc tags. release = uflash.get_version() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'uFlashdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'uFlash.tex', 'uFlash Documentation', 'Nicholas H.Tollervey', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'uflash', 'uFlash Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'uFlash', 'uFlash Documentation', author, 'uFlash', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The basename for the epub file. It defaults to the project name. #epub_basename = project # The HTML theme for the epub output. Since the default themes are not optimized # for small screen space, using the same theme for HTML and epub output is # usually not wise. This defaults to 'epub', a theme designed to save visual # space. #epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. #epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Choose between 'default' and 'includehidden'. #epub_tocscope = 'default' # Fix unsupported image types using the Pillow. #epub_fix_images = False # Scale large images. #epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. #epub_show_urls = 'inline' # If false, no index is generated. #epub_use_index = True python-uflash-1.2.4+dfsg.orig/docs/Makefile0000644000175000017500000001636113404702220017220 0ustar nicknick# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/uFlash.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/uFlash.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/uFlash" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/uFlash" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." python-uflash-1.2.4+dfsg.orig/Makefile0000644000175000017500000000331513404702220016263 0ustar nicknickXARGS := xargs -0 $(shell test $$(uname) = Linux && echo -r) GREP_T_FLAG := $(shell test $$(uname) = Linux && echo -T) all: @echo "\nThere is no default Makefile target right now. Try:\n" @echo "make clean - reset the project and remove auto-generated assets." @echo "make pyflakes - run the PyFlakes code checker." @echo "make pep8 - run the PEP8 style checker." @echo "make test - run the test suite." @echo "make coverage - view a report on test coverage." @echo "make check - run all the checkers and tests." @echo "make package - create a deployable package for the project." @echo "make publish - publish the project to PyPI." @echo "make docs - run sphinx to create project documentation.\n" clean: rm -rf build rm -rf dist rm -rf uflash.egg-info rm -rf .coverage rm -rf .tox rm -rf docs/_build find . \( -name '*.py[co]' -o -name dropin.cache \) -print0 | $(XARGS) rm find . \( -name '*.bak' -o -name dropin.cache \) -print0 | $(XARGS) rm find . \( -name '*.tgz' -o -name dropin.cache \) -print0 | $(XARGS) rm pyflakes: find . \( -name _build -o -name var -o -path ./docs \) -type d -prune -o -name '*.py' -print0 | $(XARGS) pyflakes pep8: clean find . \( -name _build -o -name var \) -type d -prune -o -name '*.py' -print0 | $(XARGS) -n 1 pycodestyle --repeat --exclude=build/*,docs/* --ignore=E731,E402,W504 test: clean py.test coverage: clean py.test --cov-report term-missing --cov=uflash tests/ check: clean pep8 pyflakes coverage package: check python setup.py sdist publish: check @echo "\nChecks pass, good to publish..." python setup.py sdist upload docs: clean $(MAKE) -C docs html @echo "\nDocumentation can be found here:" @echo file://`pwd`/docs/_build/html/index.html @echo "\n" python-uflash-1.2.4+dfsg.orig/LICENSE0000644000175000017500000000207113404702220015626 0ustar nicknickCopyright (c) 2015-2018 Nicholas H.Tollervey and others. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-uflash-1.2.4+dfsg.orig/requirements.txt0000644000175000017500000000035013404702220020103 0ustar nicknickpytest pycodestyle pyflakes coverage sphinx pytest-cov nudatus>=0.0.2 # Mock is bundled as part of unittest since Python 3.3 # mock_open can't read binary data in <= 3.4.2 mock ; python_version == '2.7' or python_version == '3.4' python-uflash-1.2.4+dfsg.orig/MANIFEST.in0000644000175000017500000000006713404702220016362 0ustar nicknickinclude CHANGES.rst include README.rst include LICENSE python-uflash-1.2.4+dfsg.orig/CONTRIBUTING.rst0000644000175000017500000000376513404702220017275 0ustar nicknickContributing to uFlash ====================== Hey! Many thanks for wanting to improve uFlash. Contributions are welcome without prejudice from *anyone* irrespective of age, gender, religion, race or sexuality. If you're thinking, "but they don't mean me", then we especially mean YOU. Good quality code and engagement with respect, humour and intelligence wins every time. * If you're from a background which isn't well-represented in most geeky groups, get involved - *we want to help you make a difference*. * If you're from a background which *is* well-represented in most geeky groups, get involved - *we want your help making a difference*. * If you're worried about not being technical enough, get involved - *your fresh perspective will be invaluable*. * If you think you're an imposter, get involved. * If your day job isn't code, get involved. * This isn't a group of experts, just people. Get involved! * We are interested in educational, social and technical problems. If you are too, get involved. * This is a new community. *No-one knows what they are doing*, so, get involved. We expect contributors to follow the Python Software Foundation's Code of Conduct: https://www.python.org/psf/codeofconduct/ Feedback may be given for contributions and, where necessary, changes will be politely requested and discussed with the originating author. Respectful yet robust argument is most welcome. Finally, contributions are subject to the following caveat: the contribution was created by the contributor who, by submitting the contribution, is confirming that they have the authority to submit the contribution and place it under the license as defined in the LICENSE file found within this repository. Checklist --------- * Your code should be commented in *plain English* (British spelling). * If your contribution is for a major block of work and you've not done so already, add yourself to the AUTHORS file following the convention found therein. * You MUST include tests. We have 100% test coverage. * Have fun!