<commit_before>'''
Install Python packages with pip to either the system or a virtualenv
'''
# Import Python libs
import os
import tempfile
import shutil
# Import Salt libs
from salt.exceptions import CommandExecutionError

def _get_pip_bin(bin_env):
    '''
    Return the pip command to call, either from a virtualenv, an argument
    passed in, or from the global modules options
    '''
    if not bin_env:
        return __salt__['cmd.which_bin'](['pip2', 'pip', 'pip-python'])

    # try to get pip bin from env
    if os.path.isdir(bin_env):
        pip_bin = os.path.join(bin_env, 'bin', 'pip')
        if os.path.isfile(pip_bin):
            return pip_bin
    return bin_env

def install(pkgs=None,
            requirements=None,
            env=None,
            bin_env=None,
            log=None,
            proxy=None,
            timeout=None,
            editable=None,
            find_links=None,
            index_url=None,
            extra_index_url=None,
            no_index=False,
            mirrors=None,
            build=None,
            target=None,
            download=None,
            download_cache=None,
            source=None,
            upgrade=False,
            force_reinstall=False,
            ignore_installed=False,
            no_deps=False,
            no_install=False,
            no_download=False,
            install_options=None,
            runas=None,
            cwd=None):
    '''
    Install packages with pip

    Install packages individually or from a pip requirements file. Install
    packages globally or to a virtualenv.

    pkgs
        comma separated list of packages to install
    requirements
        path to requirements
    bin_env
        path to pip bin or path to virtualenv. If doing a system install,
        and want to use a specific pip bin (pip-2.7, pip-2.6, etc..) just
        specify the pip bin you want.
        If installing into a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    env
        deprecated, use bin_env now
    log
        Log file where a complete (maximum verbosity) record will be kept
    proxy
        Specify a proxy in the form
        user:passwd@proxy.server:port. Note that the
        user:password@ is optional and required only if you
        are behind an authenticated proxy.  If you provide
        user@proxy.server:port then you will be prompted for a
        password.
    timeout
        Set the socket timeout (default 15 seconds)
    editable
        install something editable(ie git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed)
    find_links
        URL to look for packages at
    index_url
        Base URL of Python Package Index
    extra_index_url
        Extra URLs of package indexes to use in addition to ``index_url``
    no_index
        Ignore package index
    mirrors
        Specific mirror URLs to query (automatically adds --use-mirrors)
    build
        Unpack packages into ``build`` dir
    target
        Install packages into ``target`` dir
    download
        Download packages into ``download`` instead of installing them
    download_cache
        Cache downloaded packages in ``download_cache`` dir
    source
        Check out ``editable`` packages into ``source`` dir
    upgrade
        Upgrade all packages to the newest available version
    force_reinstall
        When upgrading, reinstall all packages even if they are already up-to-date.
    ignore_installed
        Ignore the installed packages (reinstalling instead)
    no_deps
        Ignore package dependencies
    no_install
        Download and unpack all packages, but don't actually install them
    no_download
        Don't download any packages, just install the ones
        already downloaded (completes an install run with
        --no-install)
    install_options
        Extra arguments to be supplied to the setup.py install
        command (use like --install-option="--install-
        scripts=/usr/local/bin").  Use multiple --install-
        option options to pass multiple options to setup.py
        install.  If you are using an option with a directory
        path, be sure to use absolute path.
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.install <package name>,<package2 name>

        salt '*' pip.install requirements=/path/to/requirements.txt

        salt '*' pip.install <package name> bin_env=/path/to/virtualenv

        salt '*' pip.install <package name> bin_env=/path/to/pip_bin

    Comlicated CLI example::

        salt '*' pip.install markdown,django editable=git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed upgrade=True no_deps=True

    '''
    # Switching from using `pip_bin` and `env` to just `bin_env`
    # cause using an env and a pip bin that's not in the env could
    # be problematic.
    # Still using the `env` variable, for backwards compatiblity sake
    # but going fwd you should specify either a pip bin or an env with
    # the `bin_env` argument and we'll take care of the rest.
    if env and not bin_env:
        bin_env = env

    cmd = '{0} install'.format(_get_pip_bin(bin_env))

    if pkgs:
        pkg = pkgs.replace(",", " ")
        cmd = '{cmd} {pkg} '.format(
            cmd=cmd, pkg=pkg)

    if requirements:
        if requirements.startswith('salt://'):
            req = __salt__['cp.cache_file'](requirements)
            fd_, treq = tempfile.mkstemp()
            os.close(fd_)
            shutil.copyfile(req, treq)
        cmd = '{cmd} --requirement "{requirements}" '.format(
            cmd=cmd, requirements=treq)

    if log:
        try:
            # TODO make this check if writeable
            os.path.exists(log)
        except IOError:
            raise IOError("'%s' is not writeable" % log)
        cmd = '{cmd} --log {log} '.format(
            cmd=cmd, log=log)

    if proxy:
        cmd = '{cmd} --proxy={proxy} '.format(
            cmd=cmd, proxy=proxy)

    if timeout:
        try:
            int(timeout)
        except ValueError:
            raise ValueError("'%s' is not a valid integer base 10.")
        cmd = '{cmd} --timeout={timeout} '.format(
            cmd=cmd, timeout=timeout)

    if editable:
        if editable.find('egg') == -1:
            raise Exception('You must specify an egg for this editable')
        cmd = '{cmd} --editable={editable} '.format(
            cmd=cmd, editable=editable)

    if find_links:
        if not find_links.startswith("http://"):
            raise Exception("'%s' must be a valid url" % find_links)
        cmd = '{cmd} --find-links={find_links}'.format(
            cmd=cmd, find_links=find_links)

    if index_url:
        if not index_url.startswith("http://"):
            raise Exception("'%s' must be a valid url" % index_url)
        cmd = '{cmd} --index-url="{index_url}" '.format(
            cmd=cmd, index_url=index_url)

    if extra_index_url:
        if not extra_index_url.startswith("http://"):
            raise Exception("'%s' must be a valid url" % extra_index_url)
        cmd = '{cmd} --extra-index_url="{extra_index_url}" '.format(
            cmd=cmd, extra_index_url=extra_index_url)

    if no_index:
        cmd = '{cmd} --no-index '.format(cmd=cmd)

    if mirrors:
        if not mirrors.startswith("http://"):
            raise Exception("'%s' must be a valid url" % mirrors)
        cmd = '{cmd} --use-mirrors --mirrors={mirrors} '.format(
            cmd=cmd, mirrors=mirrors)

    if build:
        cmd = '{cmd} --build={build} '.format(
            cmd=cmd, build=build)

    if target:
        cmd = '{cmd} --target={target} '.format(
            cmd=cmd, target=target)

    if download:
        cmd = '{cmd} --download={download} '.format(
            cmd=cmd, download=download)

    if download_cache:
        cmd = '{cmd} --download_cache={download_cache} '.format(
            cmd=cmd, download_cache=download_cache)

    if source:
        cmd = '{cmd} --source={source} '.format(
            cmd=cmd, source=source)

    if upgrade:
        cmd = '{cmd} --upgrade '.format(cmd=cmd)

    if force_reinstall:
        cmd = '{cmd} --force-reinstall '.format(cmd=cmd)

    if ignore_installed:
        cmd = '{cmd} --ignore-installed '.format(cmd=cmd)

    if no_deps:
        cmd = '{cmd} --no-deps '.format(cmd=cmd)

    if no_install:
        cmd = '{cmd} --no-install '.format(cmd=cmd)

    if no_download:
        cmd = '{cmd} --no-download '.format(cmd=cmd)

    if install_options:
        cmd = '{cmd} --install-options={install_options} '.format(
            cmd=cmd, install_options=install_options)

    result = __salt__['cmd.run'](cmd, runas=runas, cwd=cwd)

    if requirements:
        try:
            os.remove(treq)
        except Exception:
            pass

    return result

def uninstall(pkgs=None,
              requirements=None,
              bin_env=None,
              log=None,
              proxy=None,
              timeout=None,
              runas=None,
              cwd=None):
    '''
    Uninstall packages with pip

    Uninstall packages individually or from a pip requirements file. Uninstall
    packages globally or from a virtualenv.

    pkgs
        comma separated list of packages to install
    requirements
        path to requirements
    bin_env
        path to pip bin or path to virtualenv. If doing an uninstall from
        the system python and want to use a specific pip bin (pip-2.7,
        pip-2.6, etc..) just specify the pip bin you want.
        If uninstalling from a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    log
        Log file where a complete (maximum verbosity) record will be kept
    proxy
        Specify a proxy in the form
        user:passwd@proxy.server:port. Note that the
        user:password@ is optional and required only if you
        are behind an authenticated proxy.  If you provide
        user@proxy.server:port then you will be prompted for a
        password.
    timeout
        Set the socket timeout (default 15 seconds)
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.uninstall <package name>,<package2 name>

        salt '*' pip.uninstall requirements=/path/to/requirements.txt

        salt '*' pip.uninstall <package name> bin_env=/path/to/virtualenv

        salt '*' pip.uninstall <package name> bin_env=/path/to/pip_bin

    '''
    cmd = '{0} uninstall -y '.format(_get_pip_bin(bin_env))

    if pkgs:
        pkg = pkgs.replace(",", " ")
        cmd = '{cmd} {pkg} '.format(
            cmd=cmd, pkg=pkg)

    if requirements:
        if requirements.startswith('salt://'):
            req = __salt__['cp.cache_file'](requirements)
            fd_, treq = tempfile.mkstemp()
            os.close(fd_)
            shutil.copyfile(req, treq)
        cmd = '{cmd} --requirements "{requirements}" '.format(
            cmd=cmd, requirements=treq)

    if log:
        try:
            # TODO make this check if writeable
            os.path.exists(log)
        except IOError:
            raise IOError("'%s' is not writeable" % log)
        cmd = '{cmd} --{log} '.format(
            cmd=cmd, log=log)

    if proxy:
        cmd = '{cmd} --proxy={proxy} '.format(
            cmd=cmd, proxy=proxy)

    if timeout:
        try:
            int(timeout)
        except ValueError:
            raise ValueError("'%s' is not a valid integer base 10.")
        cmd = '{cmd} --timeout={timeout} '.format(
            cmd=cmd, timeout=timeout)

    result = __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split('\n')

    if requirements:
        try:
            os.remove(treq)
        except Exception:
            pass

    return result

