Performance enhancements

- Cache values
- Perform expensive checks last
diff --git a/CHANGELOG b/CHANGELOG
index 86da9b7..72a1609 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,10 @@
 # All notable changes to this project will be documented in this file.
 # This project adheres to [Semantic Versioning](http://semver.org/).
 
+## [0.9.1] UNRELEASED
+### Changed
+- Performance enhancements.
+
 ## [0.9.0] 2016-05-29
 ### Added
 - Added a knob (SPLIT_PENALTY_BEFORE_IF_EXPR) to adjust the split penalty
diff --git a/yapf/yapflib/comment_splicer.py b/yapf/yapflib/comment_splicer.py
index 04e3deb..d0b5eb4 100644
--- a/yapf/yapflib/comment_splicer.py
+++ b/yapf/yapflib/comment_splicer.py
@@ -69,7 +69,6 @@
             # We can't just insert it before the NEWLINE node, because as a
             # result of the way pytrees are organized, this node can be under
             # an inappropriate parent.
-            assert prev_leaf[0] is not None
             comment_column -= len(comment_prefix)
             comment_column += len(comment_prefix) - len(comment_prefix.lstrip())
             pytree_utils.InsertNodesAfter(
diff --git a/yapf/yapflib/format_decision_state.py b/yapf/yapflib/format_decision_state.py
index 137f6be..c4407c6 100644
--- a/yapf/yapflib/format_decision_state.py
+++ b/yapf/yapflib/format_decision_state.py
@@ -134,8 +134,8 @@
       bracket = current if current.ClosesScope() else previous
       if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
         if bracket.OpensScope():
-          if (unwrapped_line.IsSurroundedByBrackets(bracket) or
-              not _IsLastScopeInLine(bracket)):
+          if (not _IsLastScopeInLine(bracket) or
+              unwrapped_line.IsSurroundedByBrackets(bracket)):
             last_token = bracket.matching_bracket
           else:
             last_token = _LastTokenInLine(bracket.matching_bracket)
@@ -329,15 +329,6 @@
     self.start_of_line_level = self.paren_level
     self.lowest_level_on_line = self.paren_level
 
-    # Any break on this level means that the parent level has been broken and we
-    # need to avoid bin packing there.
-    for paren_state in self.stack:
-      paren_state.split_before_parameter = True
-
-    if (previous.value != ',' and not previous.is_binary_op and
-        not current.is_binary_op and not previous.OpensScope()):
-      self.stack[-1].split_before_parameter = True
-
     if (previous.OpensScope() or (previous.is_comment and
                                   previous.previous_token is not None and
                                   previous.previous_token.OpensScope())):
@@ -387,12 +378,12 @@
       return top_of_stack.closing_scope_indent
 
     if (previous and previous.is_string and current.is_string and
-        _IsDictionaryValue(current)):
+        format_token.Subtype.DICTIONARY_VALUE in current.subtypes):
       return previous.column
 
     if style.Get('INDENT_DICTIONARY_VALUE'):
