Пример #1
0
    def _repl_serial(self, serial_ok):
        """Thread, copies bytes from serial to out"""

        term = Terminal()

        try:
            with serial_ok, term.raw():
                save_timeout = self._serial.timeout
                # Set a timeout so that the read returns periodically with no data
                # and allows us to check whether the main thread wants us to quit.
                self._serial.timeout = 0.4
                while not self._quit_serial_reader:
                    char = self._serial.read(1)
                    if char.decode('utf-8') != '':
                        print(char.decode('utf-8'), end='', flush=True)
                self._serial.timeout = save_timeout
        except ConnectionError as e:
            self.disconnect()
            print('\r')
            eprint(str(e).replace('\n', '\r'))
        except Exception:
            # catchall, print error traceback
            from io import StringIO
            s = StringIO()
            print('\r', printing.ERR_COLOR)
            traceback.print_exc(file=s)
            eprint(s.getvalue().replace('\n', '\r'))
Пример #2
0
def do_rm(self, line):
    """rm [-f|--force] FILE...             Remove one or more files
    rm [-f|--force] PATTERN                Remove multiple files
    rm -r [-f|--force] [FILE|DIRECTORY]... Files and/or directories
    rm -r [-f|--force] PATTERN             Multiple files and/or directories

    Removes files or directories. To remove directories (and
    any contents) -r must be specified.

    """
    args = self.line_to_args(line)
    filenames = args.filename
    # Process PATTERN
    sfn = filenames[0]
    if is_pattern(sfn):
        if len(filenames) > 1:
            eprint("Usage: rm [-r] [-f] PATTERN")
            return
        filenames = process_pattern(self.boards, self.cur_dir, sfn)
        if filenames is None:
            return

    for filename in filenames:
        filename = resolve_path(self.cur_dir, filename)
        if not rm(
                self.boards, filename, recursive=args.recursive,
                force=args.force):
            if not args.force:
                eprint(
                    "Unable to remove '{}' (try -rf if you are sure)".format(
                        filename))
            break
Пример #3
0
def do_run(self, line):
    """run [FILE]

    Send file to remote for execution and print results on console.

    If FILE is not specified, executes the file from the last invocation.
    """
    global LAST_RUN_FILE
    args = line.split()
    if len(args) > 1:
        eprint("*** Only one file, please!")
        return

    if len(args) == 0:
        file = LAST_RUN_FILE
        qprint("run '{}' on micropython board".format(file))
    else:
        file = os.path.join(self.cur_dir, args[0])
        LAST_RUN_FILE = file

    print(printing.MPY_COLOR, end='')
    try:
        self.boards.default.execfile(file, data_consumer=putch, timeout=None)
    except FileNotFoundError:
        eprint("*** File not found on host, '{}'".format(file))
    except KeyboardInterrupt:
        print()
Пример #4
0
def do_repl(self, line):
    """repl [board-name] [~ line [~]]

       Enters into the regular REPL (read-eval-print-loop) with the
       MicroPython board.
       Use Control-X to exit REPL mode and return the shell.
       It may take a couple of seconds before the REPL exits.

       If you provide a line to the repl command, then that will be executed.
       If you want the REPL to exit, end the line with the ~ character.
    """
    args = self.line_to_args(line)
    if len(args) > 0 and line[0] != '~':
        name = args[0]
        line = ' '.join(args[1:])
        board = self.boards.find_board(name)
        if not board:
            eprint("No board '{}'".format(name))
            return
    else:
        board = self.boards.default

    if line[0:2] == '~ ':
        line = line[2:]

    mprint('Entering REPL.  Control-%c to exit.' % QUIT_REPL_CHAR)
    mprint('   Soft reset:  Control-D or sys.exit()')
    mprint('   Hard reset:  Reset button on board or machine.reset()')
    print(printing.MPY_COLOR, end='')

    board.repl(getch, putch)
    print()
Пример #5
0
def do_mkdir(self, line):
    """mkdir DIRECTORY...

       Creates one or more directories.
    """
    args = self.line_to_args(line)
    for filename in args:
        filename = resolve_path(self.cur_dir, filename)
        if not mkdir(self.boards, filename):
            eprint('Unable to create %s' % filename)
