Source code for imports.imports

from collections import defaultdict

import dis
import functools
import glob
import operator
import os
import pip
import re


'''Main module'''


def _excluded_imports(env_path='.env'):
    out = ['pip', 'setuptools']

    if not os.path.isfile(env_path):
        return out

    with open(env_path, 'r') as env_file:
        env = [x.strip() for x in env_file.readlines()]
        packages = [ln.split('=')[1].split(',') for ln in env]
        out = out + functools.reduce(operator.concat, packages)

    return out


def _list_installed_packages():
    packages = ["{}".format(i.key) for i in pip.get_installed_distributions()]

    return set(packages)


def _list_dependencies(packages_list):
    with_dependencies = {}
    no_dependencies = []
    for package in sorted(packages_list):
        req = pip._vendor.pkg_resources.working_set.by_key[package].requires()

        if req:
            with_dependencies[package] = [r.name for r in req]
        else:
            no_dependencies.append(package)

    return with_dependencies, no_dependencies


def _find_modules(python_file):
    with open(python_file, 'r') as s:
        statements = s.read()

        instructions = dis.get_instructions(statements)
        imports = [__ for __ in instructions if 'IMPORT' in __.opname]

        grouped = defaultdict(list)
        for instr in imports:
            grouped[instr.opname].append(instr.argval.split('.')[0])

    return grouped['IMPORT_NAME']


def _iter_modules(location):
    all_imports = []

    root_dir = os.path.join(location, '**/[!.]*.py')

    for python_file in glob.iglob(root_dir, recursive=True):
        all_imports += _find_modules(str(python_file))

    return set(all_imports)


def _load_requirements(requirements_name, path_dir):

    root_dir = os.path.join(path_dir, requirements_name)

    try:
        with open(root_dir, 'r') as f:
            requirements = [re.match(r'^(\w+(-\w+)*)', line).group(0)
                            for line in f]
    except FileNotFoundError:
        requirements = []

    return requirements


# def _usage():
#     print("Usage: imports <PATH_TO_PROJECT_FOLDER>")
#     print("For more information, please see README file.")
#     sys.exit(1)


[docs]def check(path_dir, requirements_name='requirements.txt'): '''Look for unused packages listed on project requirements''' requirements = _load_requirements(requirements_name, path_dir) imported_modules = _iter_modules(path_dir) installed_packages = _list_installed_packages() imported_modules.update(_excluded_imports()) diff = {lib for lib in installed_packages if lib not in imported_modules} with_dependencies, _ = _list_dependencies(diff) unused_dependencies = sorted([d for d in diff if d in requirements]) for unused_dependency in unused_dependencies: if with_dependencies.get(unused_dependency): print(' - {}'.format(unused_dependency)) for dependency in with_dependencies.get(unused_dependency): print('\t - {}'.format(dependency)) else: print(' - {}'.format(unused_dependency))