From 7d24afb1e4eb51a78f8e9dcdf33d3718f1e9d75e Mon Sep 17 00:00:00 2001 From: Evan Van Dam Date: Mon, 29 Oct 2018 20:26:40 -0400 Subject: [PATCH] Faster checks for installed packages/versions. `conda install` is slow. `conda list` is much faster for looking up installed packages. `conda search` is good for finding available versions. Use these instead to get a huge speed boost! --- conda.py | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/conda.py b/conda.py index 6ccddb5..06ef06e 100755 --- a/conda.py +++ b/conda.py @@ -71,6 +71,7 @@ from distutils.spawn import find_executable +from distutils.version import LooseVersion import os.path import json from ansible.module_utils.basic import AnsibleModule @@ -91,7 +92,7 @@ def run_package_operation(conda, name, version, state, dry_run, command_runner, :param on_failure: method that takes any kwargs to be called on failure :param on_success: method that takes any kwargs to be called on success """ - correct_version_installed = check_package_installed(command_runner, conda, name, version) + correct_version_installed = check_package_installed(command_runner, conda, name, version, state) # TODO: State should be an "enum" (or whatever the Py2.7 equivalent is) if not correct_version_installed and state != 'absent': @@ -112,25 +113,47 @@ def run_package_operation(conda, name, version, state, dry_run, command_runner, on_success(changed=False) -def check_package_installed(command_runner, conda, name, version): +def check_package_installed(command_runner, conda, name, version, state): """ Check whether a package with the given name and version is installed. :param command_runner: method that executes a given Conda command (given as list of string arguments), which returns JSON and returns a tuple where the first argument is the outputted JSON and the second is anything written to stderr :param name: the name of the package to check if installed :param version: the version of the package to check if installed (`None` if check for latest) + :param state: The state the package should be in :return: `True` if a package with the given name and version is installed :raises CondaUnexpectedOutputError: if the JSON returned by Conda was unexpected """ - output, stderr = run_conda_package_command( - command_runner, name, version, [conda, 'install', '--json', '--dry-run', get_install_target(name, version)]) + list_output, _ = run_conda_package_command( + command_runner, name, version, [conda, 'list', '-f', '--json', name]) - if 'message' in output and output['message'] == 'All requested packages already installed.': - return True - elif 'actions' in output and len(output['actions']) > 0: + if not list_output: + # Conda list didn't find the package installed. return False - else: - raise CondaUnexpectedOutputError(output, stderr) + + installed_version = list_output[0]['version'] + if state == 'present': + if version: + # Check the version matches the requested version. + return installed_version == version + else: + # We don't care what the version is, just that it's installed. + return True + elif state == 'latest': + # Use `conda search` to find the versions available. + latest_output, _ = run_conda_package_command( + command_runner, name, version, [conda, 'search', '--json', name]) + if name in latest_output: + # Get the latest version and check if the installed version matches. + sorted_versions = sorted(latest_output[name], + key=lambda x: LooseVersion(x['version']), + reverse=True) + latest_version = sorted_versions[0]['version'] + return installed_version == latest_version + else: + raise CondaPackageNotFoundError(name, version) + + return False def install_package(command_runner, conda, name, version=None, dry_run=False):