aboutsummaryrefslogtreecommitdiff
path: root/lib/python/milc.py
diff options
context:
space:
mode:
authorZach White <skullydazed@gmail.com>2020-10-17 21:01:11 -0700
committerGitHub <noreply@github.com>2020-10-17 21:01:11 -0700
commit445cd95d1779709673857c36b752afa6327afff1 (patch)
treef722089d82590e624d29776e41431bcaf69e0314 /lib/python/milc.py
parent7d5ba88e6f88979c017bf27e2ad0f4c7d912f9ec (diff)
downloadqmk_firmware-445cd95d1779709673857c36b752afa6327afff1.tar.gz
qmk_firmware-445cd95d1779709673857c36b752afa6327afff1.zip
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
Diffstat (limited to 'lib/python/milc.py')
-rw-r--r--lib/python/milc.py78
1 files changed, 63 insertions, 15 deletions
diff --git a/lib/python/milc.py b/lib/python/milc.py
index eb18984eb..0cdd43dc8 100644
--- a/lib/python/milc.py
+++ b/lib/python/milc.py
@@ -18,9 +18,11 @@ import logging
18import os 18import os
19import re 19import re
20import shlex 20import shlex
21import subprocess
21import sys 22import sys
22from decimal import Decimal 23from decimal import Decimal
23from pathlib import Path 24from pathlib import Path
25from platform import platform
24from tempfile import NamedTemporaryFile 26from tempfile import NamedTemporaryFile
25from time import sleep 27from time import sleep
26 28
@@ -94,29 +96,54 @@ def format_ansi(text):
94 return text + ansi_colors['style_reset_all'] 96 return text + ansi_colors['style_reset_all']
95 97
96 98
97class ANSIFormatter(logging.Formatter): 99class ANSIFormatterMixin(object):
98 """A log formatter that inserts ANSI color. 100 """A log formatter mixin that inserts ANSI color.
99 """ 101 """
100 def format(self, record): 102 def format(self, record):
101 msg = super(ANSIFormatter, self).format(record) 103 msg = super(ANSIFormatterMixin, self).format(record)
102 return format_ansi(msg) 104 return format_ansi(msg)
103 105
104 106
105class ANSIEmojiLoglevelFormatter(ANSIFormatter): 107class ANSIStrippingMixin(object):
106 """A log formatter that makes the loglevel an emoji on UTF capable terminals. 108 """A log formatter mixin that strips ANSI.
109 """
110 def format(self, record):
111 msg = super(ANSIStrippingMixin, self).format(record)
112 record.levelname = ansi_escape.sub('', record.levelname)
113 return ansi_escape.sub('', msg)
114
115
116class EmojiLoglevelMixin(object):
117 """A log formatter mixin that makes the loglevel an emoji on UTF capable terminals.
107 """ 118 """
108 def format(self, record): 119 def format(self, record):
109 if UNICODE_SUPPORT: 120 if UNICODE_SUPPORT:
110 record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors) 121 record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
111 return super(ANSIEmojiLoglevelFormatter, self).format(record) 122 return super(EmojiLoglevelMixin, self).format(record)
112 123
113 124
114class ANSIStrippingFormatter(ANSIFormatter): 125class ANSIFormatter(ANSIFormatterMixin, logging.Formatter):
115 """A log formatter that strips ANSI. 126 """A log formatter that colorizes output.
116 """ 127 """
117 def format(self, record): 128 pass
118 msg = super(ANSIStrippingFormatter, self).format(record) 129
119 return ansi_escape.sub('', msg) 130
131class ANSIStrippingFormatter(ANSIStrippingMixin, ANSIFormatterMixin, logging.Formatter):
132 """A log formatter that strips ANSI
133 """
134 pass
135
136
137class ANSIEmojiLoglevelFormatter(EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter):
138 """A log formatter that adds Emoji and ANSI
139 """
140 pass
141
142
143class ANSIStrippingEmojiLoglevelFormatter(ANSIStrippingMixin, EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter):
144 """A log formatter that adds Emoji and strips ANSI
145 """
146 pass
120 147
121 148
122class Configuration(object): 149class Configuration(object):
@@ -288,11 +315,12 @@ class MILC(object):
288 self.config_file = None 315 self.config_file = None
289 self.default_arguments = {} 316 self.default_arguments = {}
290 self.version = 'unknown' 317 self.version = 'unknown'
291 self.release_lock() 318 self.platform = platform()
292 319
293 # Figure out our program name 320 # Figure out our program name
294 self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0] 321 self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
295 self.prog_name = self.prog_name.split('/')[-1] 322 self.prog_name = self.prog_name.split('/')[-1]
323 self.release_lock()
296 324
297 # Initialize all the things 325 # Initialize all the things
298 self.read_config_file() 326 self.read_config_file()
@@ -315,6 +343,8 @@ class MILC(object):
315 strings. 343 strings.
316 344
317 If *args or **kwargs are passed they will be used to %-format the strings. 345 If *args or **kwargs are passed they will be used to %-format the strings.
346
347 If `self.config.general.color` is False any ANSI escape sequences in the text will be stripped.
318 """ 348 """
319 if args and kwargs: 349 if args and kwargs:
320 raise RuntimeError('You can only specify *args or **kwargs, not both!') 350 raise RuntimeError('You can only specify *args or **kwargs, not both!')
@@ -322,8 +352,27 @@ class MILC(object):
322 args = args or kwargs 352 args = args or kwargs
323 text = format_ansi(text) 353 text = format_ansi(text)
324 354
355 if not self.config.general.color:
356 text = ansi_escape.sub('', text)
357
325 print(text % args) 358 print(text % args)
326 359
360 def run(self, command, *args, **kwargs):
361 """Run a command with subprocess.run
362 The *args and **kwargs arguments get passed directly to `subprocess.run`.
363 """
364 if isinstance(command, str):
365 raise TypeError('`command` must be a non-text sequence such as list or tuple.')
366
367 if 'windows' in self.platform.lower():
368 safecmd = map(shlex.quote, command)
369 safecmd = ' '.join(safecmd)
370 command = [os.environ['SHELL'], '-c', safecmd]
371
372 self.log.debug('Running command: %s', command)
373
374 return subprocess.run(command, *args, **kwargs)
375
327 def initialize_argparse(self): 376 def initialize_argparse(self):
328 """Prepare to process arguments from sys.argv. 377 """Prepare to process arguments from sys.argv.
329 """ 378 """
@@ -678,14 +727,13 @@ class MILC(object):
678 self.log_print_level = logging.DEBUG 727 self.log_print_level = logging.DEBUG
679 728
680 self.log_file = self.config['general']['log_file'] or self.log_file 729 self.log_file = self.config['general']['log_file'] or self.log_file
681 self.log_file_format = self.config['general']['log_file_fmt']
682 self.log_file_format = ANSIStrippingFormatter(self.config['general']['log_file_fmt'], self.config['general']['datetime_fmt']) 730 self.log_file_format = ANSIStrippingFormatter(self.config['general']['log_file_fmt'], self.config['general']['datetime_fmt'])
683 self.log_format = self.config['general']['log_fmt'] 731 self.log_format = self.config['general']['log_fmt']
684 732
685 if self.config.general.color: 733 if self.config.general.color:
686 self.log_format = ANSIEmojiLoglevelFormatter(self.args.log_fmt, self.config.general.datetime_fmt) 734 self.log_format = ANSIEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt)
687 else: 735 else:
688 self.log_format = ANSIStrippingFormatter(self.args.log_fmt, self.config.general.datetime_fmt) 736 self.log_format = ANSIStrippingEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt)
689 737
690 if self.log_file: 738 if self.log_file:
691 self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode) 739 self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode)