kconfiglib: Update to hide tracebacks for expected errors

Update Kconfiglib to upstream revision 9c0b562c94 to get this commit in:

    Add Kconfig.__init__() helper flag for suppressing tracebacks

    Tools that don't use standard_kconfig() currently generate spammy
    tracebacks for e.g. syntax errors.

    Add a suppress_traceback flag to Kconfig.__init__() for catching
    "expected" exceptions and printing them to stderr and exiting with
    status 1. Use it to make all tools consistently hide tracebacks.

Use the new flag to hide tracebacks for expected exceptions in
kconfig.py, lint.py, and genrest.py.

Some menuconfig robustness tweaks for wonky terminals are included as
well, and a new feature for customizing .config and autoconf.h header
comments via environment variables.

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
Ulf Magnusson 2019-12-17 08:39:07 +01:00 committed by Anas Nashif
commit e181e1b773
5 changed files with 222 additions and 142 deletions

View file

@ -105,7 +105,7 @@ def init():
args = parse_args()
kconf = kconfiglib.Kconfig(args.kconfig)
kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
out_dir = args.out_dir
index_desc = args.index_desc
separate_all_index = args.separate_all_index

View file

@ -29,7 +29,8 @@ def main():
args = parse_args()
print("Parsing Kconfig tree in " + args.kconfig_root)
kconf = Kconfig(args.kconfig_root, warn_to_stderr=False)
kconf = Kconfig(args.kconfig_root, warn_to_stderr=False,
suppress_traceback=True)
# Warn for assignments to undefined symbols
kconf.warn_assign_undef = True

View file

