Exemplo n.º 1
0
def Node(*child_names):
    """Create a new Node class.

  You will typically use this when declaring a new class.
  For example:
    class Coordinate(Node("x", "y")):
      pass

  Arguments:
    *child_names: Names of the children of this node.

  Returns:
    A subclass of (named)tuple.
  """

    precondition_pairs = [preconditions.parse_arg(x) for x in child_names]
    namedtuple_type = collections.namedtuple("_",
                                             (p[0]
                                              for p in precondition_pairs))
    assert "__init__" not in namedtuple_type.__dict__  # see below

    class NamedTupleNode(namedtuple_type):
        """A Node class based on namedtuple."""

        _CHECKER = preconditions.CallChecker(precondition_pairs)

        def __init__(self, *args, **kwargs):
            if _CHECK_PRECONDITIONS:
                self._CHECKER.check(*args, **kwargs)
            # We do *not* call super() here, for performance reasons. Neither
            # namedtuple (our base class) nor tuple (namedtuple's base class) do
            # anything in __init__, so it's safe to leave it out.

        def Validate(self):
            """Re-run precondition checks on the node's data."""
            self._CHECKER.check(*self)

        def __eq__(self, other):
            """Compare two nodes for equality.

      This will return True if the two underlying tuples are the same *and* the
      two node types match.

      Arguments:
        other: The Node to compare this one with.
      Returns:
        True or False.
      """
            # This comparison blows up if "other" is an old-style class (not an
            # instance). That's fine, because trying to compare a tuple to a class is
            # almost certainly a programming error, and blowing up is better than
            # silently returning False.
            if self is other:
                return True
            elif self.__class__ is other.__class__:
                return tuple.__eq__(self, other)
            else:
                return False  # or NotImplemented

        def __ne__(self, other):
            """Compare two nodes for inequality. See __eq__."""
            return not self == other

        def __lt__(self, other):
            """Smaller than other node? Define so we can to deterministic ordering."""
            if self is other:
                return False
            elif self.__class__ is other.__class__:
                return tuple.__lt__(self, other)
            else:
                return self.__class__.__name__ < other.__class__.__name__

        def __gt__(self, other):
            """Larger than other node? Define so we can to deterministic ordering."""
            if self is other:
                return False
            elif self.__class__ is other.__class__:
                return tuple.__gt__(self, other)
            else:
                return self.__class__.__name__ > other.__class__.__name__

        def __le__(self, other):
            return self == other or self < other

        def __ge__(self, other):
            return self == other or self > other

        def __repr__(self):
            """Returns this tuple converted to a string.

      We output this as <classname>(values...). This differs from raw tuple
      output in that we use the class name, not the name of the tuple this
      class extends. Also, Nodes with only one child will be output as
      Name(value), not Name(value,) to match the constructor syntax.

      Returns:
        Representation of this tuple as a string, including the class name.
      """
            if len(self) == 1:
                return "%s(%r)" % (self.__class__.__name__, self[0])
            else:
                return "%s%r" % (self.__class__.__name__, tuple(self))

        # Expose namedtuple._replace as "Replace", so avoid lint warnings
        # and have consistent method names.
        Replace = namedtuple_type._replace  # pylint: disable=no-member,invalid-name

        def Visit(self, visitor, *args, **kwargs):
            """Visitor interface for transforming a tree of nodes to a new tree.

      You can pass a visitor, and callback functions on that visitor will be
      called for all nodes in the tree. Note that nodes are also allowed to
      be stored in lists and as the values of dictionaries, as long as these
      lists/dictionaries are stored in the named fields of the Node class.
      It's possible to overload the Visit function on Nodes, to do your own
      processing.

      Arguments:
        visitor: An instance of a visitor for this tree. For every node type you
          want to transform, this visitor implements a "Visit<Classname>"
          function named after the class of the node this function should
          target. Note that <Classname> is the *actual* class of the node, so
          if you subclass a Node class, visitors for the superclasses will *not*
          be triggered anymore. Also, visitor callbacks are only triggered
          for subclasses of Node.
        *args: Passed to the visitor callback.
        **kwargs: Passed to the visitor callback.

      Returns:
        Transformed version of this node.
      """
            # This function is overwritten below, so that we have the same im_func
            # even though we generate classes here.
            pass  # COV_NF_LINE

        Visit = _VisitNode  # pylint: disable=invalid-name

    return NamedTupleNode
Exemplo n.º 2
0
 def testParseArg(self):
     self.assertEquals(("x", None), preconditions.parse_arg("x"))
     name, cond = preconditions.parse_arg("foo: str")
     self.assertEquals("foo", name)
     self.assertClassName("str", cond)