Пример #6
0
def process_pattern(devs, cur_dir, fn):
    """Return a list of paths matching a pattern (or None on error).
    """
    directory, pattern = validate_pattern(devs, cur_dir, fn)
    if directory is not None:
        filenames = fnmatch.filter(auto(devs, listdir, directory), pattern)
        if filenames:
            return [directory + '/' + sfn for sfn in filenames]
        else:
            eprint("cannot access '{}': No such file or directory".format(fn))
Пример #7
0
 def remote_eval(self, func, *args, **kwargs):
     """Calls func with the indicated args on the micropython board, and
        converts the response back into python by using eval.
     """
     res = self.remote(func, *args, **kwargs)
     try:
         return eval(res)
     except (SyntaxError, ValueError) as e:
         eprint("*** remote_eval({}, {}, {}) -> \n{} is not valid python code".format(
             func.__name__, args, kwargs, res.decode('utf-8')))
         return None
Пример #8
0
    def __init__(self, port=None, baudrate=115200):
        self.is_circuitpy = False
        try:
            # check which ports are available
            if not port:
                for p in comports():
                    if p.vid == ADAFRUIT_VID:
                        port = p.device
                        self.is_circuitpy = True
                        break
                    elif p.vid == ESP32_VID:
                        port = p.device
                        break
                    elif p.vid:
                        qprint(f"Unknown board {p} with vid '{p.vid}' skipped")

            # did we find a valid board?
            if not port:
                eprint("No board found")
                sys.exit(1)

            # wait for port to come online
            for wait in range(3):
                if os.path.exists(port): break
                qprint("Waiting for port '{}' to come online".format(port))
                time.sleep(1)
            # try to connect
            for attempt in range(5):
                try:
                    self._serial = Serial(port,
                                          baudrate,
                                          parity='N',
                                          inter_byte_timeout=1)
                    break
                except IOError as e:
                    qprint(
                        "Waiting for serial connection at '{}'".format(port))
                    qprint(e)
                time.sleep(1)
            # send Control-C to put MicroPython in known state
            for attempt in range(20):
                try:
                    self._serial.write(b'\x03')
                    break
                except SerialException:
                    time.sleep(0.5)
                    qprint("Trying to talk to the MicroPython interpreter")
            self._port = port
            qprint(f"SerialConnection to {port} established")
        except AttributeError:
            raise ConnectionError(
                "Failed connecting to board at '{}'".format(port))
        except KeyboardInterrupt:
            self._serial = None
Пример #9
0
def do_ls(self, line):
    """ls [-a] [-l] [FILE|DIRECTORY|PATTERN]...
   PATTERN supports * ? [seq] [!seq] Unix filename matching

       List directory contents.
    """
    args = self.line_to_args(line)
    if len(args.filenames) == 0:
        args.filenames = ['.']
    for idx, fn in enumerate(args.filenames):
        if not is_pattern(fn):
            filename = resolve_path(self.cur_dir, fn)
            stat = auto(self.boards, get_stat, filename)
            mode = stat_mode(stat)
            if not mode_exists(mode):
                err = "Cannot access '{}': No such file or directory"
                eprint(err.format(filename))
                continue
            if not mode_isdir(mode):
                if args.long:
                    print_long(filename, stat, oprint)
                else:
                    oprint(filename)
                continue
            if len(args.filenames) > 1:
                if idx > 0:
                    oprint('')
                oprint("%s:" % filename)
            pattern = '*'
        else:  # A pattern was specified
            filename, pattern = validate_pattern(self.boards, self.cur_dir, fn)
            if filename is None:  # An error was printed
                continue
        files = []
        ldir_stat = auto(self.boards, listdir_stat, filename)
        if ldir_stat is None:
            err = "Cannot access '{}': No such file or directory"
            eprint(err.format(filename))
        else:
            for filename, stat in sorted(ldir_stat,
                                         key=lambda entry: entry[0]):
                if is_visible(filename) or args.all:
                    if fnmatch(filename, pattern):
                        if args.long:
                            print_long(filename, stat, oprint)
                        else:
                            files.append(decorated_filename(filename, stat))
        if len(files) > 0:
            print_cols(sorted(files), oprint,
                       shutil.get_terminal_size().columns)
