浏览代码

Improve ANSI support and --no-color (#10537)

* Improve ANSI support and --no-color

* tweak when levelname gets stripped of ansi

* sync with latest milc

* make questions work with both milc versions

* pyformat
Zach White 4 年之前
父节点
当前提交
445cd95d17
共有 3 个文件被更改,包括 76 次插入31 次删除
  1. 63 15
      lib/python/milc.py
  2. 7 15
      lib/python/qmk/cli/doctor.py
  3. 6 1
      lib/python/qmk/questions.py

+ 63 - 15
lib/python/milc.py

@@ -18,9 +18,11 @@ import logging
 import os
 import re
 import shlex
+import subprocess
 import sys
 from decimal import Decimal
 from pathlib import Path
+from platform import platform
 from tempfile import NamedTemporaryFile
 from time import sleep
 
@@ -94,29 +96,54 @@ def format_ansi(text):
     return text + ansi_colors['style_reset_all']
 
 
-class ANSIFormatter(logging.Formatter):
-    """A log formatter that inserts ANSI color.
+class ANSIFormatterMixin(object):
+    """A log formatter mixin that inserts ANSI color.
     """
     def format(self, record):
-        msg = super(ANSIFormatter, self).format(record)
+        msg = super(ANSIFormatterMixin, self).format(record)
         return format_ansi(msg)
 
 
-class ANSIEmojiLoglevelFormatter(ANSIFormatter):
-    """A log formatter that makes the loglevel an emoji on UTF capable terminals.
+class ANSIStrippingMixin(object):
+    """A log formatter mixin that strips ANSI.
+    """
+    def format(self, record):
+        msg = super(ANSIStrippingMixin, self).format(record)
+        record.levelname = ansi_escape.sub('', record.levelname)
+        return ansi_escape.sub('', msg)
+
+
+class EmojiLoglevelMixin(object):
+    """A log formatter mixin that makes the loglevel an emoji on UTF capable terminals.
     """
     def format(self, record):
         if UNICODE_SUPPORT:
             record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
-        return super(ANSIEmojiLoglevelFormatter, self).format(record)
+        return super(EmojiLoglevelMixin, self).format(record)
 
 
-class ANSIStrippingFormatter(ANSIFormatter):
-    """A log formatter that strips ANSI.
+class ANSIFormatter(ANSIFormatterMixin, logging.Formatter):
+    """A log formatter that colorizes output.
     """
-    def format(self, record):
-        msg = super(ANSIStrippingFormatter, self).format(record)
-        return ansi_escape.sub('', msg)
+    pass
+
+
+class ANSIStrippingFormatter(ANSIStrippingMixin, ANSIFormatterMixin, logging.Formatter):
+    """A log formatter that strips ANSI
+    """
+    pass
+
+
+class ANSIEmojiLoglevelFormatter(EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter):
+    """A log formatter that adds Emoji and ANSI
+    """
+    pass
+
+
+class ANSIStrippingEmojiLoglevelFormatter(ANSIStrippingMixin, EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter):
+    """A log formatter that adds Emoji and strips ANSI
+    """
+    pass
 
 
 class Configuration(object):
@@ -288,11 +315,12 @@ class MILC(object):
         self.config_file = None
         self.default_arguments = {}
         self.version = 'unknown'
-        self.release_lock()
+        self.platform = platform()
 
         # Figure out our program name
         self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
         self.prog_name = self.prog_name.split('/')[-1]
+        self.release_lock()
 
         # Initialize all the things
         self.read_config_file()
@@ -315,6 +343,8 @@ class MILC(object):
         strings.
 
         If *args or **kwargs are passed they will be used to %-format the strings.
+
+        If `self.config.general.color` is False any ANSI escape sequences in the text will be stripped.
         """
         if args and kwargs:
             raise RuntimeError('You can only specify *args or **kwargs, not both!')
@@ -322,8 +352,27 @@ class MILC(object):
         args = args or kwargs
         text = format_ansi(text)
 
+        if not self.config.general.color:
+            text = ansi_escape.sub('', text)
+
         print(text % args)
 
+    def run(self, command, *args, **kwargs):
+        """Run a command with subprocess.run
+        The *args and **kwargs arguments get passed directly to `subprocess.run`.
+        """
+        if isinstance(command, str):
+            raise TypeError('`command` must be a non-text sequence such as list or tuple.')
+
+        if 'windows' in self.platform.lower():
+            safecmd = map(shlex.quote, command)
+            safecmd = ' '.join(safecmd)
+            command = [os.environ['SHELL'], '-c', safecmd]
+
+        self.log.debug('Running command: %s', command)
+
+        return subprocess.run(command, *args, **kwargs)
+
     def initialize_argparse(self):
         """Prepare to process arguments from sys.argv.
         """
@@ -678,14 +727,13 @@ class MILC(object):
             self.log_print_level = logging.DEBUG
 
         self.log_file = self.config['general']['log_file'] or self.log_file
-        self.log_file_format = self.config['general']['log_file_fmt']
         self.log_file_format = ANSIStrippingFormatter(self.config['general']['log_file_fmt'], self.config['general']['datetime_fmt'])
         self.log_format = self.config['general']['log_fmt']
 
         if self.config.general.color:
-            self.log_format = ANSIEmojiLoglevelFormatter(self.args.log_fmt, self.config.general.datetime_fmt)
+            self.log_format = ANSIEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt)
         else:
