def config_command(_): """Print current configuration :param _: Command arguments :return: None """ printer = Printer() message = Config.to_json() printer.print_message(message=message)
def cyclic_command(args, packages=None, exit_on_failure=True): """Runs detection of cyclical dependencies :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ ignore_list = ( args.ignore or Config.ignore_list or [] ) printer = Printer() packages = ( packages or dependency_list(ignore_list=ignore_list) ) cyclic_paths = cyclic_dependencies(packages=packages) if cyclic_paths: printer.error(messages.CYCLIC_FOUND) for path in cyclic_paths: if path: path.append(path[0]) path_message = ' -> '.join(map(lambda p: p.key, path)) printer.info(message=path_message) if exit_on_failure: sys.exit(1) return False printer.success(messages.CYCLIC_OK) return True
def graph_command(args, packages=None, exit_on_failure=True): """Return or render a dependency graph :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ ignore_list = (args.ignore or Config.ignore_list or []) name = args.name or Config.graph_name filename = args.filename or Config.graph_filename file_format = args.format or Config.graph_format engine = args.engine or Config.graph_engine strict = args.strict or Config.graph_strict graph_attr = args.graph_attr or Config.graph_attributes node_attr = args.node_attr or Config.graph_node_attributes edge_attr = args.edge_attr or Config.graph_edge_attributes view = args.view or False render = args.render or False printer = Printer() packages = (packages or dependency_list(ignore_list=ignore_list)) graph = get_graph( packages=packages, name=name, filename=filename, file_format=file_format, engine=engine, strict=strict, graph_attr=graph_attr, node_attr=node_attr, edge_attr=edge_attr, ) if render: try: filepath = render_graph(graph=graph, view=view) printer.success( messages.GRAPH_EXPORTED.format(file_path=filepath, format=file_format)) except Exception as e: printer.error(messages.GRAPH_FAILED_TO_RENDER.format(error=e)) return sys.exit(1) if exit_on_failure else '' else: printer.print_message(message=graph)
def conflicts_command(args, packages=None, exit_on_failure=True): """Runs detection of dependency conflicts :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ ignore_list = (args.ignore or Config.ignore_list or []) printer = Printer() packages = (packages or dependency_list(ignore_list=ignore_list)) conflicting = [ (package, required_by) for package, required_by in conflicting_dependencies(packages=packages) ] headers = [ messages.PACKAGE, messages.INSTALLED, messages.REQUIRED, messages.REQUIRED_BY, ] tabular_data = [[ printer.colored_message(message=package.key, message_color=printer.color_package), package.version_id, required_version, required_by.key, ] for package, requirement in conflicting for required_by, required_version in requirement] if tabular_data: printer.error(messages.CONFLICTS_FOUND) printer.table(headers=headers, tabular_data=tabular_data) if exit_on_failure: sys.exit(1) return False printer.success(messages.CONFLICTS_OK) return True
def validate_files(files, printer=None, exit_on_failure=True): """Validate all provided files exist :param files: List of files :param printer: Printer object :param exit_on_failure: Enable/disable exiting application on failure :return: Whether the files are valid """ invalid_files = [] printer = printer or Printer() for file_ in files: if not os.path.exists(file_): if exit_on_failure: printer.error('File "{}" not found'.format(file_)) invalid_files.append(file_) return (True if not invalid_files else sys.exit(1) if exit_on_failure else False)
def check_requirement_version_mismatch(requirements, locked, printer=None): """Run validation that checks if there are version mismatches between requirements and locked requirements :param requirements: Collection of requirements :param locked: Collection of locked requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() locked_versions = required_version_mismatch( requirements=requirements, locked=locked ) headers = [ messages.PACKAGE, messages.REQUIRED, messages.LOCKED, ] tabular_data = [ [ printer.colored_message( message=requirement.key, message_color=printer.color_package ), requirement.specified_version, required, ] for requirement, required in locked_versions ] if tabular_data: printer.error(messages.REQUIREMENT_VERSION_MISMATCH_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.REQUIREMENT_VERSION_MISMATCH_OK) return True
def check_unnecessary_packages( packages, requirements, locked, printer=None): """Run validation that checks if there are unnecessary installed packages in the environment that are not required by anything :param packages: Collection of packages :param requirements: Collection of requirements :param locked: Collection of locked requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() lock_not_required = unnecessary_packages( packages=packages, requirements=requirements, locked=locked ) headers = [ messages.PACKAGE, messages.INSTALLED, ] tabular_data = [ [ printer.colored_message( message=package.key, message_color=printer.color_package ), package.version_id, ] for package in lock_not_required ] if tabular_data: printer.warning(messages.PACKAGE_NOT_REQUIRED_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.PACKAGE_NOT_REQUIRED_OK) return True
def check_unnecessary_locks(requirements, locked, ignore_list, printer=None): """Run validation that checks if there are unnecessary locked requirements that are not required by anything :param requirements: Collection of requirements :param locked: Collection of locked requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() lock_not_required = unnecessary_locks( requirements=requirements, locked=locked, ignore_list=ignore_list, ) headers = [ messages.PACKAGE, messages.LOCKED, ] tabular_data = [ [ printer.colored_message( message=requirement.key, message_color=printer.color_package ), requirement.specified_version, ] for requirement in lock_not_required ] if tabular_data: printer.warning(messages.LOCK_NOT_REQUIRED_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.LOCK_NOT_REQUIRED_OK) return True
def check_package_version_mismatch(packages, locked, printer=None): """Run validation that checks if there are mismatches between installed packages and locked requirements :param packages: Collection of packages :param locked: Collection of locked requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() locked_versions = lock_version_mismatch( packages=packages, locked=locked ) headers = [ messages.PACKAGE, messages.INSTALLED, messages.REQUIRED, ] tabular_data = [ [ printer.colored_message( message=package.key, message_color=printer.color_package ), package.version_id, required, ] for package, required in locked_versions ] if tabular_data: printer.error(messages.PACKAGE_VERSION_MISMATCH_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.PACKAGE_VERSION_MISMATCH_OK) return True
def lock_command(args, packages=None, exit_on_failure=True): """Display or save locked requirements for current environment :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ requirements_files = (args.requirements or Config.requirements_files or []) ignore_list = (args.ignore or Config.ignore_list or []) save_lock = args.save or False lock_filepath = args.file or Config.lock_file_path printer = Printer() if not validate_files(files=requirements_files, printer=printer, exit_on_failure=exit_on_failure): return False requirements = RequirementCollection() for requirements_file in requirements_files: requirements.extend( RequirementCollection.from_file(filepath=requirements_file)) lock_ignore_list = [ key for key in ignore_list if key not in requirements.keys() ] packages = (packages or dependency_list(ignore_list=lock_ignore_list)) locked = locked_requirements(packages=packages, requirements=requirements) if save_lock: locked.save_lock_file(filepath=lock_filepath) printer.success(message=messages.LOCK_EXPORTED.format( file_path=lock_filepath)) else: for item in locked: printer.info("{}=={}".format( printer.colored_message(item.key, color.DEFAULT_PACKAGE), item.version_id)) return True
def check_unset_locks(requirements, locked, printer=None): """Run validation that checks if there are requirements missing in locked requirements :param requirements: Collection of requirements :param locked: Collection of locked requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() locks_unset = unset_locks(requirements=requirements, locked=locked) headers = [ messages.PACKAGE, messages.INSTALLED, ] tabular_data = [ [ printer.colored_message( message=package.key, message_color=printer.color_package ), package.version_id, ] for package in locks_unset ] if tabular_data: printer.error(messages.UNSET_LOCKS_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.UNSET_LOCKS_OK) return True
def check_unlocked_requirements(requirements, printer=None): """Run validation that checks if there are requirements that are not locked to a version :param requirements: Collection of requirements :param printer: Printer object :return: Whether the validation was successful """ printer = printer or Printer() requirements_unlocked = unlocked_requirements(requirements=requirements) headers = [ messages.PACKAGE, messages.INSTALLED, ] tabular_data = [ [ printer.colored_message( message=package.key, message_color=printer.color_package ), package.version_id, ] for package in requirements_unlocked ] if tabular_data: printer.error(messages.UNLOCKED_REQUIREMENTS_FOUND) printer.table( headers=headers, tabular_data=tabular_data ) return False printer.success(messages.UNLOCKED_REQUIREMENTS_OK) return True
def list_command(args, packages=None, exit_on_failure=True): """List installed dependencies with configurable filters :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ requirements_files = (args.requirements or Config.requirements_files or []) ignore_list = (args.ignore or Config.ignore_list or []) printer = Printer() if not validate_files(files=requirements_files, printer=printer, exit_on_failure=exit_on_failure): return False requirements = RequirementCollection() for requirements_file in requirements_files: requirements.extend( RequirementCollection.from_file(filepath=requirements_file)) requirements = requirements if requirements else None packages = (packages or dependency_list(ignore_list=ignore_list, requirements=requirements)) headers = [messages.PACKAGE, messages.INSTALLED] tabular_data = [[ printer.colored_message(message=package.key, message_color=printer.color_package), package.version_id ] for package in packages] if tabular_data: printer.table(headers=headers, tabular_data=tabular_data) else: printer.info(messages.PACKAGES_NOT_FOUND) return True
import os import io import json import tempfile import contextlib import pip import pip.req import pipdeptree from dante.core.printer import Printer from dante.core.utils import filter_tree VERSION_ANY = 'Any' printer = Printer() def get_package_tree(ignore_list=None, include_only=None): """Returns dependency package tree :param ignore_list: list of dependencies to exclude from tree :param include_only: list of dependencies to include if :return: dictionary of top level packages with their dependencies """ ignore_list = [i.lower() for i in ignore_list] if ignore_list else [] include_only = [i.lower() for i in include_only] if include_only else [] packages = [ package for package in pip.get_installed_distributions() if package.key not in ignore_list ]
def exists(self, **printer_info): info = self._default() info.update(printer_info) printer = Printer(**info) return printer
def missing_requirements_command(args, packages=None, exit_on_failure=True): """Runs detection of required packages that are not installed :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ requirements_files = ( args.requirements or Config.requirements_files or [] ) ignore_list = ( args.ignore or Config.ignore_list or [] ) printer = Printer() if not validate_files( files=requirements_files, printer=printer, exit_on_failure=exit_on_failure): return False requirements = RequirementCollection() for requirements_file in requirements_files: requirements.extend( RequirementCollection.from_file(filepath=requirements_file) ) packages = ( packages or dependency_list(ignore_list=ignore_list) ) missing = [ (package, required_by) for package, required_by in missing_requirements( packages=packages, requirements=requirements, ignore_list=ignore_list ) ] headers = [ messages.PACKAGE, messages.REQUIRED, messages.REQUIRED_BY, ] tabular_data = [] for package, requirers in missing: if requirers: for required_by, required_version in requirers: tabular_data.append([ printer.colored_message( message=package.key, message_color=printer.color_package ), required_version, required_by.key, ]) else: tabular_data.append([ printer.colored_message( message=package.key, message_color=printer.color_package ), package.version.specifier, "Requirements", ]) if tabular_data: printer.error(messages.MISSING_FOUND) printer.table(headers=headers, tabular_data=tabular_data) if exit_on_failure: sys.exit(1) return False printer.success(messages.MISSING_OK) return True
def tree_command(args, packages=None, exit_on_failure=True): """Display dependency tree for a single package or the entire environment :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ package_key = args.package requirements_files = (args.requirements or Config.requirements_files or []) ignore_list = (args.ignore or Config.ignore_list or []) printer = Printer() if not validate_files(files=requirements_files, printer=printer, exit_on_failure=exit_on_failure): return False requirements = RequirementCollection() for requirements_file in requirements_files: requirements.extend( RequirementCollection.from_file(filepath=requirements_file)) package_string = '{package} [{installed}: {version}]' requirement_string = ( '{spacing}{package} [{installed}: {version} | {required}: {spec}]') packages = (packages or dependency_list(ignore_list=ignore_list)) if package_key: package = packages.get(key=package_key) if not package: printer.error( messages.PACKAGE_NOT_FOUND.format(package=package_key)) sys.exit(1) tree = {package: package_dependency_tree(dependency=package)} else: tree = dependency_tree( packages=packages, requirements=requirements if requirements else None) if not tree: printer.info(messages.PACKAGES_NOT_FOUND) def print_dependency_tree(requirements_list, indent=0): spacing = ' ' * indent for requirement in requirements_list: printer.info( requirement_string.format( spacing=spacing, package=printer.colored_message( message=requirement.key, message_color=printer.color_package), installed=messages.INSTALLED, version=requirement.version_id, required=messages.REQUIRED, spec=requirement.specified_version), ) print_dependency_tree(requirements_list[requirement], indent + 2) for dependency in tree: printer.info( package_string.format(package=printer.colored_message( message=dependency.key, message_color=printer.color_package), installed=messages.INSTALLED, version=dependency.version)) print_dependency_tree(tree[dependency], indent=2) return True
def validate_command(args, packages=None, exit_on_failure=True): """Runs requirement file validation :param args: Command arguments :param packages: Collection of packages :param exit_on_failure: Enable/disable exiting application on failure :return: None """ strict = args.strict or False ignore_list = ( args.ignore or Config.ignore_list or [] ) requirements_files = ( args.requirements or Config.requirements_files or [] ) lock_files = ( args.lock or Config.lock_files or [] ) printer = Printer() if not validate_files( files=requirements_files, printer=printer, exit_on_failure=exit_on_failure ) or not validate_files( files=lock_files, printer=printer, exit_on_failure=exit_on_failure): return False try: requirements = RequirementCollection.from_files( filepaths=requirements_files ) locked = RequirementCollection.from_files(filepaths=lock_files) except Exception as e: # Always exit on invalid requirements printer.error('{}: {}'.format(messages.REQUIREMENTS_PARSING_ERROR, e)) sys.exit(1) checks_ok = [] packages = ( packages or dependency_list(ignore_list=ignore_list) ) checks_ok.append(check_unlocked_requirements( requirements=requirements, printer=printer, )) checks_ok.append(check_unset_locks( requirements=requirements, locked=locked, printer=printer, )) checks_ok.append(check_package_version_mismatch( packages=packages, locked=locked, printer=printer, )) checks_ok.append(check_requirement_version_mismatch( requirements=requirements, locked=locked, printer=printer, )) if strict: checks_ok.append(check_unnecessary_packages( packages=packages, requirements=requirements, locked=locked, printer=printer, )) checks_ok.append(check_unnecessary_locks( requirements=requirements, locked=locked, ignore_list=ignore_list, printer=printer, )) return ( sys.exit(1) if exit_on_failure else False if not all(checks_ok) else True )