@ -554,7 +554,7 @@ from glob import iglob
from os.path import dirname, exists, expandvars, islink, join, realpath
VERSION = (13, 2, 0)
VERSION = (13, 5, 0)
# File layout:
@ -773,8 +773,8 @@ class Kconfig(object):
See Kconfig.load_config() as well.
srctree:
The value of the $srctree environment variable when the configuration was
loaded, or the empty string if $srctree wasn't set. This gives nice
The value the $srctree environment variable had when the Kconfig instance
was created, or the empty string if $srctree wasn't set. This gives nice
behavior with os.path.join(), which treats "" as the current directory,
without adding "./".
@ -789,13 +789,22 @@ class Kconfig(object):
if multiple configurations are loaded with different values for $srctree.
config_prefix:
The value of the $CONFIG_ environment variable when the configuration was
loaded. This is the prefix used (and expected) on symbol names in .config
files and C headers. Defaults to "CONFIG_". Used in the same way in the C
tools.
The value the CONFIG_ environment variable had when the Kconfig instance
was created, or "CONFIG_" if CONFIG_ wasn't set. This is the prefix used
(and expected) on symbol names in .config files and C headers. Used in
the same way in the C tools.
Like for srctree, only the value of $CONFIG_ when the configuration is
loaded matters.
config_header:
The value the KCONFIG_CONFIG_HEADER environment variable had when the
Kconfig instance was created, or the empty string if
KCONFIG_CONFIG_HEADER wasn't set. This string is inserted verbatim at the
beginning of configuration files. See write_config().
header_header:
The value the KCONFIG_AUTOHEADER_HEADER environment variable had when the
Kconfig instance was created, or the empty string if
KCONFIG_AUTOHEADER_HEADER wasn't set. This string is inserted verbatim at
the beginning of header files. See write_autoconf().
filename/linenr:
The current parsing location, for use in Python preprocessor functions.
@ -810,11 +819,13 @@ class Kconfig(object):
"_warn_assign_no_prompt",
"choices",
"comments",
"config_header",
"config_prefix",
"const_syms",
"defconfig_list",
"defined_syms",
"env_vars",
"header_header",
"kconfig_filenames",
"m",
"menus",
@ -854,7 +865,7 @@ class Kconfig(object):
#
def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
encoding="utf-8"):
encoding="utf-8", suppress_traceback=False):
"""
Creates a new Kconfig object by parsing Kconfig files.
Note that Kconfig files are not the same as .config files (which store
@ -919,7 +930,35 @@ class Kconfig(object):
anyway.
Related PEP: https://www.python.org/dev/peps/pep-0538/
suppress_traceback (default: False):
Helper for tools. When True, any EnvironmentError or KconfigError
generated during parsing is caught, the exception message is printed
to stderr together with the command name, and sys.exit(1) is called
(which generates SystemExit).
This hides the Python traceback for "expected" errors like syntax
errors in Kconfig files.
Other exceptions besides EnvironmentError and KconfigError are still
propagated when suppress_traceback is True.
"""
try:
self._init(filename, warn, warn_to_stderr, encoding)
except (EnvironmentError, KconfigError) as e:
if suppress_traceback:
cmd = sys.argv[0] # Empty string if missisng
if cmd:
cmd += ": "
# Some long exception messages have extra newlines for better
# formatting when reported as an unhandled exception. Strip
# them here.
sys.exit(cmd + str(e).strip())
raise
def _init(self, filename, warn, warn_to_stderr, encoding):
# See __init__()
self._encoding = encoding
self.srctree = os.getenv("srctree", "")
@ -943,6 +982,9 @@ class Kconfig(object):
self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
self.config_prefix))
self.config_header = os.getenv("KCONFIG_CONFIG_HEADER", "")
self.header_header = os.getenv("KCONFIG_AUTOHEADER_HEADER", "")
self.syms = {}
self.const_syms = {}
self.defined_syms = []
@ -1349,8 +1391,29 @@ class Kconfig(object):
elif self.warn_assign_override:
self._warn(msg, filename, linenr)
def write_autoconf(self, filename,
header="/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
def load_allconfig(self, filename):
"""
Helper for all*config. Loads (merges) the configuration file specified
by KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in
the Linux kernel.
Disables warnings for duplicated assignments within configuration files
for the duration of the call
(kconf.warn_assign_override/warn_assign_redun = False), and restores
the previous warning settings at the end. The KCONFIG_ALLCONFIG
configuration file is expected to override symbols.
Exits with sys.exit() (which raises a SystemExit exception) and prints
an error to stderr if KCONFIG_ALLCONFIG is set but the configuration
file can't be opened.
filename:
Command-specific configuration filename - "allyes.config",
"allno.config", etc.
"""
load_allconfig(self, filename)
def write_autoconf(self, filename=None, header=None):
r"""
Writes out symbol values as a C header file, matching the format used
by include/generated/autoconf.h in the kernel.
@ -1364,22 +1427,37 @@ class Kconfig(object):
like the modification time and possibly triggering redundant work in
build tools.
filename:
Self-explanatory.
filename (default: None):
Path to write header to.
header (default: "/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want it enclosed in '/* */' to make it a C comment,
and include a final terminating newline.
If None (the default), the path in the environment variable
KCONFIG_AUTOHEADER is used if set, and "include/generated/autoconf.h"
otherwise. This is compatible with the C tools.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want it enclosed in '/* */' to make it a C comment, and
include a trailing newline.
If None (the default), the value of the environment variable
KCONFIG_AUTOHEADER_HEADER had when the Kconfig instance was created
will be used if it was set, and no header otherwise. See the
Kconfig.header_header attribute.
"""
if filename is None:
filename = os.getenv("KCONFIG_AUTOHEADER",
"include/generated/autoconf.h")
self._write_if_changed(filename, self._autoconf_contents(header))
def _autoconf_contents(self, header):
# write_autoconf() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_AUTOHEADER_HEADER at the beginning.
# "".join()ed later
chunks = [header]
if header is None:
header = self.header_header
chunks = [header] # "".join()ed later
add = chunks.append
for sym in self.unique_defined_syms:
@ -1415,9 +1493,8 @@ class Kconfig(object):
return "".join(chunks)
def write_config(self, filename=None,
header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n",
save_old=True, verbose=None):
def write_config(self, filename=None, header=None, save_old=True,
verbose=None):
r"""
Writes out symbol values in the .config format. The format matches the
C implementation, including ordering.
@ -1439,16 +1516,21 @@ class Kconfig(object):
(OSError/IOError). KconfigError is never raised here.
filename (default: None):
Filename to save configuration to (a string).
Path to write configuration to (a string).
If None (the default), the filename in the environment variable
If None (the default), the path in the environment variable
KCONFIG_CONFIG is used if set, and ".config" otherwise. See
standard_config_filename().
header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want each line to start with '#' to make it a comment,
and include a final terminating newline.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want each line to start with '#' to make it a comment, and
include a trailing newline.
if None (the default), the value of the environment variable
KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
be used if it was set, and no header otherwise. See the
Kconfig.config_header attribute.
save_old (default: True):
If True and <filename> already exists, a copy of it will be saved to
@ -1493,7 +1575,7 @@ class Kconfig(object):
def _config_contents(self, header):
# write_config() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
#
# More memory friendly would be to 'yield' the strings and
# "".join(_config_contents()), but it was a bit slower on my system.
@ -1505,13 +1587,15 @@ class Kconfig(object):
for sym in self.unique_defined_syms:
sym._visited = False
if header is None:
header = self.config_header
chunks = [header] # "".join()ed later
add = chunks.append
# Did we just print an '# end of ...' comment?
after_end_comment = False
# "".join()ed later
chunks = [header]
add = chunks.append
node = self.top_node
while 1:
# Jump to the next node with an iterative tree walk
@ -1564,8 +1648,7 @@ class Kconfig(object):
add("\n#\n# {}\n#\n".format(node.prompt[0]))
after_end_comment = False
def write_min_config(self, filename,
header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
def write_min_config(self, filename, header=None):
"""
Writes out a "minimal" configuration file, omitting symbols whose value
matches their default value. The format matches the one produced by
@ -1581,12 +1664,17 @@ class Kconfig(object):
(OSError/IOError). KconfigError is never raised here.
filename:
Self-explanatory.
Path to write minimal configuration to.
header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want each line to start with '#' to make it a comment,
and include a final terminating newline.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want each line to start with '#' to make it a comment, and
include a final terminating newline.
if None (the default), the value of the environment variable
KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
be used if it was set, and no header otherwise. See the
Kconfig.config_header attribute.
Returns a string with a message saying which file got saved. This is
meant to reduce boilerplate in tools, which can do e.g.
@ -1603,9 +1691,12 @@ class Kconfig(object):
def _min_config_contents(self, header):
# write_min_config() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
chunks = [header]
if header is None:
header = self.config_header
chunks = [header] # "".join()ed later
add = chunks.append
for sym in self.unique_defined_syms:
@ -2122,9 +2213,9 @@ class Kconfig(object):
# it's part of a different construct
if self._reuse_tokens:
self._reuse_tokens = False
# self._tokens_i is known to be 1 here, because _parse_properties()
# leaves it like that when it can't recognize a line (or parses
# a help text)
# self._tokens_i is known to be 1 here, because _parse_props()
# leaves it like that when it can't recognize a line (or parses a
# help text)
return True
# readline() returns '' over and over at EOF, which we rely on for help
@ -2141,7 +2232,7 @@ class Kconfig(object):
self._tokens = self._tokenize(line)
# Initialize to 1 instead of 0 to factor out code from _parse_block()
# and _parse_properties(). They immediately fetch self._tokens[0].
# and _parse_props(). They immediately fetch self._tokens[0].
self._tokens_i = 1
return True
@ -2839,7 +2930,7 @@ class Kconfig(object):
sym.nodes.append(node)
self._parse_properties(node)
self._parse_props(node)
if node.is_menuconfig and not node.prompt:
self._warn("the menuconfig symbol {} has no prompt"
@ -2925,7 +3016,7 @@ class Kconfig(object):
self.menus.append(node)
self._parse_properties(node)
self._parse_props(node)
self._parse_block(_T_ENDMENU, node, node)
node.list = node.next
@ -2945,7 +3036,7 @@ class Kconfig(object):
self.comments.append(node)
self._parse_properties(node)
self._parse_props(node)
prev.next = prev = node
@ -2977,7 +3068,7 @@ class Kconfig(object):
choice.nodes.append(node)
self._parse_properties(node)
self._parse_props(node)
self._parse_block(_T_ENDCHOICE, node, node)
node.list = node.next
@ -3019,7 +3110,7 @@ class Kconfig(object):
return expr
def _parse_properties(self, node):
def _parse_props(self, node):
# Parses and adds properties to the MenuNode 'node' (type, 'prompt',
# 'default's, etc.) Properties are later copied up to symbols and
# choices in a separate pass after parsing, in e.g.
@ -3045,7 +3136,7 @@ class Kconfig(object):
if t0 in _TYPE_TOKENS:
# Relies on '_T_BOOL is BOOL', etc., to save a conversion
self._set_type(node, t0)
self._set_type(node.item, t0)
if self._tokens[1] is not None:
self._parse_prompt(node)
@ -3075,7 +3166,7 @@ class Kconfig(object):
self._parse_cond()))
elif t0 in _DEF_TOKEN_TO_TYPE:
self._set_type(node, _DEF_TOKEN_TO_TYPE[t0])
self._set_type(node.item, _DEF_TOKEN_TO_TYPE[t0])
node.defaults.append((self._parse_expr(False),
self._parse_cond()))
@ -3176,13 +3267,15 @@ class Kconfig(object):
self._reuse_tokens = True
return
def _set_type(self, node, new_type):
# UNKNOWN is falsy
if node.item.orig_type and node.item.orig_type is not new_type:
self._warn("{} defined with multiple types, {} will be used"
.format(node.item.name_and_loc, TYPE_TO_STR[new_type]))
def _set_type(self, sc, new_type):
# Sets the type of 'sc' (symbol or choice) to 'new_type'
node.item.orig_type = new_type
# UNKNOWN is falsy
if sc.orig_type and sc.orig_type is not new_type:
self._warn("{} defined with multiple types, {} will be used"
.format(sc.name_and_loc, TYPE_TO_STR[new_type]))
sc.orig_type = new_type
def _parse_prompt(self, node):
# 'prompt' properties override each other within a single definition of
@ -3372,7 +3465,7 @@ class Kconfig(object):
# The calculated sets might be larger than necessary as we don't do any
# complex analysis of the expressions.
make_depend_on = _make_depend_on # Micro-optimization
depend_on = _depend_on # Micro-optimization
# Only calculate _dependents for defined symbols. Constant and
# undefined symbols could theoretically be selected/implied, but it
@ -3383,29 +3476,29 @@ class Kconfig(object):
# The prompt conditions
for node in sym.nodes:
if node.prompt:
make_depend_on(sym, node.prompt[1])
depend_on(sym, node.prompt[1])
# The default values and their conditions
for value, cond in sym.defaults:
make_depend_on(sym, value)
make_depend_on(sym, cond)
depend_on(sym, value)
depend_on(sym, cond)
# The reverse and weak reverse dependencies
make_depend_on(sym, sym.rev_dep)
make_depend_on(sym, sym.weak_rev_dep)
depend_on(sym, sym.rev_dep)
depend_on(sym, sym.weak_rev_dep)
# The ranges along with their conditions
for low, high, cond in sym.ranges:
make_depend_on(sym, low)
make_depend_on(sym, high)
make_depend_on(sym, cond)
depend_on(sym, low)
depend_on(sym, high)
depend_on(sym, cond)
# The direct dependencies. This is usually redundant, as the direct
# dependencies get propagated to properties, but it's needed to get
# invalidation solid for 'imply', which only checks the direct
# dependencies (even if there are no properties to propagate it
# to).
make_depend_on(sym, sym.direct_dep)
depend_on(sym, sym.direct_dep)
# In addition to the above, choice symbols depend on the choice
# they're in, but that's handled automatically since the Choice is
@ -3418,11 +3511,11 @@ class Kconfig(object):
# The prompt conditions
for node in choice.nodes:
if node.prompt:
make_depend_on(choice, node.prompt[1])
depend_on(choice, node.prompt[1])
# The default symbol conditions
for _, cond in choice.defaults:
make_depend_on(choice, cond)
depend_on(choice, cond)
def _add_choice_deps(self):
# Choices also depend on the choice symbols themselves, because the
@ -3776,7 +3869,7 @@ class Kconfig(object):
.format(sym.name_and_loc))
def _parse_error(self, msg):
raise KconfigError("{}couldn't parse '{}': {}".format(
raise KconfigError("{}error: couldn't parse '{}': {}".format(
"" if self.filename is None else
"{}:{}: ".format(self.filename, self.linenr),
self._line.strip(), msg))
@ -5295,8 +5388,8 @@ class Choice(object):
self._cached_selection = _NO_CACHED_SELECTION
# is_constant is checked by _make_depend_on(). Just set it to avoid
# having to special-case choices.
# is_constant is checked by _depend_on(). Just set it to avoid having
# to special-case choices.
self.is_constant = self.is_optional = False
# See Kconfig._build_dep()
@ -6117,17 +6210,9 @@ def standard_kconfig(description=None):
metavar="KCONFIG",
default="Kconfig",
nargs="?",
help="Kconfig file (default: Kconfig)")
help="Top-level Kconfig file (default: Kconfig)")
args = parser.parse_args()
# Suppress backtraces for expected exceptions
try:
return Kconfig(args.kconfig)
except (EnvironmentError, KconfigError) as e:
# Some long exception messages have extra newlines for better
# formatting when reported as an unhandled exception. Strip them here.
sys.exit(str(e).strip())
return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
def standard_config_filename():
@ -6143,25 +6228,9 @@ def standard_config_filename():
def load_allconfig(kconf, filename):
"""
Helper for all*config. Loads (merges) the configuration file specified by
KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in the
Linux kernel.
Disables warnings for duplicated assignments within configuration files for
the duration of the call (kconf.warn_assign_override/warn_assign_redun = False),
and restores the previous warning settings at the end. The
KCONFIG_ALLCONFIG configuration file is expected to override symbols.
Exits with sys.exit() (which raises a SystemExit exception) and prints an
error to stderr if KCONFIG_ALLCONFIG is set but the configuration file
can't be opened.
kconf:
Kconfig instance to load the configuration in.
filename:
Command-specific configuration filename - "allyes.config",
"allno.config", etc.
Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
Supported for backwards compatibility. Might be removed at some point after
a long period of deprecation warnings.
"""
allconfig = os.getenv("KCONFIG_ALLCONFIG")
if allconfig is None:
@ -6237,7 +6306,7 @@ def _visibility(sc):
return vis
def _make_depend_on(sc, expr):
def _depend_on(sc, expr):
# Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
# Constant symbols in 'expr' are skipped as they can never change value
# anyway.
@ -6245,11 +6314,11 @@ def _make_depend_on(sc, expr):
if expr.__class__ is tuple:
# AND, OR, NOT, or relation
_make_depend_on(sc, expr[1])
_depend_on(sc, expr[1])
# NOTs only have a single operand
if expr[0] is not NOT:
_make_depend_on(sc, expr[2])
_depend_on(sc, expr[2])
elif not expr.is_constant:
# Non-constant symbol, or choice

