From 8b83a715d221725856784728528c2b18067ae348 Mon Sep 17 00:00:00 2001 From: Nikos Ftylitakis Date: Thu, 16 Jul 2020 15:52:14 +0300 Subject: [PATCH] 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)