Пример #10
0
 def _load(self):
     qprint("Loading configuration '{}'".format(self._config_file))
     try:
         with open(self._config_file) as f:
             self._config = literal_eval(f.read())
     except FileNotFoundError:
         oprint("WARNING: configuration '{}' does not exist, creating default".format(self._config_file))
         self._create_default()
         self._modified = True
     except SyntaxError as e:
         eprint("Syntax error in {}: {}".format(self._config_file, e))
         eprint("If the problem persists, manually check the file for "
                "invalid Python syntax or delete it and re-enter the configuration information.")
         sys.exit()
Пример #11
0
def do_connect(self, line):
    """    connect TYPE TYPE_PARAMS            Connect boards to shell49.
    connect serial [port [baud]]        Wired connection. Uses defaults from config file.
    connect telnet [url [user [pwd]]]   Wireless connection. If no url/ip address is
        specified, connects to all known boards advertising repl service via mDNS.
        Optional user (default: 'micro') and password (default: 'python').

    Note: do not connect to the same board via serial AND telnet connections.
          Doing so may block communication with the board.
    """
    args = self.line_to_args(line)
    if len(args) < 1:
        eprint('Missing connection TYPE')
        return
    connect_type = args[0]
    if connect_type == 'serial':
        port = args[1] if len(args) > 1 else self.config.get(
            0, 'port', '/dev/cu.SLAB_USBtoUART')
        baud = args[2] if len(args) > 2 else self.config.get(
            0, 'baudrate', '115200')
        try:
            baud = int(baud)
        except ValueError:
            eprint("Not a valid baudrate, '{}'".format(baud))
            return
        # Note: board may be connected over telnet, but we don't know ...
        #       in this case, connect blocks
        if self.boards.find_board(port):
            eprint("board already connected on '{}'".format(port))
            return
        self.boards.connect_serial(port, baud)
    elif connect_type == 'telnet':
        if len(args) > 1:
            user = args[2] if len(args) > 2 else 'micro'
            pwd = args[3] if len(args) > 3 else 'python'
            self.boards.connect_telnet(args[1], user, pwd)
        else:
            listener = MdnsListener()
            adv = list(listener.listen(seconds=1))
            if len(adv) == 0:
                qprint("No boards detected via mDNS.")
            for b in adv:
                qprint("Heard from '{}' ({})".format(b.url, b.ip))
                # connect only to boards in the config database
                board_id = self.config.get_board_from_name(b.hostname)
                if not board_id:
                    qprint("  not in db, skip!")
                    continue
                # we are not already connected to
                if self.boards.connected(b.hostname):
                    qprint("  already connected")
                    continue
                # let's connect!
                user = self.config.get(board_id, 'user', 'micro')
                pwd = self.config.get(board_id, 'password', 'python')
                self.boards.connect_telnet(b.url, user, pwd)
    else:
        eprint('Unrecognized connection TYPE: {}'.format(connect_type))
Пример #12
0
def do_flash(self, line):
    """flash [-l|--list] [-e|--erase] [-v|--version VERSION] [-b|--board BOARD]

    Flash firmware to microcontroller.
    """

    args = self.line_to_args(line)
    firmware_url = "https://people.eecs.berkeley.edu/~boser/iot49/firmware"
    flash_options = "--chip esp32 " \
        "--before default_reset --after hard_reset " \
        "write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect"
    id = 0
    try:
        id = self.boards.default.id
    except BoardError:
        pass
    firmware_url = self.config.get(id, "firmware_url", firmware_url)
    flash_options = self.config.get(id, "flash_options", flash_options)
    port = self.config.get(id, "port", "/dev/cu.SLAB_USBtoUART")
    baudrate = self.config.get(id, "flash_baudrate", 921600)
    board = self.config.get(id, "board", "HUZZAH32")
    if args.board:
        board = args.board

    dprint("firmware url: ", firmware_url)
    dprint("flash options:", flash_options)
    dprint("port:         ", port)
    dprint("baudrate:     ", baudrate)
    dprint("board:        ", board)

    try:
        f = Flasher(board=board, url=firmware_url)
        if args.list:
            print("available firmware versions:")
            print('\n'.join(
                ["  {:8s} {}".format(v, d) for v, d in f.versions()]))
            return
        dev = self.boards.find_board(port)
        if dev: dev.disconnect()
        if args.erase:
            f.erase_flash(port)
        f.flash(args.version,
                flash_options=flash_options,
                port=port,
                baudrate=baudrate)
    except FlasherError as e:
        eprint(e)