View file

@ -151,7 +151,7 @@ def init_kconfig():
BOARD_DIR="boards/*/*",
ARCH="*")
kconf = kconfiglib.Kconfig()
kconf = kconfiglib.Kconfig(suppress_traceback=True)
def modules_file_dir():

View file

@ -137,13 +137,16 @@ If there's an error in the style definition or if a missing style is assigned
to, the assignment will be ignored, along with a warning being printed on
stderr.
The 'default' theme is always implicitly parsed first (or the 'monochrome'
theme if the terminal lacks colors), so the following two settings have the
same effect:
The 'default' theme is always implicitly parsed first, so the following two
settings have the same effect:
MENUCONFIG_STYLE="selection=fg:white,bg:red"
MENUCONFIG_STYLE="default selection=fg:white,bg:red"
If the terminal doesn't support colors, the 'monochrome' theme is used, and
MENUCONFIG_STYLE is ignored. The assumption is that the environment is broken
somehow, and that the important thing is to get something usable.
Other features
==============
@ -182,12 +185,15 @@ See the https://github.com/zephyrproject-rtos/windows-curses repository.
"""
from __future__ import print_function
import os
import sys
_IS_WINDOWS = os.name == "nt" # Are we running on Windows?
try:
import curses
except ImportError as e:
if sys.platform != "win32":
if not _IS_WINDOWS:
raise
sys.exit("""\
menuconfig failed to import the standard Python 'curses' library. Try
@ -206,7 +212,6 @@ Exception:
import errno
import locale
import os
import re
import textwrap
@ -312,19 +317,18 @@ _STYLES = {
text=
""",
# Blue tinted style loosely resembling lxdialog
# Blue-tinted style loosely resembling lxdialog
"aquatic": """
path=fg:cyan,bg:blue,bold
separator=fg:white,bg:cyan,bold
help=path
frame=fg:white,bg:cyan,bold
body=fg:brightwhite,bg:blue
body=fg:white,bg:blue
edit=fg:black,bg:white
"""
}
# Standard colors definition
_STYLE_STD_COLORS = {
_NAMED_COLORS = {
# Basic colors
"black": curses.COLOR_BLACK,
"red": curses.COLOR_RED,
@ -550,9 +554,6 @@ def _style_to_curses(style_def):
def parse_color(color_def):
color_def = color_def.split(":", 1)[1]
if color_def in _STYLE_STD_COLORS:
return _color_from_num(_STYLE_STD_COLORS[color_def])
# HTML format, #RRGGBB
if re.match("#[A-Fa-f0-9]{6}", color_def):
return _color_from_rgb((
@ -560,19 +561,20 @@ def _style_to_curses(style_def):
int(color_def[3:5], 16),
int(color_def[5:7], 16)))
try:
color_num = _color_from_num(int(color_def, 0))
except ValueError:
_warn("Ignoring color ", color_def, "that's neither predefined "
"nor a number")
return -1
if color_def in _NAMED_COLORS:
color_num = _color_from_num(_NAMED_COLORS[color_def])
else:
try:
color_num = _color_from_num(int(color_def, 0))
except ValueError:
_warn("Ignoring color", color_def, "that's neither "
"predefined nor a number")
return -1
if not -1 <= color_num < curses.COLORS:
_warn("Ignoring color {}, which is outside the range "
"-1..curses.COLORS-1 (-1..{})"
.format(color_def, curses.COLORS - 1))
return -1
return color_num
@ -605,15 +607,26 @@ def _style_to_curses(style_def):
def _init_styles():
if curses.has_colors():
curses.use_default_colors()
try:
curses.use_default_colors()
except curses.error:
# Ignore errors on funky terminals that support colors but not
# using default colors. Worst it can do is break transparency and
# the like. Ran across this with the MSYS2/winpty setup in
# https://github.com/msys2/MINGW-packages/issues/5823, though there
# seems to be a lot of general brokenness there.
pass
# Use the 'monochrome' style template as the base on terminals without
# color
_parse_style("default" if curses.has_colors() else "monochrome", True)
# Add any user-defined style from the environment
if "MENUCONFIG_STYLE" in os.environ:
_parse_style(os.environ["MENUCONFIG_STYLE"], False)
# Use the 'default' theme as the base, and add any user-defined style
# settings from the environment
_parse_style("default", True)
if "MENUCONFIG_STYLE" in os.environ:
_parse_style(os.environ["MENUCONFIG_STYLE"], False)
else:
# Force the 'monochrome' theme if the terminal doesn't support colors.
# MENUCONFIG_STYLE is likely to mess things up here (though any colors
# would be ignored), so ignore it.
_parse_style("monochrome", True)
# color_attribs holds the color pairs we've already created, indexed by a
@ -3274,8 +3287,5 @@ def _change_c_lc_ctype_to_utf8():
return
# Are we running on Windows?
_IS_WINDOWS = os.name == "nt"
if __name__ == "__main__":
_main()