Skip to content

Commit 8942a47

Browse files
committed
west: add --color option to control output colorization
Add a global --color option with values 'always', 'never', and 'auto' to override automatic terminal detection. The command-line option takes precedence over configuration settings. When provided, it overrides the value returned by WestCommand.color_ui. fixes #651 Signed-off-by: TharakaUJ <9dmpires2k17.tuj@gmail.com>
1 parent 5f5931f commit 8942a47

2 files changed

Lines changed: 74 additions & 6 deletions

File tree

src/west/app/main.py

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class EarlyArgs(NamedTuple):
9393
version: bool # True if -V was given
9494
zephyr_base: str | None # -z argument value
9595
verbosity: int # 0 if not given, otherwise counts
96+
color: str | None # --color argument value ('always', 'never', 'auto')
9697
command_name: str | None
9798

9899
# Other arguments are appended here.
@@ -106,16 +107,18 @@ def parse_early_args(argv: list[str]) -> EarlyArgs:
106107
version = False
107108
zephyr_base = None
108109
verbosity = 0
110+
color = None
109111
command_name = None
110112
unexpected_arguments = []
111113

112114
expecting_zephyr_base = False
115+
expecting_color = False
113116

114117
def consume_more_args(rest):
115118
# Handle the 'Vv' portion of 'west -hVv'.
116119

117-
nonlocal help, version, zephyr_base, verbosity
118-
nonlocal expecting_zephyr_base
120+
nonlocal help, version, zephyr_base, verbosity, color
121+
nonlocal expecting_zephyr_base, expecting_color
119122

120123
if not rest:
121124
return
@@ -145,6 +148,13 @@ def consume_more_args(rest):
145148
for arg in argv:
146149
if expecting_zephyr_base:
147150
zephyr_base = arg
151+
expecting_zephyr_base = False
152+
elif expecting_color:
153+
if arg in ('always', 'never', 'auto'):
154+
color = arg
155+
else:
156+
unexpected_arguments.append(f'--color={arg}')
157+
expecting_color = False
148158
elif arg.startswith('-h'):
149159
help = True
150160
consume_more_args(arg[2:])
@@ -170,13 +180,23 @@ def consume_more_args(rest):
170180
zephyr_base = arg[3:]
171181
else:
172182
zephyr_base = arg[2:]
183+
elif arg == '--color':
184+
expecting_color = True
185+
elif arg.startswith('--color='):
186+
color_val = arg[8:]
187+
if color_val in ('always', 'never', 'auto'):
188+
color = color_val
189+
else:
190+
unexpected_arguments.append(arg)
173191
elif arg.startswith('-'):
174192
unexpected_arguments.append(arg)
175193
else:
176194
command_name = arg
177195
break
178196

179-
return EarlyArgs(help, version, zephyr_base, verbosity, command_name, unexpected_arguments)
197+
return EarlyArgs(
198+
help, version, zephyr_base, verbosity, color, command_name, unexpected_arguments
199+
)
180200

181201

182202
class LogFormatter(logging.Formatter):
@@ -222,6 +242,7 @@ def __init__(self):
222242
self.subparser_gen = None # an add_subparsers() return value
223243
self.cmd = None # west.commands.WestCommand, eventually
224244
self.queued_io = [] # I/O hooks we want self.cmd to do
245+
self.color_mode = None # 'always', 'never', 'auto', or None
225246

226247
for group, classes in BUILTIN_COMMAND_GROUPS.items():
227248
lst = [cls() for cls in classes]
@@ -253,9 +274,8 @@ def run(self, argv):
253274
# Use verbosity to determine west API log levels
254275
self.setup_west_logging(early_args.verbosity)
255276

256-
# Makes ANSI color escapes work on Windows, and strips them when
257-
# stdout/stderr isn't a terminal
258-
colorama.init()
277+
# Store color mode for later use
278+
self.color_mode = early_args.color
259279

