Changeset cf2f109 for menu/menuconfig.py


Ignore:
Timestamp:
06/15/2019 05:25:10 PM (5 years ago)
Author:
Pierre Labastie <pierre@…>
Branches:
ablfs-more, legacy, trunk
Children:
a4af6811
Parents:
619b313
Message:

Update to Kconfiglib version 12.4.0. This removes the need to use
".configuration.old"

File:
1 edited

Legend:

Unmodified
Added
Removed
  • menu/menuconfig.py

    r619b313 rcf2f109  
    1 #!/usr/bin/env python3
     1#!/usr/bin/env python
    22
    33# Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson
     
    88========
    99
    10 A curses-based menuconfig implementation. The interface should feel familiar to
    11 people used to mconf ('make menuconfig').
     10A curses-based Python 2/3 menuconfig implementation. The interface should feel
     11familiar to people used to mconf ('make menuconfig').
    1212
    1313Supports the same keys as mconf, and also supports a set of keybindings
     
    2121  g/Home  : Jump to beginning of list
    2222
     23[Space] toggles values if possible, and enters menus otherwise. [Enter] works
     24the other way around.
     25
    2326The mconf feature where pressing a key jumps to a menu entry with that
    2427character in it in the current menu isn't supported. A jump-to feature for
     
    2629comment (as in a Kconfig 'comment "Foo"') is available instead.
    2730
    28 Space and Enter are "smart" and try to do what you'd expect for the given menu
    29 entry.
    30 
    3131A few different modes are available:
    3232
     
    5555The KCONFIG_CONFIG environment variable specifies the .config file to load (if
    5656it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
     57
     58When overwriting a configuration file, the old version is saved to
     59<filename>.old (e.g. .config.old).
    5760
    5861$srctree is supported through Kconfiglib.
     
    97100    - fg:COLOR      Set the foreground/background colors. COLOR can be one of
    98101      * or *        the basic 16 colors (black, red, green, yellow, blue,
    99     - bg:COLOR      magenta,cyan, white and brighter versions, for example,
     102    - bg:COLOR      magenta, cyan, white and brighter versions, for example,
    100103                    brightred). On terminals that support more than 8 colors,
    101104                    you can also directly put in a color number, e.g. fg:123
     
    172175===========
    173176
    174   - Python 3 only
    175 
    176     This is mostly due to Python 2 not having curses.get_wch(), which is needed
    177     for Unicode support.
    178 
    179   - Doesn't work out of the box on Windows
    180 
    181     Can be made to work with 'pip install windows-curses' though. See the
    182     https://github.com/zephyrproject-rtos/windows-curses repository.
    183 
    184     'pip install kconfiglib' on Windows automatically installs windows-curses
    185     to make the menuconfig usable.
     177Doesn't work out of the box on Windows, but can be made to work with 'pip
     178install windows-curses'. See the
     179https://github.com/zephyrproject-rtos/windows-curses repository.
     180
     181'pip install kconfiglib' on Windows automatically installs windows-curses
     182to make the menuconfig usable.
    186183"""
     184from __future__ import print_function
     185
    187186import curses
    188187import errno
     
    194193
    195194from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
    196                        BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
     195                       BOOL, TRISTATE, STRING, INT, HEX, \
    197196                       AND, OR, \
    198197                       expr_str, expr_value, split_expr, \
     
    245244# Lines of help text shown at the bottom of the information dialog
    246245_INFO_HELP_LINES = """
    247 [ESC/q] Return to menu       [/] Jump to symbol
     246[ESC/q] Return to menu      [/] Jump to symbol
    248247"""[1:-1].split("\n")
    249248
     
    629628
    630629
    631 # Used as the entry point in setup.py
    632630def _main():
    633631    menuconfig(standard_kconfig())
     
    649647    _kconf = kconf
    650648
     649    # Filename to save configuration to
     650    _conf_filename = standard_config_filename()
     651
    651652    # Load existing configuration and set _conf_changed True if it is outdated
    652653    _conf_changed = _load_config()
    653 
    654     # Filename to save configuration to
    655     _conf_filename = standard_config_filename()
    656654
    657655    # Filename to save minimal configuration to
     
    672670    # Disable warnings. They get mangled in curses mode, and we deal with
    673671    # errors ourselves.
    674     kconf.disable_warnings()
     672    kconf.warn = False
    675673
    676674    # Make curses use the locale settings specified in the environment
     
    709707    # saving the configuration in that case.
    710708
    711     if not _kconf.load_config():
     709    print(_kconf.load_config())
     710    if not os.path.exists(_conf_filename):
    712711        # No .config
    713712        return True
     
    729728                # Unwritten symbol
    730729                return True
    731         elif sym.type in (BOOL, TRISTATE):
     730        elif sym.orig_type in (BOOL, TRISTATE):
    732731            if sym.tri_value != sym.user_value:
    733732                # Written bool/tristate symbol, new value
     
    770769#
    771770#   _conf_filename:
    772 #     .config file to save the configuration to
     771#     File to save the configuration to
    773772#
    774773#   _minconf_filename:
     
    802801
    803802
    804         c = _get_wch_compat(_menu_win)
     803        c = _getch_compat(_menu_win)
    805804
    806805        if c == curses.KEY_RESIZE:
     
    829828            _select_first_menu_entry()
    830829
    831         elif c in (curses.KEY_RIGHT, " ", "\n", "l", "L"):
    832             # Do appropriate node action. Only Space is treated specially,
    833             # preferring to toggle nodes rather than enter menus.
    834 
     830        elif c == " ":
     831            # Toggle the node if possible
    835832            sel_node = _shown[_sel_node_i]
    836 
    837             if sel_node.is_menuconfig and not \
    838                (c == " " and _prefer_toggle(sel_node.item)):
    839 
     833            if not _change_node(sel_node):
    840834                _enter_menu(sel_node)
    841835
    842             else:
     836        elif c in (curses.KEY_RIGHT, "\n", "l", "L"):
     837            # Enter the node if possible
     838            sel_node = _shown[_sel_node_i]
     839            if not _enter_menu(sel_node):
    843840                _change_node(sel_node)
    844                 if _is_y_mode_choice_sym(sel_node.item) and not sel_node.list:
    845                     # Immediately jump to the parent menu after making a choice
    846                     # selection, like 'make menuconfig' does, except if the
    847                     # menu node has children (which can happen if a symbol
    848                     # 'depends on' a choice symbol that immediately precedes
    849                     # it).
    850                     _leave_menu()
    851841
    852842        elif c in ("n", "N"):
     
    930920
    931921        if c == "y":
    932             if _try_save(_kconf.write_config, _conf_filename, "configuration"):
    933                 return "Configuration saved to '{}'".format(_conf_filename)
     922            # Returns a message to print
     923            msg = _try_save(_kconf.write_config, _conf_filename, "configuration")
     924            if msg:
     925                return msg
    934926
    935927        elif c == "n":
     
    962954    # backspace work with TERM=vt100. That makes it likely to work in sane
    963955    # environments.
    964     #
    965     # erasechar() returns a 'bytes' object. Since we use get_wch(), we need to
    966     # decode it. Just give up and avoid crashing if it can't be decoded.
    967     _ERASE_CHAR = curses.erasechar().decode("utf-8", "ignore")
     956    _ERASE_CHAR = curses.erasechar()
     957    if sys.version_info[0] >= 3:
     958        # erasechar() returns a one-byte bytes object on Python 3. This sets
     959        # _ERASE_CHAR to a blank string if it can't be decoded, which should be
     960        # harmless.
     961        _ERASE_CHAR = _ERASE_CHAR.decode("utf-8", "ignore")
    968962
    969963    _init_styles()
     
    10601054
    10611055
    1062 def _prefer_toggle(item):
    1063     # For nodes with menus, determines whether Space should change the value of
    1064     # the node's item or enter its menu. We toggle symbols (which have menus
    1065     # when they're defined with 'menuconfig') and choices that can be in more
    1066     # than one mode (e.g. optional choices). In other cases, we enter the menu.
    1067 
    1068     return isinstance(item, Symbol) or \
    1069            (isinstance(item, Choice) and len(item.assignable) > 1)
    1070 
    1071 
    10721056def _enter_menu(menu):
    1073     # Makes 'menu' the currently displayed menu. "Menu" here includes choices
    1074     # and symbols defined with the 'menuconfig' keyword.
     1057    # Makes 'menu' the currently displayed menu. In addition to actual 'menu's,
     1058    # "menu" here includes choices and symbols defined with the 'menuconfig'
     1059    # keyword.
     1060    #
     1061    # Returns False if 'menu' can't be entered.
    10751062
    10761063    global _cur_menu
     
    10791066    global _menu_scroll
    10801067
     1068    if not menu.is_menuconfig:
     1069        # Not a menu
     1070        return False
     1071
    10811072    shown_sub = _shown_nodes(menu)
    10821073    # Never enter empty menus. We depend on having a current node.
    1083     if shown_sub:
    1084         # Remember where the current node appears on the screen, so we can try
    1085         # to get it to appear in the same place when we leave the menu
    1086         _parent_screen_rows.append(_sel_node_i - _menu_scroll)
    1087 
    1088         # Jump into menu
    1089         _cur_menu = menu
    1090         _shown = shown_sub
    1091         _sel_node_i = _menu_scroll = 0
    1092 
    1093         if isinstance(menu.item, Choice):
    1094             _select_selected_choice_sym()
     1074    if not shown_sub:
     1075        return False
     1076
     1077    # Remember where the current node appears on the screen, so we can try
     1078    # to get it to appear in the same place when we leave the menu
     1079    _parent_screen_rows.append(_sel_node_i - _menu_scroll)
     1080
     1081    # Jump into menu
     1082    _cur_menu = menu
     1083    _shown = shown_sub
     1084    _sel_node_i = _menu_scroll = 0
     1085
     1086    if isinstance(menu.item, Choice):
     1087        _select_selected_choice_sym()
     1088
     1089    return True
    10951090
    10961091
     
    12261221
    12271222        # See _select_next_menu_entry()
    1228         if _sel_node_i <= _menu_scroll + _SCROLL_OFFSET:
     1223        if _sel_node_i < _menu_scroll + _SCROLL_OFFSET:
    12291224            _menu_scroll = max(_menu_scroll - 1, 0)
    12301225
     
    14191414    _path_win.erase()
    14201415
    1421     # Draw the menu path ("(top menu) -> menu -> submenu -> ...")
     1416    # Draw the menu path ("(Top) -> Menu -> Submenu -> ...")
    14221417
    14231418    menu_prompts = []
     
    14311426                            standard_sc_expr_str(menu.item))
    14321427        menu = menu.parent
    1433     menu_prompts.append("(top menu)")
     1428    menu_prompts.append("(Top)")
    14341429    menu_prompts.reverse()
    14351430
     
    14721467
    14731468        while node:
    1474             # This code is minorly performance-sensitive. Make it too slow
    1475             # (e.g., by always recursing the entire tree), and going in and out
    1476             # of menus no longer feels instant.
    1477 
    14781469            if _visible(node) or _show_all:
    14791470                res.append(node)
     
    14841475                    res += rec(node.list)
    14851476
    1486             elif node.list and isinstance(node.item, Symbol) and \
    1487                  expr_value(node.dep):
     1477            elif node.list and isinstance(node.item, Symbol):
    14881478                # Show invisible symbols if they have visible children. This
    14891479                # can happen for an m/y-valued symbol with an optional prompt
    1490                 # ('prompt "foo" is COND') that is currently disabled. The
    1491                 # expr_value(node.dep) check safely prunes the search: A node
    1492                 # with unsatisfied direct dependencies can never have visible
    1493                 # children.
     1480                # ('prompt "foo" is COND') that is currently disabled. Note
     1481                # that it applies to both 'config' and 'menuconfig' symbols.
    14941482                shown_children = rec(node.list)
    14951483                if shown_children:
     
    15541542    # tristates are toggled, while other symbol types pop up a text entry
    15551543    # dialog.
    1556 
    1557     if not isinstance(node.item, (Symbol, Choice)):
    1558         return
     1544    #
     1545    # Returns False if the value of 'node' can't be changed.
     1546
     1547    if not _changeable(node):
     1548        return False
     1549
     1550    # sc = symbol/choice
     1551    sc = node.item
     1552
     1553    if sc.orig_type in (INT, HEX, STRING):
     1554        s = sc.str_value
     1555
     1556        while True:
     1557            s = _input_dialog(
     1558                "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.orig_type]),
     1559                s, _range_info(sc))
     1560
     1561            if s is None:
     1562                break
     1563
     1564            if sc.orig_type in (INT, HEX):
     1565                s = s.strip()
     1566
     1567                # 'make menuconfig' does this too. Hex values not starting with
     1568                # '0x' are accepted when loading .config files though.
     1569                if sc.orig_type == HEX and not s.startswith(("0x", "0X")):
     1570                    s = "0x" + s
     1571
     1572            if _check_valid(sc, s):
     1573                _set_val(sc, s)
     1574                break
     1575
     1576    elif len(sc.assignable) == 1:
     1577        # Handles choice symbols for choices in y mode, which are a special
     1578        # case: .assignable can be (2,) while .tri_value is 0.
     1579        _set_val(sc, sc.assignable[0])
     1580
     1581    else:
     1582        # Set the symbol to the value after the current value in
     1583        # sc.assignable, with wrapping
     1584        val_index = sc.assignable.index(sc.tri_value)
     1585        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
     1586
     1587
     1588    if _is_y_mode_choice_sym(sc) and not node.list:
     1589        # Immediately jump to the parent menu after making a choice selection,
     1590        # like 'make menuconfig' does, except if the menu node has children
     1591        # (which can happen if a symbol 'depends on' a choice symbol that
     1592        # immediately precedes it).
     1593        _leave_menu()
     1594
     1595
     1596    return True
     1597
     1598
     1599def _changeable(node):
     1600    # Returns True if the value if 'node' can be changed
     1601
     1602    sc = node.item
     1603
     1604    if not isinstance(sc, (Symbol, Choice)):
     1605        return False
    15591606
    15601607    # This will hit for invisible symbols, which appear in show-all mode and
     
    15621609    # symbols with optional prompts)
    15631610    if not (node.prompt and expr_value(node.prompt[1])):
    1564         return
    1565 
    1566     # sc = symbol/choice
    1567     sc = node.item
    1568 
    1569     if sc.type in (INT, HEX, STRING):
    1570         s = sc.str_value
    1571 
    1572         while True:
    1573             s = _input_dialog(
    1574                 "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.type]),
    1575                 s, _range_info(sc))
    1576 
    1577             if s is None:
    1578                 break
    1579 
    1580             if sc.type in (INT, HEX):
    1581                 s = s.strip()
    1582 
    1583                 # 'make menuconfig' does this too. Hex values not starting with
    1584                 # '0x' are accepted when loading .config files though.
    1585                 if sc.type == HEX and not s.startswith(("0x", "0X")):
    1586                     s = "0x" + s
    1587 
    1588             if _check_valid(sc, s):
    1589                 _set_val(sc, s)
    1590                 break
    1591 
    1592     elif len(sc.assignable) == 1:
    1593         # Handles choice symbols for choices in y mode, which are a special
    1594         # case: .assignable can be (2,) while .tri_value is 0.
    1595         _set_val(sc, sc.assignable[0])
    1596 
    1597     elif sc.assignable:
    1598         # Set the symbol to the value after the current value in
    1599         # sc.assignable, with wrapping
    1600         val_index = sc.assignable.index(sc.tri_value)
    1601         _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
     1611        return False
     1612
     1613    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
     1614        or _is_y_mode_choice_sym(sc)
    16021615
    16031616
     
    17031716
    17041717
    1705         c = _get_wch_compat(win)
     1718        c = _getch_compat(win)
    17061719
    17071720        if c == curses.KEY_RESIZE:
     
    18471860        filename = os.path.expanduser(filename)
    18481861
    1849         if _try_save(save_fn, filename, description):
    1850             _msg("Success", "{} saved to {}".format(description, filename))
     1862        msg = _try_save(save_fn, filename, description)
     1863        if msg:
     1864            _msg("Success", msg)
    18511865            return filename
    18521866
    18531867
    18541868def _try_save(save_fn, filename, description):
    1855     # Tries to save a configuration file. Pops up an error and returns False on
    1856     # failure.
     1869    # Tries to save a configuration file. Returns a message to print on
     1870    # success.
    18571871    #
    18581872    # save_fn:
     
    18611875    # description:
    18621876    #   String describing the thing being saved
     1877    #
     1878    # Return value:
     1879    #   A message to print on success, and None on failure
    18631880
    18641881    try:
    1865         save_fn(filename)
    1866         return True
     1882        # save_fn() returns a message to print
     1883        return save_fn(filename)
    18671884    except OSError as e:
    18681885        _error("Error saving {} to '{}'\n\n{} (errno: {})"
    18691886               .format(description, e.filename, e.strerror,
    18701887                       errno.errorcode[e.errno]))
    1871         return False
     1888        return None
    18721889
    18731890
     
    19031920
    19041921
    1905         c = _get_wch_compat(win)
     1922        c = _getch_compat(win)
    19061923
    19071924        if c == curses.KEY_RESIZE:
     
    20012018    _safe_curs_set(2)
    20022019
    2003     # TODO: Code duplication with _select_{next,prev}_menu_entry(). Can this be
    2004     # factored out in some nice way?
     2020    # Logic duplication with _select_{next,prev}_menu_entry(), except we do a
     2021    # functional variant that returns the new (sel_node_i, scroll) values to
     2022    # avoid 'nonlocal'. TODO: Can this be factored out in some nice way?
    20052023
    20062024    def select_next_match():
    2007         nonlocal sel_node_i
    2008         nonlocal scroll
    2009 
    2010         if sel_node_i < len(matches) - 1:
    2011             sel_node_i += 1
    2012 
    2013             if sel_node_i >= scroll + _height(matches_win) - _SCROLL_OFFSET \
    2014                and scroll < _max_scroll(matches, matches_win):
    2015 
    2016                 scroll += 1
     2025        if sel_node_i == len(matches) - 1:
     2026            return sel_node_i, scroll
     2027
     2028        if sel_node_i + 1 >= scroll + _height(matches_win) - _SCROLL_OFFSET \
     2029           and scroll < _max_scroll(matches, matches_win):
     2030
     2031            return sel_node_i + 1, scroll + 1
     2032
     2033        return sel_node_i + 1, scroll
    20172034
    20182035    def select_prev_match():
    2019         nonlocal sel_node_i
    2020         nonlocal scroll
    2021 
    2022         if sel_node_i > 0:
    2023             sel_node_i -= 1
    2024 
    2025             if sel_node_i <= scroll + _SCROLL_OFFSET:
    2026                 scroll = max(scroll - 1, 0)
     2036        if sel_node_i == 0:
     2037            return sel_node_i, scroll
     2038
     2039        if sel_node_i - 1 < scroll + _SCROLL_OFFSET:
     2040            return sel_node_i - 1, max(scroll - 1, 0)
     2041
     2042        return sel_node_i - 1, scroll
    20272043
    20282044    while True:
     
    21002116
    21012117
    2102         c = _get_wch_compat(edit_box)
     2118        c = _getch_compat(edit_box)
    21032119
    21042120        if c == "\n":
     
    21312147
    21322148        elif c == curses.KEY_DOWN:
    2133             select_next_match()
     2149            sel_node_i, scroll = select_next_match()
    21342150
    21352151        elif c == curses.KEY_UP:
    2136             select_prev_match()
     2152            sel_node_i, scroll = select_prev_match()
    21372153
    21382154        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
     
    21402156            # etc., for free.
    21412157            for _ in range(_PG_JUMP):
    2142                 select_next_match()
     2158                sel_node_i, scroll = select_next_match()
    21432159
    21442160        # Page Up (no Ctrl-U, as it's already used by the edit box)
    21452161        elif c == curses.KEY_PPAGE:
    21462162            for _ in range(_PG_JUMP):
    2147                 select_prev_match()
     2163                sel_node_i, scroll = select_prev_match()
    21482164
    21492165        elif c == curses.KEY_END:
     
    23652381
    23662382
    2367         c = _get_wch_compat(text_win)
     2383        c = _getch_compat(text_win)
    23682384
    23692385        if c == curses.KEY_RESIZE:
     
    26012617    for node in sc.nodes:
    26022618        if node.help is not None:
    2603             s += "Help:\n\n{}\n\n" \
    2604                  .format(textwrap.indent(node.help, "  "))
     2619            s += "Help:\n\n{}\n\n".format(_indent(node.help, 2))
    26052620
    26062621    return s
     
    26712686    s = ""
    26722687    for i, term in enumerate(split_expr(expr, split_op)):
    2673         s += "{}{} {}".format(" "*indent,
     2688        s += "{}{} {}".format(indent*" ",
    26742689                              "  " if i == 0 else op_str,
    26752690                              _expr_str(term))
     
    26902705    # value they select/imply 'sym' to (n/m/y).
    26912706
    2692     s = ""
    2693 
    2694     def add_sis(expr, val, title):
    2695         nonlocal s
    2696 
     2707    def sis(expr, val, title):
    26972708        # sis = selects/implies
    26982709        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
    2699         if sis:
    2700             s += title
    2701             for si in sis:
    2702                 s += "  - {}\n".format(split_expr(si, AND)[0].name)
    2703             s += "\n"
     2710        if not sis:
     2711            return ""
     2712
     2713        res = title
     2714        for si in sis:
     2715            res += "  - {}\n".format(split_expr(si, AND)[0].name)
     2716        return res + "\n"
     2717
     2718    s = ""
    27042719
    27052720    if sym.rev_dep is not _kconf.n:
    2706         add_sis(sym.rev_dep, 2,
    2707                 "Symbols currently y-selecting this symbol:\n")
    2708         add_sis(sym.rev_dep, 1,
    2709                 "Symbols currently m-selecting this symbol:\n")
    2710         add_sis(sym.rev_dep, 0,
    2711                 "Symbols currently n-selecting this symbol (no effect):\n")
     2721        s += sis(sym.rev_dep, 2,
     2722                 "Symbols currently y-selecting this symbol:\n")
     2723        s += sis(sym.rev_dep, 1,
     2724                 "Symbols currently m-selecting this symbol:\n")
     2725        s += sis(sym.rev_dep, 0,
     2726                 "Symbols currently n-selecting this symbol (no effect):\n")
    27122727
    27132728    if sym.weak_rev_dep is not _kconf.n:
    2714         add_sis(sym.weak_rev_dep, 2,
    2715                 "Symbols currently y-implying this symbol:\n")
    2716         add_sis(sym.weak_rev_dep, 1,
    2717                 "Symbols currently m-implying this symbol:\n")
    2718         add_sis(sym.weak_rev_dep, 0,
    2719                 "Symbols currently n-implying this symbol (no effect):\n")
     2729        s += sis(sym.weak_rev_dep, 2,
     2730                 "Symbols currently y-implying this symbol:\n")
     2731        s += sis(sym.weak_rev_dep, 1,
     2732                 "Symbols currently m-implying this symbol:\n")
     2733        s += sis(sym.weak_rev_dep, 0,
     2734                 "Symbols currently n-implying this symbol (no effect):\n")
    27202735
    27212736    return s
     
    27282743    nodes = [item] if isinstance(item, MenuNode) else item.nodes
    27292744
    2730     s = "Kconfig definition{}, with propagated dependencies\n" \
     2745    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
    27312746        .format("s" if len(nodes) > 1 else "")
    27322747    s += (len(s) - 1)*"="
     
    27412756                     _include_path_info(node),
    27422757                     _menu_path_info(node),
    2743                      textwrap.indent(node.custom_str(_name_and_val_str), "  "))
     2758                     _indent(node.custom_str(_name_and_val_str), 2))
    27442759
    27452760    return s
     
    27702785                         standard_sc_expr_str(node.item)) + path
    27712786
    2772     return "(top menu)" + path
     2787    return "(Top)" + path
     2788
     2789
     2790def _indent(s, n):
     2791    # Returns 's' with each line indented 'n' spaces. textwrap.indent() is not
     2792    # available in Python 2 (it's 3.3+).
     2793
     2794    return "\n".join(n*" " + line for line in s.split("\n"))
    27732795
    27742796
     
    29532975            # .config), but skip it for choice symbols in choices in y mode,
    29542976            # and for symbols of UNKNOWN type (which generate a warning though)
    2955             if sym.user_value is None and \
    2956                sym.type != UNKNOWN and \
     2977            if sym.user_value is None and sym.orig_type and \
    29572978               not (sym.choice and sym.choice.tri_value == 2):
    29582979
     
    30063027
    30073028    # Wouldn't normally happen, and generates a warning
    3008     if item.type == UNKNOWN:
     3029    if not item.orig_type:
    30093030        return ""
    30103031
    3011     if item.type in (STRING, INT, HEX):
     3032    if item.orig_type in (STRING, INT, HEX):
    30123033        return "({})".format(item.str_value)
    30133034
     
    30433064    # Otherwise, displays an error and returns False.
    30443065
    3045     if sym.type not in (INT, HEX):
     3066    if sym.orig_type not in (INT, HEX):
    30463067        # Anything goes for non-int/hex symbols
    30473068        return True
    30483069
    3049     base = 10 if sym.type == INT else 16
     3070    base = 10 if sym.orig_type == INT else 16
    30503071    try:
    30513072        int(s, base)
    30523073    except ValueError:
    30533074        _error("'{}' is a malformed {} value"
    3054                .format(s, TYPE_TO_STR[sym.type]))
     3075               .format(s, TYPE_TO_STR[sym.orig_type]))
    30553076        return False
    30563077
    30573078    for low_sym, high_sym, cond in sym.ranges:
    30583079        if expr_value(cond):
    3059             low = int(low_sym.str_value, base)
    3060             val = int(s, base)
    3061             high = int(high_sym.str_value, base)
    3062 
    3063             if not low <= val <= high:
     3080            low_s = low_sym.str_value
     3081            high_s = high_sym.str_value
     3082
     3083            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
    30643084                _error("{} is outside the range {}-{}"
    3065                        .format(s, low_sym.str_value, high_sym.str_value))
    3066 
     3085                       .format(s, low_s, high_s))
    30673086                return False
    30683087
     
    30763095    # 'sym', or None if 'sym' doesn't have a range
    30773096
    3078     if sym.type in (INT, HEX):
     3097    if sym.orig_type in (INT, HEX):
    30793098        for low, high, cond in sym.ranges:
    30803099            if expr_value(cond):
     
    31033122
    31043123
    3105 def _get_wch_compat(win):
     3124def _getch_compat(win):
     3125    # Uses get_wch() if available (Python 3.3+) and getch() otherwise. Also
     3126    # handles a PDCurses resizing quirk.
     3127
     3128    if hasattr(win, "get_wch"):
     3129        c = win.get_wch()
     3130    else:
     3131        c = win.getch()
     3132        if 0 <= c <= 255:
     3133            c = chr(c)
     3134
    31063135    # Decent resizing behavior on PDCurses requires calling resize_term(0, 0)
    31073136    # after receiving KEY_RESIZE, while ncurses (usually) handles terminal
     
    31123141    # hack gives ncurses/PDCurses compatibility for resizing. I don't know
    31133142    # whether it would cause trouble for other implementations.
    3114 
    3115     c = win.get_wch()
    31163143    if c == curses.KEY_RESIZE:
    31173144        try:
Note: See TracChangeset for help on using the changeset viewer.