summaryrefslogtreecommitdiff
path: root/kconfiglib.py
diff options
context:
space:
mode:
authorUlf Magnusson <ulfalizer@gmail.com>2018-09-03 10:51:04 +0200
committerUlf Magnusson <ulfalizer@gmail.com>2018-09-03 12:32:20 +0200
commitef5358e8b8174716470b00691fba9946b7d307ce (patch)
tree08fdd290578728b624b5ba6e344f36a263d6873b /kconfiglib.py
parent66832138bfb0e82190b55fb410104d969cbc829b (diff)
Test symbolic constants with 'is (not)'
Saves a few % of total parsing time, and probably some evaluation time too. Microbenchmarking on Python 3 shows that 'is' is about 30% faster than '==' for comparing integers on my machine. This is safe even without assuming Python's small-integer optimization (which caches objects for small integers, guaranteeing that small integer literals always refer to the same object): We always refer to symbolic constants using their symbolic names (_T_*, etc.), so they're guaranteed to always refer to the same integer objects anyway. This optimization is safe for client code too, unless you get some unlikely situation where (1) there is no small-integer optimization, (2) expressions get pickled and unpickled or the like, which would be unsafe across Kconfiglib versions anyway, and (3) the client code tests symbolic constants with 'is' instead of '==' (which isn't advertised as safe, and a bad idea in general when pickling is involved). That's probably too obscure to worry about.
Diffstat (limited to 'kconfiglib.py')
-rw-r--r--kconfiglib.py191
1 files changed, 103 insertions, 88 deletions
diff --git a/kconfiglib.py b/kconfiglib.py
index b7b5c24..db61650 100644
--- a/kconfiglib.py
+++ b/kconfiglib.py
@@ -971,9 +971,9 @@ class Kconfig(object):
if sym.orig_type in (BOOL, TRISTATE):
# The C implementation only checks the first character
# to the right of '=', for whatever reason
- if not ((sym.orig_type == BOOL and
+ if not ((sym.orig_type is BOOL and
val.startswith(("n", "y"))) or \
- (sym.orig_type == TRISTATE and
+ (sym.orig_type is TRISTATE and
val.startswith(("n", "m", "y")))):
self._warn("'{}' is not a valid value for the {} "
"symbol {}. Assignment ignored."
@@ -1000,7 +1000,7 @@ class Kconfig(object):
# Set the choice's mode
sym.choice.set_value(val)
- elif sym.orig_type == STRING:
+ elif sym.orig_type is STRING:
match = _conf_string_match(val)
if not match:
self._warn("malformed string literal in "
@@ -1102,13 +1102,13 @@ class Kconfig(object):
.format(self.config_prefix, sym.name,
"_MODULE" if val == "m" else ""))
- elif sym.orig_type == STRING:
+ elif sym.orig_type is STRING:
f.write('#define {}{} "{}"\n'
.format(self.config_prefix, sym.name,
escape(val)))
elif sym.orig_type in (INT, HEX):
- if sym.orig_type == HEX and \
+ if sym.orig_type is HEX and \
not val.startswith(("0x", "0X")):
val = "0x" + val
@@ -1152,8 +1152,8 @@ class Kconfig(object):
f.write(item.config_string)
elif expr_value(node.dep) and \
- ((item == MENU and expr_value(node.visibility)) or
- item == COMMENT):
+ ((item is MENU and expr_value(node.visibility)) or
+ item is COMMENT):
f.write("\n#\n# {}\n#\n".format(node.prompt[0]))
@@ -1200,7 +1200,7 @@ class Kconfig(object):
if sym.choice and \
not sym.choice.is_optional and \
sym.choice._get_selection_from_defaults() is sym and \
- sym.orig_type == BOOL and \
+ sym.orig_type is BOOL and \
sym.tri_value == 2:
continue
@@ -1366,7 +1366,7 @@ class Kconfig(object):
if name in self.syms:
sym = self.syms[name]
- if sym.orig_type == STRING:
+ if sym.orig_type is STRING:
match = _conf_string_match(val)
if not match:
continue
@@ -1833,7 +1833,7 @@ class Kconfig(object):
# refer to a constant symbol named "FOO".
token = val \
if token in _STRING_LEX or \
- tokens[0] == _T_OPTION else \
+ tokens[0] is _T_OPTION else \
self._lookup_const_sym(val)
elif s.startswith("&&", i):
@@ -1997,7 +1997,7 @@ class Kconfig(object):
def _check_token(self, token):
# If the next token is 'token', removes it and returns True
- if self._tokens[self._tokens_i + 1] == token:
+ if self._tokens[self._tokens_i + 1] is token:
self._tokens_i += 1
return True
return False
@@ -2294,7 +2294,7 @@ class Kconfig(object):
node = MenuNode()
node.kconfig = self
node.item = sym
- node.is_menuconfig = (t0 == _T_MENUCONFIG)
+ node.is_menuconfig = (t0 is _T_MENUCONFIG)
node.prompt = node.help = node.list = None
node.parent = parent
node.filename = self._filename
@@ -2353,13 +2353,13 @@ class Kconfig(object):
self._leave_file()
- elif t0 == end_token:
+ elif t0 is end_token:
# We have reached the end of the block. Terminate the final
# node and return it.
prev.next = None
return prev
- elif t0 == _T_IF:
+ elif t0 is _T_IF:
node = MenuNode()
node.item = node.prompt = None
node.parent = parent
@@ -2373,7 +2373,7 @@ class Kconfig(object):
prev.next = prev = node
- elif t0 == _T_MENU:
+ elif t0 is _T_MENU:
node = MenuNode()
node.kconfig = self
node.item = MENU
@@ -2393,7 +2393,7 @@ class Kconfig(object):
prev.next = prev = node
- elif t0 == _T_COMMENT:
+ elif t0 is _T_COMMENT:
node = MenuNode()
node.kconfig = self
node.item = COMMENT
@@ -2411,7 +2411,7 @@ class Kconfig(object):
prev.next = prev = node
- elif t0 == _T_CHOICE:
+ elif t0 is _T_CHOICE:
if self._peek_token() is None:
choice = Choice()
choice.direct_dep = self.n
@@ -2449,7 +2449,7 @@ class Kconfig(object):
prev.next = prev = node
- elif t0 == _T_MAINMENU:
+ elif t0 is _T_MAINMENU:
self.top_node.prompt = (self._expect_str_and_eol(), self.y)
self.top_node.filename = self._filename
self.top_node.linenr = self._linenr
@@ -2502,31 +2502,31 @@ class Kconfig(object):
if self._peek_token() is not None:
self._parse_prompt(node)
- elif t0 == _T_DEPENDS:
+ elif t0 is _T_DEPENDS:
if not self._check_token(_T_ON):
self._parse_error('expected "on" after "depends"')
node.dep = self._make_and(node.dep,
self._expect_expr_and_eol())
- elif t0 == _T_HELP:
+ elif t0 is _T_HELP:
self._parse_help(node)
- elif t0 == _T_SELECT:
+ elif t0 is _T_SELECT:
if not isinstance(node.item, Symbol):
self._parse_error("only symbols can select")
node.selects.append((self._expect_nonconst_sym(),
self._parse_cond()))
- elif t0 == _T_IMPLY:
+ elif t0 is _T_IMPLY:
if not isinstance(node.item, Symbol):
self._parse_error("only symbols can imply")
node.implies.append((self._expect_nonconst_sym(),
self._parse_cond()))
- elif t0 == _T_DEFAULT:
+ elif t0 is _T_DEFAULT:
node.defaults.append((self._parse_expr(False),
self._parse_cond()))
@@ -2536,15 +2536,15 @@ class Kconfig(object):
node.defaults.append((self._parse_expr(False),
self._parse_cond()))
- elif t0 == _T_PROMPT:
+ elif t0 is _T_PROMPT:
self._parse_prompt(node)
- elif t0 == _T_RANGE:
+ elif t0 is _T_RANGE:
node.ranges.append((self._expect_sym(),
self._expect_sym(),
self._parse_cond()))
- elif t0 == _T_OPTION:
+ elif t0 is _T_OPTION:
if self._check_token(_T_ENV):
if not self._check_token(_T_EQUAL):
self._parse_error('expected "=" after "env"')
@@ -2609,14 +2609,14 @@ class Kconfig(object):
else:
self._parse_error("unrecognized option")
- elif t0 == _T_VISIBLE:
+ elif t0 is _T_VISIBLE:
if not self._check_token(_T_IF):
self._parse_error('expected "if" after "visible"')
node.visibility = self._make_and(node.visibility,
self._expect_expr_and_eol())
- elif t0 == _T_OPTIONAL:
+ elif t0 is _T_OPTIONAL:
if not isinstance(node.item, Choice):
self._parse_error('"optional" is only valid for choices')
@@ -2791,11 +2791,11 @@ class Kconfig(object):
# EQUAL, UNEQUAL, etc., so we can just use the token directly
return (self._next_token(), token, self._expect_sym())
- if token == _T_NOT:
+ if token is _T_NOT:
# token == _T_NOT == NOT
return (token, self._parse_factor(transform_m))
- if token == _T_OPEN_PAREN:
+ if token is _T_OPEN_PAREN:
expr_parse = self._parse_expr(transform_m)
if self._check_token(_T_CLOSE_PAREN):
return expr_parse
@@ -2917,7 +2917,7 @@ class Kconfig(object):
# The menu node is a choice, menu, or if. Finalize each child in
# it.
- if node.item == MENU:
+ if node.item is MENU:
visible_if = self._make_and(visible_if, node.visibility)
# Propagate the menu node's dependencies to each child menu node.
@@ -3415,7 +3415,7 @@ class Symbol(object):
"""
See the class documentation.
"""
- if self.orig_type == TRISTATE and \
+ if self.orig_type is TRISTATE and \
((self.choice and self.choice.tri_value == 2) or
not self.kconfig.modules.tri_value):
return BOOL
@@ -3438,7 +3438,7 @@ class Symbol(object):
# As a quirk of Kconfig, undefined symbols get their name as their
# string value. This is why things like "FOO = bar" work for seeing if
# FOO has the value "bar".
- if self.orig_type == UNKNOWN:
+ if self.orig_type is UNKNOWN:
self._cached_str_val = self.name
return self.name
@@ -3528,7 +3528,7 @@ class Symbol(object):
# The value is rewritten to a standard form if it is
# clamped
val = str(clamp) \
- if self.orig_type == INT else \
+ if self.orig_type is INT else \
hex(clamp)
if has_default:
@@ -3540,7 +3540,7 @@ class Symbol(object):
num2str(clamp), num2str(low),
num2str(high)))
- elif self.orig_type == STRING:
+ elif self.orig_type is STRING:
if vis and self.user_value is not None:
# If the symbol is visible and has a user value, use that
val = self.user_value
@@ -3572,7 +3572,7 @@ class Symbol(object):
return self._cached_tri_val
if self.orig_type not in (BOOL, TRISTATE):
- if self.orig_type != UNKNOWN:
+ if self.orig_type is not UNKNOWN:
# Would take some work to give the location here
self.kconfig._warn(
"The {} symbol {} is being evaluated in a logical context "
@@ -3627,7 +3627,7 @@ class Symbol(object):
# m is promoted to y for (1) bool symbols and (2) symbols with a
# weak_rev_dep (from imply) of y
if val == 1 and \
- (self.type == BOOL or expr_value(self.weak_rev_dep) == 2):
+ (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
val = 2
elif vis == 2:
@@ -3685,7 +3685,7 @@ class Symbol(object):
return "{}{}={}\n" \
.format(self.kconfig.config_prefix, self.name, val)
- if self.orig_type == STRING:
+ if self.orig_type is STRING:
return '{}{}="{}"\n' \
.format(self.kconfig.config_prefix, self.name, escape(val))
@@ -3740,12 +3740,12 @@ class Symbol(object):
return True
# Check if the value is valid for our type
- if not (self.orig_type == BOOL and value in (0, 2, "n", "y") or
- self.orig_type == TRISTATE and value in (0, 1, 2, "n", "m", "y") or
+ if not (self.orig_type is BOOL and value in (0, 2, "n", "y") or
+ self.orig_type is TRISTATE and value in (0, 1, 2, "n", "m", "y") or
(isinstance(value, str) and
- (self.orig_type == STRING or
- self.orig_type == INT and _is_base_n(value, 10) or
- self.orig_type == HEX and _is_base_n(value, 16)
+ (self.orig_type is STRING or
+ self.orig_type is INT and _is_base_n(value, 10) or
+ self.orig_type is HEX and _is_base_n(value, 16)
and int(value, 16) >= 0))):
# Display tristate values as n, m, y in the warning
@@ -3947,7 +3947,7 @@ class Symbol(object):
return (2,)
if not rev_dep_val:
- if self.type == BOOL or expr_value(self.weak_rev_dep) == 2:
+ if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
return (0, 2)
return (0, 1, 2)
@@ -3956,7 +3956,7 @@ class Symbol(object):
# rev_dep_val == 1
- if self.type == BOOL or expr_value(self.weak_rev_dep) == 2:
+ if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
return (2,)
return (1, 2)
@@ -4056,7 +4056,7 @@ class Symbol(object):
# Transpose mod to yes if type is bool (possibly due to modules
# being disabled)
- if val == 1 and self.type == BOOL:
+ if val == 1 and self.type is BOOL:
val = 2
return TRI_TO_STR[val]
@@ -4277,7 +4277,7 @@ class Choice(object):
"""
Returns the type of the choice. See Symbol.type.
"""
- if self.orig_type == TRISTATE and not self.kconfig.modules.tri_value:
+ if self.orig_type is TRISTATE and not self.kconfig.modules.tri_value:
return BOOL
return self.orig_type
@@ -4307,7 +4307,7 @@ class Choice(object):
val = min(val, self.visibility)
# Promote m to y for boolean choices
- return 2 if val == 1 and self.type == BOOL else val
+ return 2 if val == 1 and self.type is BOOL else val
@property
def assignable(self):
@@ -4358,8 +4358,8 @@ class Choice(object):
self._was_set = True
return True
- if not ((self.orig_type == BOOL and value in (0, 2, "n", "y") ) or
- (self.orig_type == TRISTATE and value in (0, 1, 2, "n", "m", "y"))):
+ if not ((self.orig_type is BOOL and value in (0, 2, "n", "y") ) or
+ (self.orig_type is TRISTATE and value in (0, 1, 2, "n", "m", "y"))):
# Display tristate values as n, m, y in the warning
self.kconfig._warn(
@@ -4508,8 +4508,8 @@ class Choice(object):
if vis == 2:
if not self.is_optional:
- return (2,) if self.type == BOOL else (1, 2)
- return (0, 2) if self.type == BOOL else (0, 1, 2)
+ return (2,) if self.type is BOOL else (1, 2)
+ return (0, 2) if self.type is BOOL else (0, 1, 2)
# vis == 1
@@ -4726,7 +4726,7 @@ class MenuNode(object):
if self.prompt:
res |= expr_items(self.prompt[1])
- if self.item == MENU:
+ if self.item is MENU:
res |= expr_items(self.visibility)
for value, cond in self.defaults:
@@ -4764,10 +4764,10 @@ class MenuNode(object):
s += " " + self.item.name
fields.append(s)
- elif self.item == MENU:
+ elif self.item is MENU:
fields.append("menu node for menu")
- elif self.item == COMMENT:
+ elif self.item is COMMENT:
fields.append("menu node for comment")
elif self.item is None:
@@ -4787,7 +4787,7 @@ class MenuNode(object):
fields.append("deps " + TRI_TO_STR[expr_value(self.dep)])
- if self.item == MENU:
+ if self.item is MENU:
fields.append("'visible if' deps " + \
TRI_TO_STR[expr_value(self.visibility)])
@@ -4830,13 +4830,13 @@ class MenuNode(object):
self._sym_choice_node_str(sc_expr_str_fn)
def _menu_comment_node_str(self, sc_expr_str_fn):
- s = '{} "{}"\n'.format("menu" if self.item == MENU else "comment",
+ s = '{} "{}"\n'.format("menu" if self.item is MENU else "comment",
self.prompt[0])
if self.dep is not self.kconfig.y:
s += "\tdepends on {}\n".format(expr_str(self.dep, sc_expr_str_fn))
- if self.item == MENU and self.visibility is not self.kconfig.y:
+ if self.item is MENU and self.visibility is not self.kconfig.y:
s += "\tvisible if {}\n".format(expr_str(self.visibility,
sc_expr_str_fn))
@@ -4862,7 +4862,7 @@ class MenuNode(object):
else:
lines.append("choice " + sc.name if sc.name else "choice")
- if sc.orig_type != UNKNOWN:
+ if sc.orig_type is not UNKNOWN:
indent_add(TYPE_TO_STR[sc.orig_type])
if self.prompt:
@@ -4979,18 +4979,18 @@ def expr_value(expr):
if not isinstance(expr, tuple):
return expr.tri_value
- if expr[0] == AND:
+ if expr[0] is AND:
v1 = expr_value(expr[1])
# Short-circuit the n case as an optimization (~5% faster
# allnoconfig.py and allyesconfig.py, as of writing)
return 0 if not v1 else min(v1, expr_value(expr[2]))
- if expr[0] == OR:
+ if expr[0] is OR:
v1 = expr_value(expr[1])
# Short-circuit the y case as an optimization
return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
- if expr[0] == NOT:
+ if expr[0] is NOT:
return 2 - expr_value(expr[1])
if expr[0] in _RELATIONS:
@@ -5001,7 +5001,7 @@ def expr_value(expr):
oper, op1, op2 = expr
# If both operands are strings...
- if op1.orig_type == STRING and op2.orig_type == STRING:
+ if op1.orig_type is STRING and op2.orig_type is STRING:
# ...then compare them lexicographically
comp = _strcmp(op1.str_value, op2.str_value)
else:
@@ -5013,12 +5013,12 @@ def expr_value(expr):
# parse as numbers
comp = _strcmp(op1.str_value, op2.str_value)
- if oper == EQUAL: res = comp == 0
- elif oper == UNEQUAL: res = comp != 0
- elif oper == LESS: res = comp < 0
- elif oper == LESS_EQUAL: res = comp <= 0
- elif oper == GREATER: res = comp > 0
- elif oper == GREATER_EQUAL: res = comp >= 0
+ if oper is EQUAL: res = comp == 0
+ elif oper is UNEQUAL: res = comp != 0
+ elif oper is LESS: res = comp < 0
+ elif oper is LESS_EQUAL: res = comp <= 0
+ elif oper is GREATER: res = comp > 0
+ elif oper is GREATER_EQUAL: res = comp >= 0
return 2*res
@@ -5059,17 +5059,17 @@ def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
if not isinstance(expr, tuple):
return sc_expr_str_fn(expr)
- if expr[0] == AND:
+ if expr[0] is AND:
return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
_parenthesize(expr[2], OR, sc_expr_str_fn))
- if expr[0] == OR:
+ if expr[0] is OR:
# This turns A && B || C && D into "(A && B) || (C && D)", which is
# redundant, but more readable
return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
_parenthesize(expr[2], AND, sc_expr_str_fn))
- if expr[0] == NOT:
+ if expr[0] is NOT:
if isinstance(expr[1], tuple):
return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
return "!" + sc_expr_str_fn(expr[1]) # Symbol
@@ -5096,7 +5096,7 @@ def expr_items(expr):
rec(subexpr[1])
# NOTs only have a single operand
- if subexpr[0] != NOT:
+ if subexpr[0] is not NOT:
rec(subexpr[2])
else:
@@ -5141,7 +5141,7 @@ def split_expr(expr, op):
res = []
def rec(subexpr):
- if isinstance(subexpr, tuple) and subexpr[0] == op:
+ if isinstance(subexpr, tuple) and subexpr[0] is op:
rec(subexpr[1])
rec(subexpr[2])
else:
@@ -5207,18 +5207,18 @@ def _visibility(sc):
vis = max(vis, expr_value(node.prompt[1]))
if isinstance(sc, Symbol) and sc.choice:
- if sc.choice.orig_type == TRISTATE and sc.orig_type != TRISTATE and \
- sc.choice.tri_value != 2:
+ if sc.choice.orig_type is TRISTATE and \
+ sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
# Non-tristate choice symbols are only visible in y mode
return 0
- if sc.orig_type == TRISTATE and vis == 1 and sc.choice.tri_value == 2:
+ if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value == 2:
# Choice symbols with m visibility are not visible in y mode
return 0
# Promote m to y if we're dealing with a non-tristate (possibly due to
# modules being disabled)
- if vis == 1 and sc.type != TRISTATE:
+ if vis == 1 and sc.type is not TRISTATE:
return 2
return vis
@@ -5234,7 +5234,7 @@ def _make_depend_on(sc, expr):
_make_depend_on(sc, expr[1])
# NOTs only have a single operand
- if expr[0] != NOT:
+ if expr[0] is not NOT:
_make_depend_on(sc, expr[2])
elif not expr.is_constant:
@@ -5244,7 +5244,7 @@ def _make_depend_on(sc, expr):
def _parenthesize(expr, type_, sc_expr_str_fn):
# expr_str() helper. Adds parentheses around expressions of type 'type_'.
- if isinstance(expr, tuple) and expr[0] == type_:
+ if isinstance(expr, tuple) and expr[0] is type_:
return "({})".format(expr_str(expr, sc_expr_str_fn))
return expr_str(expr, sc_expr_str_fn)
@@ -5375,11 +5375,11 @@ def _expr_depends_on(expr, sym):
elif left is not sym:
return False
- return (expr[0] == EQUAL and right is sym.kconfig.m or \
+ return (expr[0] is EQUAL and right is sym.kconfig.m or \
right is sym.kconfig.y) or \
- (expr[0] == UNEQUAL and right is sym.kconfig.n)
+ (expr[0] is UNEQUAL and right is sym.kconfig.n)
- return expr[0] == AND and \
+ return expr[0] is AND and \
(_expr_depends_on(expr[1], sym) or
_expr_depends_on(expr[2], sym))
@@ -5454,15 +5454,15 @@ def _finalize_choice(node):
# If no type is specified for the choice, its type is that of
# the first choice item with a specified type
- if choice.orig_type == UNKNOWN:
+ if choice.orig_type is UNKNOWN:
for item in choice.syms:
- if item.orig_type != UNKNOWN:
+ if item.orig_type is not UNKNOWN:
choice.orig_type = item.orig_type
break
# Each choice item of UNKNOWN type gets the type of the choice
for sym in choice.syms:
- if sym.orig_type == UNKNOWN:
+ if sym.orig_type is UNKNOWN:
sym.orig_type = choice.orig_type
def _check_dep_loop_sym(sym, ignore_choice):
@@ -5654,7 +5654,7 @@ def _check_sym_sanity(sym):
.format(TYPE_TO_STR[sym.orig_type], _name_and_loc(sym),
expr_str(default)))
- if sym.orig_type == STRING:
+ if sym.orig_type is STRING:
if not default.is_constant and not default.nodes and \
not default.name.isupper():
# 'default foo' on a string symbol could be either a symbol
@@ -5710,7 +5710,7 @@ def _int_hex_ok(sym, type_):
if not sym.nodes:
return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
- return sym.orig_type == type_
+ return sym.orig_type is type_
def _check_choice_sanity(choice):
# Checks various choice properties that are handiest to check after
@@ -5873,6 +5873,21 @@ STR_TO_TRI = {
# constants)
#
+# Note:
+#
+# The token and type constants below are safe to test with 'is', which is a bit
+# faster (~30% faster in a microbenchmark with Python 3 on my machine, and a
+# few % faster for total parsing time), even without assuming Python's small
+# integer optimization (which caches small integer objects). The constants end
+# up pointing to unique integer objects, and since we consistently refer to
+# them via the names below, we always get the same object.
+#
+# Client code would also need to use the names below, because the integer
+# values can change e.g. when tokens get added. Client code would usually test
+# with == too, which would be safe even in super obscure cases involving e.g.
+# pickling (where 'is' would be a bad idea anyway) and no small-integer
+# optimization.
+
# Are we running on Python 2?
_IS_PY2 = sys.version_info[0] < 3