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 * pyformatmaster
parent
7d5ba88e6f
commit
445cd95d17
|
@ -18,9 +18,11 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from platform import platform
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
@ -94,29 +96,54 @@ def format_ansi(text):
|
||||||
return text + ansi_colors['style_reset_all']
|
return text + ansi_colors['style_reset_all']
|
||||||
|
|
||||||
|
|
||||||
class ANSIFormatter(logging.Formatter):
|
class ANSIFormatterMixin(object):
|
||||||
"""A log formatter that inserts ANSI color.
|
"""A log formatter mixin that inserts ANSI color.
|
||||||
"""
|
"""
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
msg = super(ANSIFormatter, self).format(record)
|
msg = super(ANSIFormatterMixin, self).format(record)
|
||||||
return format_ansi(msg)
|
return format_ansi(msg)
|
||||||
|
|
||||||
|
|
||||||
class ANSIEmojiLoglevelFormatter(ANSIFormatter):
|
class ANSIStrippingMixin(object):
|
||||||
"""A log formatter that makes the loglevel an emoji on UTF capable terminals.
|
"""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):
|
def format(self, record):
|
||||||
if UNICODE_SUPPORT:
|
if UNICODE_SUPPORT:
|
||||||
record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
|
record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
|
||||||
return super(ANSIEmojiLoglevelFormatter, self).format(record)
|
return super(EmojiLoglevelMixin, self).format(record)
|
||||||
|
|
||||||
|
|
||||||
class ANSIStrippingFormatter(ANSIFormatter):
|
class ANSIFormatter(ANSIFormatterMixin, logging.Formatter):
|
||||||
"""A log formatter that strips ANSI.
|
"""A log formatter that colorizes output.
|
||||||
"""
|
"""
|
||||||
def format(self, record):
|
pass
|
||||||
msg = super(ANSIStrippingFormatter, self).format(record)
|
|
||||||
return ansi_escape.sub('', msg)
|
|
||||||
|
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):
|
class Configuration(object):
|
||||||
|
@ -288,11 +315,12 @@ class MILC(object):
|
||||||
self.config_file = None
|
self.config_file = None
|
||||||
self.default_arguments = {}
|
self.default_arguments = {}
|
||||||
self.version = 'unknown'
|
self.version = 'unknown'
|
||||||
self.release_lock()
|
self.platform = platform()
|
||||||
|
|
||||||
# Figure out our program name
|
# 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 = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
|
||||||
self.prog_name = self.prog_name.split('/')[-1]
|
self.prog_name = self.prog_name.split('/')[-1]
|
||||||
|
self.release_lock()
|
||||||
|
|
||||||
# Initialize all the things
|
# Initialize all the things
|
||||||
self.read_config_file()
|
self.read_config_file()
|
||||||
|
@ -315,6 +343,8 @@ class MILC(object):
|
||||||
strings.
|
strings.
|
||||||
|
|
||||||
If *args or **kwargs are passed they will be used to %-format the 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:
|
if args and kwargs:
|
||||||
raise RuntimeError('You can only specify *args or **kwargs, not both!')
|
raise RuntimeError('You can only specify *args or **kwargs, not both!')
|
||||||
|
@ -322,8 +352,27 @@ class MILC(object):
|
||||||
args = args or kwargs
|
args = args or kwargs
|
||||||
text = format_ansi(text)
|
text = format_ansi(text)
|
||||||
|
|
||||||
|
if not self.config.general.color:
|
||||||
|
text = ansi_escape.sub('', text)
|
||||||
|
|
||||||
print(text % args)
|
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):
|
def initialize_argparse(self):
|
||||||
"""Prepare to process arguments from sys.argv.
|
"""Prepare to process arguments from sys.argv.
|
||||||
"""
|
"""
|
||||||
|
@ -678,14 +727,13 @@ class MILC(object):
|
||||||
self.log_print_level = logging.DEBUG
|
self.log_print_level = logging.DEBUG
|
||||||
|
|
||||||
self.log_file = self.config['general']['log_file'] or self.log_file
|
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_file_format = ANSIStrippingFormatter(self.config['general']['log_file_fmt'], self.config['general']['datetime_fmt'])
|
||||||
self.log_format = self.config['general']['log_fmt']
|
self.log_format = self.config['general']['log_fmt']
|
||||||
|
|
||||||
if self.config.general.color:
|
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:
|
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:
|
if self.log_file:
|
||||||
self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode)
|
self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode)
|
||||||
|
|
|
@ -158,22 +158,14 @@ def check_udev_rules():
|
||||||
_udev_rule("03EB", "2FF9"), # AT90USB64
|
_udev_rule("03EB", "2FF9"), # AT90USB64
|
||||||
_udev_rule("03EB", "2FFB") # AT90USB128
|
_udev_rule("03EB", "2FFB") # AT90USB128
|
||||||
},
|
},
|
||||||
'kiibohd': {
|
'kiibohd': {_udev_rule("1C11", "B007")},
|
||||||
_udev_rule("1C11", "B007")
|
|
||||||
},
|
|
||||||
'stm32': {
|
'stm32': {
|
||||||
_udev_rule("1EAF", "0003"), # STM32duino
|
_udev_rule("1EAF", "0003"), # STM32duino
|
||||||
_udev_rule("0483", "DF11") # STM32 DFU
|
_udev_rule("0483", "DF11") # STM32 DFU
|
||||||
},
|
},
|
||||||
'bootloadhid': {
|
'bootloadhid': {_udev_rule("16C0", "05DF")},
|
||||||
_udev_rule("16C0", "05DF")
|
'usbasploader': {_udev_rule("16C0", "05DC")},
|
||||||
},
|
'massdrop': {_udev_rule("03EB", "6124")},
|
||||||
'usbasploader': {
|
|
||||||
_udev_rule("16C0", "05DC")
|
|
||||||
},
|
|
||||||
'massdrop': {
|
|
||||||
_udev_rule("03EB", "6124")
|
|
||||||
},
|
|
||||||
'caterina': {
|
'caterina': {
|
||||||
# Spark Fun Electronics
|
# Spark Fun Electronics
|
||||||
_udev_rule("1B4F", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 3V3/8MHz
|
_udev_rule("1B4F", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 3V3/8MHz
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
"""Functions to collect user input.
|
"""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):
|
def yesno(prompt, *args, default=None, **kwargs):
|
||||||
|
|
Loading…
Reference in New Issue