def freeze(bin_env=None,
           runas=None,
           cwd=None):
    '''
    Return a list of installed packages either globally or in the specified
    virtualenv

    bin_env
        path to pip bin or path to virtualenv. If doing an uninstall from
        the system python and want to use a specific pip bin (pip-2.7,
        pip-2.6, etc..) just specify the pip bin you want.
        If uninstalling from a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.freeze /home/code/path/to/virtualenv/
    '''

    pip_bin = _get_pip_bin(bin_env)
    activate = os.path.join(os.path.dirname(pip_bin), 'activate')
    if not os.path.isfile(activate):
        raise CommandExecutionError(
            "Could not find the path to the virtualenv's 'activate' binary"
        )

    cmd = 'source {0}; {1} freeze'.format(activate, pip_bin)
    return __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split('\n')

def list(prefix='',
         bin_env=None,
         runas=None,
         cwd=None):
    '''
    Filter list of instaslled apps from ``freeze`` and check to see if ``prefix``
    exists in the list of packages installed.

    CLI Example::

        salt '*' pip.list salt
    '''
    packages = {}
    cmd = '{0} freeze'.format(_get_pip_bin(bin_env))
    for line in __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split("\n"):
        if line.startswith('-e'):
            line = line.split('-e ')[1]
            line, name = line.split('#egg=')
            packages[name]=line

        elif len(line.split("==")) >= 2:
            name = line.split("==")[0]
            version = line.split("==")[1]
            if prefix:
                if line.lower().startswith(prefix.lower()):
                    packages[name]=version
            else:
                packages[name]=version
    return packages