Пример #13
0
def make_dir(devs, dst_dir, dry_run, recursed):
    """Creates a directory. Produces information in case of dry run.
    Isues error where necessary.
    """
    parent = os.path.split(dst_dir.rstrip(
        '/'))[0]  # Check for nonexistent parent
    parent_files = auto(devs, listdir_stat,
                        parent) if parent else True  # Relative dir
    if dry_run:
        if recursed:  # Assume success: parent not actually created yet
            qprint("Creating directory {}".format(dst_dir))
        elif parent_files is None:
            qprint("Unable to create {}".format(dst_dir))
        return True
    if not mkdir(devs, dst_dir):
        eprint("Unable to create {}".format(dst_dir))
        return False
    return True
Пример #14
0
def do_cat(self, line):
    """cat FILENAME...

    Concatenates files and sends to stdout.
    """
    # note: when we get around to supporting cat from stdin, we'll need
    #       to write stdin to a temp file, and then copy the file
    #       since we need to know the filesize when copying to the pyboard.
    args = self.line_to_args(line)
    for filename in args:
        filename = resolve_path(self.cur_dir, filename)
        mode = auto(self.boards, get_mode, filename)
        if not mode_exists(mode):
            eprint("Cannot access '%s': No such file" % filename)
            continue
        if not mode_isfile(mode):
            eprint("'%s': is not a file" % filename)
            continue
        cat(self.boards, filename, self.stdout)
Пример #15
0
def do_rsync(self, line):
    """rsync [-m|--mirror] [-n|--dry-run] [SRC_DIR [DST_DIR]]

       Synchronize destination directory tree to source directory tree.
    """
    db = self.boards.default
    args = self.line_to_args(line)
    sd = args.src_dst_dir
    if len(sd) > 2:
        eprint("*** More than one destination directory given")
        return
    src_dir = sd[0] if len(sd) > 0 else db.get_config('host_dir', '~/iot49')
    dst_dir = sd[1] if len(sd) > 1 else db.get_config('remote_dir', '/flash')
    src_dir = resolve_path(self.cur_dir, src_dir)
    dst_dir = resolve_path(self.cur_dir, dst_dir)
    if len(sd) < 2:
        qprint("synchronizing {} --> {}".format(src_dir, dst_dir))
    rsync(self.boards, src_dir, dst_dir,
          mirror=not args.mirror, dry_run=args.dry_run, recursed=True)
Пример #16
0
def do_cd(self, line):
    """cd DIRECTORY

       Changes the current directory. ~ expansion is supported, and cd -
       goes to the previous directory.
    """
    args = self.line_to_args(line)
    if len(args) == 0:
        dirname = '~'
    else:
        if args[0] == '-':
            dirname = self.prev_dir
        else:
            dirname = args[0]
    dirname = resolve_path(self.cur_dir, dirname)

    mode = auto(self.boards, get_mode, dirname)
    if mode_isdir(mode):
        self.prev_dir = self.cur_dir
        self.cur_dir = dirname
        auto(self.boards, chdir, dirname)
    else:
        eprint("Directory '%s' does not exist" % dirname)