-      if _IsDictionaryValue(current):
-        if previous and (previous.value == ':' or previous.is_pseudo_paren):
+      if previous and (previous.value == ':' or previous.is_pseudo_paren):
+        if format_token.Subtype.DICTIONARY_VALUE in current.subtypes:
           return top_of_stack.indent
 
     if (self.line.first.value in _COMPOUND_STMTS and
@@ -526,7 +517,6 @@
     split_before_closing_bracket: Whether a newline needs to be inserted before
       the closing bracket. We only want to insert a newline before the closing
       bracket if there also was a newline after the beginning left bracket.
-    split_before_parameter: Split the line after the next comma.
     num_line_splits: Number of line splits this _ParenState contains already.
       Each subsequent line split gets an increasing penalty.
   """
@@ -538,9 +528,15 @@
     self.last_space = last_space
     self.closing_scope_indent = 0
     self.split_before_closing_bracket = False
-    self.split_before_parameter = False
     self.num_line_splits = 0
 
+  def __copy__(self):
+    state = _ParenState(self.indent, self.last_space)
+    state.closing_scope_indent = self.closing_scope_indent
+    state.split_before_closing_bracket = self.split_before_closing_bracket
+    self.num_line_splits = self.num_line_splits
+    return state
+
   def __repr__(self):
     return '[indent::%d, last_space::%d, closing_scope_indent::%d]' % (
         self.indent, self.last_space, self.closing_scope_indent)
diff --git a/yapf/yapflib/format_token.py b/yapf/yapflib/format_token.py
index d593d01..35de91e 100644
--- a/yapf/yapflib/format_token.py
+++ b/yapf/yapflib/format_token.py
@@ -22,6 +22,7 @@
 from lib2to3 import pytree
 from lib2to3.pgen2 import token
 
+from yapf.yapflib import py3compat
 from yapf.yapflib import pytree_utils
 from yapf.yapflib import style
 
@@ -85,8 +86,7 @@
     Arguments:
       node: (pytree.Leaf) The node that's being wrapped.
     """
-    assert isinstance(node, pytree.Leaf)
-    self._node = node
+    self.node = node
     self.next_token = None
     self.previous_token = None
     self.matching_bracket = None
@@ -116,7 +116,7 @@
 
     if self.is_comment:
       comment_lines = [s.lstrip() for s in self.value.splitlines()]
-      self._node.value = ('\n' + spaces_before).join(comment_lines)
+      self.node.value = ('\n' + spaces_before).join(comment_lines)
 
     if not self.whitespace_prefix:
       self.whitespace_prefix = (
@@ -167,108 +167,106 @@
   def ClosesScope(self):
     return self.value in pytree_utils.CLOSING_BRACKETS
 
-  def GetPytreeNode(self):
-    return self._node
-
-  @property
-  def value(self):
-    if self.is_continuation:
-      return self._node.value.rstrip()
-    return self._node.value
-
-  @property
-  def node(self):
-    return self._node
-
-  @property
-  def node_split_penalty(self):
-    """Split penalty attached to the pytree node of this token.
-
-    Returns:
-      The penalty, or None if no annotation is attached.
-    """
-    return pytree_utils.GetNodeAnnotation(self._node,
-                                          pytree_utils.Annotation.SPLIT_PENALTY,
-                                          default=0)
-
-  @property
-  def newlines(self):
-    """The number of newlines needed before this token."""
-    return pytree_utils.GetNodeAnnotation(self._node,
-                                          pytree_utils.Annotation.NEWLINES)
-
-  @property
-  def must_split(self):
-    """Return true if the token requires a split before it."""
-    return pytree_utils.GetNodeAnnotation(self._node,
-                                          pytree_utils.Annotation.MUST_SPLIT)
-
-  @property
-  def column(self):
-    """The original column number of the node in the source."""
-    return self._node.column
-
-  @property
-  def lineno(self):
-    """The original line number of the node in the source."""
-    return self._node.lineno
-
-  @property
-  def subtypes(self):
-    """Extra type information for directing formatting."""
-    value = pytree_utils.GetNodeAnnotation(self._node,
-                                           pytree_utils.Annotation.SUBTYPE)
-    return [Subtype.NONE] if value is None else value
-
-  @property
-  def is_binary_op(self):
-    """Token is a binary operator."""
-    return Subtype.BINARY_OPERATOR in self.subtypes
-
-  @property
-  def name(self):
-    """A string representation of the node's name."""
-    return pytree_utils.NodeName(self._node)
-
   def __repr__(self):
     msg = 'FormatToken(name={0}, value={1}'.format(self.name, self.value)
     msg += ', pseudo)' if self.is_pseudo_paren else ')'
     return msg
 
   @property
+  def value(self):
+    if self.is_continuation:
+      return self.node.value.rstrip()
+    return self.node.value
+
+  @property
+  @py3compat.lru_cache()
+  def node_split_penalty(self):
+    """Split penalty attached to the pytree node of this token."""
+    return pytree_utils.GetNodeAnnotation(self.node,
+                                          pytree_utils.Annotation.SPLIT_PENALTY,
+                                          default=0)
+
+  @property
+  def newlines(self):
+    """The number of newlines needed before this token."""
+    return pytree_utils.GetNodeAnnotation(self.node,
+                                          pytree_utils.Annotation.NEWLINES)
+
+  @property
+  def must_split(self):
+    """Return true if the token requires a split before it."""
+    return pytree_utils.GetNodeAnnotation(self.node,
+                                          pytree_utils.Annotation.MUST_SPLIT)
+
+  @property
+  def column(self):
+    """The original column number of the node in the source."""
+    return self.node.column
+
+  @property
+  def lineno(self):
+    """The original line number of the node in the source."""
+    return self.node.lineno
+
+  @property
+  @py3compat.lru_cache()
+  def subtypes(self):
+    """Extra type information for directing formatting."""
+    value = pytree_utils.GetNodeAnnotation(self.node,
+                                           pytree_utils.Annotation.SUBTYPE)
+    return [Subtype.NONE] if value is None else value
+
+  @property
+  @py3compat.lru_cache()
+  def is_binary_op(self):
+    """Token is a binary operator."""
+    return Subtype.BINARY_OPERATOR in self.subtypes
+
+  @property
+  @py3compat.lru_cache()
+  def name(self):
+    """A string representation of the node's name."""
+    return pytree_utils.NodeName(self.node)
+
+  @property
   def is_comment(self):
-    return self._node.type == token.COMMENT
+    return self.node.type == token.COMMENT
 
   @property
   def is_continuation(self):
-    return self._node.type == CONTINUATION
+    return self.node.type == CONTINUATION
 
   @property
+  @py3compat.lru_cache()
   def is_keyword(self):
     return keyword.iskeyword(self.value)
 
   @property
+  @py3compat.lru_cache()
   def is_name(self):
-    return self._node.type == token.NAME and not self.is_keyword
+    return self.node.type == token.NAME and not self.is_keyword
 
   @property
   def is_number(self):
-    return self._node.type == token.NUMBER
+    return self.node.type == token.NUMBER
 
   @property
   def is_string(self):
-    return self._node.type == token.STRING
+    return self.node.type == token.STRING
 
   @property
+  @py3compat.lru_cache()
   def is_multiline_string(self):
     return (self.is_string and
             re.match(r'^[uUbB]?[rR]?(?P<delim>"""|\'\'\').*(?P=delim)$',
                      self.value, re.DOTALL) is not None)
 
   @property
+  @py3compat.lru_cache()
   def is_docstring(self):
     return self.is_multiline_string and not self.node.prev_sibling
 
   @property
+  @py3compat.lru_cache()
   def is_pseudo_paren(self):
-    return hasattr(self._node, 'is_pseudo') and self._node.is_pseudo
+    return hasattr(self.node, 'is_pseudo') and self.node.is_pseudo
diff --git a/yapf/yapflib/py3compat.py b/yapf/yapflib/py3compat.py
index 231d038..c19cab0 100644
--- a/yapf/yapflib/py3compat.py
+++ b/yapf/yapflib/py3compat.py
@@ -25,6 +25,9 @@
   import codecs
   open_with_encoding = codecs.open
 
+  import functools
+  lru_cache = functools.lru_cache
+
   range = range
   ifilter = filter
   raw_input = input
@@ -41,6 +44,14 @@
 
   open_with_encoding = io.open
 
+  # Python 2.7 doesn't have a native LRU cache, so do nothing.
+  def lru_cache(maxsize=128, typed=False):
+
+    def fake_wrapper(user_function):
+      return user_function
+
+    return fake_wrapper
+
   range = xrange
 
   from itertools import ifilter
diff --git a/yapf/yapflib/pytree_unwrapper.py b/yapf/yapflib/pytree_unwrapper.py
index 44c6511..8386693 100644
--- a/yapf/yapflib/pytree_unwrapper.py
+++ b/yapf/yapflib/pytree_unwrapper.py
@@ -107,6 +107,17 @@
       _AdjustSplitPenalty(self._cur_unwrapped_line)
     self._cur_unwrapped_line = unwrapped_line.UnwrappedLine(self._cur_depth)
 
+  _STMT_TYPES = frozenset({
+      'if_stmt',
+      'while_stmt',
+      'for_stmt',
+      'try_stmt',
+      'expect_clause',
+      'with_stmt',
+      'funcdef',
+      'classdef',
+  })
+
   # pylint: disable=invalid-name,missing-docstring
   def Visit_simple_stmt(self, node):
     # A 'simple_stmt' conveniently represents a non-compound Python statement,
@@ -119,17 +130,9 @@
     # standalone comment and in the case of it coming directly after the
     # funcdef, it is a "top" comment for the whole function.
     # TODO(eliben): add more relevant compound statements here.
-    single_stmt_suite = (node.parent and pytree_utils.NodeName(node.parent) in {
-        'if_stmt',
-        'while_stmt',
-        'for_stmt',
-        'try_stmt',
-        'expect_clause',
-        'with_stmt',
-        'funcdef',
-        'classdef',
-    })
-    is_comment_stmt = pytree_utils.NodeName(node.children[0]) == 'COMMENT'
+    single_stmt_suite = (node.parent and
+                         pytree_utils.NodeName(node.parent) in self._STMT_TYPES)
+    is_comment_stmt = pytree_utils.IsCommentStatement(node)
     if single_stmt_suite and not is_comment_stmt:
       self._cur_depth += 1
     self._StartNewLine()
@@ -159,23 +162,35 @@
         self._StartNewLine()
       self.Visit(child)
 
+  _IF_STMT_ELEMS = frozenset({'if', 'else', 'elif'})
+
   def Visit_if_stmt(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'if', 'else', 'elif'})
+    self._VisitCompoundStatement(node, self._IF_STMT_ELEMS)
+
+  _WHILE_STMT_ELEMS = frozenset({'while', 'else'})
 
   def Visit_while_stmt(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'while', 'else'})