<commit_msg>Fix for ValueError in #1922<commit_after>'''
Install Python packages with pip to either the system or a virtualenv
'''
# Import Python libs
import os
import tempfile
import shutil
# Import Salt libs
from salt.exceptions import CommandExecutionError

def _get_pip_bin(bin_env):
    '''
    Return the pip command to call, either from a virtualenv, an argument
    passed in, or from the global modules options
    '''
    if not bin_env:
        return __salt__['cmd.which_bin'](['pip2', 'pip', 'pip-python'])

    # try to get pip bin from env
    if os.path.isdir(bin_env):
        pip_bin = os.path.join(bin_env, 'bin', 'pip')
        if os.path.isfile(pip_bin):
            return pip_bin
    return bin_env

def install(pkgs=None,
            requirements=None,
            env=None,
            bin_env=None,
            log=None,
            proxy=None,
            timeout=None,
            editable=None,
            find_links=None,
            index_url=None,
            extra_index_url=None,
            no_index=False,
            mirrors=None,
            build=None,
            target=None,
            download=None,
            download_cache=None,
            source=None,
            upgrade=False,
            force_reinstall=False,
            ignore_installed=False,
            no_deps=False,
            no_install=False,
            no_download=False,
            install_options=None,
            runas=None,
            cwd=None):
    '''
    Install packages with pip

    Install packages individually or from a pip requirements file. Install
    packages globally or to a virtualenv.

    pkgs
        comma separated list of packages to install
    requirements
        path to requirements
    bin_env
        path to pip bin or path to virtualenv. If doing a system install,
        and want to use a specific pip bin (pip-2.7, pip-2.6, etc..) just
        specify the pip bin you want.
        If installing into a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    env
        deprecated, use bin_env now
    log
        Log file where a complete (maximum verbosity) record will be kept
    proxy
        Specify a proxy in the form
        user:passwd@proxy.server:port. Note that the
        user:password@ is optional and required only if you
        are behind an authenticated proxy.  If you provide
        user@proxy.server:port then you will be prompted for a
        password.
    timeout
        Set the socket timeout (default 15 seconds)
    editable
        install something editable(ie git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed)
    find_links
        URL to look for packages at
    index_url
        Base URL of Python Package Index
    extra_index_url
        Extra URLs of package indexes to use in addition to ``index_url``
    no_index
        Ignore package index
    mirrors
        Specific mirror URLs to query (automatically adds --use-mirrors)
    build
        Unpack packages into ``build`` dir
    target
        Install packages into ``target`` dir
    download
        Download packages into ``download`` instead of installing them
    download_cache
        Cache downloaded packages in ``download_cache`` dir
    source
        Check out ``editable`` packages into ``source`` dir
    upgrade
        Upgrade all packages to the newest available version
    force_reinstall
        When upgrading, reinstall all packages even if they are already up-to-date.
    ignore_installed
        Ignore the installed packages (reinstalling instead)
    no_deps
        Ignore package dependencies
    no_install
        Download and unpack all packages, but don't actually install them
    no_download
        Don't download any packages, just install the ones
        already downloaded (completes an install run with
        --no-install)
    install_options
        Extra arguments to be supplied to the setup.py install
        command (use like --install-option="--install-
        scripts=/usr/local/bin").  Use multiple --install-
        option options to pass multiple options to setup.py
        install.  If you are using an option with a directory
        path, be sure to use absolute path.
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.install <package name>,<package2 name>

        salt '*' pip.install requirements=/path/to/requirements.txt

        salt '*' pip.install <package name> bin_env=/path/to/virtualenv

        salt '*' pip.install <package name> bin_env=/path/to/pip_bin

    Comlicated CLI example::

        salt '*' pip.install markdown,django editable=git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed upgrade=True no_deps=True

    '''
    # Switching from using `pip_bin` and `env` to just `bin_env`
    # cause using an env and a pip bin that's not in the env could
    # be problematic.
    # Still using the `env` variable, for backwards compatiblity sake
    # but going fwd you should specify either a pip bin or an env with
    # the `bin_env` argument and we'll take care of the rest.
    if env and not bin_env:
        bin_env = env

    cmd = '{0} install'.format(_get_pip_bin(bin_env))

    if pkgs:
        pkg = pkgs.replace(",", " ")
        cmd = '{cmd} {pkg} '.format(
            cmd=cmd, pkg=pkg)

    if requirements:
        if requirements.startswith('salt://'):
            req = __salt__['cp.cache_file'](requirements)
            fd_, treq = tempfile.mkstemp()
            os.close(fd_)
            shutil.copyfile(req, treq)
        else:
            treq = requirements
        cmd = '{cmd} --requirement "{requirements}" '.format(
            cmd=cmd, requirements=treq)

    if log:
        try:
            # TODO make this check if writeable
            os.path.exists(log)
        except IOError:
            raise IOError("'%s' is not writeable" % log)
        cmd = '{cmd} --log {log} '.format(
            cmd=cmd, log=log)

    if proxy:
        cmd = '{cmd} --proxy={proxy} '.format(
            cmd=cmd, proxy=proxy)

    if timeout:
        try:
            int(timeout)
        except ValueError:
            raise ValueError("'%s' is not a valid integer base 10.")
        cmd = '{cmd} --timeout={timeout} '.format(
            cmd=cmd, timeout=timeout)

    if editable:
        if editable.find('egg') == -1:
            raise Exception('You must specify an egg for this editable')
        cmd = '{cmd} --editable={editable} '.format(
            cmd=cmd, editable=editable)

    if find_links:
        if not find_links.startswith("http://"):
            raise Exception("'%s' must be a valid url" % find_links)
        cmd = '{cmd} --find-links={find_links}'.format(
            cmd=cmd, find_links=find_links)

    if index_url:
        if not index_url.startswith("http://"):
            raise Exception("'%s' must be a valid url" % index_url)
        cmd = '{cmd} --index-url="{index_url}" '.format(
            cmd=cmd, index_url=index_url)

    if extra_index_url:
        if not extra_index_url.startswith("http://"):
            raise Exception("'%s' must be a valid url" % extra_index_url)
        cmd = '{cmd} --extra-index_url="{extra_index_url}" '.format(
            cmd=cmd, extra_index_url=extra_index_url)

    if no_index:
        cmd = '{cmd} --no-index '.format(cmd=cmd)

    if mirrors:
        if not mirrors.startswith("http://"):
            raise Exception("'%s' must be a valid url" % mirrors)
        cmd = '{cmd} --use-mirrors --mirrors={mirrors} '.format(
            cmd=cmd, mirrors=mirrors)

    if build:
        cmd = '{cmd} --build={build} '.format(
            cmd=cmd, build=build)

    if target:
        cmd = '{cmd} --target={target} '.format(
            cmd=cmd, target=target)

    if download:
        cmd = '{cmd} --download={download} '.format(
            cmd=cmd, download=download)

    if download_cache:
        cmd = '{cmd} --download_cache={download_cache} '.format(
            cmd=cmd, download_cache=download_cache)

    if source:
        cmd = '{cmd} --source={source} '.format(
            cmd=cmd, source=source)

    if upgrade:
        cmd = '{cmd} --upgrade '.format(cmd=cmd)

    if force_reinstall:
        cmd = '{cmd} --force-reinstall '.format(cmd=cmd)

    if ignore_installed:
        cmd = '{cmd} --ignore-installed '.format(cmd=cmd)

    if no_deps:
        cmd = '{cmd} --no-deps '.format(cmd=cmd)

    if no_install:
        cmd = '{cmd} --no-install '.format(cmd=cmd)

    if no_download:
        cmd = '{cmd} --no-download '.format(cmd=cmd)

    if install_options:
        cmd = '{cmd} --install-options={install_options} '.format(
            cmd=cmd, install_options=install_options)

    result = __salt__['cmd.run'](cmd, runas=runas, cwd=cwd)

    if requirements:
        try:
            os.remove(treq)
        except Exception:
            pass

    return result

def uninstall(pkgs=None,
              requirements=None,
              bin_env=None,
              log=None,
              proxy=None,
              timeout=None,
              runas=None,
              cwd=None):
    '''
    Uninstall packages with pip

    Uninstall packages individually or from a pip requirements file. Uninstall
    packages globally or from a virtualenv.

    pkgs
        comma separated list of packages to install
    requirements
        path to requirements
    bin_env
        path to pip bin or path to virtualenv. If doing an uninstall from
        the system python and want to use a specific pip bin (pip-2.7,
        pip-2.6, etc..) just specify the pip bin you want.
        If uninstalling from a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    log
        Log file where a complete (maximum verbosity) record will be kept
    proxy
        Specify a proxy in the form
        user:passwd@proxy.server:port. Note that the
        user:password@ is optional and required only if you
        are behind an authenticated proxy.  If you provide
        user@proxy.server:port then you will be prompted for a
        password.
    timeout
        Set the socket timeout (default 15 seconds)
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.uninstall <package name>,<package2 name>

        salt '*' pip.uninstall requirements=/path/to/requirements.txt

        salt '*' pip.uninstall <package name> bin_env=/path/to/virtualenv

        salt '*' pip.uninstall <package name> bin_env=/path/to/pip_bin

    '''
    cmd = '{0} uninstall -y '.format(_get_pip_bin(bin_env))

    if pkgs:
        pkg = pkgs.replace(",", " ")
        cmd = '{cmd} {pkg} '.format(
            cmd=cmd, pkg=pkg)

    if requirements:
        if requirements.startswith('salt://'):
            req = __salt__['cp.cache_file'](requirements)
            fd_, treq = tempfile.mkstemp()
            os.close(fd_)
            shutil.copyfile(req, treq)
        cmd = '{cmd} --requirements "{requirements}" '.format(
            cmd=cmd, requirements=treq)

    if log:
        try:
            # TODO make this check if writeable
            os.path.exists(log)
        except IOError:
            raise IOError("'%s' is not writeable" % log)
        cmd = '{cmd} --{log} '.format(
            cmd=cmd, log=log)

    if proxy:
        cmd = '{cmd} --proxy={proxy} '.format(
            cmd=cmd, proxy=proxy)

    if timeout:
        try:
            int(timeout)
        except ValueError:
            raise ValueError("'%s' is not a valid integer base 10.")
        cmd = '{cmd} --timeout={timeout} '.format(
            cmd=cmd, timeout=timeout)

    result = __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split('\n')

    if requirements:
        try:
            os.remove(treq)
        except Exception:
            pass

    return result

def freeze(bin_env=None,
           runas=None,
           cwd=None):
    '''
    Return a list of installed packages either globally or in the specified
    virtualenv

    bin_env
        path to pip bin or path to virtualenv. If doing an uninstall from
        the system python and want to use a specific pip bin (pip-2.7,
        pip-2.6, etc..) just specify the pip bin you want.
        If uninstalling from a virtualenv, just use the path to the virtualenv
        (/home/code/path/to/virtualenv/)
    runas
        User to run pip as
    cwd
        Current working directory to run pip from

    CLI Example::

        salt '*' pip.freeze /home/code/path/to/virtualenv/
    '''

    pip_bin = _get_pip_bin(bin_env)
    activate = os.path.join(os.path.dirname(pip_bin), 'activate')
    if not os.path.isfile(activate):
        raise CommandExecutionError(
            "Could not find the path to the virtualenv's 'activate' binary"
        )

    cmd = 'source {0}; {1} freeze'.format(activate, pip_bin)
    return __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split('\n')

def list(prefix='',
         bin_env=None,
         runas=None,
         cwd=None):
    '''
    Filter list of instaslled apps from ``freeze`` and check to see if ``prefix``
    exists in the list of packages installed.

    CLI Example::

        salt '*' pip.list salt
    '''
    packages = {}
    cmd = '{0} freeze'.format(_get_pip_bin(bin_env))
    for line in __salt__['cmd.run'](cmd, runas=runas, cwd=cwd).split("\n"):
        if line.startswith('-e'):
            line = line.split('-e ')[1]
            line, name = line.split('#egg=')
            packages[name]=line

        elif len(line.split("==")) >= 2:
            name = line.split("==")[0]
            version = line.split("==")[1]
            if prefix:
                if line.lower().startswith(prefix.lower()):
                    packages[name]=version
            else:
                packages[name]=version
    return packages
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Manage ruby installations and gemsets with RVM, the Ruby Version Manager.
'''

# Import python libs
import re
import os
import logging
try:
    from shlex import quote as _cmd_quote  # pylint: disable=E0611
except ImportError:
    from pipes import quote as _cmd_quote

log = logging.getLogger(__name__)

# Don't shadow built-in's.
__func_alias__ = {
    'list_': 'list'
}

__opts__ = {
    'rvm.runas': None,
}

def _get_rvm_location(runas=None):
    if runas:
        runas_home = os.path.expanduser('~{0}'.format(runas))
        rvmpath = '{0}/.rvm/bin/rvm'.format(runas_home)
        if os.path.exists(rvmpath):
            return rvmpath
    return '/usr/local/rvm/bin/rvm'

def _rvm(command, arguments=None, runas=None, cwd=None):
    if runas is None:
        runas = __salt__['config.option']('rvm.runas')
    if not is_installed(runas):
        return False

    cmd = [_get_rvm_location(runas), _cmd_quote(command)]
    if arguments:
        cmd.extend([_cmd_quote(arg) for arg in arguments.split()])

    ret = __salt__['cmd.run_all'](' '.join(cmd), runas=runas, cwd=cwd)

    if ret['retcode'] == 0:
        return ret['stdout']
    return False

def _rvm_do(ruby, command, runas=None, cwd=None):
    return _rvm('{ruby} do {command}'.
                format(ruby=ruby or 'default', command=command),
                runas=runas, cwd=cwd)

def is_installed(runas=None):
    '''
    Check if RVM is installed.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.is_installed
    '''
    return __salt__['cmd.has_exec'](_get_rvm_location(runas))

def install(runas=None):
    '''
    Install RVM system wide.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.install
    '''
    # RVM dependencies on Ubuntu 10.04:
    #   bash coreutils gzip bzip2 gawk sed curl git-core subversion
    installer = 'https://raw.githubusercontent.com/wayneeseguin/rvm/master/binscripts/rvm-installer'
    ret = __salt__['cmd.run_all'](
        # the RVM installer automatically does a multi-user install when it is
        # invoked with root privileges
        'curl -Ls {installer} | bash -s stable'.format(installer=installer),
        runas=runas,
        python_shell=True
    )
    if ret['retcode'] > 0:
        log.debug(
            'Error while downloading the RVM installer. Command '
            'returned: {0!r}'.format(ret)
        )
        return False
    return True

def install_ruby(ruby, runas=None):
    '''
    Install a ruby implementation.

    ruby
        The version of ruby to install.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.install_ruby 1.9.3-p385
    '''
    # MRI/RBX/REE dependencies for Ubuntu 10.04:
    #   build-essential openssl libreadline6 libreadline6-dev curl
    #   git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0
    #   libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libc6-dev
    #   libncurses5-dev automake libtool bison subversion ruby
    if runas and runas != 'root':
        _rvm('autolibs disable', ruby, runas=runas)
        return _rvm('install --disable-binary', ruby, runas=runas)
    else:
        return _rvm('install', ruby, runas=runas)

def reinstall_ruby(ruby, runas=None):
    '''
    Reinstall a ruby implementation.

    ruby
        The version of ruby to reinstall.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.reinstall_ruby 1.9.3-p385
    '''
    return _rvm('reinstall', ruby, runas=runas)

def list_(runas=None):
    '''
    List all rvm installed rubies.

    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.list
    '''
    rubies = []
    output = _rvm('list', '', runas=runas)
    if output:
        regex = re.compile(r'^[= ]([*> ]) ([^- ]+)-([^ ]+) \[ (.*) \]')
        for line in output.splitlines():
            match = regex.match(line)
            if match:
                rubies.append([
                    match.group(2), match.group(3), match.group(1) == '*'
                ])
    return rubies

def set_default(ruby, runas=None):
    '''
    Set the default ruby.

    ruby
        The version of ruby to make the default.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.set_default 2.0.0
    '''
    return _rvm('alias',
                'create default {ruby}'.format(ruby=ruby), runas=runas)

def get(version='stable', runas=None):
    '''
    Update RVM.

    version : stable
        Which version of RVM to install, e.g. stable or head.
    ruby
        The version of ruby to reinstall.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.get
    '''
    return _rvm('get', version, runas=runas)

def wrapper(ruby_string, wrapper_prefix, runas=None, *binaries):
    '''
    Install RVM wrapper scripts.

    ruby_string
        Ruby/gemset to install wrappers for.
    wrapper_prefix
        What to prepend to the name of the generated wrapper binaries.
    runas : None
        The user to run rvm as.
    binaries : None
        The names of the binaries to create wrappers for. When nothing is
        given, wrappers for ruby, gem, rake, irb, rdoc, ri and testrb are
        generated.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.wrapper <ruby_string> <wrapper_prefix>
    '''
    return _rvm('wrapper',
                '{ruby_string} {wrapper_prefix} {binaries}'.format(
                    ruby_string=ruby_string,
                    wrapper_prefix=wrapper_prefix,
                    binaries=' '.join(binaries)
                ),
                runas=runas)

def rubygems(ruby, version, runas=None):
    '''
    Installs a specific rubygems version in the given ruby.

    ruby
        The ruby to install rubygems for.
    version
        The version of rubygems to install or 'remove' to use the version that
        ships with 1.9
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.rubygems 2.0.0 1.8.24
    '''
    return _rvm_do(ruby, 'rubygems {0}'.format(version), runas=runas)

def gemset_create(ruby, gemset, runas=None):
    '''
    Creates a gemset.

    ruby
        The ruby version to create the gemset for.
    gemset
        The name of the gemset to create.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_create 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm gemset create {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_list(ruby='default', runas=None):
    '''
    List all gemsets for the given ruby.

    ruby : default
        The ruby version to list the gemsets for
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_list
    '''
    gemsets = []
    output = _rvm_do(ruby, 'rvm gemset list', runas=runas)
    if output:
        regex = re.compile('^   ([^ ]+)')
        for line in output.splitlines():
            match = regex.match(line)
            if match:
                gemsets.append(match.group(1))
    return gemsets

def gemset_delete(ruby, gemset, runas=None):
    '''
    Deletes a gemset.

    ruby
        The ruby version the gemset belongs to.
    gemset
        The gemset to delete.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_delete 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm --force gemset delete {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_empty(ruby, gemset, runas=None):
    '''
    Remove all gems from a gemset.

    ruby
        The ruby version the gemset belongs to.
    gemset
        The gemset to empty.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_empty 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm --force gemset empty {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_copy(source, destination, runas=None):
    '''
    Copy all gems from one gemset to another.

    source
        The name of the gemset to copy, complete with ruby version.
    destination
        The destination gemset.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_copy foobar bazquo
    '''
    return _rvm('gemset copy {0} {1}'.format(source, destination), runas=runas)

def gemset_list_all(runas=None):
    '''
    List all gemsets for all installed rubies.

    Note that you must have set a default ruby before this can work.

    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_list_all
    '''
    gemsets = {}
    current_ruby = None
    output = _rvm_do('default', 'rvm gemset list_all', runas=runas)
    if output:
        gems_regex = re.compile('^   ([^ ]+)')
        gemset_regex = re.compile('^gemsets for ([^ ]+)')
        for line in output.splitlines():
            match = gemset_regex.match(line)
            if match:
                current_ruby = match.group(1)
                gemsets[current_ruby] = []
            match = gems_regex.match(line)
            if match:
                gemsets[current_ruby].append(match.group(1))
    return gemsets

def do(ruby, command, runas=None, cwd=None):  # pylint: disable=C0103
    '''
    Execute a command in an RVM controlled environment.

    ruby:
        The ruby to use.
    command:
        The command to execute.
    runas : None
        The user to run rvm as.
    cwd : None
        The current working directory.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.do 2.0.0 <command>
    '''
    return _rvm_do(ruby, command, runas=runas, cwd=cwd)
<commit_msg>separate command from arguments to avoid problems when adding quotes<commit_after># -*- coding: utf-8 -*-
'''
Manage ruby installations and gemsets with RVM, the Ruby Version Manager.
'''

# Import python libs
import re
import os
import logging
try:
    from shlex import quote as _cmd_quote  # pylint: disable=E0611
except ImportError:
    from pipes import quote as _cmd_quote

log = logging.getLogger(__name__)

# Don't shadow built-in's.
__func_alias__ = {
    'list_': 'list'
}

__opts__ = {
    'rvm.runas': None,
}

def _get_rvm_location(runas=None):
    if runas:
        runas_home = os.path.expanduser('~{0}'.format(runas))
        rvmpath = '{0}/.rvm/bin/rvm'.format(runas_home)
        if os.path.exists(rvmpath):
            return rvmpath
    return '/usr/local/rvm/bin/rvm'

def _rvm(command, arguments=None, runas=None, cwd=None):
    if runas is None:
        runas = __salt__['config.option']('rvm.runas')
    if not is_installed(runas):
        return False

    cmd = [_get_rvm_location(runas), _cmd_quote(command)]
    if arguments:
        cmd.extend([_cmd_quote(arg) for arg in arguments.split()])

    ret = __salt__['cmd.run_all'](' '.join(cmd), runas=runas, cwd=cwd)

    if ret['retcode'] == 0:
        return ret['stdout']
    return False

def _rvm_do(ruby, command, runas=None, cwd=None):
    return _rvm('{ruby}'.format(ruby=ruby or 'default'),
                arguments='do {command}'.format(command=command),
                runas=runas, cwd=cwd)

def is_installed(runas=None):
    '''
    Check if RVM is installed.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.is_installed
    '''
    return __salt__['cmd.has_exec'](_get_rvm_location(runas))

def install(runas=None):
    '''
    Install RVM system wide.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.install
    '''
    # RVM dependencies on Ubuntu 10.04:
    #   bash coreutils gzip bzip2 gawk sed curl git-core subversion
    installer = 'https://raw.githubusercontent.com/wayneeseguin/rvm/master/binscripts/rvm-installer'
    ret = __salt__['cmd.run_all'](
        # the RVM installer automatically does a multi-user install when it is
        # invoked with root privileges
        'curl -Ls {installer} | bash -s stable'.format(installer=installer),
        runas=runas,
        python_shell=True
    )
    if ret['retcode'] > 0:
        log.debug(
            'Error while downloading the RVM installer. Command '
            'returned: {0!r}'.format(ret)
        )
        return False
    return True

def install_ruby(ruby, runas=None):
    '''
    Install a ruby implementation.

    ruby
        The version of ruby to install.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.install_ruby 1.9.3-p385
    '''
    # MRI/RBX/REE dependencies for Ubuntu 10.04:
    #   build-essential openssl libreadline6 libreadline6-dev curl
    #   git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0
    #   libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libc6-dev
    #   libncurses5-dev automake libtool bison subversion ruby
    if runas and runas != 'root':
        _rvm('autolibs', 'disable {ruby}'.format(ruby=ruby), runas=runas)
        return _rvm('install', '--disable-binary {ruby}'.format(ruby=ruby), runas=runas)
    else:
        return _rvm('install', ruby, runas=runas)

def reinstall_ruby(ruby, runas=None):
    '''
    Reinstall a ruby implementation.

    ruby
        The version of ruby to reinstall.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.reinstall_ruby 1.9.3-p385
    '''
    return _rvm('reinstall', ruby, runas=runas)

def list_(runas=None):
    '''
    List all rvm installed rubies.

    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.list
    '''
    rubies = []
    output = _rvm('list', '', runas=runas)
    if output:
        regex = re.compile(r'^[= ]([*> ]) ([^- ]+)-([^ ]+) \[ (.*) \]')
        for line in output.splitlines():
            match = regex.match(line)
            if match:
                rubies.append([
                    match.group(2), match.group(3), match.group(1) == '*'
                ])
    return rubies

def set_default(ruby, runas=None):
    '''
    Set the default ruby.

    ruby
        The version of ruby to make the default.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.set_default 2.0.0
    '''
    return _rvm('alias',
                'create default {ruby}'.format(ruby=ruby), runas=runas)

def get(version='stable', runas=None):
    '''
    Update RVM.

    version : stable
        Which version of RVM to install, e.g. stable or head.
    ruby
        The version of ruby to reinstall.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.get
    '''
    return _rvm('get', version, runas=runas)

def wrapper(ruby_string, wrapper_prefix, runas=None, *binaries):
    '''
    Install RVM wrapper scripts.

    ruby_string
        Ruby/gemset to install wrappers for.
    wrapper_prefix
        What to prepend to the name of the generated wrapper binaries.
    runas : None
        The user to run rvm as.
    binaries : None
        The names of the binaries to create wrappers for. When nothing is
        given, wrappers for ruby, gem, rake, irb, rdoc, ri and testrb are
        generated.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.wrapper <ruby_string> <wrapper_prefix>
    '''
    return _rvm('wrapper',
                '{ruby_string} {wrapper_prefix} {binaries}'.format(
                    ruby_string=ruby_string,
                    wrapper_prefix=wrapper_prefix,
                    binaries=' '.join(binaries)
                ),
                runas=runas)

def rubygems(ruby, version, runas=None):
    '''
    Installs a specific rubygems version in the given ruby.

    ruby
        The ruby to install rubygems for.
    version
        The version of rubygems to install or 'remove' to use the version that
        ships with 1.9
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.rubygems 2.0.0 1.8.24
    '''
    return _rvm_do(ruby, 'rubygems {0}'.format(version), runas=runas)

def gemset_create(ruby, gemset, runas=None):
    '''
    Creates a gemset.

    ruby
        The ruby version to create the gemset for.
    gemset
        The name of the gemset to create.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_create 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm gemset create {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_list(ruby='default', runas=None):
    '''
    List all gemsets for the given ruby.

    ruby : default
        The ruby version to list the gemsets for
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_list
    '''
    gemsets = []
    output = _rvm_do(ruby, 'rvm gemset list', runas=runas)
    if output:
        regex = re.compile('^   ([^ ]+)')
        for line in output.splitlines():
            match = regex.match(line)
            if match:
                gemsets.append(match.group(1))
    return gemsets

def gemset_delete(ruby, gemset, runas=None):
    '''
    Deletes a gemset.

    ruby
        The ruby version the gemset belongs to.
    gemset
        The gemset to delete.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_delete 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm --force gemset delete {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_empty(ruby, gemset, runas=None):
    '''
    Remove all gems from a gemset.

    ruby
        The ruby version the gemset belongs to.
    gemset
        The gemset to empty.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_empty 2.0.0 foobar
    '''
    return _rvm_do(ruby,
                   'rvm --force gemset empty {gemset}'.format(gemset=gemset),
                   runas=runas)

def gemset_copy(source, destination, runas=None):
    '''
    Copy all gems from one gemset to another.

    source
        The name of the gemset to copy, complete with ruby version.
    destination
        The destination gemset.
    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_copy foobar bazquo
    '''
    return _rvm('gemset copy {0} {1}'.format(source, destination), runas=runas)

def gemset_list_all(runas=None):
    '''
    List all gemsets for all installed rubies.

    Note that you must have set a default ruby before this can work.

    runas : None
        The user to run rvm as.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.gemset_list_all
    '''
    gemsets = {}
    current_ruby = None
    output = _rvm_do('default', 'rvm gemset list_all', runas=runas)
    if output:
        gems_regex = re.compile('^   ([^ ]+)')
        gemset_regex = re.compile('^gemsets for ([^ ]+)')
        for line in output.splitlines():
            match = gemset_regex.match(line)
            if match:
                current_ruby = match.group(1)
                gemsets[current_ruby] = []
            match = gems_regex.match(line)
            if match:
                gemsets[current_ruby].append(match.group(1))
    return gemsets

def do(ruby, command, runas=None, cwd=None):  # pylint: disable=C0103
    '''
    Execute a command in an RVM controlled environment.

    ruby:
        The ruby to use.
    command:
        The command to execute.
    runas : None
        The user to run rvm as.
    cwd : None
        The current working directory.

    CLI Example:

    .. code-block:: bash

        salt '*' rvm.do 2.0.0 <command>
    '''
    return _rvm_do(ruby, command, runas=runas, cwd=cwd)
<|endoftext|>


<commit_before>'''
Module for running ZFS command
'''

# Import Python libs
import logging

# Regular expressions for matching output
# from 'zfs help'
import re

# Import Salt libs
import salt.utils

log = logging.getLogger(__name__)

# Function alias to set mapping.
__func_alias__ = {
    'list_': 'list'
}

@salt.utils.memoize
def _check_zfs( ):
    '''
    Looks to see if zfs is present on the system.

    Optional command check.
    '''

    # Get the path to the zfs binary.
    return salt.utils.which('zfs')

def _available_commands( ):
    '''
    List available commands based on 'zfs help'. Either
    returns a list, or False.
    '''
    zfs_path = _check_zfs( )
    if not zfs_path:
        return False

    _return = [ ]
    res = __salt__['cmd.run_all']("%s help" % zfs_path)
    for line in res['stderr'].splitlines( ):
        if re.match( "	[a-zA-Z]", line ):
            for cmd in [ cmd.strip() for cmd in line.split( " " )[0].split( "|" ) ]:
                _return.append( cmd )
    return _return

def _check_command( command ):
    '''
    Simple check to see if the command is valid.
    '''
    return command in _available_commands( )

def _exit_status(retcode):
    '''
    Translate exit status of zfs
    '''
    ret = { 0 : 'Successful completion.',
            1 : 'An error occurred.',
            2 : 'Usage error.'
          }[retcode]
    return ret

def test( ):
    '''
    Testing
    '''
    return _available_commands( )

def __virtual__():
    '''
    Makes sure that ZFS is available.
    '''
    if _check_zfs( ):
        return 'zfs'
    return False

def list_(*args):
    '''
    List all filesystems and volumes properties. If 'snapshot' given : list
    snapshots

    CLI Example::

        salt '*' zfs.list [snapshot]
    '''
    ret = {}
    zfs = _check_zfs()
    if len(args) == 1 and 'snapshot' in args:
        cmd = '{0} list -t snapshot'.format(zfs)
    else:
        cmd = '{0} list'.format(zfs)
    res = __salt__['cmd.run_all'](cmd)
    retcode = res['retcode']
    if retcode != 0:
        ret['Error'] = _exit_status(retcode)
        return ret
    ret = res['stdout'].splitlines()
    return ret
<commit_msg>Starting to dynamically generate functions<commit_after>'''
Module for running ZFS command
'''

# Import Python libs
import logging

# Regular expressions for matching output
# from 'zfs help'
import re

# Import Salt libs
import salt.utils

log = logging.getLogger(__name__)

# Function alias to set mapping.
__func_alias__ = {
    'list_': 'list'
}

@salt.utils.memoize
def _check_zfs( ):
    '''
    Looks to see if zfs is present on the system.

    Optional command check.
    '''

    # Get the path to the zfs binary.
    return salt.utils.which('zfs')

def _available_commands( ):
    '''
    List available commands based on 'zfs help'. Either
    returns a list, or False.
    '''
    zfs_path = _check_zfs( )
    if not zfs_path:
        return False

    # TODO: Supress the ERROR log generated when we read stderr..
    _return = [ ]
    res = __salt__['cmd.run_all']("%s help" % zfs_path)
    for line in res['stderr'].splitlines( ):
        if re.match( "	[a-zA-Z]", line ):
            for cmd in [ cmd.strip() for cmd in line.split( " " )[0].split( "|" ) ]:
                _return.append( cmd )
    return _return

def _check_command( command ):
    '''
    Simple check to see if the command is valid.
    '''
    return command in _available_commands( )

def _exit_status(retcode):
    '''
    Translate exit status of zfs
    '''
    ret = { 0 : 'Successful completion.',
            1 : 'An error occurred.',
            2 : 'Usage error.'
          }[retcode]
    return ret

def __virtual__():
    '''
    Makes sure that ZFS is available.
    '''
    if _check_zfs( ):
        return 'zfs'
    return False

# At this point use the helper commands to dynamically generate
# functions that are available.
for available_cmd in _available_commands( ):
    # Define a new function here based on avaiable_cmd.
    # Also update __func_alias__
    pass

def list_(*args):
    '''
    List all filesystems and volumes properties. If 'snapshot' given : list
    snapshots

    CLI Example::

        salt '*' zfs.list [snapshot]
    '''
    ret = {}
    zfs = _check_zfs()
    if len(args) == 1 and 'snapshot' in args:
        cmd = '{0} list -t snapshot'.format(zfs)
    else:
        cmd = '{0} list'.format(zfs)
    res = __salt__['cmd.run_all'](cmd)
    retcode = res['retcode']
    if retcode != 0:
        ret['Error'] = _exit_status(retcode)
        return ret
    ret = res['stdout'].splitlines()
    return ret
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Read in the roster from a flat file using the renderer system
'''

# Import python libs
import os
import fnmatch
import re

# Import Salt libs
import salt.loader
from salt.template import compile_template
from salt._compat import string_types
from salt.roster import get_roster_file

import logging
log = logging.getLogger(__name__)

def targets(tgt, tgt_type='glob', **kwargs):
    '''
    Return the targets from the flat yaml file, checks opts for location but
    defaults to /etc/salt/roster
    '''
    template = get_roster_file(__opts__)

    rend = salt.loader.render(__opts__, {})
    raw = compile_template(template, rend, __opts__['renderer'], **kwargs)
    rmatcher = RosterMatcher(raw, tgt, tgt_type, 'ipv4')
    return rmatcher.targets()

class RosterMatcher(object):
    '''
    Matcher for the roster data structure
    '''
    def __init__(self, raw, tgt, tgt_type, ipv='ipv4'):
        self.tgt = tgt
        self.tgt_type = tgt_type
        self.raw = raw
        self.ipv = ipv

    def targets(self):
        '''
        Execute the correct tgt_type routine and return
        '''
        try:
            return getattr(self, 'ret_{0}_minions'.format(self.tgt_type))()
        except AttributeError:
            return {}

    def ret_glob_minions(self):
        '''
        Return minions that match via glob
        '''
        minions = {}
        for minion in self.raw:
            if fnmatch.fnmatch(minion, self.tgt):
                data = self.get_data(minion)
                if data:
                    minions[minion] = data
        log.info('minions list: {0}'.format(minions))
        return minions

    def ret_pcre_minions(self):
        '''
        Return minions that match via pcre
        '''
        minions = {}
        for minion in self.raw:
            if re.match(self.tgt, minion):
                data = self.get_data(minion)
                if data:
                    minions[minion] = data
        return minions

    def get_data(self, minion):
        '''
        Return the configured ip
        '''
        if isinstance(self.raw[minion], string_types):
            return {'host': self.raw[minion]}
        if isinstance(self.raw[minion], dict):
            return self.raw[minion]
        return False
<commit_msg>remove unused import<commit_after># -*- coding: utf-8 -*-
'''
Read in the roster from a flat file using the renderer system
'''

# Import python libs
import fnmatch
import re

# Import Salt libs
import salt.loader
from salt.template import compile_template
from salt._compat import string_types
from salt.roster import get_roster_file

import logging
log = logging.getLogger(__name__)

def targets(tgt, tgt_type='glob', **kwargs):
    '''
    Return the targets from the flat yaml file, checks opts for location but
    defaults to /etc/salt/roster
    '''
    template = get_roster_file(__opts__)

    rend = salt.loader.render(__opts__, {})
    raw = compile_template(template, rend, __opts__['renderer'], **kwargs)
    rmatcher = RosterMatcher(raw, tgt, tgt_type, 'ipv4')
    return rmatcher.targets()

class RosterMatcher(object):
    '''
    Matcher for the roster data structure
    '''
    def __init__(self, raw, tgt, tgt_type, ipv='ipv4'):
        self.tgt = tgt
        self.tgt_type = tgt_type
        self.raw = raw
        self.ipv = ipv

    def targets(self):
        '''
        Execute the correct tgt_type routine and return
        '''
        try:
            return getattr(self, 'ret_{0}_minions'.format(self.tgt_type))()
        except AttributeError:
            return {}

    def ret_glob_minions(self):
        '''
        Return minions that match via glob
        '''
        minions = {}
        for minion in self.raw:
            if fnmatch.fnmatch(minion, self.tgt):
                data = self.get_data(minion)
                if data:
                    minions[minion] = data
        log.info('minions list: {0}'.format(minions))
        return minions

    def ret_pcre_minions(self):
        '''
        Return minions that match via pcre
        '''
        minions = {}
        for minion in self.raw:
            if re.match(self.tgt, minion):
                data = self.get_data(minion)
                if data:
                    minions[minion] = data
        return minions

    def get_data(self, minion):
        '''
        Return the configured ip
        '''
        if isinstance(self.raw[minion], string_types):
            return {'host': self.raw[minion]}
        if isinstance(self.raw[minion], dict):
            return self.raw[minion]
        return False
<|endoftext|>


<commit_before>import os
from typing import Callable, Optional, Tuple, List, Union
import matplotlib
import matplotlib.pyplot as plt
import tempfile
from PIL import Image

import compas
from .artists import PlotterArtist

class Plotter:
    """Plotter for the visualization of COMPAS geometry.

    Parameters
    ----------
    view : tuple, optional
        The area of the axes that should be zoomed into view.
        Default is ``([-10, 10], [-3, 10])``.
    figsize : tuple, optional
        The size of the figure in inches.
        Default is ``(8, 5)``

    """

    def __init__(self,
                 view: Tuple[Tuple[float, float], Tuple[float, float]] = ((-8.0, 16.0), (-5.0, 10.0)),
                 figsize: Tuple[float, float] = (8.0, 5.0),
                 dpi: float = 100,
                 bgcolor: Tuple[float, float, float] = (1.0, 1.0, 1.0),
                 show_axes: bool = False):
        self._show_axes = show_axes
        self._bgcolor = None
        self._viewbox = None
        self._axes = None
        self._artists = []
        self.viewbox = view
        self.figsize = figsize
        self.dpi = dpi
        self.bgcolor = bgcolor

    @property
    def viewbox(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
        """([xmin, xmax], [ymin, ymax]): The area of the axes that is zoomed into view."""
        return self._viewbox

    @viewbox.setter
    def viewbox(self, view: Tuple[Tuple[float, float], Tuple[float, float]]):
        xlim, ylim = view
        xmin, xmax = xlim
        ymin, ymax = ylim
        self._viewbox = (xmin, xmax), (ymin, ymax)

    @property
    def axes(self) -> matplotlib.axes.Axes:
        """Returns the axes subplot matplotlib object.

        Returns
        -------
        Axes
            The matplotlib axes object.

        Notes
        -----
        For more info, see the documentation of the Axes class ([1]_) and the
        axis and tick API ([2]_).

        References
        ----------
        .. [1] https://matplotlib.org/api/axes_api.html
        .. [2] https://matplotlib.org/api/axis_api.html

        """
        if not self._axes:
            figure = plt.figure(facecolor=self.bgcolor,
                                figsize=self.figsize,
                                dpi=self.dpi)
            axes = figure.add_subplot(111, aspect='equal')
            if self.viewbox:
                xmin, xmax = self.viewbox[0]
                ymin, ymax = self.viewbox[1]
                axes.set_xlim(xmin, xmax)
                axes.set_ylim(ymin, ymax)
            axes.set_xscale('linear')
            axes.set_yscale('linear')
            if self._show_axes:
                axes.set_frame_on(True)
                axes.grid(False)
                axes.set_xticks([])
                axes.set_yticks([])
                axes.spines['top'].set_color('none')
                axes.spines['right'].set_color('none')
                axes.spines['left'].set_position('zero')
                axes.spines['bottom'].set_position('zero')
                axes.spines['left'].set_linestyle('-')
                axes.spines['bottom'].set_linestyle('-')
            else:
                axes.grid(False)
                axes.set_frame_on(False)
                axes.set_xticks([])
                axes.set_yticks([])
            axes.autoscale_view()
            plt.tight_layout()
            self._axes = axes
        return self._axes

    @property
    def figure(self) -> matplotlib.figure.Figure:
        """Returns the matplotlib figure instance.

        Returns
        -------
        Figure
            The matplotlib figure instance.

        Notes
        -----
        For more info, see the figure API ([1]_).

        References
        ----------
        .. [1] https://matplotlib.org/2.0.2/api/figure_api.html

        """
        return self.axes.get_figure()

    @property
    def canvas(self):
        """Returns the canvas of the figure instance.
        """
        return self.figure.canvas

    @property
    def bgcolor(self) -> str:
        """Returns the background color.

        Returns
        -------
        str
            The color as a string (hex colors).

        """
        return self._bgcolor

    @bgcolor.setter
    def bgcolor(self, value: Union[str, Tuple[float, float, float]]):
        """Sets the background color.

        Parameters
        ----------
        value : str, tuple
            The color specififcation for the figure background.
            Colors should be specified in the form of a string (hex colors) or
            as a tuple of normalized RGB components.

        """
        self._bgcolor = value
        self.figure.set_facecolor(value)

    @property
    def title(self) -> str:
        """Returns the title of the plot.

        Returns
        -------
        str
            The title of the plot.

        """
        return self.figure.canvas.get_window_title()

    @title.setter
    def title(self, value: str):
        """Sets the title of the plot.

        Parameters
        ----------
        value : str
            The title of the plot.

        """
        self.figure.canvas.set_window_title(value)

    @property
    def artists(self) -> List[PlotterArtist]:
        """list of :class:`compas_plotters.artists.PlotterArtist`"""
        return self._artists

    @artists.setter
    def artists(self, artists: List[PlotterArtist]):
        self._artists = artists

    # =========================================================================
    # Methods
    # =========================================================================

    def pause(self, pause: float) -> None:
        """Pause plotting during the specified interval.

        Parameters
        ----------
        pause: float
            The duration of the pause in seconds.
        """
        if pause:
            plt.pause(pause)

    def zoom_extents(self, padding: Optional[int] = None) -> None:
        """Zoom the view to the bounding box of all objects."""
        padding = padding or 0
        width, height = self.figsize
        fig_aspect = width / height
        data = []
        for artist in self.artists:
            data += artist.data
        x, y = zip(* data)
        xmin = min(x)
        xmax = max(x)
        ymin = min(y)
        ymax = max(y)
        xspan = xmax - xmin + padding
        yspan = ymax - ymin + padding
        data_aspect = xspan / yspan
        xlim = [xmin - 0.1 * xspan, xmax + 0.1 * xspan]
        ylim = [ymin - 0.1 * yspan, ymax + 0.1 * yspan]
        if data_aspect < fig_aspect:
            scale = fig_aspect / data_aspect
            xlim[0] *= scale
            xlim[1] *= scale
        else:
            scale = data_aspect / fig_aspect
            ylim[0] *= scale
            ylim[1] *= scale
        self.viewbox = (xlim, ylim)
        self.axes.set_xlim(*xlim)
        self.axes.set_ylim(*ylim)
        self.axes.autoscale_view()

    def add(self,
            item: Union[compas.geometry.Circle,
                        compas.geometry.Ellipse,
                        compas.geometry.Line,
                        compas.geometry.Point,
                        compas.geometry.Polygon,
                        compas.geometry.Polyline,
                        compas.geometry.Vector,
                        compas.datastructures.Mesh],
            artist: Optional[PlotterArtist] = None,
            **kwargs) -> PlotterArtist:
        """Add a COMPAS geometry object or data structure to the plot.
        """
        if not artist:
            artist = PlotterArtist(item, **kwargs)
        artist.plotter = self
        artist.draw()
        self._artists.append(artist)
        return artist

    def add_as(self,
               item: Union[compas.geometry.Circle,
                           compas.geometry.Ellipse,
                           compas.geometry.Line,
                           compas.geometry.Point,
                           compas.geometry.Polygon,
                           compas.geometry.Polyline,
                           compas.geometry.Vector,
                           compas.datastructures.Mesh],
               artist_type: PlotterArtist,
               **kwargs) -> PlotterArtist:
        """Add a COMPAS geometry object or data structure using a specific artist type."""
        artist = PlotterArtist(item, artist_type=artist_type, **kwargs)
        artist.plotter = self
        artist.draw()
        self._artists.append(artist)
        return artist

    def add_from_list(self, items, **kwargs) -> List[PlotterArtist]:
        """Add multiple COMPAS geometry objects and/or data structures from a list."""
        artists = []
        for item in items:
            artist = self.add(item, **kwargs)
            artists.append(artist)
        return artists

    def find(self,
             item: Union[compas.geometry.Circle,
                         compas.geometry.Ellipse,
                         compas.geometry.Line,
                         compas.geometry.Point,
                         compas.geometry.Polygon,
                         compas.geometry.Polyline,
                         compas.geometry.Vector,
                         compas.datastructures.Mesh]) -> PlotterArtist:
        """Find a geometry object or data structure in the plot."""
        for artist in self._artists:
            if item is artist.item:
                return artist

    def register_listener(self, listener: Callable) -> None:
        """Register a listener for pick events.

        Parameters
        ----------
        listener : callable
            The handler for pick events.

        Returns
        -------
        None

        Notes
        -----
        For more information, see the docs of ``mpl_connect`` ([1]_), and on event
        handling and picking ([2]_).

        References
        ----------
        .. [1] https://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.FigureCanvasBase.mpl_connect
        .. [2] https://matplotlib.org/users/event_handling.html

        """
        self.figure.canvas.mpl_connect('pick_event', listener)

    def draw(self, pause: Optional[float] = None) -> None:
        """Draw all objects included in the plot."""
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()
        if pause:
            plt.pause(pause)

    def redraw(self, pause: Optional[float] = None) -> None:
        """Updates and pauses the plot.

        Parameters
        ----------
        pause : float
            Amount of time to pause the plot in seconds.

        """
        for artist in self._artists:
            artist.redraw()
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()
        if pause:
            plt.pause(pause)

    def show(self) -> None:
        """Displays the plot.
        """
        self.draw()
        plt.show()

    def save(self, filepath: str, **kwargs) -> None:
        """Saves the plot to a file.

        Parameters
        ----------
        filepath : str
            Full path of the file.

        Notes
        -----
        For an overview of all configuration options, see [1]_.

        References
        ----------
        .. [1] https://matplotlib.org/2.0.2/api/pyplot_api.html#matplotlib.pyplot.savefig

        """
        plt.savefig(filepath, **kwargs)

    def on(self,
           interval: int = None,
           frames: int = None,
           record: bool = False,
           recording: str = None,
           dpi: int = 150) -> Callable:
        """Method for decorating callback functions in dynamic plots."""
        if record:
            if not recording:
                raise Exception('Please provide a path for the recording.')

        def outer(func: Callable):
            if record:
                with tempfile.TemporaryDirectory() as dirpath:
                    paths = []
                    for f in range(frames):
                        func(f)
                        self.redraw(pause=interval)
                        if record:
                            filepath = os.path.join(dirpath, f'frame-{f}.png')
                            paths.append(filepath)
                            self.save(filepath, dpi=dpi)
                    images = []
                    for path in paths:
                        images.append(Image.open(path))
                    images[0].save(recording, save_all=True, append_images=images[1:], optimize=False, duration=interval * 1000, loop=0)
            else:
                for f in range(frames):
                    func(f)
                    self.redraw(pause=interval)

        return outer
<commit_msg>plotter singleton<commit_after>import os
from typing import Callable, Optional, Tuple, List, Union
import matplotlib
import matplotlib.pyplot as plt
import tempfile
from PIL import Image

import compas
from .artists import PlotterArtist

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Plotter(metaclass=Singleton):
    """Plotter for the visualization of COMPAS geometry.

    Parameters
    ----------
    view : tuple, optional
        The area of the axes that should be zoomed into view.
        Default is ``([-10, 10], [-3, 10])``.
    figsize : tuple, optional
        The size of the figure in inches.
        Default is ``(8, 5)``

    """

    def __init__(self,
                 view: Tuple[Tuple[float, float], Tuple[float, float]] = ((-8.0, 16.0), (-5.0, 10.0)),
                 figsize: Tuple[float, float] = (8.0, 5.0),
                 dpi: float = 100,
                 bgcolor: Tuple[float, float, float] = (1.0, 1.0, 1.0),
                 show_axes: bool = False):
        self._show_axes = show_axes
        self._bgcolor = None
        self._viewbox = None
        self._axes = None
        self._artists = []
        self.viewbox = view
        self.figsize = figsize
        self.dpi = dpi
        self.bgcolor = bgcolor

    @property
    def viewbox(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
        """([xmin, xmax], [ymin, ymax]): The area of the axes that is zoomed into view."""
        return self._viewbox

    @viewbox.setter
    def viewbox(self, view: Tuple[Tuple[float, float], Tuple[float, float]]):
        xlim, ylim = view
        xmin, xmax = xlim
        ymin, ymax = ylim
        self._viewbox = (xmin, xmax), (ymin, ymax)

    @property
    def axes(self) -> matplotlib.axes.Axes:
        """Returns the axes subplot matplotlib object.

        Returns
        -------
        Axes
            The matplotlib axes object.

        Notes
        -----
        For more info, see the documentation of the Axes class ([1]_) and the
        axis and tick API ([2]_).

        References
        ----------
        .. [1] https://matplotlib.org/api/axes_api.html
        .. [2] https://matplotlib.org/api/axis_api.html

        """
        if not self._axes:
            figure = plt.figure(facecolor=self.bgcolor,
                                figsize=self.figsize,
                                dpi=self.dpi)
            axes = figure.add_subplot(111, aspect='equal')
            if self.viewbox:
                xmin, xmax = self.viewbox[0]
                ymin, ymax = self.viewbox[1]
                axes.set_xlim(xmin, xmax)
                axes.set_ylim(ymin, ymax)
            axes.set_xscale('linear')
            axes.set_yscale('linear')
            if self._show_axes:
                axes.set_frame_on(True)
                axes.grid(False)
                axes.set_xticks([])
                axes.set_yticks([])
                axes.spines['top'].set_color('none')
                axes.spines['right'].set_color('none')
                axes.spines['left'].set_position('zero')
                axes.spines['bottom'].set_position('zero')
                axes.spines['left'].set_linestyle('-')
                axes.spines['bottom'].set_linestyle('-')
            else:
                axes.grid(False)
                axes.set_frame_on(False)
                axes.set_xticks([])
                axes.set_yticks([])
            axes.autoscale_view()
            plt.tight_layout()
            self._axes = axes
        return self._axes

    @property
    def figure(self) -> matplotlib.figure.Figure:
        """Returns the matplotlib figure instance.

        Returns
        -------
        Figure
            The matplotlib figure instance.

        Notes
        -----
        For more info, see the figure API ([1]_).

        References
        ----------
        .. [1] https://matplotlib.org/2.0.2/api/figure_api.html

        """
        return self.axes.get_figure()

    @property
    def canvas(self):
        """Returns the canvas of the figure instance.
        """
        return self.figure.canvas

    @property
    def bgcolor(self) -> str:
        """Returns the background color.

        Returns
        -------
        str
            The color as a string (hex colors).

        """
        return self._bgcolor

    @bgcolor.setter
    def bgcolor(self, value: Union[str, Tuple[float, float, float]]):
        """Sets the background color.

        Parameters
        ----------
        value : str, tuple
            The color specififcation for the figure background.
            Colors should be specified in the form of a string (hex colors) or
            as a tuple of normalized RGB components.

        """
        self._bgcolor = value
        self.figure.set_facecolor(value)

    @property
    def title(self) -> str:
        """Returns the title of the plot.

        Returns
        -------
        str
            The title of the plot.

        """
        return self.figure.canvas.get_window_title()

    @title.setter
    def title(self, value: str):
        """Sets the title of the plot.

        Parameters
        ----------
        value : str
            The title of the plot.

        """
        self.figure.canvas.set_window_title(value)

    @property
    def artists(self) -> List[PlotterArtist]:
        """list of :class:`compas_plotters.artists.PlotterArtist`"""
        return self._artists

    @artists.setter
    def artists(self, artists: List[PlotterArtist]):
        self._artists = artists

    # =========================================================================
    # Methods
    # =========================================================================

    def pause(self, pause: float) -> None:
        """Pause plotting during the specified interval.

        Parameters
        ----------
        pause: float
            The duration of the pause in seconds.
        """
        if pause:
            plt.pause(pause)

    def zoom_extents(self, padding: Optional[int] = None) -> None:
        """Zoom the view to the bounding box of all objects."""
        padding = padding or 0
        width, height = self.figsize
        fig_aspect = width / height
        data = []
        print(self.artists)
        for artist in self.artists:
            data += artist.data
        x, y = zip(* data)
        xmin = min(x)
        xmax = max(x)
        ymin = min(y)
        ymax = max(y)
        xspan = xmax - xmin + padding
        yspan = ymax - ymin + padding
        data_aspect = xspan / yspan
        xlim = [xmin - 0.1 * xspan, xmax + 0.1 * xspan]
        ylim = [ymin - 0.1 * yspan, ymax + 0.1 * yspan]
        if data_aspect < fig_aspect:
            scale = fig_aspect / data_aspect
            xlim[0] *= scale
            xlim[1] *= scale
        else:
            scale = data_aspect / fig_aspect
            ylim[0] *= scale
            ylim[1] *= scale
        self.viewbox = (xlim, ylim)
        self.axes.set_xlim(*xlim)
        self.axes.set_ylim(*ylim)
        self.axes.autoscale_view()

    def add(self,
            item: Union[compas.geometry.Circle,
                        compas.geometry.Ellipse,
                        compas.geometry.Line,
                        compas.geometry.Point,
                        compas.geometry.Polygon,
                        compas.geometry.Polyline,
                        compas.geometry.Vector,
                        compas.datastructures.Mesh],
            artist: Optional[PlotterArtist] = None,
            **kwargs) -> PlotterArtist:
        """Add a COMPAS geometry object or data structure to the plot.
        """
        if not artist:
            artist = PlotterArtist(item, **kwargs)
        artist.draw()
        self._artists.append(artist)
        return artist

    def add_as(self,
               item: Union[compas.geometry.Circle,
                           compas.geometry.Ellipse,
                           compas.geometry.Line,
                           compas.geometry.Point,
                           compas.geometry.Polygon,
                           compas.geometry.Polyline,
                           compas.geometry.Vector,
                           compas.datastructures.Mesh],
               artist_type: PlotterArtist,
               **kwargs) -> PlotterArtist:
        """Add a COMPAS geometry object or data structure using a specific artist type."""
        artist = PlotterArtist(item, artist_type=artist_type, **kwargs)
        artist.draw()
        self._artists.append(artist)
        return artist

    def add_from_list(self, items, **kwargs) -> List[PlotterArtist]:
        """Add multiple COMPAS geometry objects and/or data structures from a list."""
        artists = []
        for item in items:
            artist = self.add(item, **kwargs)
            artists.append(artist)
        return artists

    def find(self,
             item: Union[compas.geometry.Circle,
                         compas.geometry.Ellipse,
                         compas.geometry.Line,
                         compas.geometry.Point,
                         compas.geometry.Polygon,
                         compas.geometry.Polyline,
                         compas.geometry.Vector,
                         compas.datastructures.Mesh]) -> PlotterArtist:
        """Find a geometry object or data structure in the plot."""
        for artist in self._artists:
            if item is artist.item:
                return artist

    def register_listener(self, listener: Callable) -> None:
        """Register a listener for pick events.

        Parameters
        ----------
        listener : callable
            The handler for pick events.

        Returns
        -------
        None

        Notes
        -----
        For more information, see the docs of ``mpl_connect`` ([1]_), and on event
        handling and picking ([2]_).

        References
        ----------
        .. [1] https://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.FigureCanvasBase.mpl_connect
        .. [2] https://matplotlib.org/users/event_handling.html

        """
        self.figure.canvas.mpl_connect('pick_event', listener)

    def draw(self, pause: Optional[float] = None) -> None:
        """Draw all objects included in the plot."""
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()
        if pause:
            plt.pause(pause)

    def redraw(self, pause: Optional[float] = None) -> None:
        """Updates and pauses the plot.

        Parameters
        ----------
        pause : float
            Amount of time to pause the plot in seconds.

        """
        for artist in self._artists:
            artist.redraw()
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()
        if pause:
            plt.pause(pause)

    def show(self) -> None:
        """Displays the plot.
        """
        self.draw()
        plt.show()

    def save(self, filepath: str, **kwargs) -> None:
        """Saves the plot to a file.

        Parameters
        ----------
        filepath : str
            Full path of the file.

        Notes
        -----
        For an overview of all configuration options, see [1]_.

        References
        ----------
        .. [1] https://matplotlib.org/2.0.2/api/pyplot_api.html#matplotlib.pyplot.savefig

        """
        plt.savefig(filepath, **kwargs)

    def on(self,
           interval: int = None,
           frames: int = None,
           record: bool = False,
           recording: str = None,
           dpi: int = 150) -> Callable:
        """Method for decorating callback functions in dynamic plots."""
        if record:
            if not recording:
                raise Exception('Please provide a path for the recording.')

        def outer(func: Callable):
            if record:
                with tempfile.TemporaryDirectory() as dirpath:
                    paths = []
                    for f in range(frames):
                        func(f)
                        self.redraw(pause=interval)
                        if record:
                            filepath = os.path.join(dirpath, f'frame-{f}.png')
                            paths.append(filepath)
                            self.save(filepath, dpi=dpi)
                    images = []
                    for path in paths:
                        images.append(Image.open(path))
                    images[0].save(recording, save_all=True, append_images=images[1:], optimize=False, duration=interval * 1000, loop=0)
            else:
                for f in range(frames):
                    func(f)
                    self.redraw(pause=interval)

        return outer
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Test States
==================

Provide test case states that enable easy testing of things to do with
 state calls, e.g. running, calling, logging, output filtering etc.

.. code-block:: yaml

    always-passes:
      test.succeed_without_changes:
        - name: foo

    always-fails:
      test.fail_without_changes:
        - name: foo

    always-changes-and-succeeds:
      test.succeed_with_changes:
        - name: foo

    always-changes-and-fails:
      test.fail_with_changes:
        - name: foo

    my-custom-combo:
      test.configurable_test_state:
        - name: foo
        - changes: True
        - result: False
        - comment: bar.baz

'''

# Import Python libs
import logging
import random
from salt.exceptions import SaltInvocationError

log = logging.getLogger(__name__)

def succeed_without_changes(name):
    '''
    Returns successful.

    .. versionadded:: Helium

    name
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': 'Success!'
    }
    if __opts__['test']:
        ret['result'] = True
        ret['comment'] = 'If we weren\'t testing, this would be a success!'
    return ret

def fail_without_changes(name):
    '''
    Returns failure.

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': 'Failure!'
    }

    if __opts__['test']:
        ret['result'] = False
        ret['comment'] = 'If we weren\'t testing, this would be a failure!'

    return ret

def succeed_with_changes(name):
    '''
    Returns successful and changes is not empty

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': 'Success!'
    }

    # Following the docs as written here
    # http://docs.saltstack.com/ref/states/writing.html#return-data
    ret['changes'] = {
        'testing': {
            'old': 'Unchanged',
            'new': 'Something pretended to change'
        }
    }

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = ('If we weren\'t testing, this would be successful '
                          'with changes')

    return ret

def fail_with_changes(name):
    '''
    Returns failure and changes is not empty.

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': 'Failure!'
    }

    # Following the docs as written here
    # http://docs.saltstack.com/ref/states/writing.html#return-data
    ret['changes'] = {
        'testing': {
            'old': 'Unchanged',
            'new': 'Something pretended to change'
        }
    }

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = ('If we weren\'t testing, this would be failed with '
                          'changes')

    return ret

def configurable_test_state(name, changes=True, result=True, comment=''):
    '''
    A configurable test state which determines its output based on the inputs.

    .. versionadded:: Helium

    name:
        A unique string.
    changes:
        Do we return anything in the changes field?
        Accepts True, False, and 'Random'
        Default is True
    result:
        Do we return sucessfuly or not?
        Accepts True, False, and 'Random'
        Default is True
    comment:
        String to fill the comment field with.
        Default is ''
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': comment
    }

    # E8712 is disabled because this code is a LOT cleaner if we allow it.
    if changes == "Random":
        if random.choice([True, False]):
            # Following the docs as written here
            # http://docs.saltstack.com/ref/states/writing.html#return-data
            ret['changes'] = {
                'testing': {
                    'old': 'Unchanged',
                    'new': 'Something pretended to change'
                }
            }
    elif changes == True:  # pylint: disable=E8712
        # If changes is True we place our dummy change dictionary into it.
        # Following the docs as written here
        # http://docs.saltstack.com/ref/states/writing.html#return-data
        ret['changes'] = {
            'testing': {
                'old': 'Unchanged',
                'new': 'Something pretended to change'
            }
        }
    elif changes == False:  # pylint: disable=E8712
        ret['changes'] = {}
    else:
        err = ('You have specified the state option \'Changes\' with'
            ' invalid arguments. It must be either '
            ' \'True\', \'False\', or \'Random\'')
        raise SaltInvocationError(err)

    if result == 'Random':
        # since result is a boolean, if its random we just set it here,
        ret['result'] = random.choice([True, False])
    elif result == True:  # pylint: disable=E8712
        ret['result'] = True
    elif result == False:  # pylint: disable=E8712
        ret['result'] = False
    else:
        raise SaltInvocationError('You have specified the state option '
                                  '\'Result\' with invalid arguments. It must '
                                  'be either \'True\', \'False\', or '
                                  '\'Random\'')

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = 'This is a test'

    return ret
<commit_msg>Remove references to pylint E8712<commit_after># -*- coding: utf-8 -*-
'''
Test States
==================

Provide test case states that enable easy testing of things to do with
 state calls, e.g. running, calling, logging, output filtering etc.

.. code-block:: yaml

    always-passes:
      test.succeed_without_changes:
        - name: foo

    always-fails:
      test.fail_without_changes:
        - name: foo

    always-changes-and-succeeds:
      test.succeed_with_changes:
        - name: foo

    always-changes-and-fails:
      test.fail_with_changes:
        - name: foo

    my-custom-combo:
      test.configurable_test_state:
        - name: foo
        - changes: True
        - result: False
        - comment: bar.baz

'''

# Import Python libs
import logging
import random
from salt.exceptions import SaltInvocationError

log = logging.getLogger(__name__)

def succeed_without_changes(name):
    '''
    Returns successful.

    .. versionadded:: Helium

    name
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': 'Success!'
    }
    if __opts__['test']:
        ret['result'] = True
        ret['comment'] = 'If we weren\'t testing, this would be a success!'
    return ret

def fail_without_changes(name):
    '''
    Returns failure.

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': 'Failure!'
    }

    if __opts__['test']:
        ret['result'] = False
        ret['comment'] = 'If we weren\'t testing, this would be a failure!'

    return ret

def succeed_with_changes(name):
    '''
    Returns successful and changes is not empty

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': 'Success!'
    }

    # Following the docs as written here
    # http://docs.saltstack.com/ref/states/writing.html#return-data
    ret['changes'] = {
        'testing': {
            'old': 'Unchanged',
            'new': 'Something pretended to change'
        }
    }

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = ('If we weren\'t testing, this would be successful '
                          'with changes')

    return ret

def fail_with_changes(name):
    '''
    Returns failure and changes is not empty.

    .. versionadded:: Helium

    name:
        A unique string.
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': 'Failure!'
    }

    # Following the docs as written here
    # http://docs.saltstack.com/ref/states/writing.html#return-data
    ret['changes'] = {
        'testing': {
            'old': 'Unchanged',
            'new': 'Something pretended to change'
        }
    }

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = ('If we weren\'t testing, this would be failed with '
                          'changes')

    return ret

