Changeset cf2f109 for menu/menuconfig.py
- Timestamp:
- 06/15/2019 05:25:10 PM (5 years ago)
- Branches:
- ablfs-more, legacy, trunk
- Children:
- a4af6811
- Parents:
- 619b313
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
menu/menuconfig.py
r619b313 rcf2f109 1 #!/usr/bin/env python 31 #!/usr/bin/env python 2 2 3 3 # Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson … … 8 8 ======== 9 9 10 A curses-based menuconfig implementation. The interface should feel familiar to11 people used to mconf ('make menuconfig').10 A curses-based Python 2/3 menuconfig implementation. The interface should feel 11 familiar to people used to mconf ('make menuconfig'). 12 12 13 13 Supports the same keys as mconf, and also supports a set of keybindings … … 21 21 g/Home : Jump to beginning of list 22 22 23 [Space] toggles values if possible, and enters menus otherwise. [Enter] works 24 the other way around. 25 23 26 The mconf feature where pressing a key jumps to a menu entry with that 24 27 character in it in the current menu isn't supported. A jump-to feature for … … 26 29 comment (as in a Kconfig 'comment "Foo"') is available instead. 27 30 28 Space and Enter are "smart" and try to do what you'd expect for the given menu29 entry.30 31 31 A few different modes are available: 32 32 … … 55 55 The KCONFIG_CONFIG environment variable specifies the .config file to load (if 56 56 it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used. 57 58 When overwriting a configuration file, the old version is saved to 59 <filename>.old (e.g. .config.old). 57 60 58 61 $srctree is supported through Kconfiglib. … … 97 100 - fg:COLOR Set the foreground/background colors. COLOR can be one of 98 101 * 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, 100 103 brightred). On terminals that support more than 8 colors, 101 104 you can also directly put in a color number, e.g. fg:123 … … 172 175 =========== 173 176 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. 177 Doesn't work out of the box on Windows, but can be made to work with 'pip 178 install windows-curses'. See the 179 https://github.com/zephyrproject-rtos/windows-curses repository. 180 181 'pip install kconfiglib' on Windows automatically installs windows-curses 182 to make the menuconfig usable. 186 183 """ 184 from __future__ import print_function 185 187 186 import curses 188 187 import errno … … 194 193 195 194 from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \ 196 BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN,\195 BOOL, TRISTATE, STRING, INT, HEX, \ 197 196 AND, OR, \ 198 197 expr_str, expr_value, split_expr, \ … … 245 244 # Lines of help text shown at the bottom of the information dialog 246 245 _INFO_HELP_LINES = """ 247 [ESC/q] Return to menu 246 [ESC/q] Return to menu [/] Jump to symbol 248 247 """[1:-1].split("\n") 249 248 … … 629 628 630 629 631 # Used as the entry point in setup.py632 630 def _main(): 633 631 menuconfig(standard_kconfig()) … … 649 647 _kconf = kconf 650 648 649 # Filename to save configuration to 650 _conf_filename = standard_config_filename() 651 651 652 # Load existing configuration and set _conf_changed True if it is outdated 652 653 _conf_changed = _load_config() 653 654 # Filename to save configuration to655 _conf_filename = standard_config_filename()656 654 657 655 # Filename to save minimal configuration to … … 672 670 # Disable warnings. They get mangled in curses mode, and we deal with 673 671 # errors ourselves. 674 kconf. disable_warnings()672 kconf.warn = False 675 673 676 674 # Make curses use the locale settings specified in the environment … … 709 707 # saving the configuration in that case. 710 708 711 if not _kconf.load_config(): 709 print(_kconf.load_config()) 710 if not os.path.exists(_conf_filename): 712 711 # No .config 713 712 return True … … 729 728 # Unwritten symbol 730 729 return True 731 elif sym. type in (BOOL, TRISTATE):730 elif sym.orig_type in (BOOL, TRISTATE): 732 731 if sym.tri_value != sym.user_value: 733 732 # Written bool/tristate symbol, new value … … 770 769 # 771 770 # _conf_filename: 772 # .config file to save the configuration to771 # File to save the configuration to 773 772 # 774 773 # _minconf_filename: … … 802 801 803 802 804 c = _get _wch_compat(_menu_win)803 c = _getch_compat(_menu_win) 805 804 806 805 if c == curses.KEY_RESIZE: … … 829 828 _select_first_menu_entry() 830 829 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 835 832 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): 840 834 _enter_menu(sel_node) 841 835 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): 843 840 _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 choice846 # selection, like 'make menuconfig' does, except if the847 # menu node has children (which can happen if a symbol848 # 'depends on' a choice symbol that immediately precedes849 # it).850 _leave_menu()851 841 852 842 elif c in ("n", "N"): … … 930 920 931 921 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 934 926 935 927 elif c == "n": … … 962 954 # backspace work with TERM=vt100. That makes it likely to work in sane 963 955 # 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") 968 962 969 963 _init_styles() … … 1060 1054 1061 1055 1062 def _prefer_toggle(item):1063 # For nodes with menus, determines whether Space should change the value of1064 # the node's item or enter its menu. We toggle symbols (which have menus1065 # when they're defined with 'menuconfig') and choices that can be in more1066 # 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 1072 1056 def _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. 1075 1062 1076 1063 global _cur_menu … … 1079 1066 global _menu_scroll 1080 1067 1068 if not menu.is_menuconfig: 1069 # Not a menu 1070 return False 1071 1081 1072 shown_sub = _shown_nodes(menu) 1082 1073 # 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 1095 1090 1096 1091 … … 1226 1221 1227 1222 # See _select_next_menu_entry() 1228 if _sel_node_i < =_menu_scroll + _SCROLL_OFFSET:1223 if _sel_node_i < _menu_scroll + _SCROLL_OFFSET: 1229 1224 _menu_scroll = max(_menu_scroll - 1, 0) 1230 1225 … … 1419 1414 _path_win.erase() 1420 1415 1421 # Draw the menu path ("( top menu) -> menu -> submenu -> ...")1416 # Draw the menu path ("(Top) -> Menu -> Submenu -> ...") 1422 1417 1423 1418 menu_prompts = [] … … 1431 1426 standard_sc_expr_str(menu.item)) 1432 1427 menu = menu.parent 1433 menu_prompts.append("( top menu)")1428 menu_prompts.append("(Top)") 1434 1429 menu_prompts.reverse() 1435 1430 … … 1472 1467 1473 1468 while node: 1474 # This code is minorly performance-sensitive. Make it too slow1475 # (e.g., by always recursing the entire tree), and going in and out1476 # of menus no longer feels instant.1477 1478 1469 if _visible(node) or _show_all: 1479 1470 res.append(node) … … 1484 1475 res += rec(node.list) 1485 1476 1486 elif node.list and isinstance(node.item, Symbol) and \ 1487 expr_value(node.dep): 1477 elif node.list and isinstance(node.item, Symbol): 1488 1478 # Show invisible symbols if they have visible children. This 1489 1479 # 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. 1494 1482 shown_children = rec(node.list) 1495 1483 if shown_children: … … 1554 1542 # tristates are toggled, while other symbol types pop up a text entry 1555 1543 # 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 1599 def _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 1559 1606 1560 1607 # This will hit for invisible symbols, which appear in show-all mode and … … 1562 1609 # symbols with optional prompts) 1563 1610 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) 1602 1615 1603 1616 … … 1703 1716 1704 1717 1705 c = _get _wch_compat(win)1718 c = _getch_compat(win) 1706 1719 1707 1720 if c == curses.KEY_RESIZE: … … 1847 1860 filename = os.path.expanduser(filename) 1848 1861 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) 1851 1865 return filename 1852 1866 1853 1867 1854 1868 def _try_save(save_fn, filename, description): 1855 # Tries to save a configuration file. Pops up an error and returns Falseon1856 # failure.1869 # Tries to save a configuration file. Returns a message to print on 1870 # success. 1857 1871 # 1858 1872 # save_fn: … … 1861 1875 # description: 1862 1876 # String describing the thing being saved 1877 # 1878 # Return value: 1879 # A message to print on success, and None on failure 1863 1880 1864 1881 try: 1865 save_fn(filename)1866 return True1882 # save_fn() returns a message to print 1883 return save_fn(filename) 1867 1884 except OSError as e: 1868 1885 _error("Error saving {} to '{}'\n\n{} (errno: {})" 1869 1886 .format(description, e.filename, e.strerror, 1870 1887 errno.errorcode[e.errno])) 1871 return False1888 return None 1872 1889 1873 1890 … … 1903 1920 1904 1921 1905 c = _get _wch_compat(win)1922 c = _getch_compat(win) 1906 1923 1907 1924 if c == curses.KEY_RESIZE: … … 2001 2018 _safe_curs_set(2) 2002 2019 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? 2005 2023 2006 2024 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 2017 2034 2018 2035 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 2027 2043 2028 2044 while True: … … 2100 2116 2101 2117 2102 c = _get _wch_compat(edit_box)2118 c = _getch_compat(edit_box) 2103 2119 2104 2120 if c == "\n": … … 2131 2147 2132 2148 elif c == curses.KEY_DOWN: 2133 sel ect_next_match()2149 sel_node_i, scroll = select_next_match() 2134 2150 2135 2151 elif c == curses.KEY_UP: 2136 sel ect_prev_match()2152 sel_node_i, scroll = select_prev_match() 2137 2153 2138 2154 elif c in (curses.KEY_NPAGE, "\x04"): # Page Down/Ctrl-D … … 2140 2156 # etc., for free. 2141 2157 for _ in range(_PG_JUMP): 2142 sel ect_next_match()2158 sel_node_i, scroll = select_next_match() 2143 2159 2144 2160 # Page Up (no Ctrl-U, as it's already used by the edit box) 2145 2161 elif c == curses.KEY_PPAGE: 2146 2162 for _ in range(_PG_JUMP): 2147 sel ect_prev_match()2163 sel_node_i, scroll = select_prev_match() 2148 2164 2149 2165 elif c == curses.KEY_END: … … 2365 2381 2366 2382 2367 c = _get _wch_compat(text_win)2383 c = _getch_compat(text_win) 2368 2384 2369 2385 if c == curses.KEY_RESIZE: … … 2601 2617 for node in sc.nodes: 2602 2618 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)) 2605 2620 2606 2621 return s … … 2671 2686 s = "" 2672 2687 for i, term in enumerate(split_expr(expr, split_op)): 2673 s += "{}{} {}".format( " "*indent,2688 s += "{}{} {}".format(indent*" ", 2674 2689 " " if i == 0 else op_str, 2675 2690 _expr_str(term)) … … 2690 2705 # value they select/imply 'sym' to (n/m/y). 2691 2706 2692 s = "" 2693 2694 def add_sis(expr, val, title): 2695 nonlocal s 2696 2707 def sis(expr, val, title): 2697 2708 # sis = selects/implies 2698 2709 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 = "" 2704 2719 2705 2720 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") 2712 2727 2713 2728 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") 2720 2735 2721 2736 return s … … 2728 2743 nodes = [item] if isinstance(item, MenuNode) else item.nodes 2729 2744 2730 s = "Kconfig definition{}, with p ropagated dependencies\n" \2745 s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \ 2731 2746 .format("s" if len(nodes) > 1 else "") 2732 2747 s += (len(s) - 1)*"=" … … 2741 2756 _include_path_info(node), 2742 2757 _menu_path_info(node), 2743 textwrap.indent(node.custom_str(_name_and_val_str), " "))2758 _indent(node.custom_str(_name_and_val_str), 2)) 2744 2759 2745 2760 return s … … 2770 2785 standard_sc_expr_str(node.item)) + path 2771 2786 2772 return "(top menu)" + path 2787 return "(Top)" + path 2788 2789 2790 def _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")) 2773 2795 2774 2796 … … 2953 2975 # .config), but skip it for choice symbols in choices in y mode, 2954 2976 # 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 \ 2957 2978 not (sym.choice and sym.choice.tri_value == 2): 2958 2979 … … 3006 3027 3007 3028 # Wouldn't normally happen, and generates a warning 3008 if item.type == UNKNOWN:3029 if not item.orig_type: 3009 3030 return "" 3010 3031 3011 if item. type in (STRING, INT, HEX):3032 if item.orig_type in (STRING, INT, HEX): 3012 3033 return "({})".format(item.str_value) 3013 3034 … … 3043 3064 # Otherwise, displays an error and returns False. 3044 3065 3045 if sym. type not in (INT, HEX):3066 if sym.orig_type not in (INT, HEX): 3046 3067 # Anything goes for non-int/hex symbols 3047 3068 return True 3048 3069 3049 base = 10 if sym. type == INT else 163070 base = 10 if sym.orig_type == INT else 16 3050 3071 try: 3051 3072 int(s, base) 3052 3073 except ValueError: 3053 3074 _error("'{}' is a malformed {} value" 3054 .format(s, TYPE_TO_STR[sym. type]))3075 .format(s, TYPE_TO_STR[sym.orig_type])) 3055 3076 return False 3056 3077 3057 3078 for low_sym, high_sym, cond in sym.ranges: 3058 3079 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): 3064 3084 _error("{} is outside the range {}-{}" 3065 .format(s, low_sym.str_value, high_sym.str_value)) 3066 3085 .format(s, low_s, high_s)) 3067 3086 return False 3068 3087 … … 3076 3095 # 'sym', or None if 'sym' doesn't have a range 3077 3096 3078 if sym. type in (INT, HEX):3097 if sym.orig_type in (INT, HEX): 3079 3098 for low, high, cond in sym.ranges: 3080 3099 if expr_value(cond): … … 3103 3122 3104 3123 3105 def _get_wch_compat(win): 3124 def _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 3106 3135 # Decent resizing behavior on PDCurses requires calling resize_term(0, 0) 3107 3136 # after receiving KEY_RESIZE, while ncurses (usually) handles terminal … … 3112 3141 # hack gives ncurses/PDCurses compatibility for resizing. I don't know 3113 3142 # whether it would cause trouble for other implementations. 3114 3115 c = win.get_wch()3116 3143 if c == curses.KEY_RESIZE: 3117 3144 try:
Note:
See TracChangeset
for help on using the changeset viewer.