Пример #17
0
def do_edit(self, line):
    """edit FILE

       Copies the file locally, launches an editor to edit the file.
       When the editor exits, if the file was modified then its copied
       back.

       You can specify the editor used with the --editor command line
       option when you start shell49, or by using the SHELL49_EDITOR or VISUAL
       or EDITOR environment variable. If none of those are set, then
       vi will be used.
    """
    if len(line) == 0:
        eprint("Must provide a filename")
        return
    filename = resolve_path(self.cur_dir, line)
    dev, dev_filename = self.boards.get_dev_and_path(filename)
    mode = auto(self.boards, get_mode, filename)
    if mode_exists(mode) and mode_isdir(mode):
        eprint("Unable to edit directory '{}'".format(filename))
        return
    if dev is None:
        # File is local
        os.system("{} '{}'".format(self.editor, filename))
    else:
        # File is remote
        with tempfile.TemporaryDirectory() as temp_dir:
            local_filename = os.path.join(temp_dir, os.path.basename(filename))
            if mode_exists(mode):
                print('Retrieving {} ...'.format(filename))
                cp(filename, local_filename)
            old_stat = get_stat(local_filename)
            os.system("{} '{}'".format(self.editor, local_filename))
            new_stat = get_stat(local_filename)
            if old_stat != new_stat:
                print('Updating {} ...'.format(filename))
                cp(local_filename, filename)
Пример #18
0
 def onecmd(self, line):
     """Global error catcher"""
     try:
         res = cmd.Cmd.onecmd(self, line)
         if self.interactive: self.set_prompt()
         return res
     except (BoardError, ConnectionError) as e:
         eprint("***", e)
     except UnicodeDecodeError as e:
         eprint("***", e)
     except KeyboardInterrupt:
         eprint("Command aborted")
     except Exception as e:
         # catchall, to prevent shell from quitting
         print(printing.ERR_COLOR)
         traceback.print_exc(file=sys.stdout)
Пример #19
0
def validate_pattern(devs, cur_dir, fn):
    """On success return an absolute path and a pattern.
    Otherwise print a message and return None, None
    """
    directory, pattern = parse_pattern(fn)
    if directory is None:
        eprint("Invalid pattern {}.".format(fn))
        return None, None
    target = resolve_path(cur_dir, directory)
    mode = auto(devs, get_mode, target)
    if not mode_exists(mode):
        eprint("cannot access '{}': No such file or directory".format(fn))
        return None, None
    if not mode_isdir(mode):
        eprint("cannot access '{}': Not a directory".format(fn))
        return None, None
    return directory, pattern
Пример #20
0
def do_cp(self, line):
    """cp SOURCE DEST               Copy a single SOURCE file to DEST file.
    cp SOURCE... DIRECTORY          Copy multiple SOURCE files to a directory.
    cp [-r] PATTERN DIRECTORY       Copy matching files to DIRECTORY.
    cp [-r|--recursive] [SOURCE|SOURCE_DIR]... DIRECTORY

       The destination must be a directory except in the case of
       copying a single file. To copy directories -r must be specified.
       This will cause directories and their contents to be recursively
       copied.
    """
    args = self.line_to_args(line)
    if len(args.filenames) < 2:
        eprint('Missing destination file')
        return
    dst_dirname = resolve_path(self.cur_dir, args.filenames[-1])
    dst_mode = auto(self.boards, get_mode, dst_dirname)
    d_dst = {}  # Destination directory: lookup stat by basename
    if args.recursive:
        dst_files = auto(self.boards, listdir_stat, dst_dirname)
        if dst_files is None:
            err = "cp: target {} is not a directory"
            eprint(err.format(dst_dirname))
            return
        for name, stat in dst_files:
            d_dst[name] = stat

    src_filenames = args.filenames[:-1]

    # Process PATTERN
    sfn = src_filenames[0]
    if is_pattern(sfn):
        if len(src_filenames) > 1:
            eprint("Usage: cp [-r] PATTERN DIRECTORY")
            return
        src_filenames = process_pattern(self.boards, self.cur_dir, sfn)
        if src_filenames is None:
            return

    for src_filename in src_filenames:
        if is_pattern(src_filename):
            eprint("Only one pattern permitted.")
            return
        src_filename = resolve_path(self.cur_dir, src_filename)
        src_mode = auto(self.boards, get_mode, src_filename)
        if not mode_exists(src_mode):
            eprint("File '{}' doesn't exist".format(src_filename))
            return
        if mode_isdir(src_mode):
            if args.recursive:  # Copying a directory
                src_basename = os.path.basename(src_filename)
                dst_filename = os.path.join(dst_dirname, src_basename)
                if src_basename in d_dst:
                    dst_stat = d_dst[src_basename]
                    dst_mode = stat_mode(dst_stat)
                    if not mode_isdir(dst_mode):
                        err = "Destination {} is not a directory"
                        eprint(err.format(dst_filename))
                        return
                else:
                    if not mkdir(dst_filename):
                        err = "Unable to create directory {}"
                        eprint(err.format(dst_filename))
                        return

                rsync(src_filename, dst_filename, mirror=False, dry_run=False,
                      print_func=lambda *args: None, recursed=False)
            else:
                eprint("Omitting directory {}".format(src_filename))
            continue
        if mode_isdir(dst_mode):
            dst_filename = os.path.join(
                dst_dirname, os.path.basename(src_filename))
        else:
            dst_filename = dst_dirname
        if not cp(self.boards, src_filename, dst_filename):
            err = "Unable to copy '{}' to '{}'"
            eprint(err.format(src_filename, dst_filename))
            break
