From 0bc10b7dc098716c3707238381e55cce70b53a9c Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 10 Nov 2022 12:43:27 -0800 Subject: [PATCH 1/5] Fix run_command sometimes stops --- adafruit_shell.py | 91 ++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/adafruit_shell.py b/adafruit_shell.py index 390d5cf..2d5645f 100644 --- a/adafruit_shell.py +++ b/adafruit_shell.py @@ -25,6 +25,8 @@ import os import shutil import subprocess +import shlex +import fcntl import platform import fileinput import re @@ -67,75 +69,76 @@ def run_command(self, cmd, suppress_message=False, return_output=False): """ Run a shell command and show the output as it runs """ - original_stdout = sys.stdout - original_stderr = sys.stderr - try: - # pylint: disable=consider-using-with - proc = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - # pylint: enable=consider-using-with - full_output = "" - while True: - output = proc.stdout.readline() - err = proc.stderr.read() - if err and not suppress_message: - self.error(err.decode("utf-8", errors="ignore")) - if len(output) == 0 and proc.poll() is not None: - break - if output: - decoded_output = output.decode("utf-8", errors="ignore").strip() - if not suppress_message: - self.info(decoded_output) - full_output += decoded_output - except Exception: # pylint: disable=broad-except - pass - finally: - sys.stdout = original_stdout - sys.stderr = original_stderr - if return_output: - return full_output - r = proc.poll() - if r == 0: + def non_block_read(output): + fd = output.fileno() + fl = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) + try: + return output.read() + except: + return "" + + full_output = "" + with subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True + ) as proc: + while proc.poll() is None: + err = non_block_read(proc.stderr) + if err != "" and not suppress_message: + self.error(err.strip(), end="\n\r") + output = non_block_read(proc.stdout) + if output != "" and not suppress_message: + self.info(output.strip(), end="\n\r") + full_output += output + return_code = proc.poll() + proc.stdout.close() + proc.stderr.close() + if return_output: + return full_output + if return_code: + return False return True - return False - def info(self, message): + def info(self, message, **kwargs): """ Display a message with the group in green """ if self._group is not None: - print(colored.green(self._group) + " " + message) + print(colored.green(self._group) + " " + message, **kwargs) else: - print(message) + print(message, **kwargs) - def warn(self, message): + def warn(self, message, **kwargs): """ Display a message with the group in yellow """ if self._group is not None: - print(colored.yellow(self._group) + " " + message) + print(colored.yellow(self._group) + " " + message, **kwargs) else: - print(message) + print(message, **kwargs) - def bail(self, message=None): + def bail(self, message=None, **kwargs): """ Exit and display an error message if given """ if message is None: - self.error("Exiting due to error") + self.error("Exiting due to error", **kwargs) else: - self.error(f"Exiting due to error: {message}") + self.error(f"Exiting due to error: {message}", **kwargs) sys.exit(1) - def error(self, message): + def error(self, message, **kwargs): """ - Display some inforrmation + Display some information """ if self._group is not None: - print(colored.red(self._group) + " " + message) + print(colored.red(self._group) + " " + message, **kwargs) else: - print(message) + print(message, **kwargs) @staticmethod def print_colored(message, color): From 69d333ff9bcba3ea92b96d05b06f2d7e23a98a21 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 10 Nov 2022 12:44:43 -0800 Subject: [PATCH 2/5] Rename read func --- adafruit_shell.py | 6 +++--- test.py | 16 ++++++++++++++++ test.sh | 7 +++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test.py create mode 100755 test.sh diff --git a/adafruit_shell.py b/adafruit_shell.py index 2d5645f..e599345 100644 --- a/adafruit_shell.py +++ b/adafruit_shell.py @@ -69,7 +69,7 @@ def run_command(self, cmd, suppress_message=False, return_output=False): """ Run a shell command and show the output as it runs """ - def non_block_read(output): + def read_stream(output): fd = output.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) @@ -87,10 +87,10 @@ def non_block_read(output): universal_newlines=True ) as proc: while proc.poll() is None: - err = non_block_read(proc.stderr) + err = read_stream(proc.stderr) if err != "" and not suppress_message: self.error(err.strip(), end="\n\r") - output = non_block_read(proc.stdout) + output = read_stream(proc.stdout) if output != "" and not suppress_message: self.info(output.strip(), end="\n\r") full_output += output diff --git a/test.py b/test.py new file mode 100644 index 0000000..b8bbe97 --- /dev/null +++ b/test.py @@ -0,0 +1,16 @@ +try: + from adafruit_shell import Shell +except ImportError: + raise RuntimeError("The library 'adafruit_shell' was not found. To install, try typing: sudo pip3 install adafruit-python-shell") + +shell = Shell() +shell.group="Blinka" + +def main(): + #shell.clear() + print("Running test") + shell.run_command("./test.sh") + +# Main function +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..0af4c1d --- /dev/null +++ b/test.sh @@ -0,0 +1,7 @@ +#! /bin/bash +>&2 echo This should go to stderr +echo Interactive Test +>&2 echo This should also go to stderr +read -p "Hello, who am I speaking to? " NAME +echo It\'s Nice to meet you $NAME + From 8a15f4f5d733bd5c2d29200abef96b97cf3d46de Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 10 Nov 2022 12:45:10 -0800 Subject: [PATCH 3/5] Run pre-commit --- adafruit_shell.py | 3 ++- test.py | 12 ++++++++---- test.sh | 1 - 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/adafruit_shell.py b/adafruit_shell.py index e599345..e685a26 100644 --- a/adafruit_shell.py +++ b/adafruit_shell.py @@ -69,6 +69,7 @@ def run_command(self, cmd, suppress_message=False, return_output=False): """ Run a shell command and show the output as it runs """ + def read_stream(output): fd = output.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) @@ -84,7 +85,7 @@ def read_stream(output): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True + universal_newlines=True, ) as proc: while proc.poll() is None: err = read_stream(proc.stderr) diff --git a/test.py b/test.py index b8bbe97..5ca5fd6 100644 --- a/test.py +++ b/test.py @@ -1,16 +1,20 @@ try: from adafruit_shell import Shell except ImportError: - raise RuntimeError("The library 'adafruit_shell' was not found. To install, try typing: sudo pip3 install adafruit-python-shell") + raise RuntimeError( + "The library 'adafruit_shell' was not found. To install, try typing: sudo pip3 install adafruit-python-shell" + ) shell = Shell() -shell.group="Blinka" +shell.group = "Blinka" + def main(): - #shell.clear() + # shell.clear() print("Running test") shell.run_command("./test.sh") + # Main function if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/test.sh b/test.sh index 0af4c1d..65df885 100755 --- a/test.sh +++ b/test.sh @@ -4,4 +4,3 @@ echo Interactive Test >&2 echo This should also go to stderr read -p "Hello, who am I speaking to? " NAME echo It\'s Nice to meet you $NAME - From 90aad5375b055c212d4942a16f73d3b72a9dec5f Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 10 Nov 2022 12:46:52 -0800 Subject: [PATCH 4/5] Remove unused import and delete test files --- adafruit_shell.py | 1 - test.py | 20 -------------------- test.sh | 6 ------ 3 files changed, 27 deletions(-) delete mode 100644 test.py delete mode 100755 test.sh diff --git a/adafruit_shell.py b/adafruit_shell.py index e685a26..b6355ee 100644 --- a/adafruit_shell.py +++ b/adafruit_shell.py @@ -25,7 +25,6 @@ import os import shutil import subprocess -import shlex import fcntl import platform import fileinput diff --git a/test.py b/test.py deleted file mode 100644 index 5ca5fd6..0000000 --- a/test.py +++ /dev/null @@ -1,20 +0,0 @@ -try: - from adafruit_shell import Shell -except ImportError: - raise RuntimeError( - "The library 'adafruit_shell' was not found. To install, try typing: sudo pip3 install adafruit-python-shell" - ) - -shell = Shell() -shell.group = "Blinka" - - -def main(): - # shell.clear() - print("Running test") - shell.run_command("./test.sh") - - -# Main function -if __name__ == "__main__": - main() diff --git a/test.sh b/test.sh deleted file mode 100755 index 65df885..0000000 --- a/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/bash ->&2 echo This should go to stderr -echo Interactive Test ->&2 echo This should also go to stderr -read -p "Hello, who am I speaking to? " NAME -echo It\'s Nice to meet you $NAME From 7c8bedcb4ac406bf9f6c47cb47ad518e3bd25798 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 10 Nov 2022 14:18:59 -0800 Subject: [PATCH 5/5] Fix pylint and lint --- .pylintrc | 54 +++++++---------------------------------------- adafruit_shell.py | 8 +++---- 2 files changed, 12 insertions(+), 50 deletions(-) diff --git a/.pylintrc b/.pylintrc index 54a9d35..40208c3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -9,11 +9,11 @@ # run arbitrary code extension-pkg-whitelist= -# Add files or directories to the blacklist. They should be base names, not +# Add files or directories to the ignore-list. They should be base names, not # paths. ignore=CVS -# Add files or directories matching the regex patterns to the blacklist. The +# Add files or directories matching the regex patterns to the ignore-list. The # regex matches against base names, not paths. ignore-patterns= @@ -22,12 +22,11 @@ ignore-patterns= #init-hook= # Use multiple processes to speed up Pylint. -# jobs=1 -jobs=2 +jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -55,8 +54,8 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation +# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call +disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -226,12 +225,6 @@ max-line-length=100 # Maximum number of lines in a module max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -250,46 +243,30 @@ ignore-comments=yes ignore-docstrings=yes # Ignore imports when computing similarities. -ignore-imports=no +ignore-imports=yes # Minimum lines number of a similarity. -min-similarity-lines=4 +min-similarity-lines=12 [BASIC] -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct argument names argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct attribute names attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - # Regular expression matching correct class names # class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9_]+$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ @@ -297,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # ones are exempt. docstring-min-length=-1 -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct function names function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -310,21 +284,12 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ # Include a hint for the correct naming format with invalid-name include-naming-hint=no -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct method names method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -340,9 +305,6 @@ no-docstring-rgx=^_ # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct variable names variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ diff --git a/adafruit_shell.py b/adafruit_shell.py index b6355ee..ad26fa7 100644 --- a/adafruit_shell.py +++ b/adafruit_shell.py @@ -70,12 +70,12 @@ def run_command(self, cmd, suppress_message=False, return_output=False): """ def read_stream(output): - fd = output.fileno() - fl = fcntl.fcntl(fd, fcntl.F_GETFL) - fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) + file_descriptor = output.fileno() + file_flags = fcntl.fcntl(file_descriptor, fcntl.F_GETFL) + fcntl.fcntl(file_descriptor, fcntl.F_SETFL, file_flags | os.O_NONBLOCK) try: return output.read() - except: + except TypeError: return "" full_output = ""