def configurable_test_state(name, changes=True, result=True, comment=''):
    '''
    A configurable test state which determines its output based on the inputs.

    .. versionadded:: Helium

    name:
        A unique string.
    changes:
        Do we return anything in the changes field?
        Accepts True, False, and 'Random'
        Default is True
    result:
        Do we return sucessfuly or not?
        Accepts True, False, and 'Random'
        Default is True
    comment:
        String to fill the comment field with.
        Default is ''
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': False,
        'comment': comment
    }

    if changes == 'Random':
        if random.choice([True, False]):
            # Following the docs as written here
            # http://docs.saltstack.com/ref/states/writing.html#return-data
            ret['changes'] = {
                'testing': {
                    'old': 'Unchanged',
                    'new': 'Something pretended to change'
                }
            }
    elif changes is True:
        # If changes is True we place our dummy change dictionary into it.
        # Following the docs as written here
        # http://docs.saltstack.com/ref/states/writing.html#return-data
        ret['changes'] = {
            'testing': {
                'old': 'Unchanged',
                'new': 'Something pretended to change'
            }
        }
    elif changes is False:
        ret['changes'] = {}
    else:
        err = ('You have specified the state option \'Changes\' with'
            ' invalid arguments. It must be either '
            ' \'True\', \'False\', or \'Random\'')
        raise SaltInvocationError(err)

    if result == 'Random':
        # since result is a boolean, if its random we just set it here,
        ret['result'] = random.choice([True, False])
    elif result is True:
        ret['result'] = True
    elif result is False:
        ret['result'] = False
    else:
        raise SaltInvocationError('You have specified the state option '
                                  '\'Result\' with invalid arguments. It must '
                                  'be either \'True\', \'False\', or '
                                  '\'Random\'')

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = 'This is a test'

    return ret
<|endoftext|>