+    self._VisitCompoundStatement(node, self._WHILE_STMT_ELEMS)
+
+  _FOR_STMT_ELEMS = frozenset({'for', 'else'})
 
   def Visit_for_stmt(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'for', 'else'})
+    self._VisitCompoundStatement(node, self._FOR_STMT_ELEMS)
+
+  _TRY_STMT_ELEMS = frozenset({'try', 'except', 'else', 'finally'})
 
   def Visit_try_stmt(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'try', 'except', 'else', 'finally'})
+    self._VisitCompoundStatement(node, self._TRY_STMT_ELEMS)
+
+  _EXCEPT_STMT_ELEMS = frozenset({'except'})
 
   def Visit_except_clause(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'except'})
+    self._VisitCompoundStatement(node, self._EXCEPT_STMT_ELEMS)
+
+  _FUNC_DEF_ELEMS = frozenset({'def'})
 
   def Visit_funcdef(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'def'})
+    self._VisitCompoundStatement(node, self._FUNC_DEF_ELEMS)
 
   def Visit_async_funcdef(self, node):  # pylint: disable=invalid-name
     self._StartNewLine()
@@ -183,8 +198,10 @@
     for child in node.children[1].children:
       self.Visit(child)
 
+  _CLASS_DEF_ELEMS = frozenset({'class'})
+
   def Visit_classdef(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'class'})
