import importlib import importlib.util import os import glob import json from typing import Iterator import requests import dbt.exceptions import dbt.semver PYPI_VERSION_URL = 'https://pypi.org/pypi/dbt/json' def get_latest_version(): try: resp = requests.get(PYPI_VERSION_URL) data = resp.json() version_string = data['info']['version'] except (json.JSONDecodeError, KeyError, requests.RequestException): return None return dbt.semver.VersionSpecifier.from_version_string(version_string) def get_installed_version(): return dbt.semver.VersionSpecifier.from_version_string(__version__) def get_version_information(): installed = get_installed_version() latest = get_latest_version() installed_s = installed.to_version_string(skip_matcher=True) if latest is None: latest_s = 'unknown' else: latest_s = latest.to_version_string(skip_matcher=True) version_msg = ("installed version: {}\n" " latest version: {}\n\n".format(installed_s, latest_s)) plugin_version_msg = "Plugins:\n" for plugin_name, version in _get_dbt_plugins_info(): plugin_version_msg += ' - {plugin_name}: {version}\n'.format( plugin_name=plugin_name, version=version ) if latest is None: return ("{}The latest version of dbt could not be determined!\n" "Make sure that the following URL is accessible:\n{}\n\n{}" .format(version_msg, PYPI_VERSION_URL, plugin_version_msg)) if installed == latest: return "{}Up to date!\n\n{}".format(version_msg, plugin_version_msg) elif installed > latest: return ("{}Your version of dbt is ahead of the latest " "release!\n\n{}".format(version_msg, plugin_version_msg)) else: return ("{}Your version of dbt is out of date! " "You can find instructions for upgrading here:\n" "https://docs.getdbt.com/docs/installation\n\n{}" .format(version_msg, plugin_version_msg)) def _get_adapter_plugin_names() -> Iterator[str]: spec = importlib.util.find_spec('dbt.adapters') # If None, then nothing provides an importable 'dbt.adapters', so we will # not be reporting plugin versions today if spec is None or spec.submodule_search_locations is None: return for adapters_path in spec.submodule_search_locations: version_glob = os.path.join(adapters_path, '*', '__version__.py') for version_path in glob.glob(version_glob): # the path is like .../dbt/adapters/{plugin_name}/__version__.py # except it could be \\ on windows! plugin_root, _ = os.path.split(version_path) _, plugin_name = os.path.split(plugin_root) yield plugin_name def _get_dbt_plugins_info(): for plugin_name in _get_adapter_plugin_names(): if plugin_name == 'core': continue try: mod = importlib.import_module( f'dbt.adapters.{plugin_name}.__version__' ) except ImportError: # not an adpater continue yield plugin_name, mod.version __version__ = '0.21.1' installed = get_installed_version()