188188
189189_IS_WINDOWS = os .name == "nt" # Are we running on Windows?
190190
191- try :
192- import curses
193- except ImportError as e :
194- if not _IS_WINDOWS :
195- raise
196- sys .exit (
197- """\
198- menuconfig failed to import the standard Python 'curses' library. Try
199- installing a package like windows-curses
200- (https://github.com/zephyrproject-rtos/windows-curses) by running this command
201- in cmd.exe:
202-
203- pip install windows-curses
204-
205- Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
206- installed when installing Kconfiglib via pip on Windows (because it breaks
207- installation on MSYS2).
208-
209- Exception:
210- {}: {}""" .format (
211- type (e ).__name__ , e
212- )
213- )
191+ # Defer curses import to runtime - it will be imported by _ensure_curses()
192+ # when menuconfig() is called. This avoids import errors in headless CI/CD
193+ # environments where menuconfig UI is not used.
214194
215195import errno
216196import locale
357337 """ ,
358338}
359339
360- _NAMED_COLORS = {
361- # Basic colors
362- "black" : curses .COLOR_BLACK ,
363- "red" : curses .COLOR_RED ,
364- "green" : curses .COLOR_GREEN ,
365- "yellow" : curses .COLOR_YELLOW ,
366- "blue" : curses .COLOR_BLUE ,
367- "magenta" : curses .COLOR_MAGENTA ,
368- "cyan" : curses .COLOR_CYAN ,
369- "white" : curses .COLOR_WHITE ,
370- # Bright versions
371- "brightblack" : curses .COLOR_BLACK + 8 ,
372- "brightred" : curses .COLOR_RED + 8 ,
373- "brightgreen" : curses .COLOR_GREEN + 8 ,
374- "brightyellow" : curses .COLOR_YELLOW + 8 ,
375- "brightblue" : curses .COLOR_BLUE + 8 ,
376- "brightmagenta" : curses .COLOR_MAGENTA + 8 ,
377- "brightcyan" : curses .COLOR_CYAN + 8 ,
378- "brightwhite" : curses .COLOR_WHITE + 8 ,
379- # Aliases
380- "purple" : curses .COLOR_MAGENTA ,
381- "brightpurple" : curses .COLOR_MAGENTA + 8 ,
382- }
340+ # Curses module - will be imported by _ensure_curses()
341+ curses = None
342+
343+ # Named colors cache - will be initialized by _get_named_colors()
344+ _named_colors_cache = None
345+
346+
347+ def _get_named_colors ():
348+ """Returns the named colors dictionary. Initializes it on first call."""
349+ global _named_colors_cache
350+ if _named_colors_cache is None :
351+ _named_colors_cache = {
352+ # Basic colors
353+ "black" : curses .COLOR_BLACK ,
354+ "red" : curses .COLOR_RED ,
355+ "green" : curses .COLOR_GREEN ,
356+ "yellow" : curses .COLOR_YELLOW ,
357+ "blue" : curses .COLOR_BLUE ,
358+ "magenta" : curses .COLOR_MAGENTA ,
359+ "cyan" : curses .COLOR_CYAN ,
360+ "white" : curses .COLOR_WHITE ,
361+ # Bright versions
362+ "brightblack" : curses .COLOR_BLACK + 8 ,
363+ "brightred" : curses .COLOR_RED + 8 ,
364+ "brightgreen" : curses .COLOR_GREEN + 8 ,
365+ "brightyellow" : curses .COLOR_YELLOW + 8 ,
366+ "brightblue" : curses .COLOR_BLUE + 8 ,
367+ "brightmagenta" : curses .COLOR_MAGENTA + 8 ,
368+ "brightcyan" : curses .COLOR_CYAN + 8 ,
369+ "brightwhite" : curses .COLOR_WHITE + 8 ,
370+ # Aliases
371+ "purple" : curses .COLOR_MAGENTA ,
372+ "brightpurple" : curses .COLOR_MAGENTA + 8 ,
373+ }
374+ return _named_colors_cache
383375
384376
385377def _rgb_to_6cube (rgb ):
@@ -594,8 +586,9 @@ def parse_color(color_def):
594586 )
595587 )
596588
597- if color_def in _NAMED_COLORS :
598- color_num = _color_from_num (_NAMED_COLORS [color_def ])
589+ named_colors = _get_named_colors ()
590+ if color_def in named_colors :
591+ color_num = _color_from_num (named_colors [color_def ])
599592 else :
600593 try :
601594 color_num = _color_from_num (int (color_def , 0 ))
@@ -695,6 +688,43 @@ def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
695688#
696689
697690
691+ def _ensure_curses ():
692+ """
693+ Imports curses module if not already imported. This allows the module
694+ to be imported in headless CI/CD environments without triggering import
695+ errors, as long as menuconfig() is not called.
696+ """
697+ global curses
698+ if curses is not None :
699+ return
700+
701+ try :
702+ import curses as curses_module
703+
704+ curses = curses_module
705+ except ImportError as e :
706+ if not _IS_WINDOWS :
707+ raise
708+ sys .exit (
709+ """\
710+ menuconfig failed to import the standard Python 'curses' library. Try
711+ installing a package like windows-curses
712+ (https://github.com/zephyrproject-rtos/windows-curses) by running this command
713+ in cmd.exe:
714+
715+ pip install windows-curses
716+
717+ Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
718+ installed when installing Kconfiglib via pip on Windows (because it breaks
719+ installation on MSYS2).
720+
721+ Exception:
722+ {}: {}""" .format (
723+ type (e ).__name__ , e
724+ )
725+ )
726+
727+
698728def _main ():
699729 menuconfig (standard_kconfig (__doc__ ))
700730
@@ -706,6 +736,9 @@ def menuconfig(kconf):
706736 kconf:
707737 Kconfig instance to be configured
708738 """
739+ # Import curses at runtime to avoid issues in headless environments
740+ _ensure_curses ()
741+
709742 global _kconf
710743 global _conf_filename
711744 global _conf_changed
0 commit comments