예제 #1
0
def test_scan_for_import_issues_comprehension_subscript_attribute_missing_1():
    code = dedent("""
        [123 for xx[0].yy in []]
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == [(2, DottedIdentifier('xx'))]
    assert unused == []
예제 #2
0
def test_scan_for_import_issues_comprehension_subscript_1():
    code = dedent("""
        x = []
        [123 for x[0] in []]
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == []
    assert unused == []
예제 #3
0
def test_scan_for_import_issues_class_subclass_imported_class_1():
    code = dedent("""
        from m1 import C1
        class C1(C1): pass
    """)
    missing, unused = scan_for_import_issues(code)
    assert missing == []
    assert unused == []
예제 #4
0
def test_scan_for_import_issues_setcomp_unused_1():
    code = dedent("""
        import x1, x2
        {x2 for x1 in []}
    """)
    missing, unused = scan_for_import_issues(code)
    assert missing == []
    assert unused == [(2, Import('import x1'))]
예제 #5
0
def test_scan_for_import_issues_setcomp_missing_1():
    code = dedent("""
        y1 = 1234
        {(x1,y1,z1) for x1,x2 in []}
    """)
    missing, unused = scan_for_import_issues(code)
    assert unused == []
    assert missing == [(3, DottedIdentifier('z1'))]
예제 #6
0
def test_scan_for_import_issues_dictcomp_unused_1():
    code = dedent("""
        import x1, x2, x3
        {123:x3 for x1,x2 in []}
    """)
    missing, unused = scan_for_import_issues(code)
    assert missing == []
    assert unused == [(2, Import('import x1')), (2, Import('import x2'))]
예제 #7
0
def test_scan_for_import_issues_dict_comprehension_subscript_attribute_1():
    code = dedent("""
        xx = []
        {123:456 for xx[0].yy in []}
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == []
    assert unused == []
예제 #8
0
def test_scan_for_import_issues_comprehension_attribute_1():
    code = dedent("""
        xx = []
        [123 for xx.yy in []]
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == []
    assert unused == []
예제 #9
0
def test_scan_for_import_issues_brace_identifiers_bad_1():
    code = dedent("""
        import x1, x2, x3
        def f():
            '''{x1} {x3} {if}'''
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == []
    assert unused == [(2, Import('import x2'))]
예제 #10
0
def test_scan_for_import_issues_comprehension_attribute_complex_1():
    code = dedent("""
        dd = []
        [(aa,bb) for (bb, cc.cx, dd.dx) in []]
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == [(3, DottedIdentifier('aa')),
                       (3, DottedIdentifier('cc.cx'))]
    assert unused == []
예제 #11
0
def test_scan_for_import_issues_star_import_1():
    code = dedent("""
        import x1, y1
        x1, x2
        from m2 import *
        x3
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == [(3, DottedIdentifier('x2'))]
    assert unused == [(2, Import('import y1'))]
예제 #12
0
def test_scan_for_import_issues_use_then_del_in_func_2():
    code = dedent("""
        def f1():
            x1 = 123
            print(x1); print(x2)
            del x1
    """)
    missing, unused = scan_for_import_issues(code)
    assert missing == [(4, DottedIdentifier('x2'))]
    assert unused == []
예제 #13
0
def test_scan_for_import_issues_use_then_del_in_func_1():
    code = dedent("""
        def f1():
            x1 = 1
            x1
            del x1
    """)
    missing, unused = scan_for_import_issues(code)
    assert missing == []
    assert unused == []
예제 #14
0
def test_scan_for_import_issues_star_import_deferred_1():
    code = dedent("""
        import x1, y1
        def f1():
            x1, x2
        from m2 import *
        def f1():
            x3
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == []
    assert unused == [(2, Import('import y1'))]
예제 #15
0
def test_scan_for_import_issues_star_import_local_1():
    code = dedent("""
        import x1, y1
        def f1():
            from m2 import *
            x2
        def f2():
            x3
        x1, x4
    """)
    missing, unused = scan_for_import_issues(code, parse_docstrings=True)
    assert missing == [(7, DottedIdentifier('x3')),
                       (8, DottedIdentifier('x4'))]
    assert unused == [(2, Import('import y1'))]
예제 #16
0
def fix_unused_and_missing_imports(codeblock,
                                   add_missing=True,
                                   remove_unused="AUTOMATIC",
                                   add_mandatory=True,
                                   db=None,
                                   params=None):
    r"""
    Check for unused and missing imports, and fix them automatically.

    Also formats imports.

    In the example below, C{m1} and C{m3} are unused, so are automatically
    removed.  C{np} was undefined, so an C{import numpy as np} was
    automatically added.

      >>> codeblock = PythonBlock(
      ...     'from foo import m1, m2, m3, m4\n'
      ...     'm2, m4, np.foo', filename="/tmp/foo.py")

      >>> print fix_unused_and_missing_imports(codeblock, add_mandatory=False)
      [PYFLYBY] /tmp/foo.py: removed unused 'from foo import m1'
      [PYFLYBY] /tmp/foo.py: removed unused 'from foo import m3'
      [PYFLYBY] /tmp/foo.py: added 'import numpy as np'
      import numpy as np
      from foo import m2, m4
      m2, m4, np.foo

    @type codeblock:
      L{PythonBlock} or convertible (C{str})
    @rtype:
      L{PythonBlock}
    """
    codeblock = PythonBlock(codeblock)
    if remove_unused == "AUTOMATIC":
        fn = codeblock.filename
        remove_unused = not (fn and (fn.base == "__init__.py"
                                     or ".pyflyby" in str(fn).split("/")))
    elif remove_unused is True or remove_unused is False:
        pass
    else:
        raise ValueError("Invalid remove_unused=%r" % (remove_unused, ))
    params = ImportFormatParams(params)
    db = ImportDB.interpret_arg(db, target_filename=codeblock.filename)
    # Do a first pass reformatting the imports to get rid of repeated or
    # shadowed imports, e.g. L1 here:
    #   import foo  # L1
    #   import foo  # L2
    #   foo         # L3
    codeblock = reformat_import_statements(codeblock, params=params)

    filename = codeblock.filename
    transformer = SourceToSourceFileImportsTransformation(codeblock)
    missing_imports, unused_imports = scan_for_import_issues(
        codeblock, find_unused_imports=remove_unused, parse_docstrings=True)
    logger.debug("missing_imports = %r", missing_imports)
    logger.debug("unused_imports = %r", unused_imports)
    if remove_unused and unused_imports:
        # Go through imports to remove.  [This used to be organized by going
        # through import blocks and removing all relevant blocks from there,
        # but if one removal caused problems the whole thing would fail.  The
        # CPU cost of calling without_imports() multiple times isn't worth
        # that.]
        # TODO: don't remove unused mandatory imports.  [This isn't
        # implemented yet because this isn't necessary for __future__ imports
        # since they aren't reported as unused, and those are the only ones we
        # have by default right now.]
        for lineno, imp in unused_imports:
            try:
                imp = transformer.remove_import(imp, lineno)
            except NoSuchImportError:
                logger.error(
                    "%s: couldn't remove import %r",
                    filename,
                    imp,
                )
            except LineNumberNotFoundError as e:
                logger.error("%s: unused import %r on line %d not global",
                             filename, str(imp), e.args[0])
            else:
                logger.info("%s: removed unused '%s'", filename, imp)

    if add_missing and missing_imports:
        missing_imports.sort(key=lambda k: (k[1], k[0]))
        known = db.known_imports.by_import_as
        # Decide on where to put each import to be added.  Find the import
        # block with the longest common prefix.  Tie-break by preferring later
        # blocks.
        added_imports = set()
        for lineno, ident in missing_imports:
            import_as = ident.parts[0]
            try:
                imports = known[import_as]
            except KeyError:
                logger.warning(
                    "%s:%s: undefined name %r and no known import for it",
                    filename, lineno, import_as)
                continue
            if len(imports) != 1:
                logger.error("%s: don't know which of %r to use", filename,
                             imports)
                continue
            imp_to_add = imports[0]
            if imp_to_add in added_imports:
                continue
            transformer.add_import(imp_to_add, lineno)
            added_imports.add(imp_to_add)
            logger.info("%s: added %r", filename,
                        imp_to_add.pretty_print().strip())

    if add_mandatory:
        # Todo: allow not adding to empty __init__ files?
        mandatory = db.mandatory_imports.imports
        for imp in mandatory:
            try:
                transformer.add_import(imp)
            except ImportAlreadyExistsError:
                pass
            else:
                logger.info("%s: added mandatory %r", filename,
                            imp.pretty_print().strip())

    return transformer.output(params=params)