+    self._VisitCompoundStatement(node, self._CLASS_DEF_ELEMS)
 
   def Visit_async_stmt(self, node):  # pylint: disable=invalid-name
     self._StartNewLine()
@@ -202,8 +219,10 @@
       self._StartNewLine()
       self.Visit(child)
 
+  _WITH_STMT_ELEMS = frozenset({'with'})
+
   def Visit_with_stmt(self, node):  # pylint: disable=invalid-name
-    self._VisitCompoundStatement(node, {'with'})
+    self._VisitCompoundStatement(node, self._WITH_STMT_ELEMS)
 
   def Visit_suite(self, node):  # pylint: disable=invalid-name
     # A 'suite' starts a new indentation level in Python.
@@ -273,7 +292,6 @@
     if token.value in pytree_utils.OPENING_BRACKETS:
       bracket_stack.append(token)
     elif token.value in pytree_utils.CLOSING_BRACKETS:
-      assert _BRACKET_MATCH[token.value] == bracket_stack[-1].value
       bracket_stack[-1].matching_bracket = token
       token.matching_bracket = bracket_stack[-1]
       bracket_stack.pop()
@@ -291,7 +309,7 @@
   bracket_level = 0
   for index, token in enumerate(uwline.tokens):
     if index and not bracket_level:
