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 == []
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 == []
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 == []
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'))]
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'))]
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'))]
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 == []
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 == []
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'))]
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 == []
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'))]
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 == []
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 == []
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'))]
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'))]
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)