260280
# See if we're in a workspace. It's fine if we're not.
261281
# Note that this falls back on searching from ZEPHYR_BASE
@@ -273,6 +293,9 @@ def run(self, argv):
273293
# backwards compatibility.
274294
self.config._copy_to_configparser(west.configuration.config)
275295

296+
# Initialize colorama after config is loaded so we can read color.ui setting
297+
self.init_colorama()
298+
276299
# Set self.manifest and self.extensions.
277300
self.load_manifest()
278301
self.load_extension_specs()
@@ -576,6 +599,13 @@ def make_parsers(self):
576599
help='print the program version and exit',
577600
)
578601

602+
parser.add_argument(
603+
'--color',
604+
choices=['always', 'never', 'auto'],
605+
default=None,
606+
help='when to colorize output (always, never, auto)',
607+
)
608+
579609
subparser_gen = parser.add_subparsers(metavar='<command>', dest='command')
580610

581611
return parser, subparser_gen
@@ -739,12 +769,45 @@ def setup_west_logging(self, verbosity):
739769

740770
logger.addHandler(LogHandler())
741771

772+
def init_colorama(self):
773+
color_mode = self.color_mode
774+
775+
if color_mode is None and self.config:
776+
config_value = self.config.get('color.ui', 'auto')
777+
# Handle both string values (always/never/auto) and
778+
# boolean values (true/false) for backward compatibility
779+
if config_value in ('always', 'never', 'auto'):
780+
color_mode = config_value
781+
elif config_value in ('true', 'True', '1'):
782+
color_mode = 'always'
783+
elif config_value in ('false', 'False', '0'):
784+
color_mode = 'never'
785+
else:
786+
color_mode = 'auto'
787+
788+
if color_mode == 'always':
789+
colorama.init(strip=False)
790+
elif color_mode == 'never':
791+
colorama.init(strip=True)
792+
else:
793+
# 'auto' or None: use colorama's default behavior
794+
colorama.init()
795+
796+
def apply_color_override(self):
797+
if self.color_mode == 'always':
798+
self.cmd._color_override = True
799+
elif self.color_mode == 'never':
800+
self.cmd._color_override = False
801+
# For 'auto' or None, don't set override (use config)
802+
742803
def run_builtin(self, args, unknown):
743804
self.queued_io.append(
744805
lambda cmd: cmd.dbg('args namespace:', args, level=Verbosity.DBG_EXTREME)
745806
)
746807
self.cmd = self.builtins.get(args.command, self.builtins['help'])
747808
adjust_command_verbosity(self.cmd, args)
809+
self.apply_color_override()
810+
748811
if self.mle:
749812
self.handle_builtin_manifest_load_err(args)
750813
for io_hook in self.queued_io:
@@ -771,6 +834,8 @@ def run_extension(self, name, argv):
771834
args, unknown = west_parser.parse_known_args(argv)
772835

773836
adjust_command_verbosity(self.cmd, args)
837+
self.apply_color_override()
838+
774839
self.queued_io.append(
775840
lambda cmd: cmd.dbg('args namespace:', args, level=Verbosity.DBG_EXTREME)
776841
)

src/west/commands.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def __init__(
167167
self.manifest = None
168168
self.config = None
169169
self._hooks: list[Callable[[WestCommand], None]] = []
170+
self._color_override: bool | None = None
170171

171172
def add_pre_run_hook(self, hook: Callable[['WestCommand'], None]) -> None:
172173
'''Add a hook which will be called right before do_run().
@@ -544,6 +545,8 @@ def die(self, *args, exit_code: int = 1) -> NoReturn:
544545
@property
545546
def color_ui(self) -> bool:
546547
'''Should we colorize output?'''
548+
if self._color_override is not None:
549+
return self._color_override
547550
return self.config.getboolean('color.ui', default=True) if self.has_config else True
548551

549552
#

0 commit comments

Comments
 (0)