-      pytree_utils.SetNodeAnnotation(token.GetPytreeNode(),
+      pytree_utils.SetNodeAnnotation(token.node,
                                      pytree_utils.Annotation.SPLIT_PENALTY,
                                      split_penalty.UNBREAKABLE)
     if token.value in pytree_utils.OPENING_BRACKETS:
@@ -311,7 +329,7 @@
     child = node.children[index]
     if isinstance(child, pytree.Leaf) and child.value == ',':
       next_child = node.children[index + 1]
-      if pytree_utils.NodeName(next_child) == 'COMMENT':
+      if next_child.type == grammar_token.COMMENT:
         index += 1
         if index >= num_children - 1:
           break
@@ -322,11 +340,11 @@
 def _ContainsComments(node):
   """Return True if the list has a comment in it."""
   if isinstance(node, pytree.Leaf):
-    return pytree_utils.NodeName(node) == 'COMMENT'
-  contains_comments = False
+    return node.type == grammar_token.COMMENT
   for child in node.children:
-    contains_comments = contains_comments or _ContainsComments(child)
-  return contains_comments
+    if _ContainsComments(child):
+      return True
+  return False
 
 
 def _SetMustSplitOnFirstLeaf(node):
@@ -335,7 +353,6 @@
   def FindFirstLeaf(node):
     if isinstance(node, pytree.Leaf):
       return node
-
     return FindFirstLeaf(node.children[0])
 
   pytree_utils.SetNodeAnnotation(
diff --git a/yapf/yapflib/pytree_utils.py b/yapf/yapflib/pytree_utils.py
index be8abf5..1244005 100644
--- a/yapf/yapflib/pytree_utils.py
+++ b/yapf/yapflib/pytree_utils.py
@@ -286,10 +286,8 @@
                            [_PytreeNodeRepr(c) for c in node.children])
   if isinstance(node, pytree.Leaf):
     return '%s(%s, %r)' % (node.__class__.__name__, NodeName(node), node.value)
-  # Throw an exception if it's not an expected type:
-  assert isinstance(node, (pytree.Node, pytree.Leaf)), type(node)
 
 
 def IsCommentStatement(node):
   return (NodeName(node) == 'simple_stmt' and
-          NodeName(node.children[0]) == 'COMMENT')
+          node.children[0].type == token.COMMENT)
diff --git a/yapf/yapflib/reformatter.py b/yapf/yapflib/reformatter.py
index 817da2b..2688f0a 100644
--- a/yapf/yapflib/reformatter.py
+++ b/yapf/yapflib/reformatter.py
@@ -48,12 +48,13 @@
   """
   final_lines = []
   prev_uwline = None  # The previous line.
+  indent_width = style.Get('INDENT_WIDTH')
 
   for uwline in _SingleOrMergedLines(uwlines):
     first_token = uwline.first
     _FormatFirstToken(first_token, uwline.depth, prev_uwline, final_lines)
 
-    indent_amt = style.Get('INDENT_WIDTH') * uwline.depth
+    indent_amt = indent_width * uwline.depth
     state = format_decision_state.FormatDecisionState(uwline, indent_amt)
 
     if not uwline.disable:
@@ -79,7 +80,7 @@
       while state.next_token:
         state.AddTokenToState(newline=False, dry_run=False)
     else:
-      if not _AnalyzeSolutionSpace(state, dry_run=False):
+      if not _AnalyzeSolutionSpace(state):
         # Failsafe mode. If there isn't a solution to the line, then just emit
         # it as is.
         state = format_decision_state.FormatDecisionState(uwline, indent_amt)
@@ -229,7 +230,7 @@
         if (not tok.next_token.whitespace_prefix.startswith('\n') and
             not tok.next_token.whitespace_prefix.startswith(' ')):
           if (tok.previous_token.value == ':' or
-              tok.next_token.value not in ',}])'):
+              tok.next_token.value not in frozenset(',}])')):
             formatted_line.append(' ')
 
     formatted_code.append(''.join(formatted_line))
@@ -273,7 +274,7 @@
                                                   'state_node'])
 
 
-def _AnalyzeSolutionSpace(initial_state, dry_run=False):
+def _AnalyzeSolutionSpace(initial_state):
   """Analyze the entire solution space starting from initial_state.
 
   This implements a variant of Dijkstra's algorithm on the graph that spans
@@ -284,7 +285,6 @@
   Arguments:
     initial_state: (format_decision_state.FormatDecisionState) The initial state
       to start the search from.
-    dry_run: (bool) Don't commit changes if True.
 
   Returns:
     True if a formatting solution was found. False otherwise.
@@ -313,9 +313,7 @@
     if node.state in seen:
       continue
 
-    assert penalty >= prev_penalty
     prev_penalty = penalty
-
     seen.add(node.state)
 
     # FIXME(morbo): Add a 'decision' element?
@@ -327,9 +325,7 @@
     # We weren't able to find a solution. Do nothing.
     return False
 
-  if not dry_run:
-    _ReconstructPath(initial_state, heapq.heappop(p_queue).state_node)
-
+  _ReconstructPath(initial_state, heapq.heappop(p_queue).state_node)
   return True
 
 
@@ -433,7 +429,7 @@
     # The first line in the file. Don't add blank lines.
     # FIXME(morbo): Is this correct?
     if first_token.newlines is not None:
