Ejemplo n.º 1
0
 def insert_new_blocks_after_comments(self, blocks):
     blocks = [SourceToSourceTransformationBase(block) for block in blocks]
     if isinstance(self.blocks[0], SourceToSourceImportBlockTransformation):
         # Kludge.  We should add an "output" attribute to
         # SourceToSourceImportBlockTransformation and enumerate over that,
         # instead of enumerating over the input below.
         self.blocks[0:0] = blocks
         return
     # Get the "statements" in the first block.
     statements = self.blocks[0].input.statements
     # Find the insertion point.
     for idx, statement in enumerate(statements):
         if not statement.is_comment_or_blank_or_string_literal:
             if idx == 0:
                 # First block starts with a noncomment, so insert before
                 # it.
                 self.blocks[0:0] = blocks
             else:
                 # Found a non-comment after comment, so break it up and
                 # insert in the middle.
                 self.blocks[:1] = ([
                     SourceToSourceTransformation(
                         PythonBlock.concatenate(statements[:idx],
                                                 assume_contiguous=True))
                 ] + blocks + [
                     SourceToSourceTransformation(
                         PythonBlock.concatenate(statements[idx:],
                                                 assume_contiguous=True))
                 ])
             break
     else:
         # First block is entirely comments, so just insert after it.
         self.blocks[1:1] = blocks
Ejemplo n.º 2
0
def find_unused_and_missing_imports(codeblock):
    """
    Find unused imports and missing imports, taking docstrings into account.

    Pyflakes is used to statically analyze for unused and missing imports.
    Doctests in docstrings are analyzed as code and epydoc references in
    docstrings also prevent removal.

    In the following example, 'bar' is not considered unused because there is
    a string that references it in braces:

      >>> find_unused_and_missing_imports("import foo as bar, baz\\n'{bar}'\\n")
      ([('baz', 1)], [])

    @type codeblock:
      L{PythonBlock} or convertible
    @return:
      C{(unused_imports, missing_imports)} where C{unused_imports} and
      C{missing_imports} each are sequences of C{(import_as, lineno)} tuples.
    """
    # TODO: rewrite this using our own AST parser.
    # Once we do that we can also process doctests and literal brace
    # identifiers at the proper scope level.  We should treat both doctests
    # and literal brace identifiers as "soft" uses that don't trigger missing
    # imports but do trigger as used imports.
    codeblock = PythonBlock(codeblock)
    # Run pyflakes on the main code.
    unused_imports, missing_imports = (
        _pyflakes_find_unused_and_missing_imports(codeblock))
    # Find doctests.
    doctest_blocks = codeblock.get_doctests()
    if doctest_blocks:
        # There are doctests.  Re-run pyflakes on main code + doctests.  Don't
        # report missing imports in doctests, but do treat existing imports as
        # 'used' if they are used in doctests.
        # Create one data structure to pass to pyflakes.  This is going to
        # screw up linenos but we won't use them, so it doesn't matter.
        bigblock = PythonBlock.concatenate([codeblock] + doctest_blocks,
                                           assume_contiguous=True)
        wdt_unused_imports, _ = (  # wdt = with doc tests
            _pyflakes_find_unused_and_missing_imports(bigblock))
        wdt_unused_asimports = set(import_as
                                   for import_as, lineno in wdt_unused_imports)
        # Keep only the intersection of unused imports.
        unused_imports = [(import_as, lineno)
                          for import_as, lineno in unused_imports
                          if import_as in wdt_unused_asimports]
    # Find literal brace identifiers like "... L{Foo} ...".
    # TODO: merge this into our own AST-based missing/unused-import-finder
    # (replacing pyflakes).
    literal_brace_identifiers = set(iden for f in codeblock.string_literals()
                                    for iden in brace_identifiers(f.s))
    if literal_brace_identifiers:
        # Pyflakes doesn't look at docstrings containing references like
        # "L{foo}" which require an import, nor at C{str.format} strings like
        # '''"{foo}".format(...)'''.  Don't remove supposedly-unused imports
        # which match a string literal brace identifier.
        unused_imports = [(import_as, lineno)
                          for import_as, lineno in unused_imports
                          if import_as not in literal_brace_identifiers]
    return unused_imports, missing_imports