From 76f47d6bad22c5b8ffa17dee61abec38ff9d87c8 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 9 Dec 2019 18:09:04 +0200 Subject: [PATCH 1/8] Support -v option in cli to print verbose messages for the executed operations. --- ampy/cli.py | 15 ++++++++++++++- ampy/files.py | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/ampy/cli.py b/ampy/cli.py index 4b5f361..9125fe9 100644 --- a/ampy/cli.py +++ b/ampy/cli.py @@ -40,6 +40,7 @@ _board = None +_verbose = False def windows_full_port_name(portname): @@ -83,8 +84,17 @@ def windows_full_port_name(portname): help="Delay in seconds before entering RAW MODE (default 0). Can optionally specify with AMPY_DELAY environment variable.", metavar="DELAY", ) +@click.option( + "--verbose", + "-v", + envvar="AMPY_VERBOSE", + default=False, + is_flag=True, + help="Print messages to monitor the progress of the requested operation.", + metavar="VERBOSE", +) @click.version_option() -def cli(port, baud, delay): +def cli(port, baud, delay, verbose): """ampy - Adafruit MicroPython Tool Ampy is a tool to control MicroPython boards over a serial connection. Using @@ -98,6 +108,9 @@ def cli(port, baud, delay): port = windows_full_port_name(port) _board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay) + _verbose = verbose + files.SetVerboseStatus(verbose) + @cli.command() @click.argument("remote_file") diff --git a/ampy/files.py b/ampy/files.py index 870d56d..2078e65 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -29,6 +29,12 @@ BUFFER_SIZE = 32 # Amount of data to read or write to the serial port at a time. # This is kept small because small chips and USB to serial # bridges usually have very small buffers. +_verbose = False + + +def SetVerboseStatus(verbose = False): + global _verbose + _verbose = verbose class DirectoryExistsError(Exception): @@ -71,6 +77,8 @@ def get(self, filename): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -104,7 +112,7 @@ def ls(self, directory="/", long_format=True, recursive=False): directory = "/" + directory command = """\ - try: + try: import os except ImportError: import uos as os\n""" @@ -118,10 +126,10 @@ def _listdir(dir_or_file): try: # if its a directory, then it should provide some children. children = os.listdir(dir_or_file) - except OSError: + except OSError: # probably a file. run stat() to confirm. os.stat(dir_or_file) - result.add(dir_or_file) + result.add(dir_or_file) else: # probably a directory, add to result if empty. if children: @@ -132,17 +140,17 @@ def _listdir(dir_or_file): next = dir_or_file + child else: next = dir_or_file + '/' + child - + _listdir(next) else: - result.add(dir_or_file) + result.add(dir_or_file) _listdir(directory) return sorted(result)\n""" else: command += """\ def listdir(directory): - if directory == '/': + if directory == '/': return sorted([directory + f for f in os.listdir(directory)]) else: return sorted([directory + '/' + f for f in os.listdir(directory)])\n""" @@ -152,7 +160,7 @@ def listdir(directory): command += """ r = [] for f in listdir('{0}'): - size = os.stat(f)[6] + size = os.stat(f)[6] r.append('{{0}} - {{1}} bytes'.format(f, size)) print(r) """.format( @@ -194,10 +202,14 @@ def mkdir(self, directory, exists_okay=False): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. if ex.args[2].decode("utf-8").find("OSError: [Errno 17] EEXIST") != -1: + if(_verbose): + print(" Directory already exists") if not exists_okay: raise DirectoryExistsError( "Directory already exists: {0}".format(directory) @@ -210,6 +222,8 @@ def put(self, filename, data): """Create or update the specified file with the provided data. """ # Open the file for writing on the board and write chunks of data. + if(_verbose): + print ("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -237,6 +251,8 @@ def rm(self, filename): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Deleting file: " + filename), out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -284,6 +300,8 @@ def rmdir(directory): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -298,11 +316,13 @@ def rmdir(directory): def run(self, filename, wait_output=True): """Run the provided script and return its output. If wait_output is True - (default) then wait for the script to finish and then print its output, + (default) then wait for the script to finish and then its output, otherwise just run the script and don't wait for any output. """ self._pyboard.enter_raw_repl() out = None + if(_verbose): + print("About to run: " + filename) if wait_output: # Run the file and wait for output to return. out = self._pyboard.execfile(filename) From c0427a3c15161f0b6a4df9c5ceea450b02fcf6f3 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 9 Dec 2019 18:33:20 +0200 Subject: [PATCH 2/8] minor visual edit: removed extra white space --- ampy/files.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ampy/files.py b/ampy/files.py index 2078e65..e1e583c 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -78,7 +78,7 @@ def get(self, filename): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Getting file: " + filename) + print("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -203,7 +203,7 @@ def mkdir(self, directory, exists_okay=False): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Creating directory: " + directory) + print("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. @@ -223,7 +223,7 @@ def put(self, filename, data): """ # Open the file for writing on the board and write chunks of data. if(_verbose): - print ("Putting file: " + filename) + print("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -252,7 +252,7 @@ def rm(self, filename): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Deleting file: " + filename), + print("Deleting file: " + filename), out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -301,7 +301,7 @@ def rmdir(directory): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Deleting directory: " + directory) + print("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") From 879608efc56b37965b9d1f9b26d278fa0e0b4b1d Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 23 Mar 2020 21:29:56 +0200 Subject: [PATCH 3/8] Add extra logging functionality. Enable by adding '-v' flag --- ampy/cli.py | 34 +++++++++++++++++++++++++++------- ampy/files.py | 24 +++++++++++++++++------- ampy/pyboard.py | 23 ++++++++++++++++------- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/ampy/cli.py b/ampy/cli.py index cbdf8d1..81b42f6 100644 --- a/ampy/cli.py +++ b/ampy/cli.py @@ -28,6 +28,10 @@ import click import dotenv +import logging + +logging.basicConfig(format="%(asctime)s: %(message)s") +logger = logging.getLogger("ampy") # Load AMPY_PORT et al from .ampy file # Performed here because we need to beat click's decorators. @@ -83,8 +87,17 @@ def windows_full_port_name(portname): help="Delay in seconds before entering RAW MODE (default 0). Can optionally specify with AMPY_DELAY environment variable.", metavar="DELAY", ) +@click.option( + "--verbose", + "-v", + envvar="AMPY_VERBOSE", + default=False, + is_flag=True, + help="Print messages to monitor the progress of the requested operation.", + metavar="VERBOSE", +) @click.version_option() -def cli(port, baud, delay): +def cli(port, baud, delay, verbose): """ampy - Adafruit MicroPython Tool Ampy is a tool to control MicroPython boards over a serial connection. Using @@ -92,13 +105,18 @@ def cli(port, baud, delay): scripts. """ global _board + + level = logging.INFO + if verbose: + level = logging.DEBUG + logger.setLevel(level) + # On Windows fix the COM port path name for ports above 9 (see comment in # windows_full_port_name function). if platform.system() == "Windows": port = windows_full_port_name(port) _board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay) - @cli.command() @click.argument("remote_file") @click.argument("local_file", type=click.File("wb"), required=False) @@ -126,7 +144,7 @@ def get(remote_file, local_file): contents = board_files.get(remote_file) # Print the file out if no local file was provided, otherwise save it. if local_file is None: - print(contents.decode("utf-8")) + logger.info(os.linesep + contents.decode("utf-8")) else: local_file.write(contents) @@ -191,8 +209,11 @@ def ls(directory, long_format, recursive): """ # List each file/directory on a separate line. board_files = files.Files(_board) + filesStr = os.linesep for f in board_files.ls(directory, long_format=long_format, recursive=recursive): - print(f) + filesStr += f + os.linesep + + logger.info(filesStr) @cli.command() @@ -315,7 +336,6 @@ def rmdir(remote_folder, missing_okay): ) def run(local_file, no_output): """Run a script and print its output. - Run will send the specified file to the board and execute it immediately. Any output from the board will be printed to the console (note that this is not a 'shell' and you can't send input to the program). @@ -337,7 +357,7 @@ def run(local_file, no_output): try: output = board_files.run(local_file, not no_output, not no_output) if output is not None: - print(output.decode("utf-8"), end="") + logger.info(os.linesep + output.decode("utf-8")) # , end="") except IOError: click.echo( "Failed to find or read input file: {0}".format(local_file), err=True @@ -402,7 +422,7 @@ def reset(): """ ) r = _board.eval("on_next_reset({})".format(repr(mode))) - print("here we are", repr(r)) + logger.info("here we are" + repr(r)) if r: click.echo(r, err=True) return diff --git a/ampy/files.py b/ampy/files.py index e16fa6d..dfe3a7e 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -22,10 +22,14 @@ import ast import textwrap import binascii +import logging from ampy.pyboard import PyboardError +logger = logging.getLogger("ampy") + + BUFFER_SIZE = 32 # Amount of data to read or write to the serial port at a time. # This is kept small because small chips and USB to serial # bridges usually have very small buffers. @@ -71,6 +75,7 @@ def get(self, filename): ) self._pyboard.enter_raw_repl() try: + logger.debug("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -104,7 +109,7 @@ def ls(self, directory="/", long_format=True, recursive=False): directory = "/" + directory command = """\ - try: + try: import os except ImportError: import uos as os\n""" @@ -118,10 +123,10 @@ def _listdir(dir_or_file): try: # if its a directory, then it should provide some children. children = os.listdir(dir_or_file) - except OSError: + except OSError: # probably a file. run stat() to confirm. os.stat(dir_or_file) - result.add(dir_or_file) + result.add(dir_or_file) else: # probably a directory, add to result if empty. if children: @@ -132,17 +137,17 @@ def _listdir(dir_or_file): next = dir_or_file + child else: next = dir_or_file + '/' + child - + _listdir(next) else: - result.add(dir_or_file) + result.add(dir_or_file) _listdir(directory) return sorted(result)\n""" else: command += """\ def listdir(directory): - if directory == '/': + if directory == '/': return sorted([directory + f for f in os.listdir(directory)]) else: return sorted([directory + '/' + f for f in os.listdir(directory)])\n""" @@ -152,7 +157,7 @@ def listdir(directory): command += """ r = [] for f in listdir('{0}'): - size = os.stat(f)[6] + size = os.stat(f)[6] r.append('{{0}} - {{1}} bytes'.format(f, size)) print(r) """.format( @@ -194,10 +199,12 @@ def mkdir(self, directory, exists_okay=False): ) self._pyboard.enter_raw_repl() try: + logger.debug("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. if ex.args[2].decode("utf-8").find("OSError: [Errno 17] EEXIST") != -1: + logger.debug(" Directory already exists") if not exists_okay: raise DirectoryExistsError( "Directory already exists: {0}".format(directory) @@ -210,6 +217,7 @@ def put(self, filename, data): """Create or update the specified file with the provided data. """ # Open the file for writing on the board and write chunks of data. + logger.debug("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -237,6 +245,7 @@ def rm(self, filename): ) self._pyboard.enter_raw_repl() try: + logger.debug("Deleting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -284,6 +293,7 @@ def rmdir(directory): ) self._pyboard.enter_raw_repl() try: + logger.debug("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") diff --git a/ampy/pyboard.py b/ampy/pyboard.py index 9d09147..94c8e89 100644 --- a/ampy/pyboard.py +++ b/ampy/pyboard.py @@ -39,7 +39,9 @@ import sys import time +import logging +logger = logging.getLogger("ampy") _rawdelay = None try: @@ -143,10 +145,10 @@ def __init__(self, device, baudrate=115200, user='micro', password='python', wai sys.stdout.flush() else: if delayed: - print('') + logger.info('') raise PyboardError('failed to access ' + device) if delayed: - print('') + logger.info('') def close(self): self.serial.close() @@ -192,13 +194,13 @@ def enter_raw_repl(self): self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') self.serial.write(b'\x04') # ctrl-D: soft reset data = self.read_until(1, b'soft reboot\r\n') if not data.endswith(b'soft reboot\r\n'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') # By splitting this into 2 reads, it allows boot.py to print stuff, # which will show up after the soft reboot and before the raw REPL. @@ -212,7 +214,7 @@ def enter_raw_repl(self): # End modification above. data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n') if not data.endswith(b'raw REPL; CTRL-B to exit\r\n'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') def exit_raw_repl(self): @@ -303,11 +305,18 @@ def main(): cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-c', '--command', help='program passed in as string') + cmd_parser.add_argument('-v', '--verbose', help='Print messages to monitor the progress of the requested operation.') cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') cmd_parser.add_argument('files', nargs='*', help='input files') + args = cmd_parser.parse_args() + level = logging.INFO + if args.verbose: + level = logging.DEBUG + logger.setLevel(level) + def execbuffer(buf): try: pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait) @@ -316,7 +325,7 @@ def execbuffer(buf): pyb.exit_raw_repl() pyb.close() except PyboardError as er: - print(er) + logger.error(er) sys.exit(1) except KeyboardInterrupt: sys.exit(1) @@ -338,7 +347,7 @@ def execbuffer(buf): ret, ret_err = pyb.follow(timeout=None, data_consumer=stdout_write_bytes) pyb.close() except PyboardError as er: - print(er) + logger.error(er) sys.exit(1) except KeyboardInterrupt: sys.exit(1) From 90a7638c4d05069f2d0a4721e94709fcb7feec01 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 23 Mar 2020 21:34:54 +0200 Subject: [PATCH 4/8] Update README file Added info for 'verbose' flag and updated the list of supported commands. --- README.md | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8826241..4ddc165 100644 --- a/README.md +++ b/README.md @@ -42,23 +42,39 @@ You should see usage information displayed like below: Usage: ampy [OPTIONS] COMMAND [ARGS]... ampy - Adafruit MicroPython Tool - + Ampy is a tool to control MicroPython boards over a serial connection. Using ampy you can manipulate files on the board's internal filesystem and even run scripts. - + Options: - -p, --port PORT Name of serial port for connected board. [required] - -b, --baud BAUD Baud rate for the serial connection. (default 115200) - -d, --delay DELAY Delay in seconds before entering RAW MODE (default 0) - --help Show this message and exit. - + -p, --port PORT Name of serial port for connected board. Can optionally + specify with AMPY_PORT environment variable. [required] + + -b, --baud BAUD Baud rate for the serial connection (default 115200). + Can optionally specify with AMPY_BAUD environment + variable. + + -d, --delay DELAY Delay in seconds before entering RAW MODE (default 0). + Can optionally specify with AMPY_DELAY environment + variable. + + -v, --verbose Print messages to monitor the progress of the requested + operation. + + --version Show the version and exit. + --help Show this message and exit. + Commands: - get Retrieve a file from the board. - ls List contents of a directory on the board. - put Put a file on the board. - rm Remove a file from the board. - run Run a script and print its output. + get Retrieve a file from the board. + ls List contents of a directory on the board. + mkdir Create a directory on the board. + put Put a file or folder and its contents on the board. + reset Perform soft reset/reboot of the board. + rm Remove a file from the board. + rmdir Forcefully remove a folder and all its children from the board. + run Run a script and print its output. + If you'd like to install from the Github source then use the standard Python setup.py install (or develop mode): From eedc473f539240f598ffa919c9ce114397b66c84 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 9 Dec 2019 18:09:04 +0200 Subject: [PATCH 5/8] Support -v option in cli to print verbose messages for the executed operations. --- ampy/cli.py | 15 ++++++++++++++- ampy/files.py | 32 +++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/ampy/cli.py b/ampy/cli.py index cbdf8d1..24b7bc2 100644 --- a/ampy/cli.py +++ b/ampy/cli.py @@ -40,6 +40,7 @@ _board = None +_verbose = False def windows_full_port_name(portname): @@ -83,8 +84,17 @@ def windows_full_port_name(portname): help="Delay in seconds before entering RAW MODE (default 0). Can optionally specify with AMPY_DELAY environment variable.", metavar="DELAY", ) +@click.option( + "--verbose", + "-v", + envvar="AMPY_VERBOSE", + default=False, + is_flag=True, + help="Print messages to monitor the progress of the requested operation.", + metavar="VERBOSE", +) @click.version_option() -def cli(port, baud, delay): +def cli(port, baud, delay, verbose): """ampy - Adafruit MicroPython Tool Ampy is a tool to control MicroPython boards over a serial connection. Using @@ -98,6 +108,9 @@ def cli(port, baud, delay): port = windows_full_port_name(port) _board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay) + _verbose = verbose + files.SetVerboseStatus(verbose) + @cli.command() @click.argument("remote_file") diff --git a/ampy/files.py b/ampy/files.py index e16fa6d..e56674d 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -29,6 +29,12 @@ BUFFER_SIZE = 32 # Amount of data to read or write to the serial port at a time. # This is kept small because small chips and USB to serial # bridges usually have very small buffers. +_verbose = False + + +def SetVerboseStatus(verbose = False): + global _verbose + _verbose = verbose class DirectoryExistsError(Exception): @@ -71,6 +77,8 @@ def get(self, filename): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -104,7 +112,7 @@ def ls(self, directory="/", long_format=True, recursive=False): directory = "/" + directory command = """\ - try: + try: import os except ImportError: import uos as os\n""" @@ -118,10 +126,10 @@ def _listdir(dir_or_file): try: # if its a directory, then it should provide some children. children = os.listdir(dir_or_file) - except OSError: + except OSError: # probably a file. run stat() to confirm. os.stat(dir_or_file) - result.add(dir_or_file) + result.add(dir_or_file) else: # probably a directory, add to result if empty. if children: @@ -132,17 +140,17 @@ def _listdir(dir_or_file): next = dir_or_file + child else: next = dir_or_file + '/' + child - + _listdir(next) else: - result.add(dir_or_file) + result.add(dir_or_file) _listdir(directory) return sorted(result)\n""" else: command += """\ def listdir(directory): - if directory == '/': + if directory == '/': return sorted([directory + f for f in os.listdir(directory)]) else: return sorted([directory + '/' + f for f in os.listdir(directory)])\n""" @@ -152,7 +160,7 @@ def listdir(directory): command += """ r = [] for f in listdir('{0}'): - size = os.stat(f)[6] + size = os.stat(f)[6] r.append('{{0}} - {{1}} bytes'.format(f, size)) print(r) """.format( @@ -194,10 +202,14 @@ def mkdir(self, directory, exists_okay=False): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. if ex.args[2].decode("utf-8").find("OSError: [Errno 17] EEXIST") != -1: + if(_verbose): + print(" Directory already exists") if not exists_okay: raise DirectoryExistsError( "Directory already exists: {0}".format(directory) @@ -210,6 +222,8 @@ def put(self, filename, data): """Create or update the specified file with the provided data. """ # Open the file for writing on the board and write chunks of data. + if(_verbose): + print ("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -237,6 +251,8 @@ def rm(self, filename): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Deleting file: " + filename), out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -284,6 +300,8 @@ def rmdir(directory): ) self._pyboard.enter_raw_repl() try: + if(_verbose): + print ("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") From 51b543fb03366e6a1e9e119abeb038ac244dc229 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Mon, 9 Dec 2019 18:33:20 +0200 Subject: [PATCH 6/8] minor visual edit: removed extra white space --- ampy/files.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ampy/files.py b/ampy/files.py index e56674d..a514c73 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -78,7 +78,7 @@ def get(self, filename): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Getting file: " + filename) + print("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -203,7 +203,7 @@ def mkdir(self, directory, exists_okay=False): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Creating directory: " + directory) + print("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. @@ -223,7 +223,7 @@ def put(self, filename, data): """ # Open the file for writing on the board and write chunks of data. if(_verbose): - print ("Putting file: " + filename) + print("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -252,7 +252,7 @@ def rm(self, filename): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Deleting file: " + filename), + print("Deleting file: " + filename), out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -301,7 +301,7 @@ def rmdir(directory): self._pyboard.enter_raw_repl() try: if(_verbose): - print ("Deleting directory: " + directory) + print("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") From 8b83a715d221725856784728528c2b18067ae348 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Thu, 16 Jul 2020 15:52:14 +0300 Subject: [PATCH 7/8] verbose output through python logging --- README.md | 40 ++++++++++++++++++++++++++++------------ ampy/cli.py | 28 ++++++++++++++++++---------- ampy/files.py | 21 +++++++++------------ ampy/pyboard.py | 23 ++++++++++++++++------- 4 files changed, 71 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 8826241..4ddc165 100644 --- a/README.md +++ b/README.md @@ -42,23 +42,39 @@ You should see usage information displayed like below: Usage: ampy [OPTIONS] COMMAND [ARGS]... ampy - Adafruit MicroPython Tool - + Ampy is a tool to control MicroPython boards over a serial connection. Using ampy you can manipulate files on the board's internal filesystem and even run scripts. - + Options: - -p, --port PORT Name of serial port for connected board. [required] - -b, --baud BAUD Baud rate for the serial connection. (default 115200) - -d, --delay DELAY Delay in seconds before entering RAW MODE (default 0) - --help Show this message and exit. - + -p, --port PORT Name of serial port for connected board. Can optionally + specify with AMPY_PORT environment variable. [required] + + -b, --baud BAUD Baud rate for the serial connection (default 115200). + Can optionally specify with AMPY_BAUD environment + variable. + + -d, --delay DELAY Delay in seconds before entering RAW MODE (default 0). + Can optionally specify with AMPY_DELAY environment + variable. + + -v, --verbose Print messages to monitor the progress of the requested + operation. + + --version Show the version and exit. + --help Show this message and exit. + Commands: - get Retrieve a file from the board. - ls List contents of a directory on the board. - put Put a file on the board. - rm Remove a file from the board. - run Run a script and print its output. + get Retrieve a file from the board. + ls List contents of a directory on the board. + mkdir Create a directory on the board. + put Put a file or folder and its contents on the board. + reset Perform soft reset/reboot of the board. + rm Remove a file from the board. + rmdir Forcefully remove a folder and all its children from the board. + run Run a script and print its output. + If you'd like to install from the Github source then use the standard Python setup.py install (or develop mode): diff --git a/ampy/cli.py b/ampy/cli.py index 24b7bc2..0875e44 100644 --- a/ampy/cli.py +++ b/ampy/cli.py @@ -28,6 +28,10 @@ import click import dotenv +import logging + +logging.basicConfig(format="%(asctime)s: %(message)s") +logger = logging.getLogger("ampy") # Load AMPY_PORT et al from .ampy file # Performed here because we need to beat click's decorators. @@ -102,16 +106,18 @@ def cli(port, baud, delay, verbose): scripts. """ global _board + + level = logging.INFO + if verbose: + level = logging.DEBUG + logger.setLevel(level) + # On Windows fix the COM port path name for ports above 9 (see comment in # windows_full_port_name function). if platform.system() == "Windows": port = windows_full_port_name(port) _board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay) - _verbose = verbose - files.SetVerboseStatus(verbose) - - @cli.command() @click.argument("remote_file") @click.argument("local_file", type=click.File("wb"), required=False) @@ -139,7 +145,7 @@ def get(remote_file, local_file): contents = board_files.get(remote_file) # Print the file out if no local file was provided, otherwise save it. if local_file is None: - print(contents.decode("utf-8")) + logger.info(os.linesep + contents.decode("utf-8")) else: local_file.write(contents) @@ -204,8 +210,11 @@ def ls(directory, long_format, recursive): """ # List each file/directory on a separate line. board_files = files.Files(_board) + filesStr = os.linesep for f in board_files.ls(directory, long_format=long_format, recursive=recursive): - print(f) + filesStr += f + os.linesep + + logger.info(filesStr) @cli.command() @@ -328,7 +337,6 @@ def rmdir(remote_folder, missing_okay): ) def run(local_file, no_output): """Run a script and print its output. - Run will send the specified file to the board and execute it immediately. Any output from the board will be printed to the console (note that this is not a 'shell' and you can't send input to the program). @@ -348,9 +356,9 @@ def run(local_file, no_output): # Run the provided file and print its output. board_files = files.Files(_board) try: - output = board_files.run(local_file, not no_output, not no_output) + output = board_files.run(local_file, not no_output) if output is not None: - print(output.decode("utf-8"), end="") + logger.info(os.linesep + output.decode("utf-8")) # , end="") except IOError: click.echo( "Failed to find or read input file: {0}".format(local_file), err=True @@ -415,7 +423,7 @@ def reset(): """ ) r = _board.eval("on_next_reset({})".format(repr(mode))) - print("here we are", repr(r)) + logger.info("here we are" + repr(r)) if r: click.echo(r, err=True) return diff --git a/ampy/files.py b/ampy/files.py index a514c73..c993858 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -22,9 +22,12 @@ import ast import textwrap import binascii +import logging from ampy.pyboard import PyboardError +logger = logging.getLogger("ampy") + BUFFER_SIZE = 32 # Amount of data to read or write to the serial port at a time. # This is kept small because small chips and USB to serial @@ -77,8 +80,7 @@ def get(self, filename): ) self._pyboard.enter_raw_repl() try: - if(_verbose): - print("Getting file: " + filename) + logger.debug("Getting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #2, i.e. file doesn't exist and @@ -202,14 +204,12 @@ def mkdir(self, directory, exists_okay=False): ) self._pyboard.enter_raw_repl() try: - if(_verbose): - print("Creating directory: " + directory) + logger.debug("Creating directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: # Check if this is an OSError #17, i.e. directory already exists. if ex.args[2].decode("utf-8").find("OSError: [Errno 17] EEXIST") != -1: - if(_verbose): - print(" Directory already exists") + logger.debug(" Directory already exists") if not exists_okay: raise DirectoryExistsError( "Directory already exists: {0}".format(directory) @@ -222,8 +222,7 @@ def put(self, filename, data): """Create or update the specified file with the provided data. """ # Open the file for writing on the board and write chunks of data. - if(_verbose): - print("Putting file: " + filename) + logger.debug("Putting file: " + filename) self._pyboard.enter_raw_repl() self._pyboard.exec_("f = open('{0}', 'wb')".format(filename)) size = len(data) @@ -251,8 +250,7 @@ def rm(self, filename): ) self._pyboard.enter_raw_repl() try: - if(_verbose): - print("Deleting file: " + filename), + logger.debug("Deleting file: " + filename) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") @@ -300,8 +298,7 @@ def rmdir(directory): ) self._pyboard.enter_raw_repl() try: - if(_verbose): - print("Deleting directory: " + directory) + logger.debug("Deleting directory: " + directory) out = self._pyboard.exec_(textwrap.dedent(command)) except PyboardError as ex: message = ex.args[2].decode("utf-8") diff --git a/ampy/pyboard.py b/ampy/pyboard.py index 9d09147..94c8e89 100644 --- a/ampy/pyboard.py +++ b/ampy/pyboard.py @@ -39,7 +39,9 @@ import sys import time +import logging +logger = logging.getLogger("ampy") _rawdelay = None try: @@ -143,10 +145,10 @@ def __init__(self, device, baudrate=115200, user='micro', password='python', wai sys.stdout.flush() else: if delayed: - print('') + logger.info('') raise PyboardError('failed to access ' + device) if delayed: - print('') + logger.info('') def close(self): self.serial.close() @@ -192,13 +194,13 @@ def enter_raw_repl(self): self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') self.serial.write(b'\x04') # ctrl-D: soft reset data = self.read_until(1, b'soft reboot\r\n') if not data.endswith(b'soft reboot\r\n'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') # By splitting this into 2 reads, it allows boot.py to print stuff, # which will show up after the soft reboot and before the raw REPL. @@ -212,7 +214,7 @@ def enter_raw_repl(self): # End modification above. data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n') if not data.endswith(b'raw REPL; CTRL-B to exit\r\n'): - print(data) + logger.info(data) raise PyboardError('could not enter raw repl') def exit_raw_repl(self): @@ -303,11 +305,18 @@ def main(): cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-c', '--command', help='program passed in as string') + cmd_parser.add_argument('-v', '--verbose', help='Print messages to monitor the progress of the requested operation.') cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') cmd_parser.add_argument('files', nargs='*', help='input files') + args = cmd_parser.parse_args() + level = logging.INFO + if args.verbose: + level = logging.DEBUG + logger.setLevel(level) + def execbuffer(buf): try: pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait) @@ -316,7 +325,7 @@ def execbuffer(buf): pyb.exit_raw_repl() pyb.close() except PyboardError as er: - print(er) + logger.error(er) sys.exit(1) except KeyboardInterrupt: sys.exit(1) @@ -338,7 +347,7 @@ def execbuffer(buf): ret, ret_err = pyb.follow(timeout=None, data_consumer=stdout_write_bytes) pyb.close() except PyboardError as er: - print(er) + logger.error(er) sys.exit(1) except KeyboardInterrupt: sys.exit(1) From 66ee1b118e7498f1e07f3d85019e9ac31c28f781 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Thu, 16 Jul 2020 16:49:30 +0300 Subject: [PATCH 8/8] Fix on mistakenly ommited argument in cli.run function, plus some code cleaning --- ampy/cli.py | 5 ++--- ampy/files.py | 10 ---------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/ampy/cli.py b/ampy/cli.py index 0875e44..b106b88 100644 --- a/ampy/cli.py +++ b/ampy/cli.py @@ -44,7 +44,6 @@ _board = None -_verbose = False def windows_full_port_name(portname): @@ -356,9 +355,9 @@ def run(local_file, no_output): # Run the provided file and print its output. board_files = files.Files(_board) try: - output = board_files.run(local_file, not no_output) + output = board_files.run(local_file, not no_output, not no_output) if output is not None: - logger.info(os.linesep + output.decode("utf-8")) # , end="") + logger.info(os.linesep + output.decode("utf-8")) except IOError: click.echo( "Failed to find or read input file: {0}".format(local_file), err=True diff --git a/ampy/files.py b/ampy/files.py index 6e4449e..716ba93 100644 --- a/ampy/files.py +++ b/ampy/files.py @@ -28,19 +28,9 @@ logger = logging.getLogger("ampy") - -logger = logging.getLogger("ampy") - - BUFFER_SIZE = 32 # Amount of data to read or write to the serial port at a time. # This is kept small because small chips and USB to serial # bridges usually have very small buffers. -_verbose = False - - -def SetVerboseStatus(verbose = False): - global _verbose - _verbose = verbose class DirectoryExistsError(Exception):