-      pytree_utils.SetNodeAnnotation(first_token.GetPytreeNode(),
+      pytree_utils.SetNodeAnnotation(first_token.node,
                                      pytree_utils.Annotation.NEWLINES, None)
     return 0
 
@@ -444,7 +440,8 @@
 
   prev_last_token = prev_uwline.last
   if prev_last_token.is_docstring:
-    if not indent_depth and first_token.value in {'class', 'def', 'async'}:
+    if (not indent_depth and
+        first_token.value in frozenset({'class', 'def', 'async'})):
       # Separate a class or function from the module-level docstring with two
       # blank lines.
       return TWO_BLANK_LINES
@@ -454,7 +451,7 @@
     else:
       return ONE_BLANK_LINE
 
-  if first_token.value in {'class', 'def', '@'}:
+  if first_token.value in frozenset({'class', 'def', '@'}):
     # TODO(morbo): This can go once the blank line calculator is more
     # sophisticated.
     if not indent_depth:
@@ -476,11 +473,11 @@
           else:
             prev_last_token.AdjustNewlinesBefore(TWO_BLANK_LINES)
           if first_token.newlines is not None:
-            pytree_utils.SetNodeAnnotation(first_token.GetPytreeNode(),
+            pytree_utils.SetNodeAnnotation(first_token.node,
                                            pytree_utils.Annotation.NEWLINES,
                                            None)
           return NO_BLANK_LINES
-    elif prev_uwline.first.value in {'class', 'def'}:
+    elif prev_uwline.first.value in frozenset({'class', 'def'}):
       if not style.Get('BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'):
         pytree_utils.SetNodeAnnotation(first_token.node,
                                        pytree_utils.Annotation.NEWLINES, None)
diff --git a/yapf/yapflib/subtype_assigner.py b/yapf/yapflib/subtype_assigner.py
index 835ab22..b08d586 100644
--- a/yapf/yapflib/subtype_assigner.py
+++ b/yapf/yapflib/subtype_assigner.py
@@ -66,7 +66,7 @@
     if len(node.children) > 1:
       index = 0
       while index < len(node.children):
-        if pytree_utils.NodeName(node.children[index]) != 'COMMENT':
+        if node.children[index].type != token.COMMENT:
           break
         index += 1
       if index < len(node.children):
@@ -313,7 +313,7 @@
 def _InsertPseudoParentheses(node):
   comment_node = None
   if isinstance(node, pytree.Node):
-    if pytree_utils.NodeName(node.children[-1]) == 'COMMENT':
+    if node.children[-1].type == token.COMMENT:
       comment_node = node.children[-1].clone()
       node.children[-1].remove()
 
diff --git a/yapf/yapflib/unwrapped_line.py b/yapf/yapflib/unwrapped_line.py
index ae60261..8933023 100644
--- a/yapf/yapflib/unwrapped_line.py
+++ b/yapf/yapflib/unwrapped_line.py
@@ -106,7 +106,6 @@
     Arguments:
       node: the node to append
     """
-    assert isinstance(node, pytree.Leaf)
     self.AppendToken(format_token.FormatToken(node))
 
   @property
@@ -190,6 +189,8 @@
 
 def _SpaceRequiredBetween(left, right):
   """Return True if a space is required between the left and right token."""
+  lval = left.value
+  rval = right.value
   if (left.is_pseudo_paren and _IsIdNumberStringToken(right) and
       left.previous_token and _IsIdNumberStringToken(left.previous_token)):
     # Space between keyword... tokens and pseudo parens.
@@ -206,29 +207,29 @@
   if _IsIdNumberStringToken(left) and _IsIdNumberStringToken(right):
     # Spaces between keyword, string, number, and identifier tokens.
     return True
-  if left.value == ',' and right.value == ':':
+  if lval == ',' and rval == ':':
     # We do want a space between a comma and colon.
     return True
-  if right.value in ':,':
+  if rval in ':,':
     # Otherwise, we never want a space before a colon or comma.
     return False
-  if left.value == ',' and right.value in ']})':
+  if lval == ',' and rval in ']})':
     # Add a space between ending ',' and closing bracket if requested.
     return style.Get('SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET')
-  if left.value == ',':
+  if lval == ',':
     # We want a space after a comma.
     return True
-  if left.value == 'from' and right.value == '.':
+  if lval == 'from' and rval == '.':
     # Space before the '.' in an import statement.
     return True
-  if left.value == '.' and right.value == 'import':
+  if lval == '.' and rval == 'import':
     # Space after the '.' in an import statement.
     return True
   if ((right.is_keyword or right.is_name) and
       (left.is_keyword or left.is_name)):
     # Don't merge two keywords/identifiers.
     return True
-  if left.is_string and right.value not in '[)]}.':
+  if left.is_string and rval not in '[)]}.':
     # A string followed by something other than a subscript, closing bracket,
     # or dot should have a space after it.
     return True
@@ -239,13 +240,13 @@
     # No space between two unary operators.
     return False
   if left.is_binary_op or right.is_binary_op:
-    if left.value == '**' or right.value == '**':
+    if lval == '**' or rval == '**':
       # Space around the "power" operator.
       return style.Get('SPACES_AROUND_POWER_OPERATOR')
     # Enforce spaces around binary operators.
     return True
-  if (_IsUnaryOperator(left) and left.value != 'not' and
-      (right.is_name or right.is_number or right.value == '(')):
+  if (_IsUnaryOperator(left) and lval != 'not' and
+      (right.is_name or right.is_number or rval == '(')):
     # The previous token was a unary op. No space is desired between it and
     # the current token.
     return False
@@ -261,55 +262,53 @@
       format_token.Subtype.KWARGS_STAR_STAR in left.subtypes):
     # Don't add a space after a vararg's star or a keyword's star-star.
     return False
-  if left.value == '@':
+  if lval == '@':
     # Decorators shouldn't be separated from the 'at' sign.
     return False
-  if left.value == '.' or right.value == '.':
+  if lval == '.' or rval == '.':
     # Don't place spaces between dots.
     return False
-  if ((left.value == '(' and right.value == ')') or
-      (left.value == '[' and right.value == ']') or
-      (left.value == '{' and right.value == '}')):
+  if ((lval == '(' and rval == ')') or (lval == '[' and rval == ']') or
+      (lval == '{' and rval == '}')):
     # Empty objects shouldn't be separted by spaces.
     return False
-  if (left.value in pytree_utils.OPENING_BRACKETS and
-      right.value in pytree_utils.OPENING_BRACKETS):
+  if (lval in pytree_utils.OPENING_BRACKETS and
+      rval in pytree_utils.OPENING_BRACKETS):
     # Nested objects' opening brackets shouldn't be separated.
     return False
-  if (left.value in pytree_utils.CLOSING_BRACKETS and
-      right.value in pytree_utils.CLOSING_BRACKETS):
+  if (lval in pytree_utils.CLOSING_BRACKETS and
+      rval in pytree_utils.CLOSING_BRACKETS):
     # Nested objects' closing brackets shouldn't be separated.
     return False
-  if left.value in pytree_utils.CLOSING_BRACKETS and right.value in '([':
+  if lval in pytree_utils.CLOSING_BRACKETS and rval in '([':
     # A call, set, dictionary, or subscript that has a call or subscript after
     # it shouldn't have a space between them.
     return False
-  if (left.value in pytree_utils.OPENING_BRACKETS and
-      _IsIdNumberStringToken(right)):
+  if lval in pytree_utils.OPENING_BRACKETS and _IsIdNumberStringToken(right):
     # Don't separate the opening bracket from the first item.
     return False
-  if left.is_name and right.value in '([':
+  if left.is_name and rval in '([':
     # Don't separate a call or array access from the name.
     return False
-  if right.value in pytree_utils.CLOSING_BRACKETS:
+  if rval in pytree_utils.CLOSING_BRACKETS:
     # Don't separate the closing bracket from the last item.
     # FIXME(morbo): This might be too permissive.
     return False
-  if left.value == 'print' and right.value == '(':
+  if lval == 'print' and rval == '(':
     # Special support for the 'print' function.
     return False
-  if left.value in pytree_utils.OPENING_BRACKETS and _IsUnaryOperator(right):
+  if lval in pytree_utils.OPENING_BRACKETS and _IsUnaryOperator(right):
     # Don't separate a unary operator from the opening bracket.
     return False
-  if (left.value in pytree_utils.OPENING_BRACKETS and
+  if (lval in pytree_utils.OPENING_BRACKETS and
       (format_token.Subtype.VARARGS_STAR in right.subtypes or
        format_token.Subtype.KWARGS_STAR_STAR in right.subtypes)):
     # Don't separate a '*' or '**' from the opening bracket.
     return False
-  if right.value == ';':
+  if rval == ';':
     # Avoid spaces before a semicolon. (Why is there a semicolon?!)
     return False
-  if left.value == '(' and right.value == 'await':
+  if lval == '(' and rval == 'await':
     # Special support for the 'await' keyword. Don't separate the 'await'
     # keyword from an opening paren.
     return False
@@ -321,8 +320,8 @@
   if prev_token.is_comment:
     # Must break if the previous token was a comment.
     return True
-  if (IsSurroundedByBrackets(cur_token) and cur_token.is_string and
-      prev_token.is_string):
+  if (cur_token.is_string and prev_token.is_string and
+      IsSurroundedByBrackets(cur_token)):
     # We want consecutive strings to be on separate lines. This is a
     # reasonable assumption, because otherwise they should have written them
     # all on the same line, or with a '+'.
@@ -334,32 +333,33 @@
 
 def _CanBreakBefore(prev_token, cur_token):
   """Return True if a line break may occur before the current token."""
+  pval = prev_token.value
+  cval = cur_token.value
   if py3compat.PY3:
-    if prev_token.value == 'yield' and cur_token.value == 'from':
+    if pval == 'yield' and cval == 'from':
       # Don't break before a yield argument.
       return False
-    if (prev_token.value in {'async', 'await'} and
-        cur_token.value in {'def', 'with', 'for'}):
+    if pval in {'async', 'await'} and cval in {'def', 'with', 'for'}:
       # Don't break after sync keywords.
       return False
   if cur_token.split_penalty >= split_penalty.UNBREAKABLE:
     return False
-  if prev_token.value == '@':
+  if pval == '@':
     # Don't break right after the beginning of a decorator.
     return False
-  if cur_token.value == ':':
+  if cval == ':':
     # Don't break before the start of a block of code.
     return False
-  if cur_token.value == ',':
+  if cval == ',':
     # Don't break before a comma.
     return False
-  if prev_token.is_name and cur_token.value == '(':
+  if prev_token.is_name and cval == '(':
     # Don't break in the middle of a function definition or call.
     return False
-  if prev_token.is_name and cur_token.value == '[':
+  if prev_token.is_name and cval == '[':
     # Don't break in the middle of an array dereference.
     return False
-  if prev_token.is_name and cur_token.value == '.':
+  if prev_token.is_name and cval == '.':
     # Don't break before the '.' in a dotted name.
     return False
   if cur_token.is_comment and prev_token.lineno == cur_token.lineno:
@@ -409,7 +409,9 @@
 
 def _SplitPenalty(prev_token, cur_token):
   """Return the penalty for breaking the line before the current token."""
-  if prev_token.value == 'not':
+  pval = prev_token.value
+  cval = cur_token.value
+  if pval == 'not':
     return split_penalty.UNBREAKABLE
 
   if cur_token.node_split_penalty > 0:
@@ -417,28 +419,28 @@
 
   if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
     # Prefer to split before 'and' and 'or'.
-    if prev_token.value in _LOGICAL_OPERATORS:
+    if pval in _LOGICAL_OPERATORS:
       return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
-    if cur_token.value in _LOGICAL_OPERATORS:
+    if cval in _LOGICAL_OPERATORS:
       return 0
   else:
     # Prefer to split after 'and' and 'or'.
-    if prev_token.value in _LOGICAL_OPERATORS:
+    if pval in _LOGICAL_OPERATORS:
       return 0
-    if cur_token.value in _LOGICAL_OPERATORS:
+    if cval in _LOGICAL_OPERATORS:
       return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
 
   if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'):
     # Prefer to split before '&', '|', and '^'.
-    if prev_token.value in _BITWISE_OPERATORS:
+    if pval in _BITWISE_OPERATORS:
       return style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')
-    if cur_token.value in _BITWISE_OPERATORS:
+    if cval in _BITWISE_OPERATORS:
       return 0
   else:
     # Prefer to split after '&', '|', and '^'.
-    if prev_token.value in _BITWISE_OPERATORS:
+    if pval in _BITWISE_OPERATORS:
       return 0
-    if cur_token.value in _BITWISE_OPERATORS:
+    if cval in _BITWISE_OPERATORS:
       return style.Get('SPLIT_PENALTY_BITWISE_OPERATOR')
 
   if (format_token.Subtype.COMP_FOR in cur_token.subtypes or
@@ -448,7 +450,7 @@
   if format_token.Subtype.UNARY_OPERATOR in prev_token.subtypes:
     # Try not to break after a unary operator.
     return style.Get('SPLIT_PENALTY_AFTER_UNARY_OPERATOR')
-  if prev_token.value == ',':
+  if pval == ',':
     # Breaking after a comma is fine, if need be.
     return 0
   if prev_token.is_binary_op:
@@ -458,25 +460,25 @@
       format_token.Subtype.KWARGS_STAR_STAR in prev_token.subtypes):
     # Don't split after a varargs * or kwargs **.
     return split_penalty.UNBREAKABLE
-  if prev_token.OpensScope() and cur_token.value != '(':
+  if prev_token.OpensScope() and cval != '(':
     # Slightly prefer
     return style.Get('SPLIT_PENALTY_AFTER_OPENING_BRACKET')
-  if cur_token.value == ':':
+  if cval == ':':
     # Don't split before a colon.
     return split_penalty.UNBREAKABLE
-  if cur_token.value == '=':
+  if cval == '=':
     # Don't split before an assignment.
     return split_penalty.UNBREAKABLE
   if (format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in prev_token.subtypes or
       format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in cur_token.subtypes):
     # Don't break before or after an default or named assignment.
     return split_penalty.UNBREAKABLE
-  if cur_token.value == '==':
+  if cval == '==':
     # We would rather not split before an equality operator.
     return split_penalty.STRONGLY_CONNECTED
   if cur_token.ClosesScope():
     # Give a slight penalty for splitting before the closing scope.
     return 100
-  if prev_token.value in _TERM_OPERATORS or cur_token.value in _TERM_OPERATORS:
+  if pval in _TERM_OPERATORS or cval in _TERM_OPERATORS:
     return 50
   return 0