Пример #21
0
def main():
    """The main program."""

    if sys.version_info.major < 3:
        v = sys.version_info
        eprint("Shell49 requires Python 3.6 (not {}.{}.{})".format(
            v.major, v.minor, v.micro))
        return

    default_config = os.getenv('SHELL49_CONFIG_FILE') or '~/.shell49_rc.py'
    default_editor = os.getenv('SHELL49_EDITOR') or os.getenv(
        'VISUAL') or os.getenv('EDITOR') or 'vi'
    default_nocolor = 'win32' in sys.platform
    default_debug = False
    default_quiet = False

    parser = argparse.ArgumentParser(
        prog="shell49",
        usage="%(prog)s [options] [cmd]",
        description="Remote Shell for MicroPython boards.",
        epilog=("""
Environment variables:
  SHELL49_CONFIG_FILE   configuration file (Default: '{}')
  SHELL49_EDITOR        editor (Default: {})
""".format(default_config, default_editor)),
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(
        "-c",
        "--config",
        dest="config",
        help="Set path of the configuration file (default: '%s')" %
        default_config,
        default=default_config)
    parser.add_argument("-e",
                        "--editor",
                        dest="editor",
                        help="Set the editor to use (default: '%s')" %
                        default_editor,
                        default=default_editor)
    parser.add_argument("-d",
                        "--debug",
                        dest="debug",
                        action="store_true",
                        help="Enable debug features (default %s)" %
                        default_debug,
                        default=default_debug)
    parser.add_argument("-n",
                        "--nocolor",
                        dest="nocolor",
                        action="store_true",
                        help="Turn off colorized output (default: %s)" %
                        default_nocolor,
                        default=default_nocolor)
    parser.add_argument("--quiet",
                        dest="quiet",
                        action="store_true",
                        help="Turn off some output (default: %s)" %
                        default_quiet,
                        default=False)
    parser.add_argument(
        "-a",
        "--no_auto_connect",
        dest="auto_connect",
        action="store_false",
        help="Do not automatically connect to board connected to serial port",
        default=True)
    parser.add_argument('-V',
                        '--version',
                        dest='version',
                        action='store_true',
                        help='Report the version and exit.',
                        default=False)
    parser.add_argument("-f",
                        "--file",
                        dest="filename",
                        help="File of commands to process (non-interactive).")
    parser.add_argument("cmd",
                        nargs=argparse.REMAINDER,
                        help="Optional command to execute and quit.")
    args = parser.parse_args(sys.argv[1:])

    debug(args.debug)
    quiet(args.quiet or args.cmd or args.filename)
    if args.nocolor: nocolor()

    dprint("config = %s" % args.config)
    dprint("editor = %s" % args.editor)
    dprint("debug = %s" % args.debug)
    dprint("quiet = %s" % args.quiet)
    dprint("nocolor = %s" % args.nocolor)
    dprint("auto_connect = %s" % args.auto_connect)
    dprint("version = %s" % __version__)
    dprint("cmd = [%s]" % ', '.join(args.cmd))

    if args.version:
        print(__version__)
        return

    cmd_line = ' '.join(args.cmd)
    if not args.filename and cmd_line == '':
        oprint(
            "Welcome to shell49 version {}. Type 'help' for information; Control-D to exit."
            .format(__version__))

    args.config = os.path.expanduser(args.config)
    args.config = os.path.normpath(args.config)

    with Config(args.config) as config:
        boards = ActiveBoards(config)

        # connect to board ...
        try:
            if args.auto_connect:
                boards.connect_serial(config.get('default', 'port'))
        except (ConnectionError, BoardError) as err:
            eprint(err)
        except KeyboardInterrupt:
            pass

        # start command shell
        attach_commands()
        if args.filename:
            with open(args.filename) as cmd_file:
                shell = Shell(boards, args.editor, stdin=cmd_file)
                shell.cmdloop('')
        else:
            if boards.num_boards() == 0:
                eprint(
                    "No MicroPython boards connected - use the connect command to add one."
                )
            shell = Shell(boards, args.editor)
            try:
                shell.cmdloop(cmd_line)
            except KeyboardInterrupt:
                qprint("Bye")
    print(printing.NO_COLOR)
Пример #22
0
def do_config(self, line):
    """config    Print option values.
       config [-u] [-d] [--default] OPTION [VALUE]
                 Set/delete OPTION to VALUE.
    """
    default_board = None
    board_id = 'default'
    try:
        default_board = self.boards.default
        board_id = default_board.id
    except BoardError:
        pass

    if line == '':
        # print configuration
        print_config(self.config, board_id, color=printing.MPY_COLOR)
        if default_board:
            oprint("Defaults:")
            keys = self.config.options(board_id)
            print_config(self.config, 'default', exc=keys)
        return

    # parse arguments
    args = self.line_to_args(line)
    if args.default:
        board_id = 'default'
        default_board = None
    value = ' '.join(args.value)
    try:
        # try to convert value to Python object (e.g. for numbers)
        value = literal_eval(value)
    except:
        pass
    if not args.default and not default_board:
        eprint(
            "*** No board connected, use --default to change default configuration"
        )
        return

    # delete / set option value
    if args.delete:
        # delete option
        self.config.remove(board_id, args.option)
    else:
        # set option
        try:
            self.config.set(board_id, args.option, value)
        except ConfigError as e:
            eprint("*** {}".format(e))

    # upload
    if args.upload:
        if not default_board:
            eprint("*** No board connected, cannot upload configuration")
            return
        with NamedTemporaryFile() as temp:
            temp.close()
            f = open(temp.name, 'w')
            now = datetime.now().strftime("%Y-%b-%d %H:%M:%S")
            print("# config.py, created on {}".format(now), file=f)
            for key in default_board.config_options():
                print("{} = {}".format(key,
                                       repr(default_board.get_config(key))),
                      file=f)
            print("mac_table = {}".format(repr(self.config.mac_table())),
                  file=f)
            f.close()
            dst = os.path.join(
                default_board.get_config('remote_dir', '/flash'), 'config.py')
            cp(self.boards, temp.name, dst)
            os.unlink(temp.name)
Пример #23
0
 def default(self, line):
     eprint("Unrecognized command:", line)
Пример #24
0
# Linux: no install needed
#    This module is usually unnecessary on Linux and other Unix systems with
#    default readline support.
# Win: pyreadline
#    If you are using Windows, which also ships without GNU readline, you might
#    want to consider using the pyreadline module instead, which is a readline
#    replacement written in pure Python that interacts with the Windows clipboard.
try:
    if sys.platform == 'darwin':
        import gnureadline as readline
    elif sys.platform == 'win32':
        # import pyreadline as readline
        import readline
    sys.modules['readline'] = readline
except ImportError:
    eprint("could not load platform specific readline, using default instead")
    import readline

import cmd
import inspect
import importlib
import argparse
import traceback
import os


class Shell(cmd.Cmd):
    def __init__(self, boards, editor):
        self.boards = boards
        self.config = boards.config
        self.editor = editor
Пример #25
0
def rsync(devs, src_dir, dst_dir, mirror, dry_run, recursed):
    """Synchronizes 2 directory trees."""

    # This test is a hack to avoid errors when accessing /flash. When the
    # cache synchronisation issue is solved it should be removed
    if not isinstance(src_dir, str) or not len(src_dir):
        return

    # check that source is a directory
    sstat = auto(devs, get_stat, src_dir)
    if not is_dir(sstat):
        eprint("*** Source {} is not a directory".format(src_dir))
        return

    # create destination directory if it does not exist
    sstat = auto(devs, get_stat, dst_dir)
    if not file_exists(sstat):
        qprint("Create {} on remote".format(dst_dir))
        if not dry_run:
            if recursed and not make_dir(devs, dst_dir, dry_run, recursed):
                eprint("*** Unable to create directory", dst_dir)
    elif not is_dir(sstat):
        eprint("*** Destination {} is not a directory".format(src_dir))
        return

    # get list of src & dst files and stats
    qprint("   checking {}".format(dst_dir))
    d_src = file_dir(devs, src_dir)
    d_dst = file_dir(devs, dst_dir)

    # determine what needs to be copied or deleted
    set_dst = set(d_dst.keys())
    set_src = set(d_src.keys())
    to_add = set_src - set_dst  # Files to copy to dest
    to_del = set_dst - set_src  # To delete from dest
    to_upd = set_dst.intersection(set_src)  # In both: may need updating

    if False:
        eprint("rsync {} -> {}".format(src_dir, dst_dir))
        eprint("  sources", set_src)
        eprint("  dest   ", set_dst)
        eprint("  add    ", to_add)
        eprint("  delete ", to_del)
        eprint("  update ", to_upd)

    # add ...
    for f in to_add:
        src = os.path.join(src_dir, f)
        dst = os.path.join(dst_dir, f)
        qprint("Adding {}".format(dst))
        if is_dir(d_src[f]):
            if recursed:
                rsync(devs, src, dst, mirror, dry_run, recursed)
        else:
            if not dry_run:
                if not cp(devs, src, dst):
                    eprint("*** Unable to add {} --> {}".format(src, dst))

    # delete ...
    for f in to_del:
        if not mirror:
            break
        dst = os.path.join(dst_dir, f)
        qprint("Removing {}".format(dst))
        if not dry_run:
            res = rm(devs, dst, recursive=True, force=True)
            if not res:
                eprint("Cannot remove {}", dst)

    # update ...
    for f in to_upd:
        src = os.path.join(src_dir, f)
        dst = os.path.join(dst_dir, f)
        if is_dir(d_src[f]):
            if is_dir(d_dst[f]):
                # src and dst are directories
                if recursed:
                    rsync(devs, src, dst, mirror, dry_run, recursed)
            else:
                msg = "Source '{}' is a directory and destination " \
                      "'{}' is a file. Ignoring"
                eprint(msg.format(src, dst))
        else:
            if is_dir(d_dst[f]):
                msg = "Source '{}' is a file and destination " \
                      "'{}' is a directory. Ignoring"
                eprint(msg.format(src, dst))
            else:
                if False:
                    eprint("BEB src {} > dst {} delta={}".format(
                        stat_mtime(d_src[f]), stat_mtime(d_dst[f]),
                        stat_mtime(d_src[f]) - stat_mtime(d_dst[f])))
                if stat_size(d_src[f]) != stat_size(d_dst[f]) or \
                   stat_mtime(d_src[f]) > stat_mtime(d_dst[f]):
                    msg = "Copying {} (newer than {})"
                    qprint(msg.format(src, dst))
                    if not dry_run:
                        if not cp(devs, src, dst):
                            eprint(
                                "*** Unable to update {} --> {}".format(src, dst))
                else:
                    dprint(f, "NO update src time:", stat_mtime(d_src[f]), "dst time", stat_mtime(
                        d_dst[f]), "delta", stat_mtime(d_src[f]) - stat_mtime(d_dst[f]))