<commit_before>"""
Update MSL packages.
"""
import os
import sys
import subprocess
from pkg_resources import parse_version

from colorama import Fore

from . import utils, _PKG_NAME


def update(*names, **kwargs):
    """Update MSL packages.

    MSL packages can be updated from PyPI packages_ (only if a release has been
    uploaded to PyPI) or from GitHub repositories_.

    .. note::
       If the MSL packages_ are available on PyPI then PyPI is used as the default
       URI_ to update the package. If you want to force the update to occur
       from the ``master`` branch of the GitHub `repository <https://github.com/MSLNZ>`_
       then set ``branch='master'``. If the package is not available on PyPI
       then the ``master`` branch is used as the default update URI_.

    .. _repositories: https://github.com/MSLNZ
    .. _packages: https://pypi.org/search/?q=%22Measurement+Standards+Laboratory+of+New+Zealand%22
    .. _URI: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier

    Parameters
    ----------
    *names : :class:`str`
        The name(s) of the MSL package(s) to update. If not specified then
        update all MSL packages. The ``msl-`` prefix can be omitted (e.g.,
        ``'loadlib'`` is equivalent to ``'msl-loadlib'``). Also accepts
        shell-style wildcards (e.g., ``'pr-*'``).
    **kwargs
        * branch : :class:`str`
            The name of a GitHub branch to use for the update. If :data:`None`, and no
            `tag` value has also been specified, then updates the package using the
            ``master`` branch. Default is :data:`None`.
        * tag : :class:`str`
            The name of a GitHub tag to use for the update. Default is :data:`None`.
        * update_cache : :class:`bool`
            The information about the MSL packages_ that are available on PyPI and about
            the repositories_ that are available on GitHub are cached to use for subsequent
            calls to this function. After 24 hours the cache is automatically updated. Set
            `update_cache` to be :data:`True` to force the cache to be updated when you call
            this function. Default is :data:`False`.
        * yes : :class:`bool`
            If :data:`True` then don't ask for confirmation before updating.
            The default is :data:`False` (ask before updating).

        .. attention::
           Cannot specify both a `branch` and a `tag` simultaneously.

        .. important::
           If you specify a `branch` or a `tag` then the update will be forced.
    """
    # TODO Python 2.7 does not support named arguments after using *args
    #  we can define yes=False, branch=None, tag=None, update_cache=False in the
    #  function signature if we choose to drop support for Python 2.7
    utils._check_kwargs(kwargs, {'yes', 'branch', 'tag', 'update_cache'})

    yes = kwargs.get('yes', False)
    branch = kwargs.get('branch', None)
    tag = kwargs.get('tag', None)
    update_cache = kwargs.get('update_cache', False)

    zip_name = utils._get_github_zip_name(branch, tag)
    if zip_name is None:
        return

    pkgs_installed = utils.installed()
    pkgs_github = utils.github(update_cache=update_cache)
    pkgs_pypi = utils.pypi(update_cache=update_cache)
    if not pkgs_github and not pkgs_pypi:
        return

    if not names:
        packages = pkgs_installed  # update all installed packages
    else:
        packages = utils._check_wildcards_and_prefix(names, pkgs_installed)

    w = [0, 0]
    pkgs_to_update = dict()
    for name, values in packages.items():

        err_msg = 'Cannot update {!r} -- '.format(name)

        if name not in pkgs_installed:
            utils.log.error(err_msg + 'The package is not installed')
            continue

        installed_version = pkgs_installed[name]['version']

        # use PyPI to update the package (only if the package is available on PyPI)
        using_pypi = name in pkgs_pypi and tag is None and branch is None
        repo_name = pkgs_installed[name]['repo_name']

        extras_require = values['extras_require'] if values.get('extras_require') is not None else ''

        if tag is not None:
            if tag in pkgs_github[repo_name]['tags']:
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': '[tag:{}]'.format(tag),
                    'repo_name': repo_name,
                }
            else:
                utils.log.error(err_msg + 'The {!r} tag does not exist'.format(tag))
                continue
        elif branch is not None:
            if branch in pkgs_github[repo_name]['branches']:
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': '[branch:{}]'.format(branch),
                    'repo_name': repo_name,
                }
            else:
                utils.log.error(err_msg + 'The {!r} branch does not exist'.format(branch))
                continue
        else:
            if using_pypi:
                version = pkgs_pypi[name]['version']
            else:
                version = pkgs_github[repo_name]['version']

            if not version:
                # a version number must exist on PyPI, so if this occurs it must be for a github repo
                utils.log.error(err_msg + 'The GitHub repository does not contain a release. Specify a branch or a tag')
                continue
            elif values.get('version_requested'):
                # this elif must come before the parse_version check
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': values['version_requested'],
                    'repo_name': repo_name,
                }
            elif parse_version(version) > parse_version(installed_version):
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': version,
                    'repo_name': repo_name,
                }
            else:
                utils.log.warning('The {!r} package is already the latest [{}]'.format(name, installed_version))
                continue<commit_msg>check if an MSL was installed in "editable" mode<commit_after>"""
Update MSL packages.
"""
import os
import sys
import subprocess
from pkg_resources import parse_version

from colorama import Fore

from . import utils, _PKG_NAME