-            self.log_format = ANSIStrippingFormatter(self.args.log_fmt, self.config.general.datetime_fmt)
+            self.log_format = ANSIStrippingEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt)
 
         if self.log_file:
             self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode)

+ 7 - 15
lib/python/qmk/cli/doctor.py

@@ -156,24 +156,16 @@ def check_udev_rules():
             _udev_rule("03EB", "2FF3"),  # ATmega16U4
             _udev_rule("03EB", "2FF4"),  # ATmega32U4
             _udev_rule("03EB", "2FF9"),  # AT90USB64
-            _udev_rule("03EB", "2FFB")   # AT90USB128
-        },
-        'kiibohd': {
-            _udev_rule("1C11", "B007")
+            _udev_rule("03EB", "2FFB")  # AT90USB128
         },
+        'kiibohd': {_udev_rule("1C11", "B007")},
         'stm32': {
             _udev_rule("1EAF", "0003"),  # STM32duino
-            _udev_rule("0483", "DF11")   # STM32 DFU
-        },
-        'bootloadhid': {
-            _udev_rule("16C0", "05DF")
-        },
-        'usbasploader': {
-            _udev_rule("16C0", "05DC")
-        },
-        'massdrop': {
-            _udev_rule("03EB", "6124")
+            _udev_rule("0483", "DF11")  # STM32 DFU
         },
+        'bootloadhid': {_udev_rule("16C0", "05DF")},
+        'usbasploader': {_udev_rule("16C0", "05DC")},
+        'massdrop': {_udev_rule("03EB", "6124")},
         'caterina': {
             # Spark Fun Electronics
             _udev_rule("1B4F", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'),  # Pro Micro 3V3/8MHz
@@ -190,7 +182,7 @@ def check_udev_rules():
             _udev_rule("239A", "000E", 'ENV{ID_MM_DEVICE_IGNORE}="1"'),  # ItsyBitsy 32U4 5V/16MHz
             # dog hunter AG
             _udev_rule("2A03", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'),  # Leonardo
-            _udev_rule("2A03", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"')   # Micro
+            _udev_rule("2A03", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"')  # Micro
         }
     }
 

+ 6 - 1
lib/python/qmk/questions.py

@@ -1,7 +1,12 @@
 """Functions to collect user input.
 """
 
-from milc import cli, format_ansi
+from milc import cli
+
+try:
+    from milc import format_ansi
+except ImportError:
+    from milc.ansi import format_ansi
 
 
 def yesno(prompt, *args, default=None, **kwargs):