Example #1
0
    def assertTypesMatchPytd(self, ty, pytd_src):
        """Parses pytd_src and compares with ty."""
        pytd_tree = parser.parse_string(
            textwrap.dedent(pytd_src),
            options=parser.PyiOptions(python_version=self.python_version))
        pytd_tree = pytd_tree.Visit(
            visitors.LookupBuiltins(self.loader.builtins, full_names=False))
        pytd_tree = pytd_tree.Visit(visitors.LookupLocalTypes())
        pytd_tree = pytd_tree.Visit(visitors.ClassTypeToNamedType())
        pytd_tree = pytd_tree.Visit(
            visitors.CanonicalOrderingVisitor(sort_signatures=True))
        pytd_tree.Visit(visitors.VerifyVisitor())
        ty = ty.Visit(visitors.ClassTypeToNamedType())
        ty = ty.Visit(visitors.AdjustSelf())
        ty = ty.Visit(visitors.CanonicalOrderingVisitor(sort_signatures=True))
        ty.Visit(visitors.VerifyVisitor())

        ty_src = pytd_utils.Print(ty) + "\n"
        pytd_tree_src = pytd_utils.Print(pytd_tree) + "\n"

        log.info("========== result   ==========")
        _LogLines(log.info, ty_src)
        log.info("========== expected ==========")
        _LogLines(log.info, pytd_tree_src)
        log.info("==============================")

        # In the diff output, mark expected with "-" and actual with "+".
        # (In other words, display a change from "working" to "broken")
        self.assertMultiLineEqual(pytd_tree_src, ty_src)
Example #2
0
 def VisitClass(self, cls):
     """Add superclass methods and constants to this Class."""
     if any(base for base in cls.parents
            if isinstance(base, pytd.NamedType)):
         raise AssertionError("AddInheritedMethods needs a resolved AST")
     # Filter out only the types we can reason about.
     # TODO(kramm): Do we want handle UnionTypes and GenericTypes at some point?
     bases = [
         base.cls for base in cls.parents
         if isinstance(base, pytd.ClassType)
     ]
     # Don't pull in methods that are named the same as existing methods in
     # this class, local methods override parent class methods.
     names = {m.name for m in cls.methods} | {c.name for c in cls.constants}
     # TODO(kramm): This should do full-blown MRO.
     adjust_self = visitors.AdjustSelf(force=True)
     adjust_self.class_types.append(visitors.ClassAsType(cls))
     new_methods = list(cls.methods)
     for base in bases:
         for m in base.methods:
             if m.name not in names:
                 new_methods.append(m.Visit(adjust_self))
     new_constants = list(cls.constants)
     for base in bases:
         for c in base.constants:
             if c.name not in names:
                 new_constants.append(c)
     return cls.Replace(methods=tuple(new_methods),
                        constants=tuple(new_constants))
Example #3
0
def RemoveMutableParameters(ast):
    """Change all mutable parameters in a pytd AST to a non-mutable form."""
    ast = ast.Visit(optimize.AbsorbMutableParameters())
    ast = ast.Visit(optimize.CombineContainers())
    ast = ast.Visit(optimize.MergeTypeParameters())
    ast = ast.Visit(visitors.AdjustSelf(force=True))
    return ast
Example #4
0
def Optimize(node,
             builtins=None,
             lossy=False,
             use_abcs=False,
             max_union=7,
             remove_mutable=False,
             can_do_lookup=True):
    """Optimize a PYTD tree.

  Tries to shrink a PYTD tree by applying various optimizations.

  Arguments:
    node: A pytd node to be optimized. It won't be modified - this function
        will return a new node.
    builtins: Definitions of all of the external types in node.
    lossy: Allow optimizations that change the meaning of the pytd.
    use_abcs: Use abstract base classes to represent unions like
        e.g. "float or int" as "Real".
    max_union: How many types we allow in a union before we simplify
        it to just "object".
    remove_mutable: Whether to simplify mutable parameters to normal
        parameters.
    can_do_lookup: True: We're either allowed to try to resolve NamedType
        instances in the AST, or the AST is already resolved. False: Skip any
        optimizations that would require NamedTypes to be resolved.

  Returns:
    An optimized node.
  """
    node = node.Visit(RemoveDuplicates())
    node = node.Visit(SimplifyUnions())
    node = node.Visit(CombineReturnsAndExceptions())
    node = node.Visit(Factorize())
    node = node.Visit(ApplyOptionalArguments())
    node = node.Visit(CombineContainers())
    node = node.Visit(SimplifyContainers())
    if builtins:
        superclasses = builtins.Visit(visitors.ExtractSuperClassesByName())
        superclasses.update(node.Visit(visitors.ExtractSuperClassesByName()))
        if use_abcs:
            superclasses.update(abc_hierarchy.GetSuperClasses())
        hierarchy = SuperClassHierarchy(superclasses)
        node = node.Visit(SimplifyUnionsWithSuperclasses(hierarchy))
        if lossy:
            node = node.Visit(FindCommonSuperClasses(hierarchy))
    if max_union:
        node = node.Visit(CollapseLongUnions(max_union))
    node = node.Visit(AdjustReturnAndConstantGenericType())
    if remove_mutable:
        node = node.Visit(AbsorbMutableParameters())
        node = node.Visit(CombineContainers())
        node = node.Visit(MergeTypeParameters())
        node = node.Visit(visitors.AdjustSelf())
    node = node.Visit(SimplifyContainers())
    if builtins and can_do_lookup:
        node = visitors.LookupClasses(node, builtins, ignore_late_types=True)
        node = node.Visit(RemoveInheritedMethods())
        node = node.Visit(RemoveRedundantSignatures(hierarchy))
    return node
Example #5
0
 def VisitClass(self, cls):
   """Visit a class, and change constants to methods where possible."""
   new_constants = []
   new_methods = list(cls.methods)
   for const in cls.constants:
     if self._IsSimpleCall(const.type):
       c = self._MaybeLookup(const.type)
       signatures = c.methods[0].signatures
       self._processed_count[c.name] += 1
       new_methods.append(
           pytd.Function(const.name, signatures, c.methods[0].kind))
     else:
       new_constants.append(const)  # keep
   cls = cls.Replace(constants=tuple(new_constants),
                     methods=tuple(new_methods))
   return cls.Visit(visitors.AdjustSelf(force=True))
Example #6
0
 def VisitClass(self, cls):
   """Visit a class, and change constants to methods where possible."""
   new_constants = []
   new_methods = list(cls.methods)
   adjust_self = visitors.AdjustSelf(force=True)
   adjust_self.class_types.append(visitors.ClassAsType(cls))
   for const in cls.constants:
     c = self._LookupIfSimpleCall(const.type)
     if c:
       signatures = c.methods[0].signatures
       self._processed_count[c.name] += 1
       new_method = pytd.Function(const.name, signatures, c.methods[0].kind)
       new_methods.append(new_method.Visit(adjust_self))
     else:
       new_constants.append(const)  # keep
   return cls.Replace(constants=tuple(new_constants),
                      methods=tuple(new_methods))