def update(*names, **kwargs):
    """Update MSL packages.

    MSL packages can be updated from PyPI packages_ (only if a release has been
    uploaded to PyPI) or from GitHub repositories_.

    .. note::
       If the MSL packages_ are available on PyPI then PyPI is used as the default
       URI_ to update the package. If you want to force the update to occur
       from the ``master`` branch of the GitHub `repository <https://github.com/MSLNZ>`_
       then set ``branch='master'``. If the package is not available on PyPI
       then the ``master`` branch is used as the default update URI_.

    .. _repositories: https://github.com/MSLNZ
    .. _packages: https://pypi.org/search/?q=%22Measurement+Standards+Laboratory+of+New+Zealand%22
    .. _URI: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier

    Parameters
    ----------
    *names : :class:`str`
        The name(s) of the MSL package(s) to update. If not specified then
        update all MSL packages. The ``msl-`` prefix can be omitted (e.g.,
        ``'loadlib'`` is equivalent to ``'msl-loadlib'``). Also accepts
        shell-style wildcards (e.g., ``'pr-*'``).
    **kwargs
        * branch : :class:`str`
            The name of a GitHub branch to use for the update. If :data:`None`, and no
            `tag` value has also been specified, then updates the package using the
            ``master`` branch. Default is :data:`None`.
        * tag : :class:`str`
            The name of a GitHub tag to use for the update. Default is :data:`None`.
        * update_cache : :class:`bool`
            The information about the MSL packages_ that are available on PyPI and about
            the repositories_ that are available on GitHub are cached to use for subsequent
            calls to this function. After 24 hours the cache is automatically updated. Set
            `update_cache` to be :data:`True` to force the cache to be updated when you call
            this function. Default is :data:`False`.
        * yes : :class:`bool`
            If :data:`True` then don't ask for confirmation before updating.
            The default is :data:`False` (ask before updating).

        .. attention::
           Cannot specify both a `branch` and a `tag` simultaneously.

        .. important::
           If you specify a `branch` or a `tag` then the update will be forced.
    """
    # TODO Python 2.7 does not support named arguments after using *args
    #  we can define yes=False, branch=None, tag=None, update_cache=False in the
    #  function signature if we choose to drop support for Python 2.7
    utils._check_kwargs(kwargs, {'yes', 'branch', 'tag', 'update_cache'})

    yes = kwargs.get('yes', False)
    branch = kwargs.get('branch', None)
    tag = kwargs.get('tag', None)
    update_cache = kwargs.get('update_cache', False)

    zip_name = utils._get_github_zip_name(branch, tag)
    if zip_name is None:
        return

    pkgs_installed = utils.installed()
    pkgs_github = utils.github(update_cache=update_cache)
    pkgs_pypi = utils.pypi(update_cache=update_cache)
    if not pkgs_github and not pkgs_pypi:
        return

    if not names:
        packages = pkgs_installed  # update all installed packages
    else:
        packages = utils._check_wildcards_and_prefix(names, pkgs_installed)

    w = [0, 0]
    pkgs_to_update = dict()
    for name, values in packages.items():

        err_msg = 'Cannot update {!r} -- '.format(name)

        if name not in pkgs_installed:
            utils.log.error(err_msg + 'The package is not installed')
            continue

        installed_version = pkgs_installed[name]['version']

        # use PyPI to update the package (only if the package is available on PyPI)
        using_pypi = name in pkgs_pypi and tag is None and branch is None
        repo_name = pkgs_installed[name]['repo_name']

        extras_require = values['extras_require'] if values.get('extras_require') is not None else ''

        if tag is not None:
            if tag in pkgs_github[repo_name]['tags']:
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': '[tag:{}]'.format(tag),
                    'repo_name': repo_name,
                }
            else:
                utils.log.error(err_msg + 'The {!r} tag does not exist'.format(tag))
                continue
        elif branch is not None:
            if branch in pkgs_github[repo_name]['branches']:
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': '[branch:{}]'.format(branch),
                    'repo_name': repo_name,
                }
            else:
                utils.log.error(err_msg + 'The {!r} branch does not exist'.format(branch))
                continue
        else:
            if using_pypi:
                version = pkgs_pypi[name]['version']
            else:
                # an MSL package could have been installed in "editable" mode, i.e., pip install -e .
                # and therefore it might only exist locally until it is pushed to the repository
                repo = pkgs_github.get(repo_name)
                if not repo:
                    utils.log.error(err_msg + 'The {!r} repository does not exist'.format(repo_name))
                    continue
                version = repo['version']

            if not version:
                # a version number must exist on PyPI, so if this occurs it must be for a github repo
                utils.log.error(err_msg + 'The GitHub repository does not contain a release. Specify a branch or a tag')
                continue
            elif values.get('version_requested'):
                # this elif must come before the parse_version check
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': values['version_requested'],
                    'repo_name': repo_name,
                }
            elif parse_version(version) > parse_version(installed_version):
                pkgs_to_update[name] = {
                    'installed_version': installed_version,
                    'using_pypi': using_pypi,
                    'extras_require': extras_require,
                    'version': version,
                    'repo_name': repo_name,
                }
            else:
                utils.log.warning('The {!r} package is already the latest [{}]'.format(name, installed_version))
                continue
{'fraction_non_alphanumeric': 0.09356810244470315, 'fraction_numerical': 0.0017462165308498253, 'mean_word_length': 2.0537658298155965, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6789051', 'n_tokens_mistral': 3901, 'n_tokens_neox': 3634, 'n_words': 1258}